Record from microphone to WMA format

Oct 13, 2014 at 6:26 AM
I am trying record from the default windows microphone and save the resulting audio as a wma compressed file. I have looked over your library and it's very large and overwhelming, that and the documentation is rather thin. Can you give me somewhere to start?

Much Thanks
Coordinator
Oct 13, 2014 at 3:29 PM
I've just added the "RecordToWma" sample. Take a look at it. Should be pretty much self explaining.
Oct 28, 2014 at 8:01 AM
Edited Oct 28, 2014 at 8:12 AM
Hello
Wow great library. I have search for something to use just like this. I’m new to audio programing and came across CSCore with an internet search.
I am trying to record wma from a mic for its rather small size. I had a look at your “added microphone to wma sample.” and modified it to work in a windows forms application.
I have a start button and a stop button. The problem arises when the “_wasapiCapture.Stop()
” event is called, I call this from the btnStop event. I recon this is because of the Score library running in other threads. I have tried different clean-up implementations, but as soon as “stop is called the program crashes and the resulting wma file cannot be played back. Howver if I close the program by stopping the debuging in visual studio all seems fine and the wma file works.
So my question’s
  1. How do I safely stop and dispose the “_wasapiCapture” object, when it is not called from within the using statements as in your example?
  2. How do I implement a wma writer in a thread safe manner?
  3. And are there any examples of using a specific wma code like the wma voice code for microphone recordings.
    Thanx
    Ferdinad
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using CSCore.MediaFoundation;
using CSCore.SoundIn;

namespace AudioTestCSCORE
{
public partial class Form1 : Form
{
    WasapiCapture _wasapiCapture;
    MediaFoundationEncoder _writer;
    CSCore.CoreAudioAPI.AudioMeterInformation _meterInfo;

    //delaget to update progressbar safley
    delegate void SetVolumeCallback(int iVolume);

    public Form1()
    {
        InitializeComponent();             
    }

    private void btnRecord_Click(object sender, EventArgs e)
    {
        SaveFileDialog save = new SaveFileDialog();
        save.Filter = "wma Files(*.wma)|*.wma;";
        if (save.ShowDialog() != System.Windows.Forms.DialogResult.OK) return;

        //_wasapiCapture = new WasapiCapture(true, CSCore.CoreAudioAPI.AudioClientShareMode.Shared);
        _wasapiCapture = new WasapiCapture();
        _wasapiCapture.Initialize();
        _writer = MediaFoundationEncoder.CreateWMAEncoder(_wasapiCapture.WaveFormat, save.FileName);

        _wasapiCapture.DataAvailable += new EventHandler<DataAvailableEventArgs>(_wasapiCapture_DataAvailable);

        _meterInfo = CSCore.CoreAudioAPI.AudioMeterInformation.FromDevice(_wasapiCapture.Device);
        _wasapiCapture.Start();
    }


    void _wasapiCapture_DataAvailable(object sender, DataAvailableEventArgs e)
    {
        _writer.Write(e.Data, e.Offset, e.ByteCount);

        //Tread safe volume
        int vol = (int)Math.Round(_meterInfo.GetPeakValue() * 100);
        SetVolume(vol);
    }

    private void btnStop_Click(object sender, EventArgs e)
    {
        if (_wasapiCapture != null)
        {
            _wasapiCapture.Stop();// This line causes the program to crash
            _wasapiCapture = null;
        }

        if (_writer != null)
        {
            _writer.Dispose();
            _writer = null;
        }

        if (_meterInfo != null)
        {
            _meterInfo.Dispose();
            _meterInfo = null;
        }
    }


    //Tread safe volume
    private void SetVolume(int Volume)
    {
        if (this.progressBar1.InvokeRequired)
        {
            SetVolumeCallback d = new SetVolumeCallback(SetVolume);
            this.Invoke(d, new object[] { Volume });
        }
        else
        {
            progressBar1.Value = Volume;
        }
    }
}
}
Coordinator
Oct 28, 2014 at 2:14 PM
Edited Oct 28, 2014 at 2:15 PM
Hmm I tried your Code but I could not crash the process with that piece of code. The only mistake I found is that you are using Invoke. The problem here is that this causes a deadlock. Use BeginInvoke instead (I really mention that in the documentation). Oh and I would call Dispose() instead of Stop() since Stop just stops the internal capture thread but won't dispose all the resources.
Oct 29, 2014 at 11:20 AM
Edited Oct 29, 2014 at 12:01 PM
Hello
Thanks for the quick reply. I have implemented you changes. The dispose method seems to do the trick but when I close the form I get a null reference exception.
The resulting .wma file does play but has no length, so other programs can seek through the file. I have also tried using a memory stream and the writing the file. I can you please provide an example of how to save the .wma file with tags and length.
Also I notice that the resulting play back is a bit shorter than the actual recording (a few seconds)
Would it be better to use the direct sound options? How would you implement it if using direct sound as opposed to “wasapi”


Edit
I recon I would rather record directly to the hard drive as the length might vary greatly. The memory stream was just to test things.


Regards
System.IO.MemoryStream _memoryStream = new System.IO.MemoryStream();


        private void btnRecord_Click(object sender, EventArgs e)
        {
            _wasapiCapture = new WasapiCapture();
            _wasapiCapture.Initialize();

            _writer = MediaFoundationEncoder.CreateWMAEncoder(_wasapiCapture.WaveFormat, _memoryStream);
            _wasapiCapture.DataAvailable += new EventHandler<DataAvailableEventArgs>(_wasapiCapture_DataAvailable);
            
            _meterInfo = CSCore.CoreAudioAPI.AudioMeterInformation.FromDevice(_wasapiCapture.Device);
            
            _wasapiCapture.Start();
        }

 private void btnStop_Click(object sender, EventArgs e)
        {
            if (_wasapiCapture != null)
            {
                _wasapiCapture.DataAvailable -= _wasapiCapture_DataAvailable;
                SaveFileDialog save = new SaveFileDialog();
                save.Filter = "wma Files(*.wma)|*.wma;";
                if (save.ShowDialog() != System.Windows.Forms.DialogResult.OK) return;
                using (_memoryStream)
                {
                    using (System.IO.FileStream file = new System.IO.FileStream(save.FileName, System.IO.FileMode.Create, System.IO.FileAccess.Write))
                    {
                        _memoryStream.WriteTo(file);
                    }
                }
                _wasapiCapture.Dispose();
                _wasapiCapture = null;
            }
        }

        void _wasapiCapture_DataAvailable(object sender, DataAvailableEventArgs e)
        {
            _writer.Write(e.Data, e.Offset, e.ByteCount);
            _memoryStream.Flush();
            
            //Tread safe volume
            int vol = (int)Math.Round(_meterInfo.GetPeakValue() * 100);
            SetVolume(vol);
            SetCapturedDuration(_writer.EncodedDuration);
        }


        //Tread safe volume
        private void SetVolume(int Volume)
        {
            if (this.progressBar1.InvokeRequired)
            {
                SetVolumeCallback delagetVolume = new SetVolumeCallback(SetVolume);
                this.BeginInvoke(delagetVolume, new object[] { Volume });
            }
            else
            {
                progressBar1.Value = Volume;
            }
        }

        private void SetCapturedDuration(TimeSpan Time)
        {
            if (this.label1.InvokeRequired)
            {
                SetCapturedDurationCallBack delegatCapture = new SetCapturedDurationCallBack(SetCapturedDuration);
                this.BeginInvoke(delegatCapture, new object[] { Time });
            }
            else
            {
                label1.Text = Time.ToString();
            }
        }