Decode and play a Raw Streaming of IMADPCM

Jul 17 at 9:46 PM
Hi,

I'm tryng to decode and play a raw bytes of a streaming of IMAPCM without header.

(Sorry for my bad english)

This is the codec information of the raw audio (I extract the raw streaming by VLC and the information codec by MediaInfo)

Image

With that and the information on this discussion I could create the following code that , theoritically, I decode de raw audio to PCM but the PCM stream don't have anything.

My own ImaAdpcmWaveFormat class
[StructLayout(LayoutKind.Sequential, Pack = 2)]
    class MyImaAdpcmWaveFormat : NAudio.Wave.WaveFormat
    {
        short samplesPerBlock;

        public MyImaAdpcmWaveFormat(int sampleRate, int channels, int bitsPerSample)
        {
            this.waveFormatTag = WaveFormatEncoding.ImaAdpcm; //.DviAdpcm;   
            this.sampleRate = sampleRate;
            this.channels = (short)channels;
            this.bitsPerSample = (short)bitsPerSample; //
            this.extraSize = 2;            
            this.blockAlign = 256; //
            this.averageBytesPerSecond = 4055; //
            this.samplesPerBlock = 505; // 
        }
The Load_Form method. Here I init all the necesaries elements:
        private BufferedWaveProvider bufferedWaveProvider = null;
        private WaveFormat AudioFormat = null;
        private WaveFileWriter waveWriter = null;
        private WaveFileWriter waveWriter2 = null;

private void Form1_Load(object sender, EventArgs e)
        {
            AudioFormat = new MyImaAdpcmWaveFormat(8000, 1, 4);
            WaveFormat DecodedAudioFormat = new WaveFormat(8000, 16, 1);

            bufferedWaveProvider = new NAudio.Wave.BufferedWaveProvider(DecodedAudioFormat);
            bufferedWaveProvider.BufferDuration = TimeSpan.FromSeconds(20);

            waveWriter = new NAudio.Wave.WaveFileWriter("temp.wav", AudioFormat);
            waveWriter2 = new NAudio.Wave.WaveFileWriter("temp2.wav", DecodedAudioFormat);

            waveOut = new NAudio.Wave.DirectSoundOut(70);
            waveOut.Init(bufferedWaveProvider);
            waveOut.Play();               

        }
And the method where add the decode audio to the bufferedWaveProvider:
private void update_sound(object sender, NewAudioFrameEventArgs e)
        {
            MemoryStream byteStream = new MemoryStream(e.Frame);            

            using (var reader = new RawSourceWaveStream(byteStream, AudioFormat))
            {
                byte[] decoded = ConvertNonSeekableStreamToByteArray(reader);

                waveWriter.Write(decoded, 0, decoded.Length);
                waveWriter.Flush();

                using (var pcmStream = WaveFormatConversionStream.CreatePcmStream(reader))
                {
                    byte[] decoded2 = ConvertNonSeekableStreamToByteArray(pcmStream);
                    waveWriter2.Write(decoded2, 0, decoded2.Length);
                    waveWriter2.Flush();
                    bufferedWaveProvider.AddSamples(decoded2, 0, decoded2.Length);      
                }
            }              
        }

private byte[] ConvertNonSeekableStreamToByteArray(Stream NonSeekableStream)
        {
            byte[] buffer = new byte[1024];

            using (MemoryStream ms = new MemoryStream())
            {
                int read;
                do
                {
                    read = NonSeekableStream.Read(buffer, 0, buffer.Length);
                    ms.Write(buffer, 0, read);
                }
                while (read > 0);

                return ms.ToArray();
            }
        }
When I try to play the bufferedWaveProvider it don't sound. If I try to see the temp2.wav it only have 46 byte without any byte of audio.

I have something bad? Can someone help me o give some advice?
Jul 24 at 2:48 PM
Plis Someone help me
Jul 24 at 3:32 PM
I release that waveWriter (where save the Raw stream) is growing but when i can't listen it with vlc.

Could be that MyImaAdpcmWaveFormat class have wrongs parameter?
Where o How I could compare?
Some Idea?
Coordinator
Jul 28 at 4:07 PM
best way to find out what exact format your WaveFormat should be is to examine the ACM codecs themselves as they will provide example WaveFormat structures. The NAudio demo application does this for you - select the ACM format conversion demo and click on the codec that does IMA ADPCM to see its formats enumerated.
Jul 28 at 5:52 PM
Thanks markheat for your answer.

I could extract the following information:

Format 0: 8,000 KHz, 4 bit, Mono
  FormatTag: DviAdpcm, Support Flags: Codec
  WaveFormat: DviAdpcm 8000Hz Channels: 1 Bits: 4 Block Align: 256, AverageBytesPerSecond: 4055 (32,4 kbps), Extra Size: 2
  Extra Bytes:
  F9 01 
But MyImaAdpcm wave format have that info,
AudioFormat = new MyImaAdpcmWaveFormat(8000, 1, 4); // creation of the MyImaAdpcmWaveFormat object

[StructLayout(LayoutKind.Sequential, Pack = 2)]
    class MyImaAdpcmWaveFormat : NAudio.Wave.WaveFormat
    {
        short samplesPerBlock;

        public MyImaAdpcmWaveFormat(int sampleRate, int channels, int bitsPerSample)
        {
            this.waveFormatTag = WaveFormatEncoding.ImaAdpcm; //.DviAdpcm;   
            this.sampleRate = sampleRate;
            this.channels = (short)channels;
            this.bitsPerSample = (short)bitsPerSample; //
            this.extraSize = 2;            
            this.blockAlign = 256; //
            this.averageBytesPerSecond = 4055; //
            this.samplesPerBlock = 505; // 
        }
Only forthe "samplesPerBlock" parameter I don't have any information.

Could be that the problem? How i can calculate it?
Aug 5 at 3:55 PM
I could found the code for decode de raw audio to pcm.

But when i tried to play de decode data, only noise is playback.

If i decode the raw data to pcm, can i play directly using bufferedWaveProvider?
or i need to do something with the decode data before to put in the bufferedWaveProvider?
//e.Frame is the raw encode sound data, code by IMA ADPcm
byte[] decoded = IMA_decode.adpcm_decode(e.Frame, e.Frame.Length);
bufferedWaveProvider.AddSamples(decoded, 0, decoded.Length);
Can anyone give some advice?
Thanks
Coordinator
Aug 6 at 10:00 AM
SamplesPerBlock is the extra bytes 0x1F9 = 505, so that should be correct.
What does adpcm_decode actually do?
And what is the WaveFormat of your bufferedWaveProvider?
Aug 6 at 4:15 PM
This is my code for init the bufferedWaveProvider and the DirectSoundOut:
DecodedAudioFormat = new WaveFormat(8000, 16, 1);

bufferedWaveProvider = new NAudio.Wave.BufferedWaveProvider(DecodedAudioFormat);
bufferedWaveProvider.BufferDuration = TimeSpan.FromSeconds(1);

waveWriter = new NAudio.Wave.WaveFileWriter("temp2.wav", DecodedAudioFormat);

waveOut = new NAudio.Wave.DirectSoundOut(100);
waveOut.Init(bufferedWaveProvider);
waveOut.Play();        
The waveformat of the bufferedWaveProvider is PCM at 8000 rate, 16 bit depth and 1 channel.

When I tried to use MyImaAdpcmWaveFormat class didn't work, I look something for bypass this part.

I was looking for the code to decode the raw IMADPCM to raw PCM for then only add that raw bytes to the bufferedWaveProvider.

I found the following code for the decode:
class Adpcm_code
    {
        private int pre_sample;
        private int index;

        public Adpcm_code()
        {
            pre_sample = 0;
            index = 0;
        }

        private int[] index_adjust = new int[8] {-1,-1,-1,-1,2,4,6,8};

        private int[] step_table = new int[89] 
        {
            7,8,9,10,11,12,13,14,16,17,19,21,23,25,28,31,34,37,41,45,
            50,55,60,66,73,80,88,97,107,118,130,143,157,173,190,209,230,253,279,307,337,371,
            408,449,494,544,598,658,724,796,876,963,1060,1166,1282,1411,1552,1707,1878,2066,
            2272,2499,2749,3024,3327,3660,4026,4428,4871,5358,5894,6484,7132,7845,8630,9493,
            10442,11487,12635,13899,15289,16818,18500,20350,22385,24623,27086,29794,32767
        };
    
        public byte[] adpcm_decode(byte[] raw, int len)
        {
            int i = 0;
            int code = 0;
            int sb = 0;
            int delta = 0;

            short[] pcm = new short[len * 2];
            byte[] pcm_byte = new byte[len * 4];

            len <<= 1;

            for (i = 0; i < len; i++)
            {
                if ((i & 0x01) == 1)// i es impar
                {
                    code = raw[i >> 1] & 0x0f;  // guardo los 4 bit menos significativos de raw[i >> 1]
                }
                else                // i es par
                {
                    code = raw[i >> 1] >> 4;    // guardo los 4 bit mas significativos de raw[i >> 1]
                }

                if ((code & 8) != 0) // si el 4 bit es 1
                {
                    sb = 1;
                }
                else                 // si el 4 bit es 0
                {
                    sb = 0;
                }

                code &= 7;           // Pongo en 0 el 4 bit

                //delta = (step_table[index] * code) / 4 + step_table[index] / 8;   // 
                delta = ((step_table[index] * code) >> 2) + (step_table[index] >> 3);   // 

                if (sb == 1)
                {
                    delta = -delta;
                }

                pre_sample += delta;    // 

                if (pre_sample > 32767)
                {
                    pre_sample = 32767;
                }
                else if (pre_sample < -32768)
                {
                    pre_sample = -32768;
                }

                pcm[i] = (short)pre_sample;

                index += index_adjust[code];

                if (index < 0)
                {
                    index = 0;
                }
                else if (index > 88)
                {
                    index = 88;
                }
            }

            pcm_byte = shortTobyte(pcm, pcm.Length);

            return pcm_byte;
        }
}
I extract this code for an aplication that do the decode. The problem with this aplication it is very old, is write in visual 6, and is write in c++.

So, if the decode code is doing its work, how i have to give the raw pcm bytes to the bufferedWaveProvider?

Thank for the help, i believe that i'm very close to the solution.
Coordinator
Aug 6 at 4:31 PM
just use bufferedWaveProvider.AddSamples
Aug 6 at 5:37 PM
I do that, but i can only listen noise

what could be the problem?