1
votes

I have WAV data in IEEE float at 16000Hz, 32-bit, 1-channel format in a byte array (from database). I would like to convert that to mp3 on the fly and stream it out via the response object from a WCF service.

Working with NAudio package, so far what I have working is using MediaFoundationEncoder.EncodetoMp3() to write to a file. But once that file is written out to the response, though, it's useless. I would like to just skip the file writing step and output to a stream or byte array and write that to the response object.

Is there a way to get MediaFoundationEncoder to output an mp3 in some kind of memory object (without a file)?

Alternatively, trying to use the LameMP3FileWriter, which does write to streams, I can send the IEEE float wav input directly to LameMP3FileWriter but the resulting mp3 stream sounds like a loud hiss seemingly regardless of the output format I use (44100,16,1), (22050,16,1), (11025,16,1),...

When I try to convert the IEEE float source to a PCM or upsample it using WaveFormatConversion, it errors out with

AcmNotPossible calling acmStreamOpen

if I use Wave32to16Stream on the IEEE float source, it does not error but there is no audio. This chain is not working for me:

    byte[] bytes = (Byte[])dt.Rows[0]["AudioContent"];//this is the dataTable from the database query containing the WAV audio

    var ms = new MemoryStream(bytes);
    var OLDfmt = WaveFormat.CreateIeeeFloatWaveFormat(16000,1);
    var NEWfmt = new WaveFormat(16000, 16, 1);//tried many
    var readr = new RawSourceWaveStream(ms,OLDfmt);

    //the following line errors on 'AcmNotPossible calling acmStreamOpen' 
    //var wav16 = new WaveFormatConversionStream(NEWfmt, readr);

    //the following does not error but no mp3 streams out at the end
    var wav16 = new Wave32To16Stream(readr);

    var outptMP3 = new MemoryStream();
    var writr = new LameMP3FileWriter(outptMP3, NEWfmt, LAMEPreset.STANDARD);
    wav16.CopyTo(writr);
    HttpContext.Current.Response.BufferOutput = true;
    HttpContext.Current.Response.AddHeader("Content-Type", "audio/mpeg");
    HttpContext.Current.Response.BinaryWrite(outptMP3.ToArray());

The raw audio plays fine in Chrome (as a WAV stream) if I just do a BinaryWrite of the ms MemoryStream, so I think the IEEE float source data is valid.

Correct me if wrong, but that error (AcmNotPossible) appears to mean there is no ACM codec on my machine for the IEEE float format? I query my machine and there appear to be no 32-bit ACM codecs, which is why I'm trying the Wave32To16Stream conversion.

Is there an interim step of the Lame MP3 pipeline to go from IEEE float 32-bit to mp3?

1

1 Answers

2
votes

Sorry there were so many sub-questions to my main question about transcoding a IEEE float 32-bit wave to an mp3 stream, but I can at least tell you how I got it to work using ACM codecs (I think) on my simple WCF service:

  1. change the return type on the service to a Stream
  2. set the MemoryStream position to 0 before returning

Otherwise the chain above actually does work for me. I will give the complete code, with all the extraneous SQL stuff, for the sake of completeness and because I found the answer in an unrelated SO issue:

public Stream playWAVEFileFromDB() {
        //if using the file output method, I change the return type to string and return text instead of streams
        try
        {
            SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["myconnectionstring"].ConnectionString);
            SqlCommand cmd = new SqlCommand();
            cmd.Connection = conn;
            cmd.CommandText = "SELECT theWAVEColumn FROM myTable WHERE allThoseAudiosAreStored";
            SqlDataAdapter sda = new SqlDataAdapter();
            DataTable dt = new DataTable();

            conn.Open();
            sda.SelectCommand = cmd;
            sda.Fill(dt);

            //read the raw audio from database into a byte array
            byte[] bytes = (Byte[])dt.Rows[0]["colName"];
            var ms = new MemoryStream(bytes);
            var NEWfmt = new WaveFormat(16000, 16, 1);
            var OLDfmt = WaveFormat.CreateIeeeFloatWaveFormat(16000,1);//how its stored
            var readr = new RawSourceWaveStream(ms,OLDfmt);
            var wav16 = new Wave32To16Stream(readr);
            var outptMP3 = new MemoryStream();    
            var writr = new LameMP3FileWriter(outptMP3, NEWfmt, LAMEPreset.STANDARD);
            wav16.CopyTo(writr);
            outptMP3.Position = 0;

            HttpContext.Current.Response.BufferOutput = true;
            HttpContext.Current.Response.AddHeader("Content-Type", "audio/mpeg");

            conn.Close();
            conn.Dispose();
            sda.Dispose();

            return outptMP3;
        }
            catch (Exception e)
        {
            //do the error logging stuff
            return Stream.Null;
        }
    }