SoFunction
Updated on 2025-04-09

Android recording mp3 format files

Preface

Recently, I have been working on an instant messaging project. Since I have to ensure the universality of the PC, iOS and Android, I finally unified it into MP3 format. I have always been worried about whether the MP3 format will be large, but the actual test is still acceptable. Let’s take a look at the specific steps below:

tool

The MP3 format is converted from an open source project, MP3lame. Since the project uses jni, everyone needs to configure the ndk environment. I won’t talk about the environment configuration here. You can Baidu on your own. The latest one should be well configured.

Create jni

Copy the file

After downloading (I downloaded version 3.98.4), open it, find the libmp3lame file, copy the .h and .c inside, create a jni folder in your project, and create a new folder under the jni folder (my name is lame-3.98.4_libmp3lame, which will be used later), copy the file you just copied, and then copy the include folder into it.

create

Create a file in jni,

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LAME_LIBMP3_DIR := lame-3.98.4_libmp3lame

LOCAL_MODULE  := mp3lame
LOCAL_SRC_FILES := $(LAME_LIBMP3_DIR)/ $(LAME_LIBMP3_DIR)/ $(LAME_LIBMP3_DIR)/ $(LAME_LIBMP3_DIR)/mpglib_interface.c $(LAME_LIBMP3_DIR)/ $(LAME_LIBMP3_DIR)/ $(LAME_LIBMP3_DIR)/ $(LAME_LIBMP3_DIR)/ $(LAME_LIBMP3_DIR)/ $(LAME_LIBMP3_DIR)/ $(LAME_LIBMP3_DIR)/ $(LAME_LIBMP3_DIR)/gain_analysis.c $(LAME_LIBMP3_DIR)/ $(LAME_LIBMP3_DIR)/ $(LAME_LIBMP3_DIR)/ $(LAME_LIBMP3_DIR)/quantize_pvt.c $(LAME_LIBMP3_DIR)/set_get.c $(LAME_LIBMP3_DIR)/ $(LAME_LIBMP3_DIR)/ $(LAME_LIBMP3_DIR)/ com_maxi_mp3record_MP3Recorder.c

include $(BUILD_SHARED_LIBRARY)


**Note: **LAME_LIBMP3_DIR := lame-3.98.4_libmp3lame You need to change it to the file name in your project, that is, the newly created folder under jni mentioned above.

You should have seen the last sentence com_maxi_mp3record_MP3Recorder.c
Obviously this is my own .c file. The interface used to call mp3lame corresponds to .mp3record. in my java. Let's create the java file first.

create

Create a file corresponding to your package name, which is the Java file corresponding to your package name.

package ;

import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;

/**
  * <b>Class function description:</b><div style="margin-left:40px;margin-top:-10px">
  * MP3 real-time recording function can be paused. Be careful not to be confused by using Native development.
  */
public class MP3Recorder {
 private String filePath;
 private int sampleRate;
 private boolean isRecording = false;
 private boolean isPause = false;
 private Handler handler;

 /**
   * Start recording
   */
 public static final int MSG_REC_STARTED = 1;

 /**
   * End recording
   */
 public static final int MSG_REC_STOPPED = 2;

 /**
   * Pause recording
   */
 public static final int MSG_REC_PAUSE = 3;

 /**
   * Continue recording
   */
 public static final int MSG_REC_RESTORE = 4;

 /**
   * The buffer is hung, the sampling rate is not supported by the phone
   */
 public static final int MSG_ERROR_GET_MIN_BUFFERSIZE = -1;

 /**
   * Failed when creating the file
   */
 public static final int MSG_ERROR_CREATE_FILE = -2;

 /**
   * It failed when initializing the recorder
   */
 public static final int MSG_ERROR_REC_START = -3;

 /**
   * An error occurred while recording the sound
   */
 public static final int MSG_ERROR_AUDIO_RECORD = -4;

 /**
   * It was hung during encoding
   */
 public static final int MSG_ERROR_AUDIO_ENCODE = -5;

 /**
   * It was hung while writing the file
   */
 public static final int MSG_ERROR_WRITE_FILE = -6;

 /**
   * Cannot close file stream
   */
 public static final int MSG_ERROR_CLOSE_FILE = -7;

 public MP3Recorder(int sampleRate) {
   = sampleRate;
 }

 public void setFilePath(String filePath) {
   = filePath;
 }

