'Get PCM byte array from MediaFoundationResampler, Naudio
I'm working on a method to resample a wav file, here's the method:
internal byte[] ResampleWav(byte[] rawPcmData, int frequency, int bits, int channels, int newFrequency)
{
byte[] pcmData;
using (MemoryStream AudioSample = new MemoryStream(rawPcmData))
{
RawSourceWaveStream Original = new RawSourceWaveStream(AudioSample, new WaveFormat(frequency, bits, channels));
using (MediaFoundationResampler conversionStream = new MediaFoundationResampler(Original, new WaveFormat(newFrequency, bits, channels)))
{
// Here should go the code to get the array of bytes with the resampled PCM data
}
}
return pcmData;
}
The problem here is that there isn't any property in the MediaFoundationResampler that returns the size of the buffer. The method should return an array of bytes with the resampled PCM data only.
Thanks in advance!
--Edit
After some time working, I could get this:
internal byte[] WavChangeFrequency(byte[] rawPcmData, int frequency, int bits, int channels, int newFrequency)
{
byte[] pcmData;
using (MemoryStream AudioSample = new MemoryStream(rawPcmData))
{
RawSourceWaveStream Original = new RawSourceWaveStream(AudioSample, new WaveFormat(frequency, bits, channels));
using (MediaFoundationResampler conversionStream = new MediaFoundationResampler(Original, newFrequency))
{
//Start reading PCM data
using (MemoryStream wavData = new MemoryStream())
{
byte[] readBuffer = new byte[1024];
while ((conversionStream.Read(readBuffer, 0, readBuffer.Length)) != 0)
{
wavData.Write(readBuffer, 0, readBuffer.Length);
}
pcmData = wavData.ToArray();
}
}
}
return pcmData;
}
"Seems" to work fine, but there's another problem, seems that the PCM data byte array is greater than expected. Here's one of the tests I've tested with the method:
Input settings:
44100Hz
16 Bits
01 Channel
1846324 Bytes of PCM data
Expected (when I resample the same wav file with Audition, Audacity and WaveFormatConversionStream I get this):
22050Hz
16 Bits
01 Channel
923162 Bytes
MediaFoundationResampler result:
22050Hz
16 Bits
01 Channel
923648 Bytes
And the size changes drastically if I change the size of the readBuffer array.
The main problem is that MediaFoundationResampler doesn't have the property Length to know the real size of the resampled PCM data buffer. Using WaveFormatConversionStream the code would be this, but the quality is not very good:
internal byte[] WavChangeFrequency(byte[] rawPcmData, int frequency, int bits, int channels, int newFrequency)
{
byte[] pcmData;
using (MemoryStream AudioSample = new MemoryStream(rawPcmData))
{
RawSourceWaveStream Original = new RawSourceWaveStream(AudioSample, new WaveFormat(frequency, bits, channels));
using (WaveFormatConversionStream wavResampler = new WaveFormatConversionStream(new WaveFormat(newFrequency, bits, channels), Original))
{
pcmData = new byte[wavResampler.Length];
wavResampler.Read(pcmData, 0, pcmData.Length);
}
}
return pcmData;
}
What should I do to get the expected PCM data array, using the MediaFoundationResampler?
Solution 1:[1]
Disclaimer
I'm not familiar with the NAudio Library, so there might be a more proper way of doing this.
EDIT
Still not a good answer, seems still off by a few bytes... Some corrections to the code, using Mark Heath (NAudio creator) comment on this answer: https://stackoverflow.com/a/14481756/9658671
I keep the answer here for now, as it might help for finding a real answer, but I'll edit or remove it if necessary.
/EDIT
The difference in length between the file produced by Audition and your code is 923648 - 923162 = 486 bytes, which is less than your 1024 buffer.
It can be explained by the following mechanism:
At the very last call to the Read method, the remaining byte count is inferior to your buffer size. So instead of getting 1024 bytes, you get less.
But your code still adds a full 1024 byte group, instead of a smaller number. That explains the 486 bytes difference and the fact that this number will change if you choose another buffer size.
Fixing this should be easy.
From NAudio documentation: https://github.com/naudio/NAudio/blob/fb35ce8367f30b8bc5ea84e7d2529e172cf4c381/Docs/WaveProviders.md
The Read method returns the number for bytes that were read. This should never be more than numBytes and can only be less if the end of the audio stream is reached. NAudio playback devices will stop playing when Read returns 0.
So instead of pushing always 1024 bytes at each iteration, just push the number returned by the Read method.
Also, from Mark Heath comment:
the buffer size should be configurable to be an exact multiple of the block align of the WaveStream
So instead of choosing a "random" buffer size, use a multiple of the block align.
internal byte[] WavChangeFrequency(byte[] rawPcmData, int frequency, int bits, int channels, int newFrequency, int BlockAlign)
{
byte[] pcmData;
var BufferSize = BlockAlign * 1024;
using (MemoryStream AudioSample = new MemoryStream(rawPcmData))
{
RawSourceWaveStream Original = new RawSourceWaveStream(AudioSample, new WaveFormat(frequency, bits, channels));
using (MediaFoundationResampler conversionStream = new MediaFoundationResampler(Original, newFrequency))
{
//Start reading PCM data
using (MemoryStream wavData = new MemoryStream())
{
var ByteCount = 0;
var readBuffer = new byte[BufferSize];
while ((ByteCount = conversionStream.Read(readBuffer, 0, readBuffer.Length)) != 0)
{
wavData.Write(readBuffer, 0, ByteCount);
}
pcmData = wavData.ToArray();
}
}
}
return pcmData;
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
