private IEnumerable <ISample> SplitInternal(VagChunk chunk) { if (chunk.Channels == 1) { var length = (int)(chunk.Length ?? chunk.Data.Length); var totalSamples = length * 28 / 16; var output = new float[totalSamples]; _vagDecrypter.Decrypt(chunk.Data, output, length, new VagState()); yield return(new Sample { Data = output }); } else { var interleave = chunk.Interleave; var interval = interleave * chunk.Channels; var buffer = new float[interleave * 28 / 16]; var length = chunk.Length ?? chunk.Data.Length; for (var channel = 0; channel < chunk.Channels; channel++) { var output = new List <float>(); var state = new VagState(); var offset = channel * interleave; while (offset < length) { _vagDecrypter.Decrypt(chunk.Data.AsSpan(offset), buffer, interleave, state); offset += interval; output.AddRange(buffer); } yield return(new Sample { Data = output }); } } }
public int Decrypt(ReadOnlySpan <byte> input, Span <float> output, int length, VagState state) { var inOffset = 0; var outOffset = 0; var last0 = state.Prev0; var last1 = state.Prev1; var enabled = state.Enabled; var maxLength = Math.Min(length, input.Length - 0x0F); while (inOffset < length) { if (inOffset >= maxLength) { enabled = false; } var flags = input[inOffset + 1]; if (!enabled) { for (var i = 2; i < 16; i++) { output[outOffset++] = 0; output[outOffset++] = 0; } } else { var fm = input[inOffset]; var filter = fm >> 4; var magnitude = fm & 0xF; if (magnitude > 12 || filter > 4) { magnitude = 12; filter = 0; } for (var i = 2; i < 16; i++) { var deltas = input[inOffset + i]; var delta0 = (deltas & 0x0F) << 28; var delta1 = (deltas & 0xF0) << 24; var coeff0 = VagCoefficients.Coeff0[filter]; var coeff1 = VagCoefficients.Coeff1[filter]; var filter0 = coeff0 * last0; var filter1 = coeff1 * last1; var sample = (delta0 >> (magnitude + 16)) + ((filter0 + filter1) >> 6); if (sample > short.MaxValue) { sample = short.MaxValue; } else if (sample < short.MinValue) { sample = short.MinValue; } last1 = last0; last0 = sample; output[outOffset++] = sample / 32768f; filter0 = coeff0 * last0; filter1 = coeff1 * last1; sample = (delta1 >> (magnitude + 16)) + ((filter0 + filter1) >> 6); if (sample > short.MaxValue) { sample = short.MaxValue; } else if (sample < short.MinValue) { sample = short.MinValue; } last1 = last0; last0 = sample; output[outOffset++] = sample / 32768f; } if ((flags & 1) != 0) { enabled = false; } } if ((flags & 5) == 4) { enabled = true; } inOffset += 16; } state.Prev0 = last0; state.Prev1 = last1; state.Enabled = enabled; return(outOffset); }
public void Encrypt(ReadOnlySpan <float> input, Span <byte> output, int length, VagState state) { var statePrev0 = state.Prev0; var statePrev1 = state.Prev1; var filterCount = VagCoefficients.Coeff0.Length; const int maxMagnitude = 12; const int magnitudeCount = 13; var inOffset = 0; var outOffset = 0; var maxOffset = length - 27; var frameDiff = new double[filterCount * magnitudeCount]; var frameDiffSpan = frameDiff.AsSpan(); var workBuffer = new byte[16 * filterCount * magnitudeCount]; var workBufferSpan = workBuffer.AsSpan(); var last0Buffer = new int[filterCount * magnitudeCount]; var last1Buffer = new int[filterCount * magnitudeCount]; var filterMagnitude = new int[filterCount]; var inputSampleBuffer = new int[28]; var lastOutOffset = -16; while (inOffset < maxOffset) { var inBuffer = input.Slice(inOffset); var outBuffer = output.Slice(outOffset); workBufferSpan.Fill(0x00); frameDiffSpan.Fill(0f); for (var index = 0; index < 28; index++) { inputSampleBuffer[index] = (int)(inBuffer[index] * 32768f); } for (var filter = 0; filter < filterCount; filter++) { filterMagnitude[filter] = maxMagnitude; var coeff0 = VagCoefficients.Coeff0[filter]; var coeff1 = VagCoefficients.Coeff1[filter]; for (var magnitude = 0; magnitude < filterMagnitude[filter]; magnitude++) { var diffIndex = filter + magnitude * filterCount; var workBufferIndex = diffIndex * 16; var last0 = statePrev0; var last1 = statePrev1; workBuffer[workBufferIndex] = unchecked ((byte)(magnitude | (filter << 4))); for (var index = 0; index < 28; index++) { var filter0 = last0 * coeff0; var filter1 = last1 * coeff1; var inputSample = inputSampleBuffer[index]; var sampleToEncode = inputSample - ((filter0 + filter1) >> 6); if (sampleToEncode > 32767) { sampleToEncode = 32767; } if (sampleToEncode < -32768) { sampleToEncode = -32768; } var nybble = ((sampleToEncode << (magnitude + 16)) >> 28) & 0xF; if (nybble == 0x8 || nybble == 0x7) { filterMagnitude[filter] = Math.Min(filterMagnitude[filter], magnitude); } var encodedSample = ((nybble << 28) >> (magnitude + 16)) + ((filter0 + filter1) >> 6); double outDiff = encodedSample - inputSample; frameDiff[diffIndex] += outDiff * outDiff; var shift = (index & 1) << 2; var workIndex = 2 + (index >> 1); workBuffer[workBufferIndex + workIndex] |= unchecked ((byte)(nybble << shift)); last1 = last0; last0 = encodedSample; } last0Buffer[diffIndex] = last0; last1Buffer[diffIndex] = last1; } } // Determine the most accurate frame var bestFrameDiffIndex = 0; var bestFrameDiff = double.MaxValue; for (var filter = 0; filter < filterCount; filter++) { var highestMagnitude = filterMagnitude[filter]; var lowestMagnitude = Math.Max(0, highestMagnitude - 1); for (var magnitude = highestMagnitude; magnitude >= lowestMagnitude; magnitude--) { var diffIndex = filter + magnitude * filterCount; if (frameDiff[diffIndex] >= bestFrameDiff) { continue; } bestFrameDiffIndex = diffIndex; bestFrameDiff = frameDiff[diffIndex]; } } // Write out the best frame. workBufferSpan.Slice(bestFrameDiffIndex * 16, 16).CopyTo(outBuffer); // If the frame is empty, write out 0x0C for filter/magnitude. if ((outBuffer[0] & 0xF0) == 0x00) { var isEmptyFrame = true; for (var i = 2; i < 16; i++) { if (outBuffer[i] != 0) { isEmptyFrame = false; break; } } if (isEmptyFrame) { outBuffer[0] = 0x0C; } } statePrev1 = last1Buffer[bestFrameDiffIndex]; statePrev0 = last0Buffer[bestFrameDiffIndex]; lastOutOffset = outOffset; inOffset += 28; outOffset += 16; } // Mark the end of the stream. if (lastOutOffset >= 0) { output[lastOutOffset + 1] |= 0x01; } // Mark the start of the stream. if (outOffset >= 0) { output[1] |= 0x04; } state.Prev0 = statePrev0; state.Prev1 = statePrev1; }