16

I want to create simple pcm to mp3 C++ project. I want it to use LAME. I love LAME but it's really big. so I need some kind of OpenSource working from pure code with pure lame code workflow simplifier. So to say I give it File with PCM and DEST file. Call something like:

LameSimple.ToMP3(file with PCM, File with MP3 , 44100, 16, MP3, VBR);

ore such thing in 4 - 5 lines (examples of course should exist) and I have vhat I needed It should be light, simple, powerfool, opensource, crossplatform.

Is there any thing like this?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Rella
  • 65,003
  • 109
  • 363
  • 636
  • 1
    Despite of lame sources quite large, there is no need to learn full sources. You need only look trough parsing command lime parameters and their mapping to internal lame structures. – VitalyVal Mar 22 '10 at 22:33

4 Answers4

56

Lame really isn't difficult to use, although there are a lot of optional configuration functions if you need them. It takes slightly more than 4-5 lines to encode a file, but not much more. Here is a working example I knocked together (just the basic functionality, no error checking):

#include <stdio.h>
#include <lame/lame.h>

int main(void)
{
    int read, write;

    FILE *pcm = fopen("file.pcm", "rb");
    FILE *mp3 = fopen("file.mp3", "wb");

    const int PCM_SIZE = 8192;
    const int MP3_SIZE = 8192;

    short int pcm_buffer[PCM_SIZE*2];
    unsigned char mp3_buffer[MP3_SIZE];

    lame_t lame = lame_init();
    lame_set_in_samplerate(lame, 44100);
    lame_set_VBR(lame, vbr_default);
    lame_init_params(lame);

    do {
        read = fread(pcm_buffer, 2*sizeof(short int), PCM_SIZE, pcm);
        if (read == 0)
            write = lame_encode_flush(lame, mp3_buffer, MP3_SIZE);
        else
            write = lame_encode_buffer_interleaved(lame, pcm_buffer, read, mp3_buffer, MP3_SIZE);
        fwrite(mp3_buffer, write, 1, mp3);
    } while (read != 0);

    lame_close(lame);
    fclose(mp3);
    fclose(pcm);

    return 0;
}
Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • 1
    please, could you share a project file, I realy do not undestan something - how to connect LAME to your CPP project, please help – Rella Mar 23 '10 at 01:11
  • 3
    I built it on Ubuntu linux with `gcc lametest.c -lmp3lame` (after installing the library and header files with `sudo apt-get install libmp3lame-dev`). I've no idea how to install and link with libraries on Mac and Windows, I'm afraid. – Mike Seymour Mar 23 '10 at 01:17
  • 1
    Ser Please would you mind sharing some links (or, better direct instructions) for installing Lame for developers on linux. – Rella Mar 23 '10 at 01:21
  • btw post your answer here http://stackoverflow.com/questions/2419928/where-to-get-pure-c-lame-mp3-encoder-pcm-to-mp3-example win 100 bounty=) thank you WWmuch for this code example. – Rella Mar 23 '10 at 01:23
  • On Ubuntu, Debian, or other apt-based distribution, open a shell and type `sudo apt-get install libmp3lame-dev`. Otherwise, use your distribution's package manager to search for something like that, or download the source from http://sourceforge.net/projects/lame/files/ and follow whatever instructions you find there to build and install it. – Mike Seymour Mar 23 '10 at 01:28
  • @MikeSeymour how to convert audio like low , medium,high quality mp3 with the help of lame encoder in ios – Ravindhiran Aug 22 '13 at 04:43
  • Is there an API somewhere out there? – Ahmed Fasih Sep 03 '13 at 19:06
  • I Get an converted file that is incomplete , for example like 30 seconds audio is converted to mp3 but its of 26 seconds , last 4 seconds being chopped off. Any one else facing the issue , what should I check? – Bhumit Mehta Mar 05 '15 at 09:52
  • Remember you have to skip the RIFF part of the wave file otherwise you got about 1 second of garbage in the resulted mp3 file ;) – Dumbo Feb 07 '17 at 21:08
  • Why This particular number "8192". I didn't understand the significance. Can I not simply read the whole pcm file in a buffer and directly use `lame_encode_buffer_interleaved`? – RC0993 Jul 15 '19 at 06:05
  • You'll get some extra metadata if you put `lame_get_lametag_frame` after `lame_flush` (like start/end padding) – theicfire Mar 07 '20 at 04:01
