Android Realization of Accompaniment Recording Synthesis MP3

  • 2021-10-27 09:18:46
  • OfStack

In this paper, we share the specific code of Android accompaniment recording synthesis MP3 for your reference, the specific contents are as follows

The basic implementation ideas are as follows:

1. Use the recording class (AudioRecord) of android to realize recording.


 /**
  * 播放伴奏
  */
 private MediaPlayer player;
 /**
  * 返回按钮
  */
 private ImageView btnBack;
 /**
  * 切换歌曲
  */
 private Button btnSwitchSong;
 /**
  * 伴唱时长
  */
 private TextView tv_recod_time;
 /**
  * 歌词VIEW
  */
 private LyricView lv_lyric;
 /**
  * 开始录制
  */
 private Button btnPlay;
 /**
  * 标题
  */
 private TextView ivTitle;
 private boolean canPlay = true;
 private boolean isPause = false;
 
 /***
  * 背景音乐模式
  */
 private BackgroudMusicMode mode = BackgroudMusicMode.Accompany;
 /**
  * 歌曲id
  */
 private String songId;
 /**
  * 歌曲名称
  */
 private String songName;
 /**
  * 歌手名字
  */
 private String singerName;
 /**
  * 伴奏文件
  */
 private File file;
 /**
  * 是否正在录制
  */
 private boolean isStart = false;
 /**
  * 录音状态
  */
 private boolean starting = false;
 /**
  * 伴奏时间
  */
 private int bztimetmp = 0;
 /**
  * 伴奏时间
  */
 private String bztime = "";
 
 /**
  * 录制时间
  */
 private int recordTimeLength=0;
 /**
  * 更新伴奏时间
  */
 private RecordTask rt = null;
 
 /**
  * 录制频率,单位hz.这里的值注意了,写的不好,可能实例化AudioRecord对象的时候,会出错。我开始写成11025就不行。这取决于硬件设备
  * 设置音频采样率,44100是目前的标准,但是某些设备仍然支持22050,16000,11025
  */
 private int sampleRateInHz = 44100;
 /**
  * 设置音频的录制的声道CHANNEL_IN_STEREO为双声道,CHANNEL_CONFIGURATION_MONO为单声道
  */
 private int channelConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;
 
 /**
  * 音频数据格式:PCM 16位每个样本。保证设备支持。PCM 8位每个样本。不1定能得到设备支持。
  */
 private int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
 
 /**
  * 调整播放音量
  */
 private AudioManager audioManager;
 
 /**
  * 最大音量
  */
 private int maxVolume = 0;
 
 /**
  * 当前音量
  */
 private int currentVolume = 0;
 
 /**
  * AudioRecord 写入缓冲区大小
  */
 protected int m_in_buf_size;
 /**
  * 录制音频对象
  */
 private AudioRecord mRecorder;
 /**
  * 录入的字节数组
  */
 private byte[] m_in_bytes;
 /**
  * 存放录入字节数组的大小
  */
 private LinkedList<byte[]> m_in_q;
 /**
  * AudioTrack 播放缓冲大小
  */
 private int m_out_buf_size;
 /**
  * 播放音频对象
  */
 private AudioTrack mAudioTrack;
 /**
  * 播放的字节数组
  */
 private byte[] m_out_bytes;
 /**
  * 录制音频线程
  */
 private Thread record;
 /**
  * 播放音频线程
  */
 private Thread play;
 /**
  * 让线程停止的标志
  */
 private boolean flag = true;
 /**
  * 是否启动回声
  */
 private boolean room_flag = true;
 
 /***上面有个播放歌词的组件
  /***
  * 初始化
  */
 private void init() {
  
  audioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
  maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL);
  currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
  registerHeadsetPlugReceiver();
  ycApplication = (YueChangApplication) getApplication();
  coverDao = new CoverDao(getApplicationContext());
  Bundle bundle = getIntent().getExtras();
  songId = bundle.getString("songId");
  songName = bundle.getString("songName");
  singerName = bundle.getString("singerName");
  if (songId != null) {
   // AudioRecord 得到录制最小缓冲区的大小
   m_in_buf_size = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
   // 实例化播放音频对象
   mRecorder = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRateInHz, channelConfig, audioFormat,
     m_in_buf_size);
   // 实例化1个字节数组,长度为最小缓冲区的长度
   m_in_bytes = new byte[m_in_buf_size];
   // 实例化1个链表,用来存放字节组数
   m_in_q = new LinkedList<byte[]>();
 
   // AudioTrack 得到播放最小缓冲区的大小
   m_out_buf_size = AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
   // 实例化播放音频对象
   mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRateInHz, channelConfig, audioFormat,
     m_out_buf_size, AudioTrack.MODE_STREAM);
   // 实例化1个长度为播放最小缓冲大小的字节数组
   m_out_bytes = new byte[m_out_buf_size];
   record = new Thread(new recordSound());
   
