我正在尝试使用pcm
编写mp3
到Lame
转换程序。虽然pcm数据确实转换为mp3文件,但输出非常吱吱作响。在我发布问题之前,以下是我的代码:
/*
Sample program to generate a single sinusoid and encode it in mp3.
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <include/lame/lame.h>
#include <assert.h>
#include <string.h>
int main(int argc, char *argv[]) {
unsigned int sampleRate = 16000; /*assumed.*/
unsigned int nSecondsAudio = 4;
float *arr;
lame_global_flags *gfp;
unsigned char mp3buffer[2304]; /*some odd buffer sizes hard-coded.*/
int pcm_samples_1d[2*1152];
int pcm_samples_2d[2][1152];
int read = 0, write = 0;
int return_code = 1;
int mp3buf_size;
FILE *mp3;
FILE *pcm;
int framesize = 0;
int i = 0, j = 0, num_samples_encoded = 0;
/*Step 1. Generate sinusoid.*/
/*arr = (float *) malloc(sizeof(float) * nSecondsAudio * sampleRate);
arr = generateSinusoid(sampleRate, nSecondsAudio);*/
/*Step 2. See if encoder exists.*/
char *s = (char *) malloc(sizeof(char)*200);
s = get_lame_version();
printf("Lame version = %s\n", s);
/* Init lame flags.*/
gfp = lame_init();
if(!gfp) {
printf("Unable to initialize gfp object.");
} else {
printf("Able to initialize gfp object.\n");
}
/* set other parameters.*/
lame_set_num_channels(gfp, 1);
/*lame_set_num_samples(gfp, (nSecondsAudio * sampleRate));*/
lame_set_in_samplerate(gfp, sampleRate);
lame_set_quality(gfp, 5); /* set for high speed and good quality. */
lame_set_mode(gfp, 3); /* the input audio is mono */
lame_set_out_samplerate(gfp, sampleRate);
printf("Able to set a number of parameters too.");
framesize = lame_get_framesize(gfp);
printf("Framesize = %d\n", framesize);
assert(framesize <= 1152);
/* set more internal variables. check for failure.*/
if(lame_init_params(gfp) == -1) {
printf("Something failed in setting internal parameters.");
}
/* encode the pcm array as mp3.*
* Read the file. Encode whatever is read.
* As soon as end of file is reached, flush the buffers.
* Write everything to a file.
* Write headers too.
*/
/* Open PCM file for reading from.*/
pcm = fopen("out.pcm", "rb"); /*hard-coded to the only available pcm file.*/
if(!pcm) {
printf("Cannot open pcm file for reading.");
return 1;
}
mp3 = fopen("out.mp3", "wb+");
if(!mp3) {
printf("Cannot open file for writing.");
return 1;
}
do {
read = fread(pcm_samples_1d, sizeof(short), 2304, pcm); /*reads framesize shorts from pcm file.*/
printf("Read %d shorts from file.\n", read);
/* check for number of samples read. if 0, start flushing, else encode.*/
if(read > 0) {
/* got data in 1D array. convert it to 2D */
/* snippet below taken from lame source code. needs better understanding. pcm_samples_2d[0] = contents of buffer. pcm_samples_2d[1] = 0 since number of channels is always one.*/
memset(pcm_samples_2d[1], 0, 1152 * sizeof(int)); /*set all other samples with 0.*/
memset(pcm_samples_2d[0], 0, 1152 * sizeof(int));
i = 0, j = 0;
for(i = 0; i < 1152; i++) {
pcm_samples_2d[0][i] = pcm_samples_1d[i];
}
/* encode samples. */
num_samples_encoded = lame_encode_buffer_int(gfp, pcm_samples_2d[0], pcm_samples_2d[1], read, mp3buffer, sizeof(mp3buffer));
printf("number of samples encoded = %d\n", num_samples_encoded);
/* check for value returned.*/
if(num_samples_encoded > 1) {
printf("It seems the conversion was successful.\n");
} else if(num_samples_encoded == -1) {
printf("mp3buf was too small");
return 1;
} else if(num_samples_encoded == -2) {
printf("There was a malloc problem.");
return 1;
} else if(num_samples_encoded == -3) {
printf("lame_init_params() not called.");
return 1;
} else if(num_samples_encoded == -4) {
printf("Psycho acoustic problems.");
return 1;
} else {
printf("The conversion was not successful.");
return 1;
}
printf("Contents of mp3buffer = \n");
for(i = 0; i < 2304; i++) {
printf("mp3buffer[%d] = %d\n", i, mp3buffer[i]);
}
write = (int) fwrite(mp3buffer, sizeof(char), num_samples_encoded, mp3);
if(write != num_samples_encoded) {
printf("There seems to have been an error writing to mp3 within the loop.\n");
return 1;
} else {
printf("Writing of %d samples a success.\n", write);
}
}
} while(read > 0);
/* in case where the number of samples read is 0, or negative, start flushing.*/
read = lame_encode_flush(gfp, mp3buffer, sizeof(mp3buffer)); /*this may yield one more mp3 buffer.*/
if(read < 0) {
if(read == -1) {
printf("mp3buffer is probably not big enough.\n");
} else {
printf("MP3 internal error.\n");
}
return 1;
} else {
printf("Flushing stage yielded %d frames.\n", read);
}
write = (int) fwrite(mp3buffer, 1, read, mp3);
if(write != read) {
printf("There seems to have been an error writing to mp3.\n");
return 1;
}
/*samples have been written. write ID3 tag.*/
read = lame_get_id3v1_tag(gfp, mp3buffer, sizeof(mp3buffer));
if(sizeof(read) > sizeof(mp3buffer)) {
printf("Buffer too small to write ID3v1 tag.\n");
} else {
if(read > 0) {
write = (int) fwrite(mp3buffer, 1, read, mp3);
if(read != write) {
printf("more errors in writing id tag to mp3 file.\n");
}
}
}
lame_close(gfp);
fclose(pcm);
fclose(mp3);
return 0;
}
我的问题:
1.我的输入pcm数据采样为16kHz,单声道,16位编码。鉴于只有一个通道,lame_encode_buffer_int输入的左右通道是什么?
2.我不确定我是否理解从1d数组转换为2d数组(代码中为pcm_samples_1d到pcm_samples_2d)以及this question中给出的“转换”过程。
3.为什么我会发出尖锐的声音?在代码中,使用的库是使用--enable-debug
标志从源代码编译的。然而,我无法介入使用gdb
的功能。我还应该做些什么?
到目前为止我尝试了什么:
1.阅读LAME项目的文档(或网上可用的文档)
2.阅读SO和其他论坛上发布的问题
3.浏览源代码:lame.h
,frontend/main.c
frontend / get_audio.c`等。
欢迎任何帮助。
答案 0 :(得分:1)
此:
char *s = (char *) malloc(sizeof(char)*200);
s = get_lame_version();
错了,它会泄漏内存。删除malloc()
调用,无论如何你都没有使用分配的内存,因为你用get_lame_version()
返回来覆盖指针。
此外,don't cast the return value of malloc()
in C,并且避免sizeof (char)
,因为它始终为1.如果要“锁定”指针类型的分配,请使用:
s = malloc(200 * sizeof *s);
为了更具体地说明你的代码,1d / 2d数组只是简单的可怕,如果不知道LAME API,我就不可能知道代码是否正确,我没有。它可能与单声道/立体声有关,因为它似乎就是它的作用。
不确定MP3是否可以使用静音通道,由于某种原因(可能会产生点击),这可能是非法的输入数据。
答案 1 :(得分:0)
以下是我为解决问题所做的工作:
试图在Audacity
中播放输出音频文件。将该文件的“速度”降低50%得到了正确的输出。这意味着问题可能是输入和输出采样率不同,重采样操作不会在lame_encode_buffer_int
内发生。将该例程更改为lame_encode_buffer
,处理其他例程中的重新采样。这使编码工作。
答案 2 :(得分:0)
我认为您的问题确实是LAME不支持16kHz,并且lame_encode_buffer_int()
函数不会自动对数据重新采样。
也可能是设置输出采样率:
lame_set_out_samplerate(gfp, sampleRate);
与MP3不兼容时会出现问题。
有效的MP3(MPEG-1,第III层)采样率是:
LAME可能选择了32kHz,这就是为什么将Audacity的速率减半可以将音频恢复到理想的速率的原因。
关于单声道/立体声问题,只要您只有单声道数据,就不要指定正确的缓冲区(使用NULL
)。只要您定义了一个通道,该方法就可以起作用:
lame_set_num_channels(gfp, 1);
...snip...
lame_encode_buffer_int(gfp, pcm_samples_1d, NULL,
read, mp3buffer, sizeof(mp3buffer));
lame_encode_buffer()
也可以将正确的缓冲区设置为NULL
。
lame_encode_buffer(gfp, pcm_samples_1d, NULL
read, mp3bufer, sizeof(mp3buffer));
内部,这意味着您的1d缓冲区将同时用于两个通道,但是低电平采样总会忽略正确的缓冲区。
正如他在回答中提到的那样,该代码还存在一些与LAME不直接相关的问题...如果您仍然有它,并且您有兴趣并想要查看,您可以尝试将其发布在{{ 3}}。