6

inspired by Mike Seymour's answer I created a pure C++ wrapper which allows to encode / decode WAV and MP3 files in just 2 lines of code

convimp3::Codec::encode( "test.wav", "test.mp3" );
convimp3::Codec::decode( "test.mp3", "test_decoded.wav" );

no need to bother about sample rate, byte rate and number of channels - this info is obtained from WAV or MP3 file during encoding / decoding.

The library doesn't use old C i/o functions, but C++ streams only. I find it more elegant.

For convinience I created a very thin C++ wrapper over LAME and called it lameplus and a small library for extraction of sampling information from WAV files.

All files can be found here:

encoding/decoding: https://github.com/trodevel/convimp3

lameplus: https://github.com/trodevel/lameplus

wav handling: also on github, repository is wave

Aran Mulholland
  • 23,555
  • 29
  • 141
  • 228
trodevel
  • 143
  • 1
  • 5
2

I have successfully used libmp3lame in the way mike seymour proposed. I am now trying to use the same approach using posix threads to speed up encoding. I am greating one lame_t pointer, and have several threads doing bits of the conversion, taking care that each thread has a unique bit of the pcm track that it transcodes.

I use one global lame_t structure that is used for the encoding in each thread. My code works for 1 thread (no parallel execution), it also works if I delay the thread creation in parallel mode (such that there is no parallel execution, but the data structures are arrays).

When I run my code in parallel mode, I get a lot of errors such as

Internal buffer inconsistency. flushbits <> ResvSizebit reservoir error: 
l3_side->main_data_begin: 5440 
Resvoir size:             4088 
resv drain (post)         1 
resv drain (pre)          184 
header and sideinfo:      288 
data bits:                1085 
total bits:               1374 (remainder: 6) 
bitsperframe:             3336 
This is a fatal error.  It has several possible causes:90%  LAME compiled with buggy version of gcc using advanced optimizations 9%  Your system is overclocked 1%  bug in LAME encoding libraryfinished encoding 
Internal buffer inconsistency. flushbits <> ResvSizefinished encoding 

For referernce, I attach the code that I am using, that compiles just fine.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <math.h>
#include <iostream>
#include <string>
#include <lame/lame.h>
#include <pthread.h>
#include <thread>
#include <chrono>

using namespace std;

typedef struct Data{
    lame_t lame;
    FILE * wav_file;
    short int * pcm_buffer;
    unsigned char * mp3_buffer;
    unsigned long mp3_buffer_size;
    unsigned long first_sample;
    unsigned long n_samples;
    unsigned long items_read;
    unsigned long mp3_bytes_to_write;
    pthread_mutex_t *mutexForReading;
} Data;

void *encode_chunk(void *arg)
{
    Data * data = (Data *) arg;

    unsigned long offset = 40 + 2 * 2 * data->first_sample;
    pthread_mutex_lock(data->mutexForReading);
    fseek(data->wav_file, offset, SEEK_SET);

    data->items_read = fread(data->pcm_buffer, 2*sizeof(short int) , data->n_samples, data->wav_file);

    cout << "first sample " << data->first_sample << " n_samples "<<  data->n_samples << " items read " << data->items_read << " data address " << data << " mp3 a " << static_cast<void *> (data->mp3_buffer) << endl;
    pthread_mutex_unlock(data->mutexForReading);

    if (data->items_read != 0) 
    {
        data->mp3_bytes_to_write = lame_encode_buffer_interleaved(data->lame, 
                                                                  data->pcm_buffer, 
                                                                  data->items_read,
                                                                  data->mp3_buffer, 
                                                                  data->mp3_buffer_size);
    }
    cout << "finished encoding " << endl;
    return NULL;
}

