0
0

I think these are quite basic questions, but I couldn’t find any answer anywhere so I post my questions here.

I would like to record a sound from the main entry device (microphone) and encode it on the fly to save it as a .mp3 or .mp4(aac) or .3gp(aac) file on my disk.
The recording example I found in the FMOD ex package for windows allows to record sound and save as a .wav file on the disk, but not encode it.

So my questions are:

-Is it possible to modify the example given in "FMOD Programmers API Windows\examples\recording" to save an encoded file directly (in one of the 3 formats stated above)?

-Or is there a way to encode the wav file to mp3 or aac afterwards using FMOD?

-If not can someone point me to another library that can encode a wav file with mp3 or aac codec?

Thank you in advance for your answers!

  • You must to post comments
0
0

Check the examples\plugin_dev\output_mp3 example.
That takes audio in blocks and encodes it on the fly into mp3 data. You will just need to retrofit it to use a record buffer instead of the output plugin callback.

  • You must to post comments
0
0

Thank you Brett !

Very glad to hear it’s possible to encode with FMOD, without using another library ๐Ÿ˜€
I’m having a look at it right away.

  • You must to post comments
0
0

I was a bit busy recently but I finally found time to test the recording to mp3.

To people who are new to this (like me), despite what I said in my previous post we actually need to use an external library called LAME to encode to MP3, but fortunately its use is not so complicated (and the dll and example are available in FMOD_examples\plugin_dev\output_mp3)

I ended up with a [b:30e9tnx5]working[/b:30e9tnx5] version, although I am not sure if I’m doing it properly, and I would like your comments ๐Ÿ˜‰

