16

我想创建简单的 pcm 到 mp3 C++ 项目。我希望它使用 LAME。我喜欢 LAME,但它真的很大。所以我需要某种开源代码,使用纯代码工作流简化器。可以这么说,我给它一个带有 PCM 和 DEST 文件的文件。调用类似的东西:

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

在 4 - 5 行中找到这样的东西(当然应该存在示例),我有我需要的东西它应该是轻量级、简单、强力傻瓜、开源、跨平台。

有没有这样的事情?

4

4 回答 4

53

Lame 确实不难使用,虽然有很多可选的配置功能,如果你需要的话。对一个文件进行编码需要略多于 4-5 行,但也不会更多。这是我拼凑的一个工作示例(只是基本功能,没有错误检查):

#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;
}
于 2010-03-23T00:52:47.183 回答
6

受 Mike Seymour 的回答启发,我创建了一个纯 C++ 包装器,它允许在 2 行代码中编码/解码 WAV 和 MP3 文件

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

无需担心采样率、字节率和通道数 - 此信息是在编码/解码期间从 WAV 或 MP3 文件中获得的。

该库不使用旧的 C i/o 函数,而仅使用 C++ 流。我觉得它更优雅。

为了方便起见,我在 LAME 上创建了一个非常薄的 C++ 包装器,并将其命名为 lameplus 和一个用于从 WAV 文件中提取采样信息的小型库。

所有文件都可以在这里找到:

编码/解码:https ://github.com/trodevel/convimp3

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

wav 处理:也在 github 上,存储库是wave

于 2015-04-26T01:52:05.407 回答
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
于 2013-02-01T01:14:11.380 回答
1

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.

于 2014-08-03T23:36:45.113 回答