int main(int argc, char * argv[])
{
    int read,write;

    FILE *wav = fopen("test.wav", "rb");
    FILE *mp3 = fopen("file.mp3", "wb");

    fseek(wav,0,SEEK_END);

    unsigned long file_size_wav = ftell(wav);
    unsigned long bytes_PCM = file_size_wav - 40;
    unsigned long n_total_samples = bytes_PCM / 4;

    const unsigned long MAX_SAMPLE_NUMBER = pow(2,10);
    const unsigned short NTHREADS = 2;
    const unsigned long MAX_MP3_SIZE = int(MAX_SAMPLE_NUMBER * 1.25 + 7200) + 1;

    short int pcm_buffer[NTHREADS][MAX_SAMPLE_NUMBER * 2]; // 2 channels
    unsigned char mp3_buffer[NTHREADS][MAX_MP3_SIZE]; // according to libmp3lame api

    lame_t lame = lame_init();
    lame_set_in_samplerate(lame, 44100);
    lame_set_VBR(lame, vbr_default);

    // lame_set_brate(lame, 128); // only for CBR mode
    // lame_set_quality(lame, 2); 
    // lame_set_mode(lame, JOINT_STEREO); // 1 joint stereo , 3 mono

    lame_init_params(lame);

    Data data_ptr[NTHREADS];

    unsigned short n_main_loops = n_total_samples / MAX_SAMPLE_NUMBER / NTHREADS + 1;

    cout << "total samples " << n_total_samples << endl;
    cout << "Number of iterations in main loop : " << n_main_loops << endl;

    unsigned long samples_remaining = n_total_samples;
    unsigned long current_sample = 0;

    pthread_t threadID[NTHREADS];
    pthread_mutex_t mutexForReading = PTHREAD_MUTEX_INITIALIZER;

    for (unsigned long i = 0 ; i < n_main_loops; i ++)
    {
        for (unsigned short j = 0; j < NTHREADS; j++ )
        {
            Data data;
            data.lame = lame;
            data.wav_file = wav;
            data.pcm_buffer = pcm_buffer[j];
            data.mp3_buffer = mp3_buffer[j];
            data.first_sample = current_sample;
            data.n_samples = min(MAX_SAMPLE_NUMBER, n_total_samples - current_sample);
            data.mutexForReading = &mutexForReading;

            current_sample += data.n_samples;
            samples_remaining -= data.n_samples;

            data_ptr[j] = data;

            if (data_ptr[j].n_samples > 0)

            {   
                cout << "creating " << i << " " << j << " " << data_ptr[j].first_sample << " " << data_ptr[j].n_samples << endl;
                pthread_create( &threadID[j], 
                           NULL, 
                           encode_chunk, 
                           (void *) (&data_ptr[j]));
            }

        }
        for (unsigned short j = 0; j < NTHREADS; j++)
        {
            if (data_ptr[j].n_samples > 0)
            {   
                pthread_join( threadID[j], NULL); 
            } 
        }

        for (unsigned short j = 0; j< NTHREADS; j++)
            if (data_ptr[j].n_samples > 0)
            {

                fwrite(data_ptr[j].mp3_buffer, data_ptr[j].mp3_bytes_to_write, 1, mp3);
            }
            else
            {
                data_ptr[j].mp3_bytes_to_write = lame_encode_flush(lame, data_ptr[j].mp3_buffer, data_ptr[j].mp3_buffer_size);

            }

    }




    lame_close(lame);
    fclose(mp3);
    fclose(wav);

}

Maybe someone knows if lame can not be used in this way in parallel code. I did not find any hints if this is possible or not.

The problem seems to be that the global lame_t structure is accessed by several threads at the same time. I thought that this would only be reading, so no problem, but I seem to be mistaken.

I also thought that a workaround might be to create a lame_t object for each thread. I tried that, using the threads to encode mutually exclusive bits of the original wav file.

The code compiles and runs without problems, but the resulting file contains no sound.

If anyone is interested, I can add the code. It is just a minor modification of the above code with lame_t being an array of size NTHREADS.

user142295
  • 83
  • 4
1

I got this to work by changing 41000 to around 8000:

lame_set_in_samplerate(lame, 44100);

to

lame_set_in_samplerate(lame, 8000);

And compiled prog.c with:

gcc prog.c -lmp3lame -o prog

The file.pcm does not sound good as file.mp3. I got a perfect conversion when I used this bash command:

lame -V 5 file.wav file.mp3
Nathan Tuggy
  • 2,237
  • 27
  • 30
  • 38
keghn
  • 61
  • 4