// Reference: https://wiki.multimedia.cx/index.php/Microsoft_ADPCM public ISound Decode(ReadOnlySpan <byte> data, IWaveFormat fmtChunk, MicrosoftAdpcmFormat microsoftAdpcmFormat) { var channels = fmtChunk.Channels; var channelSamplesPerFrame = microsoftAdpcmFormat.SamplesPerBlock; var buffer = new float[channelSamplesPerFrame]; var frameSize = fmtChunk.BlockAlign; var max = data.Length / frameSize * frameSize; var output = Enumerable.Range(0, channels).Select(i => new List <float>()).ToArray(); // Apply coefficients var coefficients = new int[Math.Max(MicrosoftAdpcmConstants.DefaultCoefficients.Length, microsoftAdpcmFormat.Coefficients.Length)].AsMemory(); MicrosoftAdpcmConstants.DefaultCoefficients.AsSpan().CopyTo(coefficients.Span); microsoftAdpcmFormat.Coefficients.AsSpan().CopyTo(coefficients.Span); for (var offset = 0; offset < max; offset += frameSize) { var mem = data.Slice(offset, frameSize); for (var channel = 0; channel < channels; channel++) { DecodeFrame(mem, buffer, channel, channels, coefficients.Span); output[channel].AddRange(buffer); } } return(new Sound { Samples = output.Select(s => new Sample { Data = s }).Cast <ISample>().ToList() }); }
public IRiffContainer Encode(ISound sound, int samplesPerBlock) { var sampleRate = sound[NumericData.Rate]; if (sampleRate == null) { var sampleRates = sound .Samples .Select(s => s[NumericData.Rate]) .Where(r => r != null) .Distinct() .ToArray(); sampleRate = sampleRates.SingleOrDefault(); } if (sampleRate == null) { sampleRate = 44100; } var channels = sound.Samples.Count; var byteRate = sampleRate * channels * 2 / 4; //not accurate but not far off var container = new RiffContainer { Format = "WAVE", Chunks = new List <IRiffChunk>() }; var extraFormat = new MicrosoftAdpcmFormat { Coefficients = MicrosoftAdpcmConstants.DefaultCoefficients, SamplesPerBlock = 500 }; var format = new RiffFormat { Format = 2, SampleRate = (int)sampleRate, Channels = channels, ByteRate = (int)byteRate, BitsPerSample = 4, BlockAlign = _microsoftAdpcmEncoder.GetBlockSize(samplesPerBlock, channels), ExtraData = extraFormat.ToBytes() }; container.Chunks.Add(_formatEncoder.Encode(format)); container.Chunks.Add(new RiffChunk { Id = "data", Data = _microsoftAdpcmEncoder.Encode(sound, samplesPerBlock) }); return(container); }
public ISound Decode(Stream stream) { var riff = _riffStreamReader.Read(stream); if (riff.Format != "WAVE") { throw new RhythmCodexException("RIFF type must be WAVE."); } var fmt = riff.Chunks.FirstOrDefault(c => c.Id == "fmt "); if (fmt == null) { throw new RhythmCodexException("RIFF must contain the fmt chunk."); } var format = _waveFmtDecoder.Decode(fmt); var data = riff.Chunks.FirstOrDefault(c => c.Id == "data"); if (data == null) { throw new RhythmCodexException("RIFF must contain the data chunk."); } var result = new Sound { [NumericData.Rate] = format.SampleRate, Samples = new List <ISample>() }; switch (format.Format) { case 0x0001: // raw PCM { float[] decoded; switch (format.BitsPerSample) { case 8: decoded = _pcmDecoder.Decode8Bit(data.Data); break; case 16: decoded = _pcmDecoder.Decode16Bit(data.Data); break; case 24: decoded = _pcmDecoder.Decode24Bit(data.Data); break; case 32: decoded = _pcmDecoder.Decode32Bit(data.Data); break; default: throw new RhythmCodexException("Invalid bits per sample."); } foreach (var channel in decoded.Deinterleave(1, format.Channels)) { result.Samples.Add(new Sample { [NumericData.Rate] = format.SampleRate, Data = channel }); } break; } case 0x0002: // Microsoft ADPCM { var exFormat = new MicrosoftAdpcmFormat(format.ExtraData); var decoded = _microsoftAdpcmDecoder.Decode(data.Data.AsSpan(), format, exFormat); foreach (var sample in decoded.Samples) { result.Samples.Add(sample); } break; } case 0x0003: // 32-bit float { var decoded = _pcmDecoder.DecodeFloat(data.Data); foreach (var channel in decoded.Deinterleave(1, format.Channels)) { result.Samples.Add(new Sample { [NumericData.Rate] = format.SampleRate, Data = channel }); } break; } case 0x0011: // IMA ADPCM { var exFormat = new ImaAdpcmFormat(format.ExtraData); var decoded = _imaAdpcmDecoder.Decode(new ImaAdpcmChunk { Channels = format.Channels, Rate = format.SampleRate, Data = data.Data, ChannelSamplesPerFrame = exFormat.SamplesPerBlock }); foreach (var sample in decoded.SelectMany(s => s.Samples)) { result.Samples.Add(sample); } break; } } return(result); }