2013-04-18 3 views
1

Я слежу за this tutorial по поводу использования LAME mp3 на Android с jni. Запись, кажется, работает, и я получаю выход в формате mp3, но при воспроизведении звук замедляется и разворачивается.Lame encoded mp3 audio slowed down - Android

Я попытался поставить все соответствующие коды ниже. Любые указания относительно того, почему это происходит? Заранее спасибо за вашу помощь.

Редактировать: OK, поэтому просто чтобы проверить, что я импортировал необработанные данные в 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); 
} 

Edit - ИТАК из интереса я скачал APK учебник предоставляет на мой телефон и побежал , Это прекрасно работает. Таким образом, это говорит о том, что проблема меньше с учебником и еще кое-что, что я сделал. Я буду пересматривать это, когда у меня есть время и посмотреть, могу ли я определить, где я ошибся

+0

Сверху моей головы, которая звучит как проблема с частотой выборки между тем, что вы записываете, и тем, что вы используете для воспроизведения звука назад. –

+2

Когда вы записываете, вы пытаетесь использовать множество различных конфигураций, пока не будете найдите тот, который работает, но когда вы инициализируете LAME, вы предполагаете стерео с частотой 44,1 кГц. Вы подтвердили, что на самом деле запись составляет стерео с частотой 44,1 кГц (даже если это так, вероятно, вы должны исправить свой вызов 'initEncoder', чтобы использовать фактическую конфигурацию). – Michael

+0

Thats true @Michael, хотя я знаю из журнала, что рекордер 44.1khz Мне нужно это исправить. Но не уверен, что это поможет моей текущей проблеме. –

ответ

1

Вы побудили меня снова рассмотреть мою проблему, и я нашел проблему для меня. Возможно, это то, что происходит для вас. Проверьте частоту дискретизации wav-файла, который вы использовали. Я предположил или слишком быстро посмотрел на меня и подумал, что он сказал 44100; но это было 48000! Я исправил свою проблему с:

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

Возможно, ваш код не читает правильную частоту дискретизации по какой-то нечетной причине?

+0

Я посмотрю на это спасибо –

+0

Это исправило мою проблему с lame.all.js при преобразовании от микрофонного входа, пришлось установить частоту дискретизации до 48000 независимо от частоты дискретизации микрофона. – timmacp

2

Чтобы указать указатель, вам нужно вызвать lame_encode_buffer_interleaved(), если вы используете 2 канала (.stereo) для записи.

Это заняло у меня несколько дней, чтобы понять это, это код, который вы можете использовать:

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); 
} 
+0

Я попробую это спасибо –

3

Вы называете initEncoder с каналов, и инициализировать AudioRecord с STEREO и MONO , но wrapper.c может иметь дело только с 1 каналом:

 

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

Вышеуказанные нормы требуют, чтобы источник звука моно с 1 канала , Если вы хотите поддержать STEREO, обратите внимание на lame_encode_buffer метод

 

int CDECL lame_encode_buffer (                                
    lame_global_flags* gfp,   /* global context handle   */                     
    const short int  buffer_l [], /* PCM data for left channel  */                     
    const short int  buffer_r [], /* PCM data for right channel */                     
    const int   nsamples,  /* number of samples per channel */                     
    unsigned char*  mp3buf,  /* pointer to encoded MP3 stream */                     
    const int   mp3buf_size); /* number of valid octets in this                     
              stream      */ 

+0

Спасибо. Мне нужно обойтись, чтобы попробовать некоторые из этих вещей. –

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, чтобы она использовала две строки в качестве источника и обрабатывала все дважды.

Смежные вопросы