This project has moved and is read-only. For the latest updates, please go here.

Feature Suggestion: Direct To Disk Audio Streaming

Apr 5, 2009 at 2:21 AM
Hi Mark,

In the process of writing the next Tutorial for NAudio I was looking at options for Recording audio. From what I can see, at least in the demo application, the way to do this is use the sound card to open the Sound Card's input for capture and then send the incoming data through the wave stream to the WaveFileWriter class. This is a great feature for when capturing Audio from an external source is required but OpenSebJ has a slightly different requirement, as it is looking to prodominatly capture the audio being generated throgh NAudio. So in this scenario, the audio is being mixed by the WaveMixerStream32 class, then sent to the Audio Hardware by which ever output method has been used and then relying on the Audio Hardware's mixer to be able to reroute the Audio Output to the Input via an effective loopback (a WhatUHear or Wave or Stero Mix or what ever the particular sound card calls it).

What I am seeking to propose is that a Direct To Disk option is added in the WaveMixerStream32 class allowing us to stream out the mixed audio directly from NAudio to a file. This allows for a few other options to be imediatly possiable for OpenSebJ and any other application. It means that if there is another Audio Source on the computer playing audio at the same time, say a click track being played in another application, that you want to play as a backing against but do not wish to record the backing at the same time, then streaming the content being mixed by NAudio before it reaches the Audio Hardware allows for this. I actually had this feature requested in a previous version of OpenSebJ by a user who I think viewed this some what as an accessibility issue http://sourceforge.net/tracker/?func=detail&aid=1723572&group_id=146061&atid=763961

I've had a crack at this and have implemented the following changes in a local copy of WaveMixerStream32

public class WaveMixerStream32 : WaveStream
{

<...>

        private bool streamToDisk;
        private string streamToDiskFileName;
        WaveFileWriter writer;

<...>

        public void StartStreamingToDisk()
        {
            if (streamToDiskFileName != "")
            {
                streamToDisk = true;
            }
        }

        public void PauseStreamingToDisk()
        {
            streamToDisk = false;
        }

        public void ResumeStreamingToDisk()
        {
            streamToDisk = true;
        }

        public void StopStreamingToDisk()
        {
            streamToDisk = false;
            writer.Close();
        }


        public void StreamMixToDisk(string FileName)
        {
            streamToDiskFileName = FileName;
            writer = new WaveFileWriter(FileName, this.WaveFormat);
        }

        private void WriteMixStreamOut(byte[] buffer, int offset, int count)
        {
            writer.WriteData(buffer, offset, count);
        }


<...>

        public override int Read(byte[] buffer, int offset, int count)
        {

        <...>

            position += count;
            // If streamToDisk has been enabled the mixed audio will be streamed directly to a wave file, so we need to send the data to the wave file writer
            if (streamToDisk)
            {
                WriteMixStreamOut(readBuffer, 0, count);
            }
            return count;
        }

        <...>

I hope that makes sense, it's only a minor modification to the class but based on the tests I ran the recorded audio sounded perfect as there was no additional conversion required and there is no additional interference in the recorded audio, so silence really is silence. I wasn't sure if this is the right location to implement this change but based on where the mixing is occurring and the data that's required to stream the mixed output it seems like the logical place to pick it up.

What do you think, worthy of inclusion?

Cheers,
Sebastian

Apr 11, 2009 at 4:55 AM
Amendment to that last post required, I picked up the wrong buffer in the Read method, when streaming to disk - I only noticed it today when testing with multiple samples simultaneously though the sampler, should have been:

if (streamToDisk)
{
       WriteMixStreamOut(buffer, 0, count);
}

Cheers,
Sebastian