3

I am a completely noob in topics related to audio handling with programming, but I want to convert PCM audio data to MP3.

My audio is in PCM format, the frequency is 8kHz, the bitwidth is 8 and the kind of byte is unsigned, and channel is mono.

I am using liblamemp3, and I found the way of doing it by the lame.exe frontend, by executing this command line:

$ ./lame -r -s 8 --bitwidth 8 --unsigned -m m ../../../../voice8K16bitmono.pcm output.mp3

The result is an .mp3 that I can hear well. (After some tries).

The problem is that I want to do it on the fly, so I am trying to code some source to do it without invoking the frontend.

My source code is:

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

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

    FILE *pcm = fopen(argv[1], "rb");
    FILE *mp3 = fopen("file.mp3", "wb");

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

    unsigned short pcm_buffer;
    unsigned short pcm_buffer_le;
    unsigned char mp3_buffer[MP3_SIZE];

    lame_t lame = lame_init();
    lame_set_num_samples(lame, 8000);
    lame_set_in_samplerate(lame, 8000);
    lame_set_out_samplerate(lame, 8000);
    lame_set_num_channels(lame, 1);
    lame_set_mode(lame, 3);
    lame_init_params(lame);
    lame_print_config(lame);
    //framesize is bits / channels = 8.
    do {
        read = fread(&pcm_buffer, sizeof(short), 1, pcm);
    pcm_buffer = (pcm_buffer>>8) | (pcm_buffer<<8);
    if (read == 0) {
            write = lame_encode_flush(lame, mp3_buffer, MP3_SIZE);
            fwrite(mp3_buffer, sizeof(char), write, mp3);
        break;
    }
    //pcm_buffer_le[0] = (pcm_buffer[0]>>8) | (pcm_buffer[0]<<8);
        write = lame_encode_buffer_interleaved(lame, &pcm_buffer, sizeof(short), mp3_buffer, MP3_SIZE);
        fwrite(mp3_buffer, sizeof(char), write, mp3);
    } while (1);

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

    return 0;
}

I took it from an example, and tried to apply the settings I need. But the resulting .mp3 sounds like a reactor and it doesn't sound well. So I am missing some/lot of code.

I want to do it, so, can anybody help?

Thanks in advance.

Puffy
  • 401
  • 6
  • 13
  • maybe your byte order is backwards? – Russ Schultz Oct 29 '15 at 00:48
  • Oh, thanks. So I must start from the end of the PCM buffer? – Puffy Oct 29 '15 at 13:33
  • no no no no. The byte order within each PCM sample. It's a common problem that some systems (x86, ARM) are little endian, and other systems (PPC, network, etc) are big endian. The PCM data you're reading might be big endian, and the library is expecting little endian. Or maybe you're not actually reading PCM data, but a wav file header, plus PCM, etc. – Russ Schultz Oct 29 '15 at 13:52
  • Ok. If I am not wrong, each PCM sample is 8 bytes (4 shorts). Should I swap the order of each short and pass frames of 8 bytes sized, to the lame_encode_buffer_interleaved(), but previously swapped? Sorry but I don't know anything about audio... – Puffy Oct 29 '15 at 14:19
  • also the .pcm file hasn't header. It's just RAW PCM audio data. – Puffy Oct 29 '15 at 14:26
  • Look at the lame documentation for what it expects Pcm inputs to be, but I expect it to be 16 bits per mono sample. – Russ Schultz Oct 29 '15 at 15:00
  • So 1 sample is one frame? Or... is it 2 samples per frame (L and R)? A packet is 1+ frames. Thanks for your help! I searched for the lame documentation but they don't have. I just can read the lame.h but there're a lot of concepts I just don't understand. I know that using the interleaved encoding routine it sounds in both speakers ^^ so one thing that I have clear. – Puffy Oct 29 '15 at 15:35
  • No, a frame is some oddball number like 1148 samples or something like that. Its unfortunate that the LAME api documentation isn't easily findable on the net, but google 'lame code examples' and go from there. – Russ Schultz Oct 29 '15 at 15:50
  • The problem is that seems very strange to work with this parameters and then there're no examples. I googled times and times, about pcm 8 bitwidth encoding with lame, but no results. Most common is 16 bit and 32. I've changed my code and now it works better I think, I can hear my voice but still hearing the reactor. I also added byte swapping and I guess I must read by frames/packets, because lame should work like this. How can I know the frame size for my audio configuration? Thanks in advance. – Puffy Oct 29 '15 at 16:39
  • Sorry, I can't answer much more on the topic, it's been years since I've done MP3 coding, and never with the lame encoder. – Russ Schultz Oct 29 '15 at 18:17
  • Can you post a link to your .pcm file? – Tim Oct 29 '15 at 18:34
  • https://drive.google.com/file/d/0B-d-wIFRMjyOOGUwWUhJRkJyYnM/view?usp=sharing – Puffy Oct 29 '15 at 18:43
  • Thanks Russ, no problem ;) – Puffy Oct 29 '15 at 18:45