 /**
   * Opening movie
   */
 public void start() {
  if (isRecording) {
   return;
  }

  new Thread() {
   @Override
   public void run() {
    
      .setThreadPriority(.THREAD_PRIORITY_URGENT_AUDIO);
    // Obtain the appropriate buffer size based on several defined configurations    final int minBufferSize = (
      sampleRate, AudioFormat.CHANNEL_IN_MONO,
      AudioFormat.ENCODING_PCM_16BIT);
    if (minBufferSize &lt; 0) {
     if (handler != null) {
      (MSG_ERROR_GET_MIN_BUFFERSIZE);
     }
     return;
    }
    AudioRecord audioRecord = new AudioRecord(
      , sampleRate,
      AudioFormat.CHANNEL_IN_MONO,
      AudioFormat.ENCODING_PCM_16BIT, minBufferSize * 2);
    // 5 seconds of buffering    short[] buffer = new short[sampleRate * (16 / 8) * 1 * 5];
    byte[] mp3buffer = new byte[(int) (7200 +  * 2 * 1.25)];

    FileOutputStream output = null;
    try {
     File file = createSDFile(filePath);
     output = new FileOutputStream(file);
    } catch (FileNotFoundException e) {
     if (handler != null) {
      (MSG_ERROR_CREATE_FILE);
     }
     return;
    } catch (IOException e) {
     // TODO Auto-generated catch block
     ();
    }
    (sampleRate, 1, sampleRate, 32);
    isRecording = true; // Recording status    isPause = false; // Recording status    try {
     try {
      (); // Turn on recording to obtain audio data
      if (mListener != null) {
       ();
      }
     } catch (IllegalStateException e) {
      // No recording...      if (handler != null) {
       (MSG_ERROR_REC_START);
      }
      return;
     }

     try {
      // Start recording      if (handler != null) {
       (MSG_REC_STARTED);
      }

      int readSize = 0;
      boolean pause = false;
      while (isRecording) {
       /*--pause--*/
       if (isPause) {
        if (!pause) {
         (MSG_REC_PAUSE);
         pause = true;
        }
        continue;
       }
       if (pause) {
        (MSG_REC_RESTORE);
        pause = false;
       }
       /*--End--*/
       /*--Real-time recording and writing data-*/
       readSize = (buffer, 0,
         minBufferSize);
       voiceLevel = getVoiceSize(readSize, buffer);
       if (readSize &lt; 0) {
        if (handler != null) {
         (MSG_ERROR_AUDIO_RECORD);
        }
        break;
       } else if (readSize == 0) {
        ;
       } else {
        int encResult = (buffer,
          buffer, readSize, mp3buffer);
        if (encResult &lt; 0) {
         if (handler != null) {
          (MSG_ERROR_AUDIO_ENCODE);
         }
         break;
        }
        if (encResult != 0) {
         try {
          (mp3buffer, 0, encResult);
         } catch (IOException e) {
          if (handler != null) {
           (MSG_ERROR_WRITE_FILE);
          }
          break;
         }
        }
       }
       /*--End--*/
      }
      /*--Recording is over-*/
      int flushResult = (mp3buffer);
      if (flushResult &lt; 0) {
       if (handler != null) {
        (MSG_ERROR_AUDIO_ENCODE);
       }
      }
      if (flushResult != 0) {
       try {
        (mp3buffer, 0, flushResult);
       } catch (IOException e) {
        if (handler != null) {
         (MSG_ERROR_WRITE_FILE);
        }
       }
      }
      try {
       ();
      } catch (IOException e) {
       if (handler != null) {
        (MSG_ERROR_CLOSE_FILE);
       }
      }
      /*--End--*/
     } finally {
      ();
      ();
     }
    } finally {
     ();
     isRecording = false;
    }
    if (handler != null) {
     (MSG_REC_STOPPED);
    }
   }
  }.start();
 }

 public void stop() {
  isRecording = false;
 }

 public void pause() {
  isPause = true;
 }

 public void restore() {
  isPause = false;
 }

 public boolean isRecording() {
  return isRecording;
 }

 public boolean isPaus() {
  if (!isRecording) {
   return false;
  }
  return isPause;
 }

 // Get the level of sound public int getVoiceSize(int r, short[] buffer) {
  if (isRecording) {
   try {
    long v = 0;
    // Take out the buffer content and perform square sum operation    for (int i = 0; i &lt; ; i++) {
     v += buffer[i] * buffer[i];
    }
    // Divide the sum of squares by the total length of the data to get the volume.    double mean = v / (double) r;
    double volume = 10 * Math.log10(mean);
    return (((int) volume / 10) - 1);
   } catch (Exception e) {
    // TODO Auto-generated catch block

   }
  }

  return 1;
 }
 /**
   * Create a file on the SD card
   *
   * @throws IOException
   */
 public static File createSDFile(String fileName) throws IOException {
  File file = new File(fileName);
  if (!isFileExists(file))
   if (()) {
    ();
   } else {
    ();
   }
  return file;
 }
 private int voiceLevel;

 public int getVoiceLevel() {
  return voiceLevel;
 }

 public interface AudioStageListener {
  void wellPrepared();
 }

 public AudioStageListener mListener;

 public void setOnAudioStageListener(AudioStageListener listener) {
  mListener = listener;
 }

 /**
   * Recording status management
   *
   * @see RecMicToMp3#MSG_REC_STARTED
   * @see RecMicToMp3#MSG_REC_STOPPED
   * @see RecMicToMp3#MSG_REC_PAUSE
   * @see RecMicToMp3#MSG_REC_RESTORE
   * @see RecMicToMp3#MSG_ERROR_GET_MIN_BUFFERSIZE
   * @see RecMicToMp3#MSG_ERROR_CREATE_FILE
   * @see RecMicToMp3#MSG_ERROR_REC_START
   * @see RecMicToMp3#MSG_ERROR_AUDIO_RECORD
   * @see RecMicToMp3#MSG_ERROR_AUDIO_ENCODE
   * @see RecMicToMp3#MSG_ERROR_WRITE_FILE
   * @see RecMicToMp3#MSG_ERROR_CLOSE_FILE
   */
 public void setHandle(Handler handler) {
   = handler;
 }

 /*--The following is the Native part--*/
 static {
  ("mp3lame");
 }

 /**
   * Initialize recording parameters
   */
 public static void init(int inSamplerate, int outChannel,
   int outSamplerate, int outBitrate) {
  init(inSamplerate, outChannel, outSamplerate, outBitrate, 7);
 }

 /**
   * Initialize recording parameters quality: 0=very very slow 9=very bad very fast
   */
 public native static void init(int inSamplerate, int outChannel,
   int outSamplerate, int outBitrate, int quality);

 /**
   * Audio data encoding (PCM leftward, PCM rightward, MP3 output)
   */
 public native static int encode(short[] buffer_l, short[] buffer_r,
   int samples, byte[] mp3buf);

 /**
   * It is said that the buffer zone must be cleaned after recording
   */
 public native static int flush(byte[] mp3buf);

 /**
   * End encoding
   */
 public native static void close();
}

