2008-11-04 01:14:25 +00:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Text;
|
|
|
|
|
|
using System.IO;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Collections.Specialized;
|
2008-11-28 22:20:17 +00:00
|
|
|
|
using CUETools.Codecs;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
|
2008-11-18 14:48:26 +00:00
|
|
|
|
//Copyright (c) 2008 Gregory S. Chudov.
|
2008-11-04 01:14:25 +00:00
|
|
|
|
//This library is based on ALAC decoder by David Hammerton.
|
|
|
|
|
|
//See http://crazney.net/programs/itunes/alac.html for details.
|
|
|
|
|
|
//Copyright (c) 2004 David Hammerton.
|
|
|
|
|
|
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
|
|
|
|
// in the Software without restriction, including without limitation the rights
|
|
|
|
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
|
|
// copies of the Software, and to permit persons to whom the Software is furnished
|
|
|
|
|
|
// to do so, subject to the following conditions:
|
|
|
|
|
|
//The above copyright notice and this permission notice shall be included in all
|
|
|
|
|
|
//copies or substantial portions of the Software.
|
|
|
|
|
|
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
|
|
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
|
|
|
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
|
|
|
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
|
|
|
|
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
|
|
|
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
|
|
|
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
|
|
|
2008-11-28 22:20:17 +00:00
|
|
|
|
namespace CUETools.Codecs.ALAC
|
2008-11-04 01:14:25 +00:00
|
|
|
|
{
|
2010-02-06 23:17:07 +00:00
|
|
|
|
[AudioDecoderClass("builtin alac", "m4a")]
|
2008-11-04 01:14:25 +00:00
|
|
|
|
public class ALACReader : IAudioSource
|
|
|
|
|
|
{
|
2008-11-10 10:31:35 +00:00
|
|
|
|
public ALACReader(string path, Stream IO)
|
2008-11-04 01:14:25 +00:00
|
|
|
|
{
|
|
|
|
|
|
_path = path;
|
2008-11-10 10:31:35 +00:00
|
|
|
|
_IO = IO != null ? IO : new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
|
2008-11-05 13:15:33 +00:00
|
|
|
|
_buff = new byte[512];
|
2008-11-04 22:00:29 +00:00
|
|
|
|
_tags = new NameValueCollection();
|
2008-11-04 01:14:25 +00:00
|
|
|
|
qtmovie_read();
|
2010-02-06 23:17:07 +00:00
|
|
|
|
if (!_formatRead || pcm.BitsPerSample != 16 || pcm.ChannelCount != 2 || pcm.SampleRate != 44100)
|
2008-11-04 01:14:25 +00:00
|
|
|
|
throw new Exception("Invalid ALAC file.");
|
2008-11-10 10:31:35 +00:00
|
|
|
|
_saved_mdat_pos = _IO.Position;
|
2009-03-04 21:30:56 +00:00
|
|
|
|
calculate_length();
|
2008-11-04 01:14:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2010-02-06 23:17:07 +00:00
|
|
|
|
public ALACReader(AudioPCMConfig _pcm, int rice_historymult, int rice_initialhistory, int rice_kmodifier, int blocksize)
|
2009-08-30 21:58:54 +00:00
|
|
|
|
{
|
2010-02-06 23:17:07 +00:00
|
|
|
|
pcm = _pcm;
|
2009-08-30 21:58:54 +00:00
|
|
|
|
|
2010-02-06 23:17:07 +00:00
|
|
|
|
setinfo_max_samples_per_frame = blocksize;
|
2009-08-30 21:58:54 +00:00
|
|
|
|
setinfo_rice_historymult = (byte)rice_historymult;
|
|
|
|
|
|
setinfo_rice_initialhistory = (byte)rice_initialhistory;
|
|
|
|
|
|
setinfo_rice_kmodifier = (byte)rice_kmodifier;
|
|
|
|
|
|
|
|
|
|
|
|
_predicterror_buffer_a = new int[setinfo_max_samples_per_frame];
|
|
|
|
|
|
_predicterror_buffer_b = new int[setinfo_max_samples_per_frame];
|
|
|
|
|
|
_outputsamples_buffer_a = new int[setinfo_max_samples_per_frame];
|
|
|
|
|
|
_outputsamples_buffer_b = new int[setinfo_max_samples_per_frame];
|
|
|
|
|
|
_framesBuffer = new byte[65536];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2009-05-01 15:16:26 +00:00
|
|
|
|
private void InitTables()
|
2008-11-04 01:14:25 +00:00
|
|
|
|
{
|
2009-05-01 15:16:26 +00:00
|
|
|
|
if (_predicterror_buffer_a != null)
|
|
|
|
|
|
return;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
|
2010-02-06 23:17:07 +00:00
|
|
|
|
setinfo_max_samples_per_frame = (int)read_uint32(_codecData, 24);
|
2009-05-01 15:16:26 +00:00
|
|
|
|
byte setinfo_7a = read_uint8(_codecData, 28);
|
|
|
|
|
|
byte setinfo_sample_size = read_uint8(_codecData, 29);
|
|
|
|
|
|
setinfo_rice_historymult = read_uint8(_codecData, 30);
|
|
|
|
|
|
setinfo_rice_initialhistory = read_uint8(_codecData, 31);
|
|
|
|
|
|
setinfo_rice_kmodifier = read_uint8(_codecData, 32);
|
|
|
|
|
|
byte setinfo_7f = read_uint8(_codecData, 33);
|
|
|
|
|
|
ushort setinfo_80 = read_uint16(_codecData, 34);
|
|
|
|
|
|
uint setinfo_82 = read_uint32(_codecData, 36); // maxframesize
|
|
|
|
|
|
uint setinfo_86 = read_uint32(_codecData, 40); // bitrate
|
|
|
|
|
|
uint setinfo_8a_rate = read_uint32(_codecData, 44); // samplerate
|
|
|
|
|
|
|
|
|
|
|
|
_predicterror_buffer_a = new int[setinfo_max_samples_per_frame];
|
|
|
|
|
|
_predicterror_buffer_b = new int[setinfo_max_samples_per_frame];
|
|
|
|
|
|
_outputsamples_buffer_a = new int[setinfo_max_samples_per_frame];
|
|
|
|
|
|
_outputsamples_buffer_b = new int[setinfo_max_samples_per_frame];
|
|
|
|
|
|
|
|
|
|
|
|
_samplesInBuffer = 0;
|
|
|
|
|
|
_framesBuffer = new byte[65536];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-02-06 23:17:07 +00:00
|
|
|
|
public int Read(AudioBuffer buff, int maxLength)
|
2009-05-01 15:16:26 +00:00
|
|
|
|
{
|
|
|
|
|
|
InitTables();
|
2008-11-04 01:14:25 +00:00
|
|
|
|
|
2010-02-06 23:17:07 +00:00
|
|
|
|
buff.Prepare(this, maxLength);
|
|
|
|
|
|
|
|
|
|
|
|
int offset = 0;
|
|
|
|
|
|
int sampleCount = buff.Length;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
|
|
|
|
|
|
while (_samplesInBuffer < sampleCount)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_samplesInBuffer > 0)
|
|
|
|
|
|
{
|
2010-02-06 23:17:07 +00:00
|
|
|
|
deinterlace(buff.Samples, offset, _samplesInBuffer);
|
|
|
|
|
|
sampleCount -= _samplesInBuffer;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
offset += _samplesInBuffer;
|
|
|
|
|
|
_samplesInBuffer = 0;
|
|
|
|
|
|
_samplesBufferOffset = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-02-06 23:17:07 +00:00
|
|
|
|
int sampleDuration;
|
|
|
|
|
|
int sampleSize;
|
|
|
|
|
|
if (_iSample >= _sample_byte_size.Length)
|
|
|
|
|
|
return offset;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
get_sample_info(_iSample, out sampleDuration, out sampleSize);
|
2010-02-06 23:17:07 +00:00
|
|
|
|
_IO.Read(_framesBuffer, 0, sampleSize);
|
2009-08-30 21:58:54 +00:00
|
|
|
|
decodeFrame(sampleSize);
|
2008-11-04 01:14:25 +00:00
|
|
|
|
if (sampleDuration != _samplesInBuffer)
|
|
|
|
|
|
throw new Exception("sample count mismatch");
|
|
|
|
|
|
_samplesInBuffer -= _samplesBufferOffset;
|
|
|
|
|
|
_sampleOffset += _samplesInBuffer;
|
|
|
|
|
|
_iSample++;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-02-06 23:17:07 +00:00
|
|
|
|
deinterlace(buff.Samples, offset, sampleCount);
|
2008-11-04 01:14:25 +00:00
|
|
|
|
_samplesInBuffer -= sampleCount;
|
|
|
|
|
|
_samplesBufferOffset += sampleCount;
|
|
|
|
|
|
if (_samplesInBuffer == 0)
|
|
|
|
|
|
_samplesBufferOffset = 0;
|
2010-02-06 23:17:07 +00:00
|
|
|
|
return offset + sampleCount;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void Close()
|
|
|
|
|
|
{
|
2008-11-10 10:31:35 +00:00
|
|
|
|
_IO.Close();
|
2008-11-04 01:14:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2010-02-06 23:17:07 +00:00
|
|
|
|
public long Length
|
2008-11-04 01:14:25 +00:00
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
return _sampleCount;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-02-06 23:17:07 +00:00
|
|
|
|
public long Remaining
|
2008-11-04 01:14:25 +00:00
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
return _sampleCount - _sampleOffset + _samplesInBuffer;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-02-06 23:17:07 +00:00
|
|
|
|
public long Position
|
2008-11-04 01:14:25 +00:00
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
return _sampleOffset - _samplesInBuffer;
|
|
|
|
|
|
}
|
|
|
|
|
|
set
|
|
|
|
|
|
{
|
|
|
|
|
|
_sampleOffset = value;
|
|
|
|
|
|
_samplesInBuffer = 0;
|
|
|
|
|
|
_samplesBufferOffset = 0;
|
|
|
|
|
|
|
|
|
|
|
|
_iSample = 0;
|
2010-02-06 23:17:07 +00:00
|
|
|
|
long durOffs = 0;
|
|
|
|
|
|
int sampleDuration;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
long fileOffs = 0;
|
2010-02-06 23:17:07 +00:00
|
|
|
|
int sampleSize;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
do
|
|
|
|
|
|
{
|
|
|
|
|
|
if (durOffs == value)
|
|
|
|
|
|
{
|
2008-11-10 10:31:35 +00:00
|
|
|
|
_IO.Position = _saved_mdat_pos + fileOffs;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2009-08-30 21:58:54 +00:00
|
|
|
|
if ((int)_iSample >= _sample_byte_size.Length)
|
|
|
|
|
|
throw new Exception("seeking past end of stream");
|
2008-11-04 01:14:25 +00:00
|
|
|
|
get_sample_info(_iSample, out sampleDuration, out sampleSize);
|
|
|
|
|
|
durOffs += sampleDuration;
|
|
|
|
|
|
fileOffs += sampleSize;
|
|
|
|
|
|
_iSample++;
|
|
|
|
|
|
} while (durOffs <= value);
|
2008-11-10 10:31:35 +00:00
|
|
|
|
_IO.Position = _saved_mdat_pos + fileOffs - sampleSize;
|
2010-02-06 23:17:07 +00:00
|
|
|
|
_samplesBufferOffset = (int) (value + sampleDuration - durOffs);
|
2008-11-04 01:14:25 +00:00
|
|
|
|
_iSample--;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-02-06 23:17:07 +00:00
|
|
|
|
public AudioPCMConfig PCM { get { return pcm; } }
|
2008-12-01 20:24:16 +00:00
|
|
|
|
|
2008-11-04 01:14:25 +00:00
|
|
|
|
public string Path
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
return _path;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-02-06 23:17:07 +00:00
|
|
|
|
private void get_sample_info(long iSample, out int sampleDuration, out int sampleSize)
|
2008-11-04 01:14:25 +00:00
|
|
|
|
{
|
|
|
|
|
|
// if (iSample >= _sample_byte_size.Length)
|
2010-02-06 23:17:07 +00:00
|
|
|
|
int duration_index_accum = 0;
|
|
|
|
|
|
int duration_cur_index = 0;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
while (_time_to_sample_count[duration_cur_index] + duration_index_accum <= iSample)
|
|
|
|
|
|
{
|
|
|
|
|
|
duration_index_accum += _time_to_sample_count[duration_cur_index];
|
2009-08-30 21:58:54 +00:00
|
|
|
|
if (duration_cur_index == _time_to_sample_count.Length - 1)
|
|
|
|
|
|
throw new Exception("seeking past end of stream");
|
2008-11-04 01:14:25 +00:00
|
|
|
|
duration_cur_index ++;
|
|
|
|
|
|
}
|
|
|
|
|
|
sampleDuration = _time_to_sample_duration[duration_cur_index];
|
|
|
|
|
|
sampleSize = _sample_byte_size[iSample];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2009-03-04 21:30:56 +00:00
|
|
|
|
private void calculate_length()
|
|
|
|
|
|
{
|
|
|
|
|
|
_sampleCount = 0;
|
|
|
|
|
|
uint duration_cur_index = 0;
|
|
|
|
|
|
for (duration_cur_index = 0; duration_cur_index < _time_to_sample_count.Length; duration_cur_index++)
|
|
|
|
|
|
_sampleCount += _time_to_sample_count[duration_cur_index] * _time_to_sample_duration[duration_cur_index];
|
2009-05-01 15:16:26 +00:00
|
|
|
|
// try a work around for ffdshow-generated buggy files
|
|
|
|
|
|
if (_time_to_sample_count.Length == 1 && _IO.CanSeek)
|
|
|
|
|
|
{
|
2010-02-06 23:17:07 +00:00
|
|
|
|
int sample_count_0 = _time_to_sample_count[0] - 1;
|
|
|
|
|
|
int sample_duration_0 = _time_to_sample_duration[0];
|
2009-05-01 15:16:26 +00:00
|
|
|
|
Position = sample_count_0 * sample_duration_0;
|
2010-02-06 23:17:07 +00:00
|
|
|
|
int sampleDuration;
|
|
|
|
|
|
int sampleSize;
|
2009-05-01 15:16:26 +00:00
|
|
|
|
if ((int)_iSample < _sample_byte_size.Length)
|
|
|
|
|
|
{
|
|
|
|
|
|
get_sample_info(_iSample, out sampleDuration, out sampleSize);
|
|
|
|
|
|
InitTables();
|
|
|
|
|
|
_IO.Read(_framesBuffer, 0, (int)sampleSize);
|
2009-08-30 21:58:54 +00:00
|
|
|
|
decodeFrame(sampleSize);
|
2009-05-01 15:16:26 +00:00
|
|
|
|
if (_samplesInBuffer < sampleDuration)
|
|
|
|
|
|
{
|
2010-02-06 23:17:07 +00:00
|
|
|
|
_time_to_sample_duration = new int[2] { sample_duration_0, _samplesInBuffer };
|
|
|
|
|
|
_time_to_sample_count = new int[2] { sample_count_0, 1 };
|
2009-05-01 15:16:26 +00:00
|
|
|
|
_sampleCount -= sampleDuration - _samplesInBuffer;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
Position = 0;
|
|
|
|
|
|
}
|
2009-03-04 21:30:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2008-11-05 13:15:33 +00:00
|
|
|
|
private byte [] stream_read_bytes(int len)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (len > 512)
|
|
|
|
|
|
throw new Exception("Decoding failed.");
|
2008-11-10 10:31:35 +00:00
|
|
|
|
if (_IO.Read(_buff, 0, len) != len)
|
2008-11-05 13:15:33 +00:00
|
|
|
|
throw new Exception("Decoding failed.");
|
|
|
|
|
|
return _buff;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2008-11-04 01:14:25 +00:00
|
|
|
|
private uint read_uint32(byte [] buff, int pos)
|
|
|
|
|
|
{
|
|
|
|
|
|
return (uint)((buff[pos] << 24) + (buff[pos+1] << 16) + (buff[pos+2] << 8) + (buff[pos+3] << 0));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private uint stream_read_uint32()
|
|
|
|
|
|
{
|
2008-11-05 13:15:33 +00:00
|
|
|
|
return read_uint32(stream_read_bytes(4), 0);
|
2008-11-04 01:14:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private ushort read_uint16(byte[] buff, int pos)
|
|
|
|
|
|
{
|
|
|
|
|
|
return (ushort)((buff[pos] << 8) + buff[pos + 1]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private ushort stream_read_uint16()
|
|
|
|
|
|
{
|
2008-11-05 13:15:33 +00:00
|
|
|
|
return read_uint16(stream_read_bytes(2), 0);
|
2008-11-04 01:14:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private byte read_uint8(byte[] buff, int pos)
|
|
|
|
|
|
{
|
|
|
|
|
|
return buff[pos];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private byte stream_read_uint8()
|
|
|
|
|
|
{
|
2008-11-05 13:15:33 +00:00
|
|
|
|
return stream_read_bytes(1)[0];
|
2008-11-04 01:14:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void stream_skip (UInt32 skip)
|
|
|
|
|
|
{
|
2008-11-10 10:31:35 +00:00
|
|
|
|
_IO.Position += skip;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* supports reading 1 to 24 bits, in big endian format */
|
|
|
|
|
|
private uint readbits_24 (byte []buff, ref int pos, int bits)
|
|
|
|
|
|
{
|
|
|
|
|
|
uint result = (((uint)buff[pos]) << 24) | (((uint)buff[pos + 1]) << 16) | (((uint)buff[pos + 2]) << 8) | ((uint)buff[pos + 3]);
|
|
|
|
|
|
result <<= _bitaccumulator;
|
|
|
|
|
|
result >>= 32 - bits;
|
|
|
|
|
|
|
|
|
|
|
|
int new_accumulator = (_bitaccumulator + bits);
|
|
|
|
|
|
pos += (new_accumulator >> 3);
|
|
|
|
|
|
_bitaccumulator = (new_accumulator & 7);
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* supports reading 1 to 24 bits, in big endian format */
|
|
|
|
|
|
private unsafe uint readbits_24(byte* buff, ref int pos, int bits)
|
|
|
|
|
|
{
|
|
|
|
|
|
uint result = (((uint)buff[pos]) << 24) | (((uint)buff[pos + 1]) << 16) | (((uint)buff[pos + 2]) << 8) | ((uint)buff[pos + 3]);
|
|
|
|
|
|
result <<= _bitaccumulator;
|
|
|
|
|
|
result >>= 32 - bits;
|
|
|
|
|
|
|
|
|
|
|
|
int new_accumulator = (_bitaccumulator + bits);
|
|
|
|
|
|
pos += (new_accumulator >> 3);
|
|
|
|
|
|
_bitaccumulator = (new_accumulator & 7);
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* supports reading 1 to 16 bits, in big endian format */
|
2008-11-05 21:01:12 +00:00
|
|
|
|
private unsafe uint peekbits_9(byte* buff, int pos)
|
2008-11-04 01:14:25 +00:00
|
|
|
|
{
|
2008-11-05 21:01:12 +00:00
|
|
|
|
uint result = (((uint)buff[pos]) << 8) | (((uint)buff[pos + 1]));
|
2008-11-04 01:14:25 +00:00
|
|
|
|
result <<= _bitaccumulator;
|
2008-11-05 21:01:12 +00:00
|
|
|
|
result &= 0x0000ffff;
|
|
|
|
|
|
result >>= 7;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* supports reading 1 to 16 bits, in big endian format */
|
|
|
|
|
|
private void skipbits(ref int pos, int bits)
|
|
|
|
|
|
{
|
|
|
|
|
|
int new_accumulator = (_bitaccumulator + bits);
|
|
|
|
|
|
pos += (new_accumulator >> 3);
|
|
|
|
|
|
_bitaccumulator = (new_accumulator & 7);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* supports reading 1 to 32 bits, in big endian format */
|
|
|
|
|
|
private uint readbits(byte[] buff, ref int pos, int bits)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (bits <= 24)
|
|
|
|
|
|
return readbits_24(buff, ref pos, bits);
|
|
|
|
|
|
|
|
|
|
|
|
ulong result = (((ulong)buff[pos]) << 32) | (((ulong)buff[pos + 1]) << 24) | (((ulong)buff[pos + 2]) << 16) | (((ulong)buff[pos + 3]) << 8) | ((ulong)buff[pos + 4]);
|
|
|
|
|
|
result <<= _bitaccumulator;
|
|
|
|
|
|
result &= 0x00ffffffffff;
|
|
|
|
|
|
result >>= 40 - bits;
|
|
|
|
|
|
int new_accumulator = (_bitaccumulator + bits);
|
|
|
|
|
|
pos += (new_accumulator >> 3);
|
|
|
|
|
|
_bitaccumulator = (new_accumulator & 7);
|
|
|
|
|
|
return (uint)result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* supports reading 1 to 32 bits, in big endian format */
|
|
|
|
|
|
private unsafe uint readbits(byte * buff, ref int pos, int bits)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (bits <= 24)
|
|
|
|
|
|
return readbits_24(buff, ref pos, bits);
|
|
|
|
|
|
|
|
|
|
|
|
ulong result = (((ulong)buff[pos]) << 32) | (((ulong)buff[pos + 1]) << 24) | (((ulong)buff[pos + 2]) << 16) | (((ulong)buff[pos + 3]) << 8) | ((ulong)buff[pos + 4]);
|
|
|
|
|
|
result <<= _bitaccumulator;
|
|
|
|
|
|
result &= 0x00ffffffffff;
|
|
|
|
|
|
result >>= 40 - bits;
|
|
|
|
|
|
int new_accumulator = (_bitaccumulator + bits);
|
|
|
|
|
|
pos += (new_accumulator >> 3);
|
|
|
|
|
|
_bitaccumulator = (new_accumulator & 7);
|
|
|
|
|
|
return (uint)result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* reads a single bit */
|
|
|
|
|
|
private uint readbit(byte[] buff, ref int pos)
|
|
|
|
|
|
{
|
|
|
|
|
|
int new_accumulator;
|
|
|
|
|
|
uint result = buff[pos];
|
|
|
|
|
|
result <<= _bitaccumulator;
|
|
|
|
|
|
result = result >> 7 & 1;
|
|
|
|
|
|
new_accumulator = (_bitaccumulator + 1);
|
|
|
|
|
|
pos += (new_accumulator / 8);
|
|
|
|
|
|
_bitaccumulator = (new_accumulator % 8);
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void unreadbits(ref int pos, int bits)
|
|
|
|
|
|
{
|
|
|
|
|
|
int new_accumulator = (_bitaccumulator - bits);
|
|
|
|
|
|
pos += (new_accumulator >> 3);
|
|
|
|
|
|
|
|
|
|
|
|
_bitaccumulator = (new_accumulator & 7);
|
|
|
|
|
|
if (_bitaccumulator < 0)
|
|
|
|
|
|
_bitaccumulator *= -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2008-11-05 21:01:12 +00:00
|
|
|
|
private static int count_leading_zeroes(uint input)
|
2008-11-04 01:14:25 +00:00
|
|
|
|
{
|
2009-08-30 21:58:54 +00:00
|
|
|
|
int zeroes = 0;
|
2008-11-05 21:01:12 +00:00
|
|
|
|
uint shifted_input = input >> 16;
|
|
|
|
|
|
if (shifted_input == 0)
|
|
|
|
|
|
zeroes += 16;
|
|
|
|
|
|
else
|
|
|
|
|
|
input = shifted_input;
|
|
|
|
|
|
shifted_input = input >> 8;
|
|
|
|
|
|
if (shifted_input == 0)
|
|
|
|
|
|
zeroes += 8;
|
|
|
|
|
|
else
|
|
|
|
|
|
input = shifted_input;
|
2009-08-30 21:58:54 +00:00
|
|
|
|
return zeroes + BitReader.byte_to_unary_table[input];
|
2008-11-04 01:14:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private unsafe void readPredictor(ref int pos, ref predictor_t predictor_info)
|
|
|
|
|
|
{
|
|
|
|
|
|
fixed (predictor_t* pr = &predictor_info)
|
|
|
|
|
|
{
|
|
|
|
|
|
pr->prediction_type = (int)readbits(_framesBuffer, ref pos, 4);
|
|
|
|
|
|
pr->prediction_quantitization = (int)readbits(_framesBuffer, ref pos, 4);
|
|
|
|
|
|
pr->ricemodifier = (int)readbits(_framesBuffer, ref pos, 3);
|
|
|
|
|
|
pr->predictor_coef_num = (int)readbits(_framesBuffer, ref pos, 5);
|
|
|
|
|
|
|
|
|
|
|
|
/* read the predictor table */
|
2008-11-05 21:01:12 +00:00
|
|
|
|
pr->predictor_coef_table_sum = 0;
|
|
|
|
|
|
for (int i = pr->predictor_coef_num - 1; i >= 0; i--)
|
2008-11-04 01:14:25 +00:00
|
|
|
|
{
|
|
|
|
|
|
pr->predictor_coef_table[i] = (short)readbits(_framesBuffer, ref pos, 16);
|
2008-11-05 21:01:12 +00:00
|
|
|
|
pr->predictor_coef_table_sum += pr->predictor_coef_table[i];
|
2008-11-04 01:14:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private unsafe int decode_scalar(byte * buff, ref int pos, int k, int limit, int readsamplesize)
|
|
|
|
|
|
{
|
2008-11-05 21:01:12 +00:00
|
|
|
|
uint next = peekbits_9(buff, pos);
|
2009-08-30 21:58:54 +00:00
|
|
|
|
int x = (next >> 8 == 0) ? 0 :
|
|
|
|
|
|
1 + BitReader.byte_to_unary_table[(~next) & 0xff];
|
|
|
|
|
|
if (x == 9) /* RICE THRESHOLD 9 bits */
|
2008-11-05 21:01:12 +00:00
|
|
|
|
{
|
|
|
|
|
|
skipbits(ref pos, 9);
|
|
|
|
|
|
return (int)readbits(buff, ref pos, readsamplesize);
|
|
|
|
|
|
}
|
|
|
|
|
|
skipbits(ref pos, x + 1);
|
2008-11-04 01:14:25 +00:00
|
|
|
|
if (k >= limit)
|
|
|
|
|
|
k = limit;
|
|
|
|
|
|
if (k == 1)
|
|
|
|
|
|
return x;
|
|
|
|
|
|
int extrabits = (int) readbits(buff, ref pos, k);
|
|
|
|
|
|
x = (x << k) - x; // /* multiply x by 2^k - 1, as part of their strange algorithm */
|
|
|
|
|
|
if (extrabits > 1)
|
|
|
|
|
|
return x + extrabits - 1;
|
|
|
|
|
|
unreadbits(ref pos, 1);
|
|
|
|
|
|
return x;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-02-06 23:17:07 +00:00
|
|
|
|
private unsafe void basterdised_rice_decompress(int output_size, ref int pos, ref predictor_t predictor_info, ref int[] predicterror_buffer, int readsamplesize)
|
2008-11-04 01:14:25 +00:00
|
|
|
|
{
|
|
|
|
|
|
fixed (predictor_t* pr = &predictor_info)
|
|
|
|
|
|
fixed (int* output_buffer = &predicterror_buffer[0])
|
|
|
|
|
|
fixed (byte* buff = &_framesBuffer[0])
|
|
|
|
|
|
{
|
|
|
|
|
|
uint history = setinfo_rice_initialhistory;
|
|
|
|
|
|
int rice_kmodifier = setinfo_rice_kmodifier;
|
|
|
|
|
|
int rice_historymult = pr->ricemodifier * setinfo_rice_historymult / 4;
|
|
|
|
|
|
int sign_modifier = 0;
|
|
|
|
|
|
|
|
|
|
|
|
for (int output_count = 0; output_count < output_size; output_count++)
|
|
|
|
|
|
{
|
2008-11-05 21:01:12 +00:00
|
|
|
|
int x = sign_modifier + decode_scalar(buff, ref pos, 31 - count_leading_zeroes((history >> 9) + 3), rice_kmodifier, readsamplesize);
|
2009-02-19 04:09:59 +00:00
|
|
|
|
output_buffer[output_count] = (x >> 1) ^ - (x & 1);
|
2008-11-04 01:14:25 +00:00
|
|
|
|
sign_modifier = 0;
|
|
|
|
|
|
|
|
|
|
|
|
/* now update the history */
|
2008-11-05 21:01:12 +00:00
|
|
|
|
history = (uint)(history + (x * rice_historymult)
|
2008-11-04 01:14:25 +00:00
|
|
|
|
- ((history * rice_historymult) >> 9));
|
|
|
|
|
|
|
2008-11-05 21:01:12 +00:00
|
|
|
|
if (x > 0xffff)
|
2008-11-04 01:14:25 +00:00
|
|
|
|
history = 0xffff;
|
|
|
|
|
|
|
|
|
|
|
|
/* special case: there may be compressed blocks of 0 */
|
|
|
|
|
|
if ((history < 128) && (output_count + 1 < output_size))
|
|
|
|
|
|
{
|
2008-11-05 21:01:12 +00:00
|
|
|
|
int k = 7 - (31 - count_leading_zeroes(history)) + (((int)history + 16) >> 6);
|
2008-11-04 01:14:25 +00:00
|
|
|
|
int block_size = decode_scalar(buff, ref pos, k, rice_kmodifier, 16);
|
|
|
|
|
|
if (block_size > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (output_count + 1 + block_size > output_size)
|
|
|
|
|
|
throw new Exception("buffer overflow: " + output_size.ToString() + " vs " + (output_count + 1 + block_size).ToString());
|
|
|
|
|
|
//block_size = (int) output_size - output_count - 1;
|
|
|
|
|
|
for (int p = 0; p < block_size; p++)
|
|
|
|
|
|
output_buffer[output_count + 1 + p] = 0;
|
|
|
|
|
|
output_count += block_size;
|
|
|
|
|
|
}
|
|
|
|
|
|
sign_modifier = block_size > 0xffff ? 0 : 1;
|
|
|
|
|
|
history = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static int extend_sign32(int val, int bits)
|
|
|
|
|
|
{
|
|
|
|
|
|
return (val << (32 - bits)) >> (32 - bits);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2008-11-05 21:01:12 +00:00
|
|
|
|
private static short sign_only(int v)
|
2008-11-04 01:14:25 +00:00
|
|
|
|
{
|
2008-11-05 21:01:12 +00:00
|
|
|
|
return (short)(1 - ((v >> 30) & 2));
|
2008-11-04 01:14:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2010-02-06 23:17:07 +00:00
|
|
|
|
private unsafe void predictor_decompress_fir_adapt(int output_size, ref predictor_t predictor_info, ref int[] error_buffer, ref int[] buffer_out, int readsamplesize)
|
2008-11-04 01:14:25 +00:00
|
|
|
|
{
|
|
|
|
|
|
int i;
|
|
|
|
|
|
|
2008-11-05 21:01:12 +00:00
|
|
|
|
fixed (predictor_t* pr = &predictor_info)
|
|
|
|
|
|
fixed (int* buf_out = &buffer_out[0], buf_err = &error_buffer[0])
|
2008-11-04 01:14:25 +00:00
|
|
|
|
{
|
2008-11-05 21:01:12 +00:00
|
|
|
|
if (pr->predictor_coef_num == 0)
|
2008-11-04 01:14:25 +00:00
|
|
|
|
{
|
2008-11-05 21:01:12 +00:00
|
|
|
|
for (i = 0; i < output_size; i++)
|
|
|
|
|
|
buf_out[i] = buf_err[i];
|
|
|
|
|
|
return;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2008-11-05 21:01:12 +00:00
|
|
|
|
int sample = 0;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
|
2008-11-05 21:01:12 +00:00
|
|
|
|
if (pr->predictor_coef_num == 0x1f)
|
|
|
|
|
|
{ /* 11111 - max value of predictor_coef_num */
|
|
|
|
|
|
/* second-best case scenario for fir decompression,
|
|
|
|
|
|
* error describes a small difference from the previous sample only
|
|
|
|
|
|
*/
|
|
|
|
|
|
if (output_size <= 1)
|
|
|
|
|
|
return;
|
|
|
|
|
|
for (i = 0; i < output_size; i++)
|
|
|
|
|
|
{
|
2009-02-19 04:09:59 +00:00
|
|
|
|
sample = extend_sign32(sample + buf_err[i], readsamplesize);
|
2008-11-05 21:01:12 +00:00
|
|
|
|
buf_out[i] = sample;
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2008-11-05 21:01:12 +00:00
|
|
|
|
if (output_size <= predictor_info.predictor_coef_num || pr->predictor_coef_num < 0)
|
|
|
|
|
|
throw new Exception("invalid output size");
|
2008-11-04 01:14:25 +00:00
|
|
|
|
|
2008-11-05 21:01:12 +00:00
|
|
|
|
/* read warm-up samples */
|
|
|
|
|
|
for (i = 0; i <= predictor_info.predictor_coef_num; i++)
|
|
|
|
|
|
{
|
2009-02-19 04:09:59 +00:00
|
|
|
|
sample = extend_sign32(sample + buf_err[i], readsamplesize);
|
2008-11-05 21:01:12 +00:00
|
|
|
|
buf_out[i] = sample;
|
|
|
|
|
|
}
|
2008-11-04 01:14:25 +00:00
|
|
|
|
|
2008-11-05 21:01:12 +00:00
|
|
|
|
/* general case */
|
|
|
|
|
|
int* buf_pos = buf_out;
|
|
|
|
|
|
int predictor_coef_table_sum = pr->predictor_coef_table_sum;
|
|
|
|
|
|
for (i = (int)pr->predictor_coef_num + 1; i < output_size; i++)
|
2008-11-04 01:14:25 +00:00
|
|
|
|
{
|
|
|
|
|
|
int j;
|
|
|
|
|
|
int sum = 0;
|
|
|
|
|
|
int outval;
|
|
|
|
|
|
int error_val = buf_err[i];
|
2008-11-05 21:01:12 +00:00
|
|
|
|
int sample_val = *(buf_pos++);
|
2008-11-04 01:14:25 +00:00
|
|
|
|
|
2008-11-05 21:01:12 +00:00
|
|
|
|
for (j = 0; j < pr->predictor_coef_num; j++)
|
|
|
|
|
|
sum += buf_pos[j] * pr->predictor_coef_table[j];
|
|
|
|
|
|
sum -= predictor_coef_table_sum * sample_val;
|
|
|
|
|
|
outval = (1 << (pr->prediction_quantitization - 1)) + sum;
|
|
|
|
|
|
outval >>= pr->prediction_quantitization;
|
|
|
|
|
|
outval += sample_val + error_val;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
|
2009-02-19 04:09:59 +00:00
|
|
|
|
buf_pos[pr->predictor_coef_num] = extend_sign32(outval, readsamplesize);
|
2008-11-04 01:14:25 +00:00
|
|
|
|
|
2008-11-05 21:01:12 +00:00
|
|
|
|
if (error_val != 0)
|
2008-11-04 01:14:25 +00:00
|
|
|
|
{
|
2008-11-05 21:01:12 +00:00
|
|
|
|
short error_sign = sign_only(error_val);
|
|
|
|
|
|
for (j = 0; j < pr->predictor_coef_num; j++)
|
2008-11-04 01:14:25 +00:00
|
|
|
|
{
|
2008-11-05 21:01:12 +00:00
|
|
|
|
int val = sample_val - buf_pos[j];
|
|
|
|
|
|
if (val == 0)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
short sign = sign_only(error_sign * val);
|
|
|
|
|
|
pr->predictor_coef_table[j] -= sign;
|
|
|
|
|
|
predictor_coef_table_sum -= sign;
|
|
|
|
|
|
val *= sign; /* absolute value with same sign as error */
|
|
|
|
|
|
error_val -= (val >> pr->prediction_quantitization) * (j + 1);
|
|
|
|
|
|
if (error_val * error_sign <= 0)
|
|
|
|
|
|
break;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2008-11-05 21:01:12 +00:00
|
|
|
|
pr->predictor_coef_table_sum = predictor_coef_table_sum;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-02-06 23:17:07 +00:00
|
|
|
|
internal unsafe void deinterlace(int[,] samplesBuffer, int offset, int sampleCount)
|
2008-11-04 01:14:25 +00:00
|
|
|
|
{
|
2009-05-01 15:16:26 +00:00
|
|
|
|
if (sampleCount <= 0 || sampleCount > _samplesInBuffer)
|
2008-11-04 01:14:25 +00:00
|
|
|
|
return;
|
|
|
|
|
|
|
2008-11-05 21:01:12 +00:00
|
|
|
|
int i;
|
2008-11-07 22:43:26 +00:00
|
|
|
|
fixed (int* buf_a = &_outputsamples_buffer_a[_samplesBufferOffset], buf_b = &_outputsamples_buffer_b[_samplesBufferOffset])
|
|
|
|
|
|
fixed (int* buf_s = &samplesBuffer[offset, 0])
|
2008-11-04 01:14:25 +00:00
|
|
|
|
{
|
|
|
|
|
|
/* weighted interlacing */
|
2008-11-07 22:43:26 +00:00
|
|
|
|
if (_interlacing_leftweight != 0)
|
2008-11-04 01:14:25 +00:00
|
|
|
|
{
|
2009-05-01 15:16:26 +00:00
|
|
|
|
for (i = 0; i < sampleCount; i++)
|
2008-11-04 01:14:25 +00:00
|
|
|
|
{
|
2009-02-19 04:09:59 +00:00
|
|
|
|
int midright = buf_a[i];
|
|
|
|
|
|
int diff = buf_b[i];
|
2008-11-04 01:14:25 +00:00
|
|
|
|
|
2009-02-19 04:09:59 +00:00
|
|
|
|
midright -= (diff * _interlacing_leftweight) >> _interlacing_shift;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
|
2009-02-19 04:09:59 +00:00
|
|
|
|
buf_s[i * 2] = midright + diff;
|
|
|
|
|
|
buf_s[i * 2 + 1] = midright;
|
|
|
|
|
|
|
|
|
|
|
|
#if DEBUG
|
2010-04-18 22:42:23 +00:00
|
|
|
|
if (buf_s[i * 2] >= (1 << pcm.BitsPerSample) || buf_s[i * 2] < -(1 << pcm.BitsPerSample) ||
|
|
|
|
|
|
buf_s[i * 2 + 1] >= (1 << pcm.BitsPerSample) || buf_s[i * 2 + 1] < -(1 << pcm.BitsPerSample)
|
2009-02-19 04:09:59 +00:00
|
|
|
|
)
|
|
|
|
|
|
throw new Exception("overflow in ALAC decoder");
|
|
|
|
|
|
#endif
|
2008-11-04 01:14:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* otherwise basic interlacing took place */
|
2010-02-06 23:17:07 +00:00
|
|
|
|
AudioSamples.Interlace(buf_s, buf_a, buf_b, sampleCount);
|
2008-11-04 01:14:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2009-08-30 21:58:54 +00:00
|
|
|
|
internal int DecodeFrame(byte[] buffer, int pos, int len)
|
|
|
|
|
|
{
|
|
|
|
|
|
Array.Copy(buffer, pos, _framesBuffer, 0, len);
|
2010-02-06 23:17:07 +00:00
|
|
|
|
decodeFrame(len);
|
2009-08-30 21:58:54 +00:00
|
|
|
|
return len; // pos
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-02-06 23:17:07 +00:00
|
|
|
|
private void decodeFrame(int sampleSize)
|
2008-11-04 01:14:25 +00:00
|
|
|
|
{
|
|
|
|
|
|
_bitaccumulator = 0;
|
|
|
|
|
|
int pos = 0;
|
|
|
|
|
|
|
|
|
|
|
|
int channels = (int) readbits(_framesBuffer, ref pos, 3);
|
|
|
|
|
|
if (channels != 1)
|
|
|
|
|
|
throw new Exception("Not stereo");
|
|
|
|
|
|
|
|
|
|
|
|
readbits(_framesBuffer, ref pos, 4);
|
|
|
|
|
|
readbits(_framesBuffer, ref pos, 12); /* unknown, skip 12 bits */
|
|
|
|
|
|
bool hassize = 0 != readbits(_framesBuffer, ref pos, 1); /* the output sample size is stored soon */
|
|
|
|
|
|
int wasted_bytes = (int) readbits(_framesBuffer, ref pos, 2); /* unknown ? */
|
|
|
|
|
|
bool isnotcompressed = 0 != readbits(_framesBuffer, ref pos, 1); /* whether the frame is compressed */
|
2010-02-06 23:17:07 +00:00
|
|
|
|
int outputSamples = hassize ? (int)readbits(_framesBuffer, ref pos, 32) : setinfo_max_samples_per_frame;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
|
2010-04-18 22:42:23 +00:00
|
|
|
|
int readsamplesize = pcm.BitsPerSample - (wasted_bytes * 8) + pcm.ChannelCount - 1;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
if (!isnotcompressed)
|
|
|
|
|
|
{
|
|
|
|
|
|
/* compressed */
|
|
|
|
|
|
|
2008-11-07 22:43:26 +00:00
|
|
|
|
_interlacing_shift = (byte)readbits(_framesBuffer, ref pos, 8);
|
|
|
|
|
|
_interlacing_leftweight = (byte)readbits(_framesBuffer, ref pos, 8);
|
2008-11-04 01:14:25 +00:00
|
|
|
|
|
|
|
|
|
|
if (wasted_bytes != 0)
|
|
|
|
|
|
throw new Exception("FIXME: unimplemented, unhandling of wasted_bytes");
|
|
|
|
|
|
|
|
|
|
|
|
readPredictor(ref pos, ref predictor_info_a);
|
|
|
|
|
|
readPredictor(ref pos, ref predictor_info_b);
|
|
|
|
|
|
basterdised_rice_decompress(outputSamples, ref pos, ref predictor_info_a, ref _predicterror_buffer_a, readsamplesize);
|
|
|
|
|
|
if (predictor_info_a.prediction_type == 0)
|
|
|
|
|
|
predictor_decompress_fir_adapt(outputSamples, ref predictor_info_a, ref _predicterror_buffer_a, ref _outputsamples_buffer_a, readsamplesize);
|
|
|
|
|
|
else
|
|
|
|
|
|
throw new Exception("FIXME: unhandled prediction type.");
|
|
|
|
|
|
basterdised_rice_decompress(outputSamples, ref pos, ref predictor_info_b, ref _predicterror_buffer_b, readsamplesize);
|
|
|
|
|
|
if (predictor_info_b.prediction_type == 0)
|
|
|
|
|
|
predictor_decompress_fir_adapt(outputSamples, ref predictor_info_b, ref _predicterror_buffer_b, ref _outputsamples_buffer_b, readsamplesize);
|
|
|
|
|
|
else
|
|
|
|
|
|
throw new Exception("FIXME: unhandled prediction type.");
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
/* not compressed, easy case */
|
2010-02-06 23:17:07 +00:00
|
|
|
|
int bps = pcm.BitsPerSample;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
for (int i = 0; i < outputSamples; i++)
|
|
|
|
|
|
{
|
2010-02-06 23:17:07 +00:00
|
|
|
|
_outputsamples_buffer_a[i] = extend_sign32((int)readbits(_framesBuffer, ref pos, bps), bps);
|
|
|
|
|
|
_outputsamples_buffer_b[i] = extend_sign32((int)readbits(_framesBuffer, ref pos, bps), bps);
|
2008-11-04 01:14:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
/* wasted_bytes = 0; */
|
2008-11-07 22:43:26 +00:00
|
|
|
|
_interlacing_shift = 0;
|
|
|
|
|
|
_interlacing_leftweight = 0;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2009-08-30 21:58:54 +00:00
|
|
|
|
if (readbits(_framesBuffer, ref pos, 3) != 7)
|
|
|
|
|
|
throw new Exception("Invalid frame.");
|
2008-11-04 01:14:25 +00:00
|
|
|
|
|
|
|
|
|
|
_samplesInBuffer = outputSamples;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 'mvhd' movie header atom */
|
2008-11-05 13:15:33 +00:00
|
|
|
|
private void qtmovie_read_chunk_mvhd(string path, uint length, object param)
|
2008-11-04 01:14:25 +00:00
|
|
|
|
{
|
2008-11-05 13:15:33 +00:00
|
|
|
|
UInt32 size_remaining = length;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
|
|
|
|
|
|
/* version */
|
|
|
|
|
|
stream_read_uint8();
|
|
|
|
|
|
size_remaining -= 1;
|
|
|
|
|
|
stream_read_uint8();
|
|
|
|
|
|
stream_read_uint8();
|
|
|
|
|
|
stream_read_uint8();
|
|
|
|
|
|
size_remaining -= 3;
|
|
|
|
|
|
|
|
|
|
|
|
stream_read_uint32(); /* creation time */
|
|
|
|
|
|
size_remaining -= 4;
|
|
|
|
|
|
stream_read_uint32(); /* modification time */
|
|
|
|
|
|
size_remaining -= 4;
|
|
|
|
|
|
|
|
|
|
|
|
stream_read_uint32(); /* time scale */
|
|
|
|
|
|
size_remaining -= 4;
|
|
|
|
|
|
_sampleCount = stream_read_uint32(); /* duration */
|
|
|
|
|
|
size_remaining -= 4;
|
|
|
|
|
|
|
|
|
|
|
|
stream_read_uint32(); /* preferred scale */
|
|
|
|
|
|
size_remaining -= 4;
|
|
|
|
|
|
|
|
|
|
|
|
stream_read_uint16(); /* preferred volume */
|
|
|
|
|
|
size_remaining -= 2;
|
|
|
|
|
|
|
|
|
|
|
|
stream_skip(10); /* reserved */
|
|
|
|
|
|
size_remaining -= 10;
|
|
|
|
|
|
stream_skip(36); /* display matrix */
|
|
|
|
|
|
size_remaining -= 36;
|
|
|
|
|
|
|
|
|
|
|
|
stream_read_uint32(); size_remaining -= 4; /* preview time */
|
|
|
|
|
|
stream_read_uint32(); size_remaining -= 4; /* preview duration */
|
|
|
|
|
|
stream_read_uint32(); size_remaining -= 4; /* poster time */
|
|
|
|
|
|
stream_read_uint32(); size_remaining -= 4; /* selection time */
|
|
|
|
|
|
stream_read_uint32(); size_remaining -= 4; /* selection duration */
|
|
|
|
|
|
stream_read_uint32(); size_remaining -= 4; /* current time */
|
|
|
|
|
|
stream_read_uint32(); size_remaining -= 4; /* next track ID */
|
|
|
|
|
|
|
|
|
|
|
|
if (size_remaining > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new Exception("ehm, size remianing?");
|
|
|
|
|
|
// stream_skip(size_remaining);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2008-11-05 21:01:12 +00:00
|
|
|
|
private void qtmovie_read_chunk_stsd(string path, uint length, object param)
|
2008-11-04 01:14:25 +00:00
|
|
|
|
{
|
|
|
|
|
|
uint i;
|
|
|
|
|
|
UInt32 numentries;
|
2008-11-05 21:01:12 +00:00
|
|
|
|
UInt32 size_remaining = length;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
|
|
|
|
|
|
/* version */
|
|
|
|
|
|
stream_read_uint8();
|
|
|
|
|
|
size_remaining -= 1;
|
|
|
|
|
|
/* flags */
|
|
|
|
|
|
stream_read_uint8();
|
|
|
|
|
|
stream_read_uint8();
|
|
|
|
|
|
stream_read_uint8();
|
|
|
|
|
|
size_remaining -= 3;
|
|
|
|
|
|
|
|
|
|
|
|
numentries = stream_read_uint32();
|
|
|
|
|
|
size_remaining -= 4;
|
|
|
|
|
|
|
|
|
|
|
|
if (numentries != 1)
|
|
|
|
|
|
throw new Exception("only expecting one entry in sample description atom!");
|
|
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < numentries; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
UInt32 entry_size;
|
|
|
|
|
|
UInt16 version;
|
|
|
|
|
|
|
|
|
|
|
|
UInt32 entry_remaining;
|
|
|
|
|
|
|
|
|
|
|
|
entry_size = stream_read_uint32();
|
|
|
|
|
|
UInt32 format = stream_read_uint32();
|
|
|
|
|
|
entry_remaining = entry_size;
|
|
|
|
|
|
entry_remaining -= 8;
|
|
|
|
|
|
|
|
|
|
|
|
/* sound info: */
|
|
|
|
|
|
|
|
|
|
|
|
stream_skip(6); /* reserved */
|
|
|
|
|
|
entry_remaining -= 6;
|
|
|
|
|
|
|
|
|
|
|
|
version = stream_read_uint16();
|
|
|
|
|
|
if (version != 1)
|
|
|
|
|
|
throw new Exception("unknown version!?");
|
|
|
|
|
|
entry_remaining -= 2;
|
|
|
|
|
|
|
|
|
|
|
|
/* revision level */
|
|
|
|
|
|
stream_read_uint16();
|
|
|
|
|
|
/* vendor */
|
|
|
|
|
|
stream_read_uint32();
|
|
|
|
|
|
entry_remaining -= 6;
|
|
|
|
|
|
|
|
|
|
|
|
/* EH?? spec doesn't say theres an extra 16 bits here.. but there is! */
|
|
|
|
|
|
stream_read_uint16();
|
|
|
|
|
|
entry_remaining -= 2;
|
|
|
|
|
|
|
2010-02-06 23:17:07 +00:00
|
|
|
|
int _channelCount = (int)stream_read_uint16();
|
2008-11-04 01:14:25 +00:00
|
|
|
|
|
2010-02-06 23:17:07 +00:00
|
|
|
|
int _bitsPerSample = stream_read_uint16();
|
2008-11-04 01:14:25 +00:00
|
|
|
|
entry_remaining -= 4;
|
|
|
|
|
|
|
|
|
|
|
|
/* compression id */
|
|
|
|
|
|
stream_read_uint16();
|
|
|
|
|
|
/* packet size */
|
|
|
|
|
|
stream_read_uint16();
|
|
|
|
|
|
entry_remaining -= 4;
|
|
|
|
|
|
|
|
|
|
|
|
/* sample rate - 32bit fixed point = 16bit?? */
|
2010-02-06 23:17:07 +00:00
|
|
|
|
int _sampleRate = stream_read_uint16();
|
2008-11-04 01:14:25 +00:00
|
|
|
|
entry_remaining -= 2;
|
|
|
|
|
|
|
|
|
|
|
|
/* skip 2 */
|
|
|
|
|
|
stream_skip(2);
|
|
|
|
|
|
entry_remaining -= 2;
|
|
|
|
|
|
|
2010-02-06 23:17:07 +00:00
|
|
|
|
pcm = new AudioPCMConfig(_bitsPerSample, _channelCount, _sampleRate);
|
|
|
|
|
|
|
2008-11-04 01:14:25 +00:00
|
|
|
|
/* remaining is codec data */
|
|
|
|
|
|
|
|
|
|
|
|
//#if 0
|
|
|
|
|
|
// qtmovie->res->codecdata_len = stream_read_uint32();
|
|
|
|
|
|
// if (qtmovie->res->codecdata_len != entry_remaining)
|
|
|
|
|
|
// fprintf(stderr, "perhaps not? %i vs %i\n",
|
|
|
|
|
|
// qtmovie->res->codecdata_len, entry_remaining);
|
|
|
|
|
|
// entry_remaining -= 4;
|
|
|
|
|
|
// stream_read_uint32(); /* 'alac' */
|
|
|
|
|
|
// entry_remaining -= 4;
|
|
|
|
|
|
|
|
|
|
|
|
// qtmovie->res->codecdata = malloc(qtmovie->res->codecdata_len - 8);
|
|
|
|
|
|
|
|
|
|
|
|
// stream_read(qtmovie->stream,
|
|
|
|
|
|
// entry_remaining,
|
|
|
|
|
|
// qtmovie->res->codecdata);
|
|
|
|
|
|
// entry_remaining = 0;
|
|
|
|
|
|
|
|
|
|
|
|
//#else
|
|
|
|
|
|
/* 12 = audio format atom, 8 = padding */
|
|
|
|
|
|
uint _codecDataLen = entry_remaining + 12 + 8;
|
|
|
|
|
|
_codecData = new byte[_codecDataLen];
|
|
|
|
|
|
/* audio format atom */
|
|
|
|
|
|
_codecData[0] = 0; _codecData[1] = 0; _codecData[2] = 0; _codecData[3] = 0x0C;
|
|
|
|
|
|
_codecData[4] = (byte)'f'; _codecData[5] = (byte)'r'; _codecData[6] = (byte)'m'; _codecData[7] = (byte)'a';
|
|
|
|
|
|
_codecData[8] = (byte)'a'; _codecData[9] = (byte)'l'; _codecData[10] = (byte)'a'; _codecData[11] = (byte)'c';
|
2008-11-10 10:31:35 +00:00
|
|
|
|
_IO.Read(_codecData, 12, (int)entry_remaining);
|
2008-11-04 01:14:25 +00:00
|
|
|
|
entry_remaining -= entry_remaining;
|
|
|
|
|
|
|
|
|
|
|
|
//#endif
|
|
|
|
|
|
if (entry_remaining > 0)
|
|
|
|
|
|
stream_skip(entry_remaining);
|
|
|
|
|
|
|
|
|
|
|
|
_formatRead = true;
|
|
|
|
|
|
if (format != FCC_ALAC)
|
|
|
|
|
|
throw new Exception("Expecting ALAC data format");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2008-11-05 21:01:12 +00:00
|
|
|
|
private void qtmovie_read_chunk_stts(string path, uint length, object param)
|
2008-11-04 01:14:25 +00:00
|
|
|
|
{
|
|
|
|
|
|
uint i;
|
|
|
|
|
|
UInt32 numentries;
|
2008-11-05 21:01:12 +00:00
|
|
|
|
UInt32 size_remaining = length;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
|
|
|
|
|
|
/* version */
|
|
|
|
|
|
stream_read_uint8();
|
|
|
|
|
|
size_remaining -= 1;
|
|
|
|
|
|
/* flags */
|
|
|
|
|
|
stream_read_uint8();
|
|
|
|
|
|
stream_read_uint8();
|
|
|
|
|
|
stream_read_uint8();
|
|
|
|
|
|
size_remaining -= 3;
|
|
|
|
|
|
|
|
|
|
|
|
numentries = stream_read_uint32();
|
|
|
|
|
|
size_remaining -= 4;
|
|
|
|
|
|
|
2010-02-06 23:17:07 +00:00
|
|
|
|
_time_to_sample_count = new int[numentries];
|
|
|
|
|
|
_time_to_sample_duration = new int[numentries];
|
2008-11-04 01:14:25 +00:00
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < numentries; i++)
|
|
|
|
|
|
{
|
2010-02-06 23:17:07 +00:00
|
|
|
|
_time_to_sample_count[i] = (int)stream_read_uint32();
|
|
|
|
|
|
_time_to_sample_duration[i] = (int)stream_read_uint32();
|
2008-11-04 01:14:25 +00:00
|
|
|
|
size_remaining -= 8;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (size_remaining > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new Exception("ehm, size remianing?");
|
|
|
|
|
|
// stream_skip(size_remaining);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2008-11-05 21:01:12 +00:00
|
|
|
|
private void qtmovie_read_chunk_stsz(string path, uint length, object param)
|
2008-11-04 01:14:25 +00:00
|
|
|
|
{
|
|
|
|
|
|
uint i;
|
|
|
|
|
|
UInt32 numentries;
|
2008-11-05 21:01:12 +00:00
|
|
|
|
UInt32 size_remaining = length;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
|
|
|
|
|
|
/* version */
|
|
|
|
|
|
stream_read_uint8();
|
|
|
|
|
|
size_remaining -= 1;
|
|
|
|
|
|
/* flags */
|
|
|
|
|
|
stream_read_uint8();
|
|
|
|
|
|
stream_read_uint8();
|
|
|
|
|
|
stream_read_uint8();
|
|
|
|
|
|
size_remaining -= 3;
|
|
|
|
|
|
|
|
|
|
|
|
/* default sample size */
|
|
|
|
|
|
if (stream_read_uint32() != 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new Exception("i was expecting variable samples sizes\n");
|
|
|
|
|
|
//stream_read_uint32();
|
|
|
|
|
|
//size_remaining -= 4;
|
|
|
|
|
|
//return;
|
|
|
|
|
|
}
|
|
|
|
|
|
size_remaining -= 4;
|
|
|
|
|
|
|
|
|
|
|
|
numentries = stream_read_uint32();
|
|
|
|
|
|
size_remaining -= 4;
|
|
|
|
|
|
|
2010-02-06 23:17:07 +00:00
|
|
|
|
_sample_byte_size = new int[numentries];
|
2008-11-04 01:14:25 +00:00
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < numentries; i++)
|
|
|
|
|
|
{
|
2010-02-06 23:17:07 +00:00
|
|
|
|
_sample_byte_size[i] = (int)stream_read_uint32();
|
2008-11-04 01:14:25 +00:00
|
|
|
|
size_remaining -= 4;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (size_remaining > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new Exception("ehm, size remianing?\n");
|
|
|
|
|
|
//stream_skip(qtmovie->stream, size_remaining);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* media handler inside mdia */
|
2008-11-05 13:15:33 +00:00
|
|
|
|
private void qtmovie_read_chunk_hdlr(string path, uint length, object param)
|
2008-11-04 01:14:25 +00:00
|
|
|
|
{
|
|
|
|
|
|
UInt32 comptype, compsubtype;
|
2008-11-05 13:15:33 +00:00
|
|
|
|
UInt32 size_remaining = length;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
|
|
|
|
|
|
/* version */
|
|
|
|
|
|
stream_read_uint8();
|
|
|
|
|
|
size_remaining -= 1;
|
|
|
|
|
|
/* flags */
|
|
|
|
|
|
stream_read_uint8();
|
|
|
|
|
|
stream_read_uint8();
|
|
|
|
|
|
stream_read_uint8();
|
|
|
|
|
|
size_remaining -= 3;
|
|
|
|
|
|
|
|
|
|
|
|
/* component type */
|
|
|
|
|
|
comptype = stream_read_uint32();
|
|
|
|
|
|
compsubtype = stream_read_uint32();
|
|
|
|
|
|
size_remaining -= 8;
|
|
|
|
|
|
|
|
|
|
|
|
/* component manufacturer */
|
|
|
|
|
|
stream_read_uint32();
|
|
|
|
|
|
size_remaining -= 4;
|
|
|
|
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
|
|
stream_read_uint32();
|
|
|
|
|
|
stream_read_uint32();
|
|
|
|
|
|
size_remaining -= 8;
|
|
|
|
|
|
|
|
|
|
|
|
/* name */
|
|
|
|
|
|
UInt32 strlen = stream_read_uint8();
|
|
|
|
|
|
byte[] str = new byte[strlen];
|
2008-11-10 10:31:35 +00:00
|
|
|
|
_IO.Read(str, 0, (int)strlen);
|
2008-11-04 01:14:25 +00:00
|
|
|
|
size_remaining -= 1 + strlen;
|
|
|
|
|
|
|
|
|
|
|
|
if (size_remaining > 0)
|
|
|
|
|
|
stream_skip(size_remaining);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void read_chunk_ftyp(UInt32 chunk_len)
|
|
|
|
|
|
{
|
|
|
|
|
|
UInt32 type = stream_read_uint32();
|
|
|
|
|
|
if (type != FCC_M4A)
|
|
|
|
|
|
throw new Exception("not M4A file.");
|
|
|
|
|
|
UInt32 minor_ver = stream_read_uint32();
|
|
|
|
|
|
stream_skip(chunk_len - 16);
|
|
|
|
|
|
//UInt32 size_remaining = chunk_len - 16; /* FIXME: can't hardcode 16, size may be 64bit */
|
|
|
|
|
|
/* compatible brands */
|
|
|
|
|
|
//while (size_remaining > 0)
|
|
|
|
|
|
//{
|
|
|
|
|
|
// /* unused */
|
|
|
|
|
|
// /*fourcc_t cbrand =*/
|
|
|
|
|
|
// stream_read_uint32();
|
|
|
|
|
|
// size_remaining -= 4;
|
|
|
|
|
|
//}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void read_chunk_mdat(UInt32 chunk_len, bool skip_mdat)
|
|
|
|
|
|
{
|
|
|
|
|
|
UInt32 size_remaining = chunk_len - 8; /* FIXME WRONG */
|
|
|
|
|
|
if (size_remaining == 0)
|
|
|
|
|
|
return;
|
|
|
|
|
|
if (skip_mdat)
|
|
|
|
|
|
{
|
2008-11-10 10:31:35 +00:00
|
|
|
|
_saved_mdat_pos = _IO.Position;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
stream_skip(size_remaining);
|
|
|
|
|
|
}
|
|
|
|
|
|
//#if 0
|
|
|
|
|
|
// qtmovie->res->mdat = malloc(size_remaining);
|
|
|
|
|
|
|
|
|
|
|
|
// stream_read(qtmovie->stream, size_remaining, qtmovie->res->mdat);
|
|
|
|
|
|
//#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2008-11-05 03:56:43 +00:00
|
|
|
|
private delegate void qtmovie_read_atom (string path, uint length, object param);
|
|
|
|
|
|
|
|
|
|
|
|
private void qtmovie_read_meta_name(string path, uint length, object param)
|
|
|
|
|
|
{
|
|
|
|
|
|
uint language = stream_read_uint32();
|
2008-11-05 13:15:33 +00:00
|
|
|
|
_meta_name = new ASCIIEncoding().GetString(stream_read_bytes((int)length - 4), 0, (int)length - 4);
|
2008-11-05 03:56:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void qtmovie_read_meta_mean(string path, uint length, object param)
|
|
|
|
|
|
{
|
|
|
|
|
|
uint language = stream_read_uint32();
|
2008-11-05 13:15:33 +00:00
|
|
|
|
_meta_mean = new ASCIIEncoding().GetString(stream_read_bytes((int)length - 4), 0, (int)length - 4);
|
2008-11-05 03:56:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void qtmovie_read_meta_data(string path, uint length, object param)
|
|
|
|
|
|
{
|
|
|
|
|
|
uint tag_format = stream_read_uint32();
|
|
|
|
|
|
uint language = stream_read_uint32();
|
|
|
|
|
|
int str_size = (int)length - 8;
|
|
|
|
|
|
if (str_size <= 0) return;
|
|
|
|
|
|
if (tag_format != 1) throw new Exception(path + ": not a string");
|
2008-11-05 13:15:33 +00:00
|
|
|
|
_meta_data = new UTF8Encoding().GetString(stream_read_bytes(str_size), 0, str_size);
|
2008-11-05 03:56:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void qtmovie_read_meta_freeform(string path, uint length, object param)
|
|
|
|
|
|
{
|
|
|
|
|
|
_meta_data = _meta_name = _meta_mean = null;
|
|
|
|
|
|
qtmovie_read_lst(path, length, param);
|
|
|
|
|
|
if (_meta_data == null || _meta_name == null || _meta_mean == null)
|
|
|
|
|
|
throw new Exception(path + " doesn't contain data, name or mean");
|
|
|
|
|
|
_tags.Add(_meta_name, _meta_data);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void qtmovie_read_meta_string(string path, uint length, object param)
|
|
|
|
|
|
{
|
|
|
|
|
|
_meta_data = null;
|
|
|
|
|
|
qtmovie_read_lst(path, length, param);
|
|
|
|
|
|
if (_meta_data == null)
|
|
|
|
|
|
throw new Exception(path + " doesn't contain data");
|
|
|
|
|
|
_tags.Add((string)param, _meta_data);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void qtmovie_read_meta_binary(string path, uint length, object param)
|
|
|
|
|
|
{
|
|
|
|
|
|
uint tag_format = stream_read_uint32();
|
|
|
|
|
|
uint language = stream_read_uint32();
|
|
|
|
|
|
int str_size = (int)length - 8;
|
|
|
|
|
|
if (str_size <= 0) return;
|
|
|
|
|
|
if (tag_format != 0) throw new Exception(path + " not a binary");
|
|
|
|
|
|
byte[] value = new byte[str_size];
|
2008-11-10 10:31:35 +00:00
|
|
|
|
if (_IO.Read(value, 0, str_size) != str_size)
|
2008-11-05 03:56:43 +00:00
|
|
|
|
throw new Exception("Decoding failed.");
|
|
|
|
|
|
if (path.EndsWith(".trkn.data"))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (str_size >= 4)
|
|
|
|
|
|
_tags.Add("TRACKNUMBER", value[3].ToString());
|
|
|
|
|
|
if (str_size >= 6)
|
|
|
|
|
|
_tags.Add("TOTALTRACKS", value[5].ToString());
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (path.EndsWith(".disk.data"))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (str_size >= 4)
|
|
|
|
|
|
_tags.Add("DISCNUMBER", value[3].ToString());
|
|
|
|
|
|
if (str_size >= 6)
|
|
|
|
|
|
_tags.Add("TOTALDISCS", value[5].ToString());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2008-11-05 13:15:33 +00:00
|
|
|
|
private void qtmovie_read_nul(string path, uint length, object param)
|
|
|
|
|
|
{
|
|
|
|
|
|
stream_skip(length);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2008-11-05 03:56:43 +00:00
|
|
|
|
private void qtmovie_read_lst(string path, uint length, object param)
|
|
|
|
|
|
{
|
|
|
|
|
|
uint size_remaining = length;
|
|
|
|
|
|
|
|
|
|
|
|
if (param != null && param is uint)
|
|
|
|
|
|
{
|
|
|
|
|
|
size_remaining -= (uint) param;
|
|
|
|
|
|
stream_skip((uint)param);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
StringBuilder chunk_path = new StringBuilder(path);
|
|
|
|
|
|
chunk_path.Append('.');
|
|
|
|
|
|
while (size_remaining > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
uint sub_chunk_len = stream_read_uint32();
|
|
|
|
|
|
if (sub_chunk_len <= 1 || sub_chunk_len > size_remaining)
|
|
|
|
|
|
throw new Exception("strange size for chunk inside "+path+".");
|
2008-11-05 13:15:33 +00:00
|
|
|
|
stream_read_bytes(4);
|
2008-11-05 03:56:43 +00:00
|
|
|
|
for (int c = 0; c < 4; c++)
|
|
|
|
|
|
chunk_path.Append((char)_buff[c]);
|
|
|
|
|
|
|
|
|
|
|
|
string chunk_path_str = chunk_path.ToString();
|
|
|
|
|
|
qtmovie_read_atom handler;
|
|
|
|
|
|
if (_qtmovie_parsers.TryGetValue(chunk_path_str, out handler))
|
|
|
|
|
|
handler(chunk_path_str, sub_chunk_len - 8, _qtmovie_parser_params[chunk_path_str]);
|
|
|
|
|
|
else
|
|
|
|
|
|
stream_skip(sub_chunk_len - 8);
|
|
|
|
|
|
chunk_path.Length -= 4;
|
|
|
|
|
|
size_remaining -= sub_chunk_len;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2008-11-05 13:15:33 +00:00
|
|
|
|
private void qtmovie_add_any_parser (string path, qtmovie_read_atom handler, object param)
|
2008-11-05 03:56:43 +00:00
|
|
|
|
{
|
|
|
|
|
|
_qtmovie_parsers.Add(path, handler);
|
|
|
|
|
|
_qtmovie_parser_params.Add(path, param);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void qtmovie_add_lst_parser(string path, object param)
|
|
|
|
|
|
{
|
2008-11-05 13:15:33 +00:00
|
|
|
|
qtmovie_add_any_parser(path, new qtmovie_read_atom(qtmovie_read_lst), param);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void qtmovie_add_nul_parser(string path)
|
|
|
|
|
|
{
|
|
|
|
|
|
qtmovie_add_any_parser(path, new qtmovie_read_atom(qtmovie_read_nul), null);
|
2008-11-05 03:56:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void qtmovie_add_tag_parser(string path, string flacName)
|
|
|
|
|
|
{
|
2008-11-05 13:15:33 +00:00
|
|
|
|
qtmovie_add_any_parser(path, new qtmovie_read_atom(qtmovie_read_meta_string), flacName);
|
|
|
|
|
|
qtmovie_add_any_parser(path + ".data", new qtmovie_read_atom(qtmovie_read_meta_data), null);
|
2008-11-05 03:56:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void qtmovie_add_tag_parser(string path)
|
|
|
|
|
|
{
|
|
|
|
|
|
qtmovie_add_lst_parser(path, null);
|
2008-11-05 13:15:33 +00:00
|
|
|
|
qtmovie_add_any_parser(path + ".data", new qtmovie_read_atom(qtmovie_read_meta_binary), null);
|
2008-11-05 03:56:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2008-11-04 01:14:25 +00:00
|
|
|
|
private void qtmovie_read()
|
|
|
|
|
|
{
|
|
|
|
|
|
bool found_moov = false;
|
|
|
|
|
|
bool found_mdat = false;
|
2008-11-05 03:56:43 +00:00
|
|
|
|
|
|
|
|
|
|
_qtmovie_parsers = new Dictionary<string, qtmovie_read_atom>();
|
|
|
|
|
|
_qtmovie_parser_params = new Dictionary<string, object>();
|
|
|
|
|
|
|
2008-11-05 13:15:33 +00:00
|
|
|
|
qtmovie_add_lst_parser("top.moov", null);
|
|
|
|
|
|
qtmovie_add_any_parser("top.moov.mvhd", new qtmovie_read_atom(qtmovie_read_chunk_mvhd), null);
|
|
|
|
|
|
qtmovie_add_nul_parser("top.moov.elst");
|
|
|
|
|
|
qtmovie_add_nul_parser("top.moov.iods");
|
|
|
|
|
|
qtmovie_add_lst_parser("top.moov.trak", null); /* 'trak' - a movie track */
|
|
|
|
|
|
qtmovie_add_nul_parser("top.moov.trak.tkhd");
|
|
|
|
|
|
qtmovie_add_nul_parser("top.moov.trak.edts");
|
|
|
|
|
|
qtmovie_add_lst_parser("top.moov.trak.mdia", null);
|
|
|
|
|
|
qtmovie_add_any_parser("top.moov.trak.mdia.hdlr", new qtmovie_read_atom(qtmovie_read_chunk_hdlr), null);
|
2008-11-05 21:01:12 +00:00
|
|
|
|
qtmovie_add_lst_parser("top.moov.trak.mdia.minf", null);
|
|
|
|
|
|
qtmovie_add_nul_parser("top.moov.trak.mdia.minf.smhd"); // required, unused
|
|
|
|
|
|
qtmovie_add_nul_parser("top.moov.trak.mdia.minf.dinf"); // required, unused
|
|
|
|
|
|
qtmovie_add_lst_parser("top.moov.trak.mdia.minf.stbl", null); // SAMPLE TABLE, required
|
|
|
|
|
|
qtmovie_add_any_parser("top.moov.trak.mdia.minf.stbl.stsd", new qtmovie_read_atom(qtmovie_read_chunk_stsd), null); // _codecData
|
|
|
|
|
|
qtmovie_add_any_parser("top.moov.trak.mdia.minf.stbl.stts", new qtmovie_read_atom(qtmovie_read_chunk_stts), null); // _time_to_sample_*
|
|
|
|
|
|
qtmovie_add_any_parser("top.moov.trak.mdia.minf.stbl.stsz", new qtmovie_read_atom(qtmovie_read_chunk_stsz), null); // _sample_byte_size
|
|
|
|
|
|
qtmovie_add_nul_parser("top.moov.trak.mdia.minf.stbl.stsc"); /* skip these, no indexing for us! */
|
|
|
|
|
|
qtmovie_add_nul_parser("top.moov.trak.mdia.minf.stbl.stco"); /* skip these, no indexing for us! */
|
2009-02-19 04:09:59 +00:00
|
|
|
|
qtmovie_add_nul_parser("top.moov.udta");
|
|
|
|
|
|
//qtmovie_add_lst_parser("top.moov.udta", null);
|
|
|
|
|
|
//qtmovie_add_lst_parser("top.moov.udta.meta", (uint)4);
|
|
|
|
|
|
//qtmovie_add_lst_parser("top.moov.udta.meta.ilst", null);
|
|
|
|
|
|
//qtmovie_add_tag_parser("top.moov.udta.meta.ilst.<2E>nam", "TITLE");
|
|
|
|
|
|
//qtmovie_add_tag_parser("top.moov.udta.meta.ilst.<2E>ART", "ARTIST");
|
|
|
|
|
|
//qtmovie_add_tag_parser("top.moov.udta.meta.ilst.<2E>wrt", "COMPOSER");
|
|
|
|
|
|
//qtmovie_add_tag_parser("top.moov.udta.meta.ilst.<2E>alb", "ALBUM");
|
|
|
|
|
|
//qtmovie_add_tag_parser("top.moov.udta.meta.ilst.<2E>day", "DATE");
|
|
|
|
|
|
//qtmovie_add_tag_parser("top.moov.udta.meta.ilst.<2E>gen", "GENRE");
|
|
|
|
|
|
//qtmovie_add_tag_parser("top.moov.udta.meta.ilst.disk");
|
|
|
|
|
|
//qtmovie_add_tag_parser("top.moov.udta.meta.ilst.trkn");
|
|
|
|
|
|
//qtmovie_add_any_parser("top.moov.udta.meta.ilst.----", new qtmovie_read_atom(qtmovie_read_meta_freeform), null);
|
|
|
|
|
|
//qtmovie_add_any_parser("top.moov.udta.meta.ilst.----.mean", new qtmovie_read_atom(qtmovie_read_meta_mean), null);
|
|
|
|
|
|
//qtmovie_add_any_parser("top.moov.udta.meta.ilst.----.name", new qtmovie_read_atom(qtmovie_read_meta_name), null);
|
|
|
|
|
|
//qtmovie_add_any_parser("top.moov.udta.meta.ilst.----.data", new qtmovie_read_atom(qtmovie_read_meta_data), null);
|
2008-11-05 03:56:43 +00:00
|
|
|
|
|
2008-11-04 01:14:25 +00:00
|
|
|
|
while (true)
|
|
|
|
|
|
{
|
|
|
|
|
|
UInt32 chunk_len = stream_read_uint32();
|
|
|
|
|
|
if (chunk_len == 1)
|
|
|
|
|
|
throw new Exception("need 64bit support.");
|
|
|
|
|
|
UInt32 chunk_id = stream_read_uint32();
|
|
|
|
|
|
switch (chunk_id)
|
|
|
|
|
|
{
|
|
|
|
|
|
case FCC_FTYP:
|
|
|
|
|
|
read_chunk_ftyp(chunk_len);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case FCC_MOOV:
|
2008-11-05 13:15:33 +00:00
|
|
|
|
qtmovie_read_lst("top.moov", chunk_len - 8, null);
|
2008-11-04 01:14:25 +00:00
|
|
|
|
if (found_mdat)
|
|
|
|
|
|
{
|
2008-11-10 10:31:35 +00:00
|
|
|
|
_IO.Position = _saved_mdat_pos;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
found_moov = true;
|
|
|
|
|
|
break;
|
|
|
|
|
|
/* if we hit mdat before we've found moov, record the position
|
|
|
|
|
|
* and move on. We can then come back to mdat later.
|
|
|
|
|
|
* This presumes the stream supports seeking backwards.
|
|
|
|
|
|
*/
|
|
|
|
|
|
case FCC_MDAT:
|
|
|
|
|
|
read_chunk_mdat(chunk_len, !found_moov);
|
|
|
|
|
|
if (found_moov)
|
|
|
|
|
|
return;
|
|
|
|
|
|
found_mdat = true;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
/* these following atoms can be skipped !!!! */
|
|
|
|
|
|
case FCC_FREE:
|
|
|
|
|
|
stream_skip(chunk_len - 8); /* FIXME not 8 */
|
|
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
throw new Exception(String.Format("(top) unknown chunk id: {0}.", chunk_id));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
string _path;
|
2008-11-10 10:31:35 +00:00
|
|
|
|
Stream _IO;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
|
|
|
|
|
|
byte[] _codecData;
|
2010-02-06 23:17:07 +00:00
|
|
|
|
int[] _time_to_sample_count, _time_to_sample_duration, _sample_byte_size;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
long _saved_mdat_pos;
|
|
|
|
|
|
bool _formatRead;
|
|
|
|
|
|
int _bitaccumulator;
|
2010-02-06 23:17:07 +00:00
|
|
|
|
int setinfo_max_samples_per_frame;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
byte setinfo_rice_initialhistory;
|
|
|
|
|
|
byte setinfo_rice_kmodifier;
|
|
|
|
|
|
byte setinfo_rice_historymult;
|
|
|
|
|
|
|
|
|
|
|
|
int[] _predicterror_buffer_a,
|
|
|
|
|
|
_predicterror_buffer_b,
|
|
|
|
|
|
_outputsamples_buffer_a,
|
|
|
|
|
|
_outputsamples_buffer_b;
|
|
|
|
|
|
predictor_t predictor_info_a;
|
|
|
|
|
|
predictor_t predictor_info_b;
|
|
|
|
|
|
|
|
|
|
|
|
NameValueCollection _tags;
|
2010-02-06 23:17:07 +00:00
|
|
|
|
int _samplesInBuffer, _samplesBufferOffset;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
byte[] _framesBuffer;
|
|
|
|
|
|
byte[] _buff;
|
2008-11-07 22:43:26 +00:00
|
|
|
|
byte _interlacing_shift;
|
|
|
|
|
|
byte _interlacing_leftweight;
|
2010-02-06 23:17:07 +00:00
|
|
|
|
AudioPCMConfig pcm;
|
|
|
|
|
|
long _sampleCount;
|
|
|
|
|
|
long _sampleOffset;
|
|
|
|
|
|
long _iSample;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
|
2008-11-05 03:56:43 +00:00
|
|
|
|
Dictionary<string, qtmovie_read_atom> _qtmovie_parsers;
|
|
|
|
|
|
Dictionary<string, object> _qtmovie_parser_params;
|
|
|
|
|
|
string _meta_data, _meta_name, _meta_mean;
|
|
|
|
|
|
|
2008-11-04 01:14:25 +00:00
|
|
|
|
unsafe struct predictor_t
|
|
|
|
|
|
{
|
|
|
|
|
|
public fixed short predictor_coef_table[32];
|
2008-11-05 21:01:12 +00:00
|
|
|
|
public int predictor_coef_table_sum;
|
2008-11-04 01:14:25 +00:00
|
|
|
|
public int predictor_coef_num;
|
|
|
|
|
|
public int prediction_type;
|
|
|
|
|
|
public int prediction_quantitization;
|
|
|
|
|
|
public int ricemodifier;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const UInt32 FCC_FTYP = ('f' << 24) + ('t' << 16) + ('y' << 8) + ('p' << 0);
|
|
|
|
|
|
const UInt32 FCC_MOOV = ('m' << 24) + ('o' << 16) + ('o' << 8) + ('v' << 0);
|
|
|
|
|
|
const UInt32 FCC_MDAT = ('m' << 24) + ('d' << 16) + ('a' << 8) + ('t' << 0);
|
|
|
|
|
|
const UInt32 FCC_FREE = ('f' << 24) + ('r' << 16) + ('e' << 8) + ('e' << 0);
|
|
|
|
|
|
const UInt32 FCC_M4A = ('M' << 24) + ('4' << 16) + ('A' << 8) + (' ' << 0);
|
|
|
|
|
|
const UInt32 FCC_ALAC = ('a' << 24) + ('l' << 16) + ('a' << 8) + ('c' << 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2008-11-04 22:00:29 +00:00
|
|
|
|
|