Search code examples
c#naudio

NAudio, Mixing two raw audio streams into a .wav file


 string path = string.Empty;
        private void SaveAudioRecording()
        {
            timer.Dispose();
            if (string.IsNullOrEmpty(path))
            {
                path = Directory.GetCurrentDirectory() + "\\AudioRecordings_"+clientSession.clientInfo[0];
                Directory.CreateDirectory(path);
            }

            WaveMixerStream32 mix= new WaveMixerStream32();
            foreach (var stream in new MemoryStream[] { audioOutputMemoryStream, audioInputMemoryStream })
            {
                stream.Position = 0;
                if (stream != null && stream.Length > 0 && stream.Position < stream.Length)
                {
                    RawSourceWaveStream reader = new RawSourceWaveStream(stream,new WaveFormat(41100,1));
                    WaveChannel32 chanel = new WaveChannel32(reader);
                    mix.AddInputStream(chanel);
                }
            }


            using (FileStream stream = File.Create(System.IO.Path.Combine(path, "Audio_recording_" + Directory.EnumerateFiles(path).Count().ToString() + ".wav")))
            {
                byte[] buffer = new byte[mix.Length];
                mix.Read(buffer, 0, buffer.Length);
                stream.Write(buffer, 0, buffer.Length);
                WriteWavHeader(stream,Convert.ToInt32(stream.Length));
                stream.Flush();
            }



            recordButton.IsEnabled = true;
        }

        private void WriteWavHeader(Stream stream, int audioDataSize)
        {
            using (var writer = new BinaryWriter(stream, Encoding.UTF8, true))
            {
                writer.Write(Encoding.ASCII.GetBytes("RIFF"));
                writer.Write(audioDataSize + 36); // Total file size - 8 bytes
                writer.Write(Encoding.ASCII.GetBytes("WAVE"));
                writer.Write(Encoding.ASCII.GetBytes("fmt "));
                writer.Write(16); // Length of format data
                writer.Write((ushort)1); // PCM format
                writer.Write((ushort)1); // Not Stereo
                writer.Write(41100); // Sample rate
                writer.Write(82200); // Byte rate (Sample rate * Channels * BitsPerSample / 8)
                writer.Write((ushort)4); // Block align (Channels * BitsPerSample / 8)
                writer.Write((ushort)16); // Bits per sample
                writer.Write(Encoding.ASCII.GetBytes("data"));
                writer.Write(audioDataSize); // Size of audio data
            }
        }

The file its ctreating is corupted, cant start it, but it do weigh a bit, the bytes do be written.

I was trying to mix two raw audio streams, into a complete .wav file, therefore writing a waveheader on it, the wave header do work, because I've tested it earlier. But now I've changed the code, which is corrupting the file.

What could possibly be the issue?


Solution

  • First of all, you're writing the header after the data, it should be the opposite.

    WriteWavHeader(stream,Convert.ToInt32(stream.Length));
    stream.Write(buffer, 0, buffer.Length);
    

    But if you do only this, you need then to adapt the "audioDataSize" to match the data size. If you keep the argument "stream.Length" to the function WriteWavHeader, it will be the size of the header, not the data, as the stream a that moment only contains the header data. So, use the buffer.Length instead. That's the data size.

    WriteWavHeader(stream,Convert.ToInt32(buffer.Length));
    stream.Write(buffer, 0, buffer.Length);
    

    Should do the trick. (Convert.ToInt32() call is not mandatory in that case, Length is already a int if i remember correctly