//   if(ycApplication.isHeadsetplug()){
    
//   }else{
//    m_out_trk = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRateInHz, channelConfig, audioFormat,
//      m_out_buf_size, AudioTrack.MODE_STREAM);
//   }
   
 
  }
 }
 
  /**
  *
  * 类描述:录音线程 
  *
  * @version 1.0
  */
 class recordSound implements Runnable {
  @Override
  public void run() {
   // 初始化输出流
   DataOutputStream dos = null;
   try {
    File audioFile = new File(SongUtil.getRecordSingPCMPath(songId));
    // 初始化输出流
    dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(audioFile)));
    byte[] bytes_pkg;
    if (mRecorder.getState() == AudioRecord.STATE_UNINITIALIZED) {
     // 实例化播放音频对象
     mRecorder = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRateInHz, channelConfig,
       audioFormat, m_in_buf_size);
    }
 
    // 开始录音
    mRecorder.startRecording();
    while (flag) {
     int size = mRecorder.read(m_in_bytes, 0, m_in_buf_size);
     bytes_pkg = m_in_bytes.clone();
     if (m_in_q.size() >= 2) {
      m_in_q.removeFirst();
     }
     m_in_q.add(bytes_pkg);
     if ((ycApplication.isHeadsetplug() && ycApplication.isOpenInEarphone())
       || (!ycApplication.isHeadsetplug() && ycApplication.isOpenInSpeaker())) {
      //Log.d(SingSingleActivity.this.getClass().getName(), "启动录音播放1");
      if (play == null||!room_flag) {
       //Log.d(SingSingleActivity.this.getClass().getName(), "启动录音播放2");
       room_flag = true;
       play = new Thread(new playRecord());
       // 启动播放线程
       play.start();
      }
 
     } else {
      if(room_flag||play != null){
       //Log.d(SingSingleActivity.this.getClass().getName(), "关闭录音播放1");
       room_flag = false;
       if (play != null) {
        play.interrupt();
       }
       play = null;
      }
     }
     // 写入PCM文件
     dos.write(bytes_pkg, 0, size);
     dos.flush();
    }
 
   } catch (Exception e) {
    // TODO: handle exception
    e.printStackTrace();
   } finally {
    try {
     // 关闭录音
     if (mRecorder != null) {
      try {
       if (mRecorder.getState() == AudioRecord.STATE_INITIALIZED) {
        // 关闭录音
        mRecorder.stop();
        mRecorder.release();
       }
      } catch (Exception e2) {
       // TODO: handle exception
      }
     }
     if (dos != null) {
      dos.close();
     }
    } catch (Exception e2) {
     // TODO: handle exception
     e2.printStackTrace();
    }
   }
 
  }
 }

2. After recording, call the open source tool (Mad) to achieve PCM synthesis output to MP3 file.

The main composition methods called:


/***
 *  Method description: Local method call JNI Merge mp3PCM And sourcePCM
 * @param sourcePCM
 * @param mp3PCM
 * @param mixPCM
 * @return
 */
 public static native int mix2PCMToPCM(String sourcePCM, String mp3PCM, String mixPCM);
 
 String recordPCMPath = SongUtil.getRecordSingPCMPath(songId); // Recorded generated PCM Documents 
      String accompanyPCMPath = SongUtil.getAccompanySongPCMPath(songId); // Accompaniment decoding generated PCM Documents 
      String mixPCMPath = SongUtil.getMixSingPCMPath(songId); // Synthetic PCM Documents 
      String mixMP3Path = SongUtil.getMixSingMp3Path(songId); // Synthetic MP3 Documents 
      //  Mix 
      int code = SongEncodeUtil.mix2PCMToPCM(recordPCMPath, accompanyPCMPath, mixPCMPath);
      if (code == 0) {
       //  Convert mixed audio format  TO mp3
       int i = SimpleLame.convert(mixPCMPath, mixMP3Path, m_in_buf_size);
       Log.i(SingSingleActivity.this.getClass().getName(), " Conversion " + i + " Mix complete ");
       saveMp3File(mixMP3Path);
      }

Related articles: