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

ExecutionEngineException using LameMP3FileWriter

May 19, 2014 at 8:32 PM
I am attempting to create a streamed conversion for our GSM.610 encoded WAV files (call recordings) to an Mp3 file. Here is the code within the HttpHandler:
    BackgroundWorker bw = new BackgroundWorker();
    HttpContext httpContext;

    public void ProcessRequest(HttpContext context)
    {
        //Build context object and add Bin path to conversion DLLs
        httpContext = context;
        CheckAddBinPath();

        //Get file name from Query String
        if (context.Request.QueryString["AudioFilename"] == null)
            return;
        else
            sAudioFilename = context.Request.QueryString["AudioFilename"].ToString();

         //Buffered conversion
         bw.DoWork += new DoWorkEventHandler(bw_DoWork);
         bw.RunWorkerAsync();

         //All in 1 conversion
        //httpContext.Response.Buffer = false;
        //httpContext.Response.ContentType = "audio/mpeg";
        //httpContext.Response.BinaryWrite(ConvertGSMToMP3(new AudioFileReader(sPath + sAudioFilename)));
        //httpContext.Response.Flush();
    }
    private void bw_DoWork(object sender, DoWorkEventArgs e)
    {
        //Variables
        int iFileSize = 0;          //Size of file in bytes
        int iTotalCount = 0;        //Total bytes read
        int iBufferSize = 2048;     //Buffer size cap/default
        bool bProcessing = true;    //Determines the processing/converting state of the current file

        //Read file and get file size
        var GSMchunk = new AudioFileReader(sPath + sAudioFilename);

        //Check if the file is smaller than the default byte size
        iFileSize = (int)GSMchunk.Length;
        if (iBufferSize > iFileSize)
            iBufferSize = iFileSize;

        //While reading
        while (bProcessing)
        {
            //Buffer to be converted
            byte[] bBuffer = new byte[iBufferSize];

            //Read the next buffer dependant on remaining bytes in file   
            if (iBufferSize > (iFileSize - iTotalCount))
            {
                //Last read
                iBufferSize = iFileSize - iTotalCount;
                GSMchunk.Read(bBuffer, iTotalCount, iBufferSize);
                bProcessing = false;
            }
            else
            {
                //Continue reading
                iTotalCount += GSMchunk.Read(bBuffer, iTotalCount, iBufferSize);
            }

            //Convert the current buffer
            byte[] bAudioConverted = ConvertGSMToMP3(bBuffer);

            //Send converted buffer to the audio player
            httpContext.Response.Buffer = false;
            httpContext.Response.ContentType = "audio/mpeg";
            httpContext.Response.BinaryWrite(bAudioConverted);
        }

        //Clean up and flush the response context
        GSMchunk.Close();
        httpContext.Response.Flush();
    }
    //Conversion passing in buffer
    public byte[] ConvertGSMToMP3(byte[] bBuffer)
    {
        byte[] MP3data = null;

        try
        {
            using (MemoryStream GSMstream = new MemoryStream(bBuffer))
            {
                using (MemoryStream MP3strm = new MemoryStream())
                {
                    using (var MP3wri = new LameMP3FileWriter(MP3strm, WaveFormat.CreateIeeeFloatWaveFormat(8000, 1), 32))
                    {
                        GSMstream.CopyTo(MP3wri); //####EXCEPTION THROWN HERE####
                        MP3data = MP3strm.ToArray();   
                    }
                }
            }
        }
        catch
        {
            MP3data = null;
        }

        return MP3data;
    }
When this is run, the first buffer converts as expected. However, when we arrive at the 2nd conversion, I believe the LameMP3FileWriter object does not dispose of the unmanaged code (in libmp3lame.32.dll) and the debugger stalls when attempting to convert on the second pass. This stalls on the commented line '//####EXCEPTION THROWN HERE####'.

I have taken the LameMP3FileWriter line out of the using statement and manually called MP3wri.Dispose(); and MP3wri.Flush(); after I moved the array to the output byte array. This works as expected the first time around, but rather than throwing the ExecutionEngineException on the second pass, it throws an AccessViolation exception. This further proves my theory that the resource was not being disposed of initially, but manually disposing the resource causes the next pass to lose track of "where" the dll is.

My question to the kind person who may understand this issue and can help me out here: am I using this library incorrectly, or is there some issue with the LameMP3FileWriter object that does not properly dispose of the unmanaged dll? What do I need to do to get this to work as expected?

Note: If I alter the code to do the entire conversion in one pass, the conversion works flawlessly. However, with large files (4+ hour long calls), this will not work. I do not want my users to wait for 2+ minutes while the file converts in memory. I want to stream what I have completed while the rest of the file is being converted.
May 21, 2014 at 11:37 AM
LameMP3FileWriter is not part of NAudio. You might want to ask whoever wrote it.
May 22, 2014 at 8:19 PM
Edited May 22, 2014 at 8:20 PM
markheath wrote:
LameMP3FileWriter is not part of NAudio. You might want to ask whoever wrote it.
With only using NAudio's library, I need to do the following:
  1. Grab a WAV GSM610 encoded audio file.
  2. Set a buffer size and convert only on that buffer until the entire file is complete.
  3. While converting the file, stream the converted audio bytes to the HTML5 audio player.
Is this possible with NAudio? If so, how can I do this?
May 23, 2014 at 3:21 PM
Yes, NAudio can decode GSM610 using the WaveFormatConversionStream. You are in complete control of how many bytes you play.
With regards to streaming the audio, NAudio does not provide any network capabilities, so you'd need to do that yourself.