Especially, when using the LAME library, in the main loop in main.cpp I don’t use the dwSamples returned in beInitStream to feed the samples in beEncodeChunk and just feed the samples as they come, despite what they say in the documentation ([url:30e9tnx5]http://www.fi.muni.cz/~qruzicka/Smid/man.htm[/url:30e9tnx5]):

[quote:30e9tnx5]
BE_ERR beEncodeChunk( HBE_STREAM hbeStream, DWORD nSamples, PSHORT pSamples, PBYTE pOutput, PDWORD pdwOutput )

nSamples: Number of samples to be encoded for this call. This should be identical to what is returned by beInitStream(), unless you are encoding the last chunk, which might be smaller.
[/quote:30e9tnx5]

I post my code so that you can get the whole picture:

[code:30e9tnx5]
//main.cpp

include <windows.h>

include <stdio.h>

include <conio.h>

include "fmod.hpp"

include "fmod_errors.h"

include "Lame.h"

void ERRCHECK(FMOD_RESULT result)
{
if (result != FMOD_OK)
{
printf("FMOD error! (%d) %s\n", result, FMOD_ErrorString(result));
exit(-1);
}
}

int main(int argc, char *argv[])
{
FMOD::System *system = 0;
FMOD::Sound *sound = 0;
FMOD_RESULT result;
FMOD_CREATESOUNDEXINFO exinfo;
int key, recorddriver, numdrivers, count;
unsigned int version;
//FILE *fp;
unsigned int datalength = 0, soundlength;

Lame lame;
if(!lame.init())
{
    return false;
}

/*
    Create a System object and initialize.
*/
result = FMOD::System_Create(&amp;system);
ERRCHECK(result);

result = system-&gt;getVersion(&amp;version);
ERRCHECK(result);

if (version &lt; FMOD_VERSION)
{
    printf(&quot;Error!  You are using an old version of FMOD %08x.  This program requires %08x\n&quot;, version, FMOD_VERSION);
    return 0;
}

/* 
    System initialization
*/
printf(&quot;---------------------------------------------------------\n&quot;);    
printf(&quot;Select OUTPUT type\n&quot;);    
printf(&quot;---------------------------------------------------------\n&quot;);    
printf(&quot;1 :  DirectSound\n&quot;);
printf(&quot;2 :  Windows Multimedia WaveOut\n&quot;);
printf(&quot;3 :  ASIO\n&quot;);
printf(&quot;---------------------------------------------------------\n&quot;);
printf(&quot;Press a corresponding number or ESC to quit\n&quot;);

do
{
    key = _getch();
} while (key != 27 &amp;&amp; key &lt; '1' &amp;&amp; key &gt; '5');

switch (key)
{
    case '1' :  result = system-&gt;setOutput(FMOD_OUTPUTTYPE_DSOUND);
                break;
    case '2' :  result = system-&gt;setOutput(FMOD_OUTPUTTYPE_WINMM);
                break;
    case '3' :  result = system-&gt;setOutput(FMOD_OUTPUTTYPE_ASIO);
                break;
    default  :  return 1; 
}  
ERRCHECK(result);

/*
    Enumerate record devices
*/

result = system-&gt;getRecordNumDrivers(&amp;numdrivers);
ERRCHECK(result);

printf(&quot;---------------------------------------------------------\n&quot;);    
printf(&quot;Choose a RECORD driver\n&quot;);
printf(&quot;---------------------------------------------------------\n&quot;);    
for (count=0; count &lt; numdrivers; count++)
{
    char name[256];

    result = system-&gt;getRecordDriverInfo(count, name, 256, 0);
    ERRCHECK(result);

    printf(&quot;%d : %s\n&quot;, count + 1, name);
}
printf(&quot;---------------------------------------------------------\n&quot;);
printf(&quot;Press a corresponding number or ESC to quit\n&quot;);

recorddriver = 0;
do
{
    key = _getch();
    if (key == 27)
    {
        return 0;
    }
    recorddriver = key - '1';
} while (recorddriver &lt; 0 || recorddriver &gt;= numdrivers);

printf(&quot;\n&quot;);

result = system-&gt;init(32, FMOD_INIT_NORMAL, 0);
ERRCHECK(result);

memset(&amp;exinfo, 0, sizeof(FMOD_CREATESOUNDEXINFO));

exinfo.cbsize           = sizeof(FMOD_CREATESOUNDEXINFO);
exinfo.numchannels      = 1;
exinfo.format           = FMOD_SOUND_FORMAT_PCM16;
exinfo.defaultfrequency = 44100;
exinfo.length           = exinfo.defaultfrequency * sizeof(short) * exinfo.numchannels * 2;

result = system-&gt;createSound(0, FMOD_2D | FMOD_SOFTWARE | FMOD_OPENUSER, &amp;exinfo, &amp;sound);
ERRCHECK(result);

printf(&quot;========================================================================\n&quot;);
printf(&quot;Record to disk example.  Copyright (c) Firelight Technologies 2004-2011.\n&quot;);
printf(&quot;========================================================================\n&quot;);
printf(&quot;\n&quot;);
printf(&quot;Press a key to start recording\n&quot;);
printf(&quot;\n&quot;);

_getch();

result = system-&gt;recordStart(recorddriver, sound, true);
ERRCHECK(result);

printf(&quot;Press 'Esc' to quit\n&quot;);
printf(&quot;\n&quot;);

result = sound-&gt;getLength(&amp;soundlength, FMOD_TIMEUNIT_PCM);
ERRCHECK(result);

/*
    Main loop.
*/
do
{
    static unsigned int lastrecordpos = 0;
    unsigned int recordpos = 0;

    if (_kbhit())
    {
        key = _getch();
    }

    system-&gt;getRecordPosition(recorddriver, &amp;recordpos);
    ERRCHECK(result);

    if (recordpos != lastrecordpos)        
    {
        void *ptr1, *ptr2;
        int blocklength;
        unsigned int len1, len2;

        blocklength = (int)recordpos - (int)lastrecordpos; 
        if (blocklength &lt; 0)
        {
            blocklength += soundlength;
        }

        /*
        Lock the sound to get access to the raw data.
        */
        sound-&gt;lock(lastrecordpos * exinfo.numchannels * 2, blocklength * exinfo.numchannels * 2, &amp;ptr1, &amp;ptr2, &amp;len1, &amp;len2);   /* * exinfo.numchannels * 2 = stereo 16bit.  1 sample = 4 bytes. */

        /*
        Encode and Write it to disk.
        */
        PSHORT pWAVBuffer_input = NULL;
        if (ptr1 &amp;&amp; len1)
        {
            lame.encode((PSHORT)ptr1, len1);
        }
        if (ptr2 &amp;&amp; len2)
        {
            lame.encode((PSHORT)ptr2, len2);
        }

        /*
        Unlock the sound to allow FMOD to use it again.
        */
        sound-&gt;unlock(ptr1, ptr2, len1, len2);
    }

    lastrecordpos = recordpos;

    printf(&quot;%-23s. Record buffer pos = %6d : Record time = %02d:%02d\n&quot;, &quot;Recording to record.wav&quot;/*(timeGetTime() / 500) &amp; 1 ? &quot;Recording to record.wav&quot; : &quot;*/, recordpos, datalength / exinfo.defaultfrequency / exinfo.numchannels / 2 / 60, (datalength / exinfo.defaultfrequency / exinfo.numchannels / 2) % 60);

    system-&gt;update();

    Sleep(10);

} while (key != 27);

printf(&quot;\n&quot;);

/*
    Shut down
*/
result = sound-&gt;release();
ERRCHECK(result);

result = system-&gt;release();
ERRCHECK(result);

if (!lame.close())
{
    return 1;
}

return 0;

}
[/code:30e9tnx5]

[code:30e9tnx5]
//Lame.h

ifndef _LAMEWRAPPER_H

define _LAMEWRAPPER_H

include <windows.h>

include <stdio.h>

include "BladeMP3EncDll.h"

class Lame
{
public:
Lame();
~Lame();

bool init();
DWORD encode(PSHORT pWAVBuffer_input, DWORD length);
bool close();

FILE               *mFP;    
HINSTANCE           mDLL;
HBE_STREAM          hbeStream;
PBYTE               pMP3Buffer;
PSHORT              pWAVBuffer;
BE_VERSION          Version;
DWORD               dwMP3Buffer;
DWORD               dwSamples;

BEINITSTREAM        beInitStream;
BEENCODECHUNK       beEncodeChunk;
BEDEINITSTREAM      beDeinitStream;
BECLOSESTREAM       beCloseStream;
BEVERSION           beVersion;
BEWRITEVBRHEADER    beWriteVBRHeader;

};

endif

[/code:30e9tnx5]

[code:30e9tnx5]
//Lame.cpp

include "Lame.h"

include "log.h"

Lame::Lame(): mFP(NULL),
mDLL(NULL),
hbeStream(0),
pMP3Buffer(NULL),
pWAVBuffer(NULL),
//Version(),
dwMP3Buffer(0),
dwSamples(0),
beInitStream(NULL),
beEncodeChunk(NULL),
beDeinitStream(NULL),
beCloseStream(NULL),
beVersion(NULL),
beWriteVBRHeader(NULL)
{
}

Lame::~Lame()
{
}

bool Lame::init()
{
BE_CONFIG beConfig = {0,};
BE_ERR err = 0;
char filename[256];
int outputrate = 44100;
int outputchannels = 1;

write_log(&quot;init\n&quot;);

ifdef _WIN64

mDLL = LoadLibrary(&quot;lame_enc64.dll&quot;);

else

write_log(&quot;init20\n&quot;);
mDLL = LoadLibrary(&quot;lame_enc.dll&quot;);

endif

if (!mDLL)
{
    write_log(&quot;inti30\n&quot;);
    return false;
}

/*
    Get Interface functions from the DLL
*/
beInitStream    = (BEINITSTREAM)     GetProcAddress(mDLL, TEXT_BEINITSTREAM);
beEncodeChunk   = (BEENCODECHUNK)    GetProcAddress(mDLL, TEXT_BEENCODECHUNK);
beDeinitStream  = (BEDEINITSTREAM)   GetProcAddress(mDLL, TEXT_BEDEINITSTREAM);
beCloseStream   = (BECLOSESTREAM)    GetProcAddress(mDLL, TEXT_BECLOSESTREAM);
beVersion       = (BEVERSION)        GetProcAddress(mDLL, TEXT_BEVERSION);
beWriteVBRHeader= (BEWRITEVBRHEADER) GetProcAddress(mDLL, TEXT_BEWRITEVBRHEADER);

// Check if all interfaces are present
if(!beInitStream || !beEncodeChunk || !beDeinitStream || !beCloseStream || !beVersion || !beWriteVBRHeader)
{
    write_log(&quot;init40\n&quot;);
    return false;
}

// Get the version number
beVersion( &amp;Version );

memset(&amp;beConfig,0,sizeof(beConfig));                   // clear all fields

// use the LAME config structure
beConfig.dwConfig = BE_CONFIG_LAME;

// this are the default settings for testcase.wav

// FMOD NOTE : The 'hwnd' param could be used to pass in info about the bitrate and encoding settings.

beConfig.format.LHV1.dwStructVersion    = 1;
beConfig.format.LHV1.dwStructSize       = sizeof(beConfig);     
beConfig.format.LHV1.dwSampleRate       = outputrate;           // INPUT FREQUENCY
beConfig.format.LHV1.dwReSampleRate     = 0;                    // DON&quot;T RESAMPLE
beConfig.format.LHV1.nMode              = outputchannels == 1 ? BE_MP3_MODE_MONO : BE_MP3_MODE_JSTEREO; // OUTPUT IN STREO
beConfig.format.LHV1.dwBitrate          = 128;                  // MINIMUM BIT RATE
beConfig.format.LHV1.nPreset            = LQP_R3MIX;            // QUALITY PRESET SETTING
beConfig.format.LHV1.dwMpegVersion      = MPEG1;                // MPEG VERSION (I or II)
beConfig.format.LHV1.dwPsyModel         = 0;                    // USE DEFAULT PSYCHOACOUSTIC MODEL 
beConfig.format.LHV1.dwEmphasis         = 0;                    // NO EMPHASIS TURNED ON
beConfig.format.LHV1.bOriginal          = TRUE;                 // SET ORIGINAL FLAG
beConfig.format.LHV1.bWriteVBRHeader    = TRUE;                 // Write INFO tag

// beConfig.format.LHV1.dwMaxBitrate = 320; // MAXIMUM BIT RATE
// beConfig.format.LHV1.bCRC = TRUE; // INSERT CRC
// beConfig.format.LHV1.bCopyright = TRUE; // SET COPYRIGHT FLAG
// beConfig.format.LHV1.bPrivate = TRUE; // SET PRIVATE FLAG
// beConfig.format.LHV1.bWriteVBRHeader = TRUE; // YES, WRITE THE XING VBR HEADER
// beConfig.format.LHV1.bEnableVBR = TRUE; // USE VBR
// beConfig.format.LHV1.nVBRQuality = 5; // SET VBR QUALITY
beConfig.format.LHV1.bNoRes = TRUE; // No Bit resorvoir

// Preset Test
// beConfig.format.LHV1.nPreset = LQP_PHONE;

// Init the MP3 Stream
err = beInitStream(&amp;beConfig, &amp;dwSamples, &amp;dwMP3Buffer, &amp;hbeStream);

// Check result
if(err != BE_ERR_SUCCESSFUL)
{
    write_log(&quot;init50\n&quot;);
    return false;
}

// Allocate MP3 buffer
pMP3Buffer = (PBYTE)malloc(dwMP3Buffer);
if(!pMP3Buffer)
{
    write_log(&quot;init60\n&quot;);
    return false;
}

// Allocate WAV buffer
pWAVBuffer = (PSHORT)malloc(dwSamples * sizeof(SHORT));
if (!pWAVBuffer)
{
    write_log(&quot;init70\n&quot;);
    return false;
}

errno_t err_no = strncpy_s(filename, &quot;fmodoutput.mp3&quot;, 256);

err_no = fopen_s(&amp;mFP, filename, &quot;wb&quot;);
if (!mFP)
{
    write_log(&quot;init80\n&quot;);
    return false;
}

write_log(&quot;init90\n&quot;);
return true;

}

DWORD Lame::encode(PSHORT pWAVBuffer, DWORD dwRead)
{
DWORD dwWrite=0;
BE_ERR err;

write_log(&quot;encode\n&quot;);

{
    // Encode samples
    err = beEncodeChunk(hbeStream, dwRead / sizeof(SHORT), pWAVBuffer, pMP3Buffer, &amp;dwWrite);

    // Check result
    if(err != BE_ERR_SUCCESSFUL)
    {
        beCloseStream(hbeStream);
        return 0;
    }

    // write dwWrite bytes that are returned in the pMP3Buffer to disk
    if (fwrite(pMP3Buffer, 1, dwWrite, mFP) != dwWrite)
    {
        return 0;
    }
}

return dwWrite;

}

bool Lame::close()
{
DWORD dwWrite=0;
BE_ERR err;

// Deinit the stream
err = beDeinitStream(hbeStream, pMP3Buffer, &amp;dwWrite);

// Check result
if(err != BE_ERR_SUCCESSFUL)
{
    beCloseStream(hbeStream);
    return false;
}

// Are there any bytes returned from the DeInit call?
// If so, write them to disk
if( dwWrite )
{
    if( fwrite( pMP3Buffer, 1, dwWrite, mFP ) != dwWrite )
    {
        return false;
    }
}

// close the MP3 Stream
beCloseStream( hbeStream );

if (pWAVBuffer)
{
    free(pWAVBuffer);
    pWAVBuffer = 0;
}

if (pMP3Buffer)
{
    free(pMP3Buffer);
    pMP3Buffer = 0;
}

if (mFP)
{
    fclose(mFP);
    mFP = 0;
}

return true;    

}

[/code:30e9tnx5]

  • You must to post comments
Showing 3 results
Your Answer

Please first to submit.