1 Answers1

1
  1. Each sample is only 1 byte, so instead of reading in sizeof(short) bytes, you should read in 1 byte.
  2. You have to convert the unsigned 8-bit PCM samples into signed 16-bit PCM samples (reference).
  3. Since the input is mono, use lame_encode_buffer() instead of lame_encode_buffer_interleaved(), and set the buffer_r parameter to NULL (reference).

Here is a modified working version of your code:

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

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

    FILE *pcm = fopen(argv[1], "rb");
    FILE *mp3 = fopen("file.mp3", "wb");

    const int MP3_SIZE = 8192;

    unsigned char pcm_buffer_c;
    short pcm_buffer;
    unsigned char mp3_buffer[MP3_SIZE];

    lame_t lame = lame_init();
    lame_set_num_samples(lame, 8000);
    lame_set_in_samplerate(lame, 8000);
    lame_set_out_samplerate(lame, 8000);
    lame_set_num_channels(lame, 1);
    lame_set_mode(lame, 3);
    lame_init_params(lame);
    lame_print_config(lame);
    //framesize is bits / channels = 8.
    do {
        read = fread(&pcm_buffer_c, sizeof(char), 1, pcm);
        pcm_buffer = (short)(pcm_buffer_c - 0x80) << 8;
        if (read == 0) {
            write = lame_encode_flush(lame, mp3_buffer, MP3_SIZE);
            fwrite(mp3_buffer, sizeof(char), write, mp3);
            break;
        }
        write = lame_encode_buffer(lame, &pcm_buffer, NULL, read, mp3_buffer, MP3_SIZE);
        fwrite(mp3_buffer, sizeof(char), write, mp3);
    } while (1);

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

    return 0;
}

The above code reads in 1 byte at a time from the input file, which isn't very efficient. Here's how you would read multiple bytes at a time (I also cleaned up some of your code and removed some unnecessary functions). There is no error checking in this code, so make sure you add checks for the return values of all the LAME library functions.

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

#define PCM_BUF_SIZE 1024
#define MP3_SIZE 8192

int main(int argc, char *argv[])
{
    FILE *pcm = fopen(argv[1], "rb");
    FILE *mp3 = fopen("file.mp3", "wb");

    int n_bytes_read;
    int n_bytes_write;
    int i;

    short pcm_buffer_s[PCM_BUF_SIZE];
    unsigned char pcm_buffer[PCM_BUF_SIZE];
    unsigned char mp3_buffer[MP3_SIZE];

    lame_t lame = lame_init();
    lame_set_in_samplerate(lame, 8000);
    lame_set_num_channels(lame, 1);
    lame_set_mode(lame, 3);
    lame_init_params(lame);
    lame_print_config(lame);
    do {
        n_bytes_read = fread(pcm_buffer, sizeof(char), PCM_BUF_SIZE, pcm);
        for (i = 0; i < n_bytes_read; i++) {
            pcm_buffer_s[i] = (short)(pcm_buffer[i] - 0x80) << 8;
        }
        if (n_bytes_read == 0) {
            n_bytes_write = lame_encode_flush(lame, mp3_buffer, MP3_SIZE);
        } else {
            n_bytes_write = lame_encode_buffer(lame, pcm_buffer_s, NULL,
                    n_bytes_read, mp3_buffer, MP3_SIZE);
        }
        fwrite(mp3_buffer, sizeof(char), n_bytes_write, mp3);
    } while (n_bytes_read > 0);

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

    return 0;
}
Community
  • 1
  • 1
Tim
  • 4,790
  • 4
  • 33
  • 41
  • Thank you very much Tim! It really works. I just added a condition into while(1), just: while (n_bytes_read > 0); I will take care about LAME functions. Thanks again! I'm reading the conversion from unsigned 8 bits to signed 16. :))) – Puffy Oct 29 '15 at 21:57
  • I'm glad it worked for you! Also, thank for pointing out the problem with the loop, it's fixed now. – Tim Oct 29 '15 at 22:14
  • Hello again! Now that I've this working, I want to know if it's possible to revert the process and obtain the raw pcm of the beggining, with LAME. I am taking a look to the hip_decode() function, but I am not able to do this at least with the command line. (Maybe is not supported). I tried this command line: ./lame --decode -t file.mp3 file.pcm. Thanks in advance! ^^ – Puffy Oct 29 '15 at 22:49
  • You should ask this in a separate question. – Tim Oct 30 '15 at 17:44
  • Thanks, I'm doing it if I need help, but I found the way. Just hip_decode() and read buffers before. But I am obtaining 16bit PCM, which for the pourpose is not really bad. Anyway I've the trick you did for converting, I supose I would be able to do the opposite. Thanks a lot Tim, you are the man ^^ – Puffy Oct 30 '15 at 22:09