1

我一直在关注this tutorial on using LAME mp3 on Android with jni。录音似乎正在工作,我得到了 mp3 的输出,但在播放时,音频已经减慢并降低了音调。

我试图将所有相关代码放在下面。关于为什么会发生这种情况的任何指导?在此先感谢您的帮助。

编辑:好的,只是为了检查我将原始数据导入 Audacity 并且可以正常播放,所以这一定是编码阶段的问题。

Java类:

public class Record extends Activity implements OnClickListener {

    static {
        System.loadLibrary("mp3lame");
    }

    private native void initEncoder(int numChannels, int sampleRate, int bitRate, int mode, int quality);

    private native void destroyEncoder();

    private native int encodeFile(String sourcePath, String targetPath);

    private static final int RECORDER_BPP = 16;
    private static final String AUDIO_RECORDER_FILE_EXT_WAV = ".wav";
    private static final String AUDIO_RECORDER_FOLDER = "AberdeenSoundsites";
    private static final String AUDIO_RECORDER_TEMP_FILE = "record_temp.raw";
    private static final int[] RECORDER_SAMPLERATES = {44100, 22050, 11025, 8000};
    private static final int RECORDER_CHANNELS = AudioFormat.CHANNEL_IN_STEREO;
    private static final int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;

    public static final int NUM_CHANNELS = 2;
    public static final int SAMPLE_RATE = 44100;
    public static final int BITRATE = 320;
    public static final int MODE = 1;
    public static final int QUALITY = 2;
        private short[] mBuffer;
    private File rawFile;
    private File encodedFile;

    private int sampleRate;
    private String filename;

    private AudioRecord recorder = null;
    private int bufferSize = 0;
    private Thread recordingThread = null;
    private boolean isRecording = false;


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.record);

        initEncoder(NUM_CHANNELS, SAMPLE_RATE, BITRATE, MODE, QUALITY);

        stopButton = (Button) findViewById(R.id.stop_button);
        stopButton.setOnClickListener(this);
        timer = (TextView) findViewById(R.id.recording_time);

        bufferSize = AudioRecord.getMinBufferSize(44100, RECORDER_CHANNELS, RECORDER_AUDIO_ENCODING);
    }

    private void startRecording() {
        stopped = false;
        stopButton.setText(R.string.stop_button_label);

        // Set up and start audio recording
        recorder = findAudioRecord();
        recorder.startRecording();
        isRecording = true;

        rawFile = getFile("raw");
        mBuffer = new short[bufferSize];
        startBufferedWrite(rawFile);
        }

    private void stopRecording() {
        mHandler.removeCallbacks(startTimer);
        stopped = true;

        if(recorder != null){
            isRecording = false;

            recorder.stop();
            recorder.release();

            recorder = null;
            recordingThread = null;
        }

        encodedFile = getFile("mp3");
        int result = encodeFile(rawFile.getAbsolutePath(), encodedFile.getAbsolutePath());
        if (result == 0) {
            Toast.makeText(Record.this, "Encoded to " + encodedFile.getName(), Toast.LENGTH_SHORT)
                    .show();
        }
    }

    private void startBufferedWrite(final File file) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                DataOutputStream output = null;
                try {
                    output = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
                    while (isRecording) {
                        int readSize = recorder.read(mBuffer, 0, mBuffer.length);
                        for (int i = 0; i < readSize; i++) {
                            output.writeShort(mBuffer[i]);
                        }
                    }
                } catch (IOException e) {
                    Toast.makeText(Record.this, e.getMessage(), Toast.LENGTH_SHORT).show();
                } finally {
                    if (output != null) {
                        try {
                            output.flush();
                        } catch (IOException e) {
                            Toast.makeText(Record.this, e.getMessage(), Toast.LENGTH_SHORT).show();
                        } finally {
                            try {
                                output.close();
                            } catch (IOException e) {
                                Toast.makeText(Record.this, e.getMessage(), Toast.LENGTH_SHORT).show();
                            }
                        }
                    }
                }
            }
        }).start();
    }

    private File getFile(final String suffix) {
        Time time = new Time();
        time.setToNow();
        return new File(Environment.getExternalStorageDirectory()+"/MyAppFolder", time.format("%Y%m%d%H%M%S") + "." + suffix);
    }

    public AudioRecord findAudioRecord() {
        for (int rate : RECORDER_SAMPLERATES) {
            for (short audioFormat : new short[] { AudioFormat.ENCODING_PCM_16BIT, AudioFormat.ENCODING_PCM_8BIT }) {
                for (short channelConfig : new short[] { AudioFormat.CHANNEL_IN_STEREO, AudioFormat.CHANNEL_IN_MONO  }) {
                    try {
                        Log.d("AberdeenSoundsites", "Attempting rate " + rate + "Hz, bits: " + audioFormat + ", channel: "
                                + channelConfig);
                        int bufferSize = AudioRecord.getMinBufferSize(rate, channelConfig, audioFormat);

                        if (bufferSize != AudioRecord.ERROR_BAD_VALUE) {
                            // check if we can instantiate and have a success
                            AudioRecord recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, rate, channelConfig, audioFormat, bufferSize);
                            sampleRate = rate;
                            if (recorder.getState() == AudioRecord.STATE_INITIALIZED)
                                return recorder;
                        }
                    } catch (Exception e) {
                        Log.e("MyApp", rate + "Exception, keep trying.",e);
                    }
                }
            }
        }
        Log.e("MyApp", "No settings worked :(");
        return null;
    }

C包装:

#include <stdio.h>
#include <stdlib.h>
#include <jni.h>
#include <android/log.h> 
#include "libmp3lame/lame.h"

