filoe / cscore

An advanced audio library, written in C#. Provides tons of features. From playing/recording audio to decoding/encoding audio streams/files to processing audio data in realtime (e.g. applying custom effects during playback, create visualizations,...). The possibilities are nearly unlimited.
Other
2.2k stars 454 forks source link

Is there any way to get concurrent access to the wav file? #355

Closed mithunvs closed 6 years ago

mithunvs commented 6 years ago

Im new to cscore audio library. Im trying to write a wave file while recording in the wasapi capture data available event, at the same time i need the written data to post to an API in fixed intervals.I want to get access to the file being written but if i dispose the file it will not write in the same fileand if i dont dispose the file i cant use the file to post to the API .Please help..

filoe commented 6 years ago

So you want to save the audio data in a file AND post it to an api at the same time? Would suggest to intercept the audio data. Use this sample as a reference: https://github.com/filoe/cscore/blob/master/Samples/Recorder/MainWindow.cs

Some dummy code to show you an example:

//1. init capture
var soundIn = new WasapiCapture ...
soundIn.Initialize();

//2. wrap capture to an audio stream
var soundInSource = new SoundInSource(soundIn);

//3. wrap audio stream within an NotificationSource in order to intercept samples
var notificationSource = new NotificationSource(soundInSource);
notificationSource.Interval = 5000; //5 seconds
notificationSource.BlockRead += (s, e) =>
{
    //send data provided by eventargs to api
};

//4. convert whole chain back to WaveSource to write wave to the file
var waveSource = notificationSource.ToWaveSource();

//5. initialize the wave writer
var writer = new WaveWriter("out.wav", waveSource.WaveFormat);

//buffer for reading from the wavesource
byte[] buffer = new byte[waveSource.WaveFormat.BytesPerSecond / 2];

//6. if capture serves new data to the audio stream chain, read from the last chain element (wavesource) and write it back to the file
     this will process audio data from capture to notificationSource to wavesource and will also trigger the blockread event of the notificationSource
soundInSource.DataAvailable += (s, e)
{
    int read;
    while((read = waveSource.Read(buffer, 0, buffer.Length)) > 0)
        writer.Write(buffer, 0, read);
};
mithunvs commented 6 years ago

I have to write the samples in the file ,If i provide 5 second interval, I have to get 5 seconds of data written to a file and in next 5 seconds next data should be written to another file and so on. If u understand my problem please give me a solution.I have to sent these files to my api

filoe commented 6 years ago

The source above will do that. As soon as a block of 5 seconds was processed, it will provide it to you. Use e.Data to get the read samples.

mithunvs commented 6 years ago

I used a code like this because the e.data gives float values but the wave writer only writes the byte values and still i get a 1 second of data,can u please suggest the code to get a 5 second data ` class recorder { private WasapiCapture soundIn; private IWriteable writer; public void Start() { soundIn = new WasapiLoopbackCapture(); soundIn.Initialize(); var soundInSource = new SoundInSource(soundIn);
var notificationSource = new NotificationSource(soundInSource.ToSampleSource());

        notificationSource.Interval = 5000;
        notificationSource.BlockRead += (s, e) =>
        {
            float[] interceptsBuff = new float[notificationSource.WaveFormat.BytesPerSecond / 2];

            string directory = System.IO.Directory.GetCurrentDirectory();
            string filename = /*DateTime.Now.Ticks.ToString()*/ "sample"+ ".wav";
            directory = directory + "\\Cache\\";
            string fileName = System.IO.Path.Combine(directory + filename);
            var intercepts = new WaveWriter(fileName, notificationSource.WaveFormat);
            interceptsBuff = e.Data;
            byte[] buff= new byte[interceptsBuff.Length * 4];
            Buffer.BlockCopy(interceptsBuff, 0, buff, 0, buff.Length);
            intercepts.Write(buff, 0, e.Length);
            intercepts.Dispose();

        };

        var waveSource = notificationSource.ToWaveSource();
        writer = new WaveWriter("out.wav", waveSource.WaveFormat);
        byte[] buffer = new byte[waveSource.WaveFormat.BytesPerSecond / 2];

        soundInSource.DataAvailable += (s, e) =>            
            {

                int read;
                while ((read = waveSource.Read(buffer, 0, buffer.Length)) > 0)
                    writer.Write(e.Data, 0, read);
            };
        soundIn.Start();

    }
    public void stop()
    {
        soundIn.Stop();
        soundIn.Dispose();
        soundIn = null;

        if (writer is IDisposable)
            ((IDisposable)writer).Dispose();
    }

}`
filoe commented 6 years ago

So you want to create a new wave file every few seconds? Why would you want to do that? To post it to an api? Why don't you just stream the byte data to the api?

mithunvs commented 6 years ago

Yes i want to create a file every few seconds. I need this 5 seconds data for later use. Is there any way for doing that.

filoe commented 6 years ago

Well, it is doing what you want. Take a look at this one:

//1. init capture
            var soundIn = new WasapiLoopbackCapture();
            soundIn.Initialize();

            //2. wrap capture to an audio stream
            var soundInSource = new SoundInSource(soundIn);

            //3. wrap audio stream within an NotificationSource in order to intercept samples
            var notificationSource = new NotificationSource(soundInSource.ToSampleSource());
            notificationSource.Interval = 5000; //5 seconds
            notificationSource.BlockRead += (s, e) =>
            {
                var filename = $"{DateTime.Now.Ticks}.wav";

                //send data provided by eventargs to api
                using (var writer = new WaveWriter(filename, notificationSource.WaveFormat))
                {
                    Debug.Assert(e.Data.Length == e.Length);
                    writer.WriteSamples(e.Data, 0, e.Length);
                }

                var length = TimeConverterFactory.Instance.GetTimeConverterForSource(notificationSource)
                    .ToTimeSpan(notificationSource.WaveFormat, e.Length);
                Console.WriteLine($"Written {e.Length} ({length:g}) samples to file {filename}");
            };

            //4. convert whole chain back to WaveSource to write wave to the file
            var waveSource = notificationSource.ToWaveSource();

            //buffer for reading from the wavesource
            byte[] buffer = new byte[waveSource.WaveFormat.BytesPerSecond / 2];

            //5. if capture serves new data to the audio stream chain, read from the last chain element (wavesource) and write it back to the file
            // this will process audio data from capture to notificationSource to wavesource and will also trigger the blockread event of the notificationSource
            soundInSource.DataAvailable += (s, e) =>
            {
                int read;
                while ((read = waveSource.Read(buffer, 0, buffer.Length)) > 0)
                {
                    //do nothing
                }
            };

            soundIn.Start();

            Console.ReadKey();
mithunvs commented 6 years ago

Thanks that's what I've been looking for. Works like a charm. By the way you have a great library

filoe commented 6 years ago

Take a look at the source of the NotificationSource and write your own implementation of it.

mithunvs commented 6 years ago

Yes that worked perfectly.Thank you for your advice. Keep up the good work