热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

Android实现伴奏录音合成MP3

这篇文章主要为大家详细介绍了Android实现伴奏录音合成MP3,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

本文实例为大家分享了Android实现伴奏录音合成MP3的具体代码,供大家参考,具体内容如下

基本实现思路如下:

1.利用android自带的录音类(AudioRecord)实现录音.

 /**
  * 播放伴奏
  */
 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位每个样本。不一定能得到设备支持。
  */
 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 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);
   // 实例化一个字节数组,长度为最小缓冲区的长度
   m_in_bytes = new byte[m_in_buf_size];
   // 实例化一个链表,用来存放字节组数
   m_in_q = new LinkedList();
 
   // 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);
   // 实例化一个长度为播放最小缓冲大小的字节数组
   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.录音完成后,调用开源工具(Mad)实现PCM合成输出到MP3文件.

主要调用的合成方法:

/***
 * 方法描述:本地方法调用JNI合并mp3PCM与sourcePCM
 * @param sourcePCM
 * @param mp3PCM
 * @param mixPCM
 * @return
 */
 public static native int mix2PCMToPCM(String sourcePCM, String mp3PCM, String mixPCM);
 
 String recordPCMPath = SongUtil.getRecordSingPCMPath(songId); //录音生成的PCM文件
      String accompanyPCMPath = SongUtil.getAccompanySongPCMPath(songId); //伴奏解码生成的PCM文件
      String mixPCMPath = SongUtil.getMixSingPCMPath(songId); //合成后的PCM文件
      String mixMP3Path = SongUtil.getMixSingMp3Path(songId); //合成后的MP3文件
      // 混音
      int code = SongEncodeUtil.mix2PCMToPCM(recordPCMPath, accompanyPCMPath, mixPCMPath);
      if (code == 0) {
       // 转换混合后音频格式 TO mp3
       int i = SimpleLame.convert(mixPCMPath, mixMP3Path, m_in_buf_size);
       Log.i(SingSingleActivity.this.getClass().getName(), "转换" + i + "混音完成");
       saveMp3File(mixMP3Path);
      }

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • 深入理解 Oracle 存储函数:计算员工年收入
    本文介绍如何使用 Oracle 存储函数查询特定员工的年收入。我们将详细解释存储函数的创建过程,并提供完整的代码示例。 ... [详细]
  • 本文总结了2018年的关键成就,包括职业变动、购车、考取驾照等重要事件,并分享了读书、工作、家庭和朋友方面的感悟。同时,展望2019年,制定了健康、软实力提升和技术学习的具体目标。 ... [详细]
  • 在计算机技术的学习道路上,51CTO学院以其专业性和专注度给我留下了深刻印象。从2012年接触计算机到2014年开始系统学习网络技术和安全领域,51CTO学院始终是我信赖的学习平台。 ... [详细]
  • CSS 布局:液态三栏混合宽度布局
    本文介绍了如何使用 CSS 实现液态的三栏布局,其中各栏具有不同的宽度设置。通过调整容器和内容区域的属性,可以实现灵活且响应式的网页设计。 ... [详细]
  • Linux 系统启动故障排除指南:MBR 和 GRUB 问题
    本文详细介绍了 Linux 系统启动过程中常见的 MBR 扇区和 GRUB 引导程序故障及其解决方案,涵盖从备份、模拟故障到恢复的具体步骤。 ... [详细]
  • 本文介绍了如何使用jQuery根据元素的类型(如复选框)和标签名(如段落)来获取DOM对象。这有助于更高效地操作网页中的特定元素。 ... [详细]
  • 本文将详细介绍如何使用剪映应用中的镜像功能,帮助用户轻松实现视频的镜像效果。通过简单的步骤,您可以快速掌握这一实用技巧。 ... [详细]
  • 本文介绍如何在 Xcode 中使用快捷键和菜单命令对多行代码进行缩进,包括右缩进和左缩进的具体操作方法。 ... [详细]
  • 在Linux系统中配置并启动ActiveMQ
    本文详细介绍了如何在Linux环境中安装和配置ActiveMQ,包括端口开放及防火墙设置。通过本文,您可以掌握完整的ActiveMQ部署流程,确保其在网络环境中正常运行。 ... [详细]
  • 本文介绍如何通过Windows批处理脚本定期检查并重启Java应用程序,确保其持续稳定运行。脚本每30分钟检查一次,并在需要时重启Java程序。同时,它会将任务结果发送到Redis。 ... [详细]
  • 本章将深入探讨移动 UI 设计的核心原则,帮助开发者构建简洁、高效且用户友好的界面。通过学习设计规则和用户体验优化技巧,您将能够创建出既美观又实用的移动应用。 ... [详细]
  • 本文探讨了如何通过最小生成树(MST)来计算严格次小生成树。在处理过程中,需特别注意所有边权重相等的情况,以避免错误。我们首先构建最小生成树,然后枚举每条非树边,检查其是否能形成更优的次小生成树。 ... [详细]
  • 当iOS设备越狱后,某些插件可能会导致系统崩溃(白苹果)。此时,可以通过进入安全模式来排查并删除有问题的插件。本文将详细介绍如何通过特定按键组合进入不加载MobileSubstrate的安全模式,并提供相关背景知识。 ... [详细]
  • C++: 实现基于类的四面体体积计算
    本文介绍如何使用C++编程语言,通过定义类和方法来计算由四个三维坐标点构成的四面体体积。文中详细解释了四面体体积的数学公式,并提供了两种不同的实现方式。 ... [详细]
  • 如何优化2060显卡设置以提升《Apex英雄》游戏体验
    《Apex英雄》作为一款热门的战术竞技游戏,吸引了大量玩家。本文将探讨如何通过优化GeForce RTX 2060显卡设置,确保在《Apex英雄》中获得最佳性能和流畅的游戏体验。 ... [详细]
author-avatar
手机用户2502860565
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有