naudio / NAudio

Audio and MIDI library for .NET
MIT License
5.38k stars 1.09k forks source link

Resampling audio from url to byte array or stream. Linux/.NET Core #1057

Closed aadupirn closed 7 months ago

aadupirn commented 10 months ago

Hello,

I can't seem to get an audio byte array resampled in a way that will work debugging locally using .NET Core on my windows machine as well as hosted on Linux.

I've tried a few different ways with different providers and can't seem to get things working at all. Either the output is unintelligble or it won't run on the Linux Server (using MediaFoundationResampler)

I'm now attempting to use the WdlResamplingSampleProvider which from my understanding should work anywhere.

Any tips?

Here's what I've currently got:

public Stream ConvertMp3ToRawAudioData(string mp3Url)
        {
            using (HttpClient httpClient = new HttpClient())
            {
                var mp3Data = httpClient.GetByteArrayAsync(mp3Url).Result;
                using (MemoryStream inMemoryFile = new MemoryStream(mp3Data))
                {
                    using (var reader = new Mp3FileReader(inMemoryFile))
                    {
                        var resampler = new WdlResamplingSampleProvider(reader.ToSampleProvider(), 16000);
                        var outMemoryFile = new MemoryStream();
                        WaveFileWriter.WriteWavFileToStream(outMemoryFile, resampler.ToWaveProvider());
                        return outMemoryFile;

                        //var wp = new SampleToWaveProvider(resampler);
                        //var outMemoryFile = new MemoryStream();
                        //var writer = new WaveFileWriter(outMemoryFile, wp.WaveFormat);
                        //byte[] b = new byte[wp.WaveFormat.AverageBytesPerSecond];
                        //while (true)
                        //{
                        //    int read = wp.Read(b, 0, b.Length);
                        //    if (read > 0)
                        //        writer.Write(b, 0, read);
                        //    else
                        //        break;
                        //}

                        //outMemoryFile.Position = 0;
                        //return outMemoryFile;
                    }
                }
            }
        }
chris-crucible commented 10 months ago

I was just having a similar issue and have something that seems to work. You can ignore the Mp3FrameDecompressor piece; I'm using NLayer because I need to run on non-Windows platforms. Key thing that was breaking it for me for a while was not calling waveFileWriter.Flush(), which then fails to write the header. So the resulting file had all the right data but was only playable in some apps (VLC), but not others.

public byte[] Resample(byte[] data, int samplingRate)
{
    var builder = new Mp3FileReaderBase.FrameDecompressorBuilder(wf => new Mp3FrameDecompressor(wf));
    using var memoryStream = new MemoryStream(data);
    using var mp3Reader = new Mp3FileReaderBase(memoryStream, builder);
    var sampleProvider = mp3Reader.ToSampleProvider();
    var resampler = new WdlResamplingSampleProvider(sampleProvider, samplingRate).ToWaveProvider();

    var outputStream = new MemoryStream();
    using var waveFileWriter = new WaveFileWriter(outputStream, resampler.WaveFormat);
    byte[] buffer = new byte[resampler.WaveFormat.AverageBytesPerSecond];
    int read;
    while((read = resampler.Read(buffer, 0, buffer.Length)) > 0)
        waveFileWriter.Write(buffer, 0, read);
    waveFileWriter.Flush();
    return outputStream.ToArray();
}
chris-crucible commented 10 months ago

Actually, just returning outputStream.GetBuffer() in my example, rather than ToArray, works as well, without needing to call Flush.