Create a c file

When creating a c file, create it under jni, name it "." "." and replace it with "_". For example: com_maxi_mp3record_MP3Recorder.c. Of course, there must be a header file: com_maxi_mp3record_MP3Recorder.h.

com_maxi_mp3record_MP3Recorder.c

#include "lame-3.98.4_libmp3lame/"
#include "com_maxi_mp3record_MP3Recorder.h"

static lame_global_flags *glf = NULL;

JNIEXPORT void JNICALL Java_com_maxi_mp3record_MP3Recorder_init(
  JNIEnv *env, jclass cls, jint inSamplerate, jint outChannel,
  jint outSamplerate, jint outBitrate, jint quality) {
 if (glf != NULL) {
  lame_close(glf);
  glf = NULL;
 }
 glf = lame_init();
 lame_set_in_samplerate(glf, inSamplerate);
 lame_set_num_channels(glf, outChannel);
 lame_set_out_samplerate(glf, outSamplerate);
 lame_set_brate(glf, outBitrate);
 lame_set_quality(glf, quality);
 lame_init_params(glf);
}

JNIEXPORT jint JNICALL Java_com_maxi_mp3record_MP3Recorder_encode(
  JNIEnv *env, jclass cls, jshortArray buffer_l, jshortArray buffer_r,
  jint samples, jbyteArray mp3buf) {
 jshort* j_buffer_l = (*env)->GetShortArrayElements(env, buffer_l, NULL);

 jshort* j_buffer_r = (*env)->GetShortArrayElements(env, buffer_r, NULL);

 const jsize mp3buf_size = (*env)->GetArrayLength(env, mp3buf);
 jbyte* j_mp3buf = (*env)->GetByteArrayElements(env, mp3buf, NULL);

 int result = lame_encode_buffer(glf, j_buffer_l, j_buffer_r,
   samples, j_mp3buf, mp3buf_size);

 (*env)->ReleaseShortArrayElements(env, buffer_l, j_buffer_l, 0);
 (*env)->ReleaseShortArrayElements(env, buffer_r, j_buffer_r, 0);
 (*env)->ReleaseByteArrayElements(env, mp3buf, j_mp3buf, 0);

 return result;
}

