/// <summary> /// Decode the standard format. /// Copied from: "http://en.wikipedia.org/wiki/ADX_(file_format)" /// </summary> /// <param name="buffer">buffer is where the decoded samples will be put</param> /// <param name="samples_needed">samples_needed states how many sample 'sets' (one sample from every channel) need to be decoded to fill the buffer</param> /// <param name="looping_enabled">looping_enabled is a boolean flag to control use of the built-in loop</param> /// <returns>Returns the number of sample 'sets' in the buffer that could not be filled (EOS)</returns> public override byte[] Decode(byte[] encoded, bool looping_enabled) { BitReader bit_reader = new BitReader(encoded, true); List<byte> bufferOut = new List<byte>(); Initialize_Decode(); // Param: uint samples_needed = adx_header.total_samples; uint samples_per_block = (uint)((adx_header.block_size - 2) * 8 / adx_header.sample_bitdepth); short[] scale = new short[adx_header.channel_count]; if (looping_enabled && adx_header.loop_enabled == 0) looping_enabled = false; if (looping_enabled) // Mine sample_index = adx_header.loop_begin_sample_index; // Loop until the requested number of samples are decoded, or the end of file is reached while (samples_needed > 0 && sample_index < adx_header.total_samples) { // Calculate the number of samples that are left to be decoded in the current block uint sample_offset = sample_index % samples_per_block; uint samples_can_get = samples_per_block - sample_offset; // Clamp the samples we can get during this run if they won't fit in the buffer if (samples_can_get > samples_needed) samples_can_get = samples_needed; // Clamp the number of samples to be acquired if the stream isn't long enough or the loop trigger is nearby if (looping_enabled && sample_index + samples_can_get > adx_header.loop_end_sample_index) samples_can_get = adx_header.loop_end_sample_index - sample_index; else if (sample_index + samples_can_get > adx_header.total_samples) samples_can_get = adx_header.total_samples - sample_index; // Calculate the bit address of the start of the frame that sample_index resides in and record that location ulong started_at = (ulong)(adx_header.copyright_offset + 4 + sample_index / samples_per_block * adx_header.block_size * adx_header.channel_count) * 8; // Read the scale values from the start of each block in this frame for (uint i = 0; i < adx_header.channel_count; ++i) { bit_reader.Seek((int)(started_at + adx_header.block_size * i * 8)); scale[i] = bit_reader.Read_Short(); } // Pre-calculate the stop value for sample_offset uint sample_endoffset = sample_offset + samples_can_get; // Save the bitstream address of the first sample immediately after the scale in the first block of the frame started_at += 16; while (sample_offset < sample_endoffset) { for (uint i = 0; i < adx_header.channel_count; ++i) { // Predict the next sample double sample_prediction = coefficient[0] * past_samples[i * 2 + 0] + coefficient[1] * past_samples[i * 2 + 1]; // Seek to the sample offset, read and sign extend it to a 32bit integer bit_reader.Seek((int)((int)started_at + adx_header.sample_bitdepth * sample_offset + adx_header.block_size * 8 * i)); int sample_error = 0; if (adx_header.sample_bitdepth == 4) sample_error = bit_reader.Read_4Bits(); // Scale the error correction value sample_error *= scale[i]; // Calculate the sample by combining the prediction with the error correction int sample = sample_error + (int)sample_prediction; // Update the past samples with the newer sample past_samples[i * 2 + 1] = past_samples[i * 2 + 0]; past_samples[i * 2 + 0] = sample; // Clamp the decoded sample to the valid range for a 16bit integer if (sample > 32767) sample = 32767; else if (sample < -32768) sample = -32768; bufferOut.AddRange(BitConverter.GetBytes((short)sample)); } ++sample_offset; // We've decoded one sample from every block, advance block offset by 1 ++sample_index; // This also means we're one sample further into the stream --samples_needed; // And so there is one less set of samples that need to be decoded } // Check if we hit the loop end marker, if we did we need to jump to the loop start if (looping_enabled && sample_index == adx_header.loop_end_sample_index) //sample_index = adx_header.loop_begin_sample_index; break; } //return samples_needed; return bufferOut.ToArray(); }