#define LOG_TAG "LAME ENCODER"
#define LOGD(format, args...)  __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, format, ##args);
#define BUFFER_SIZE 8192
#define be_short(s) ((short) ((unsigned short) (s) << 8) | ((unsigned short) (s) >> 8))

lame_t lame;

int read_samples(FILE *input_file, short *input) {
    int nb_read;
    nb_read = fread(input, 1, sizeof(short), input_file) / sizeof(short);

    int i = 0;
    while (i < nb_read) {
        input[i] = be_short(input[i]);
        i++;
    }

    return nb_read;
}

void Java_myPacakage_myApp_Record_initEncoder(JNIEnv *env,
        jobject jobj, jint in_num_channels, jint in_samplerate, jint in_brate,
        jint in_mode, jint in_quality) {
    lame = lame_init();

    LOGD("Init parameters:");
    lame_set_num_channels(lame, in_num_channels);
    LOGD("Number of channels: %d", in_num_channels);
    lame_set_in_samplerate(lame, in_samplerate);
    LOGD("Sample rate: %d", in_samplerate);
    lame_set_brate(lame, in_brate);
    LOGD("Bitrate: %d", in_brate);
    lame_set_mode(lame, in_mode);
    LOGD("Mode: %d", in_mode);
    lame_set_quality(lame, in_quality);
    LOGD("Quality: %d", in_quality);

    int res = lame_init_params(lame);
    LOGD("Init returned: %d", res);
}

void Java_myPacakage_myApp_Record_destroyEncoder(
        JNIEnv *env, jobject jobj) {
    int res = lame_close(lame);
    LOGD("Deinit returned: %d", res);
}

void Java_myPacakage_myApp_Record_encodeFile(JNIEnv *env,
        jobject jobj, jstring in_source_path, jstring in_target_path) {
    const char *source_path, *target_path;
    source_path = (*env)->GetStringUTFChars(env, in_source_path, NULL);
    target_path = (*env)->GetStringUTFChars(env, in_target_path, NULL);

    FILE *input_file, *output_file;
    input_file = fopen(source_path, "rb");
    output_file = fopen(target_path, "wb");

    short input[BUFFER_SIZE];
    char output[BUFFER_SIZE];
    int nb_read = 0;
    int nb_write = 0;
    int nb_total = 0;

    LOGD("Encoding started");
    while (nb_read = read_samples(input_file, input)) {
        nb_write = lame_encode_buffer(lame, input, input, nb_read, output,
                BUFFER_SIZE);
        fwrite(output, nb_write, 1, output_file);
        nb_total += nb_write;
    }
    LOGD("Encoded %d bytes", nb_total);

    nb_write = lame_encode_flush(lame, output, BUFFER_SIZE);
    fwrite(output, nb_write, 1, output_file);
    LOGD("Flushed %d bytes", nb_write);

    fclose(input_file);
    fclose(output_file);
}

编辑-好吧,出于兴趣,我下载了教程提供给我的手机的apk并运行了它。这很好用。因此,这表明本教程的问题较少,而我所做的更多。当我有空的时候我会重新检查一下,看看我是否能确定我哪里出错了

4

4 回答 4

3

你用2个通道调用initEncoder ,用 STEREO 和 MONO初始化AudioRecord,但是 wrapper.c 只能处理 1 个通道:

nb_write = lame_encode_buffer(lame,输入,输入,nb_read,输出,BUFFER_SIZE);

上述代码要求源音频为单声道,具有 1 个声道。如果要支持 STEREO,请注意lame_encode_buffer方法

int CDECL lame_encode_buffer (                                                                                                                                
    lame_global_flags* gfp, /* 全局上下文句柄 */                                                                                
    const short int buffer_l [], /* 左声道的 PCM 数据 */                                                                                
    const short int buffer_r [], /* 右声道的 PCM 数据 */                                                                                
    const int nsamples, /* 每个通道的样本数 */                                                                                
    unsigned char* mp3buf, /* 指向编码 MP3 流的指针 */                                                                                
    常量 int mp3buf_size ); /* 此中的有效八位字节数                                                                                  
                                          溪流 */

于 2014-05-21T02:22:18.607 回答
3

给你一个指针,如果你使用 2 个通道 (.stereo) 来录制,你需要调用 lame_encode_buffer_interleaved()。

我花了几天时间才弄清楚,这是您可以使用的代码:

if (lame_get_num_channels(glf) == 2)
{
    result = lame_encode_buffer_interleaved(glf, j_buffer_l, samples/2, j_mp3buf, mp3buf_size);
}
else
{
    result = lame_encode_buffer(glf, j_buffer_l, j_buffer_r, samples, j_mp3buf, mp3buf_size);
}
于 2013-12-13T20:25:19.127 回答
1

你刺激我重新审视自己的问题,我为自己找到了问题。也许这就是你正在发生的事情。检查您使用的 wav 文件的采样率。我太快地假设或看着我的,以为它说的是 44100;但它是48000!我解决了我的问题:

lame_set_in_samplerate(lame, 48000);
lame_set_out_samplerate(lame, 44100);

也许您的代码由于某种奇怪的原因没有读取正确的采样率?

于 2013-05-06T13:55:18.743 回答
0

你可以重写

nb_write = lame_encode_buffer(lame, input, input, nb_read, output, BUFFER_SIZE);

nb_write = lame_encode_buffer(lame, input1, input2, nb_read, output, BUFFER_SIZE);

并使用 2 个单声道原始文件作为输入。当然,您必须调整您的 encodeFile - 函数,以便它将两个字符串作为源并处理所有内容两次。

于 2016-05-06T16:58:33.263 回答