JNIEXPORT jint JNICALL Java_com_maxi_mp3record_MP3Recorder_flush(
  JNIEnv *env, jclass cls, jbyteArray mp3buf) {
 const jsize mp3buf_size = (*env)->GetArrayLength(env, mp3buf);
 jbyte* j_mp3buf = (*env)->GetByteArrayElements(env, mp3buf, NULL);

 int result = lame_encode_flush(glf, j_mp3buf, mp3buf_size);

 (*env)->ReleaseByteArrayElements(env, mp3buf, j_mp3buf, 0);

 return result;
}

JNIEXPORT void JNICALL Java_com_maxi_mp3record_MP3Recorder_close(
  JNIEnv *env, jclass cls) {
 lame_close(glf);
 glf = NULL;
}

com_maxi_mp3record_MP3Recorder.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <>
/* Header for class com_maxi_mp3record_MP3Recorder */

#ifndef _Included_com_maxi_mp3record_MP3Recorder
#define _Included_com_maxi_mp3record_MP3Recorder
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:  com_maxi_mp3record_MP3Recorder
 * Method: init
 * Signature: (IIIII)V
 */
JNIEXPORT void JNICALL Java_com_maxi_mp3record_MP3Recorder_init
 (JNIEnv *, jclass, jint, jint, jint, jint, jint);

/*
 * Class:  com_maxi_mp3record_MP3Recorder
 * Method: encode
 * Signature: ([S[SI[B)I
 */
JNIEXPORT jint JNICALL Java_com_maxi_mp3record_MP3Recorder_encode
 (JNIEnv *, jclass, jshortArray, jshortArray, jint, jbyteArray);

/*
 * Class:  com_maxi_mp3record_MP3Recorder
 * Method: flush
 * Signature: ([B)I
 */
JNIEXPORT jint JNICALL Java_com_maxi_mp3record_MP3Recorder_flush
 (JNIEnv *, jclass, jbyteArray);

/*
 * Class:  com_maxi_mp3record_MP3Recorder
 * Method: close
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_maxi_mp3record_MP3Recorder_close
 (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

Don't just copy these two files without looking at the content. They are all interfaces connecting Java and C, so there are no errors. For example, #ifndef _Included_com_maxi_mp3record_MP3Recorder, this must be replaced with your corresponding package name, and everything in it must be replaced with the corresponding package name of your own program.

Compile ndk

Create under your project file and write:
APP_ABI := all
If not added, NDK will only compile "armeabi", but Android has many different types of processors, so we need more than just arm. I believe that you will definitely have all ndk configured after you have done it. Then open the terminal and enter your project (find ndk-bulid in your ndk directory. It is best to add it to the environment variables. It is more convenient for future compilation. You have not added environment variables by default here), and execute ndk-bulid. Wait for a while and you will find that there is an obj folder in your project. "arm64-v8a", "armeabi", "armeabi-v7a", "mips", "mips64", "x86", and "x86_64" will be generated under the obj folder. Open it, and there will be one in each folder. OK, that's right, that's the "skateboard shoes" you want. Put it in your libs file and do not create it yourself, and each platform can load it.

How to use

MP3Recorder recorder = new MP3Recorder(8000);
(voicePath);//Record save directory();//Start recording();//The recording ends()//This is my encapsulated audio amplitude interface, which you can use to display the sound size when recording and adjust the data by yourself.

Summarize

I have been recording with MediaRecorder before, and I found that the recording can only be in amr, acc and other formats. It feels impossible to use lame to convert MP3 to MP3. I tried but failed. I don’t know what the specific reason is, so everyone can study it if they have time, so don’t try it if they don’t have time.

The sound recorded by Mp3lame is quite reliable (but it is said that iOS has some salsa sound), and the size of the recording is still acceptable, and the five-second audio is about 20k. It is very convenient to use. If you have any questions or suggestions, please leave a message.

The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.