当前位置: 移动技术网 > IT编程>移动开发>Android > Android录制mp3格式文件

Android录制mp3格式文件

2019年07月24日  | 移动技术网IT编程  | 我要评论

魔法少女同人之不同的方向,汉王影院,幸福百米mv

前言

最近做一个即时通信类的项目,由于要保证pc端,ios端和android端的通用性,最终统一为mp3格式,一直担心mp3格式会不会很大,但是实测还是可以接受的。下面来看看具体步骤:

工具

mp3格式是用一个开源项目转的,mp3lame,由于该项目用到了jni,所以需要大家配置好ndk环境,环境配置在此就不多说了,大家可以自行百度,最新的应该很好配置。

创建jni

拷贝文件

下载好后(我下载的是3.98.4版本)打开,找到libmp3lame文件,将里面的.h和.c拷贝下来,在自己的工程里创建jni文件夹,在jni文件夹下新建一个文件夹(我的命名为lame-3.98.4_libmp3lame,后面会用到),将刚才拷贝的文件复制进去,然后再把include文件夹里的lame.h也拷贝进去。

创建android.mk

在jni中创建文件,android.mk

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)/bitstream.c $(lame_libmp3_dir)/fft.c $(lame_libmp3_dir)/id3tag.c $(lame_libmp3_dir)/mpglib_interface.c $(lame_libmp3_dir)/presets.c $(lame_libmp3_dir)/quantize.c $(lame_libmp3_dir)/reservoir.c $(lame_libmp3_dir)/tables.c $(lame_libmp3_dir)/util.c $(lame_libmp3_dir)/vbrtag.c $(lame_libmp3_dir)/encoder.c $(lame_libmp3_dir)/gain_analysis.c $(lame_libmp3_dir)/lame.c $(lame_libmp3_dir)/newmdct.c $(lame_libmp3_dir)/psymodel.c $(lame_libmp3_dir)/quantize_pvt.c $(lame_libmp3_dir)/set_get.c $(lame_libmp3_dir)/takehiro.c $(lame_libmp3_dir)/vbrquantize.c $(lame_libmp3_dir)/version.c com_maxi_mp3record_mp3recorder.c

include $(build_shared_library)


**注意:**lame_libmp3_dir := lame-3.98.4_libmp3lame 需要将其改为你的项目中的文件名,即上面说的jni下新建的文件夹。

大家应该看到了最后一句的com_maxi_mp3record_mp3recorder.c
很明显这是我自己创建的.c文件。用来调用mp3lame中的接口的,对应着我java中的com.maxi.mp3record.mp3recorder.java。咱们先创建java文件。

创建mp3recorder.java

对应你的包名建一个mp3recorder.java文件,该文件是java文件对应你的包名建立即可。

package cn.ctvonline.android.modules.project.widget;

import java.io.file;
import java.io.filenotfoundexception;
import java.io.fileoutputstream;
import java.io.ioexception;
import android.media.audioformat;
import android.media.audiorecord;
import android.media.mediarecorder;
import android.os.handler;

/**
 * <b>类功能描述:</b><div style="margin-left:40px;margin-top:-10px">
 * mp3实时录制功能,可暂停,注意因踩用native开发,不能混淆
 */
public class mp3recorder {
 private string filepath;
 private int samplerate;
 private boolean isrecording = false;
 private boolean ispause = false;
 private handler handler;

 /**
  * 开始录音
  */
 public static final int msg_rec_started = 1;

 /**
  * 结束录音
  */
 public static final int msg_rec_stopped = 2;

 /**
  * 暂停录音
  */
 public static final int msg_rec_pause = 3;

 /**
  * 继续录音
  */
 public static final int msg_rec_restore = 4;

 /**
  * 缓冲区挂了,采样率手机不支持
  */
 public static final int msg_error_get_min_buffersize = -1;

 /**
  * 创建文件时扑街了
  */
 public static final int msg_error_create_file = -2;

 /**
  * 初始化录音器时扑街了
  */
 public static final int msg_error_rec_start = -3;

 /**
  * 录紧音的时候出错
  */
 public static final int msg_error_audio_record = -4;

 /**
  * 编码时挂了
  */
 public static final int msg_error_audio_encode = -5;

 /**
  * 写文件时挂了
  */
 public static final int msg_error_write_file = -6;

 /**
  * 没法关闭文件流
  */
 public static final int msg_error_close_file = -7;

 public mp3recorder(int samplerate) {
  this.samplerate = samplerate;
 }

 public void setfilepath(string filepath) {
  this.filepath = filepath;
 }

 /**
  * 开片
  */
 public void start() {
  if (isrecording) {
   return;
  }

  new thread() {
   @override
   public void run() {
    android.os.process
      .setthreadpriority(android.os.process.thread_priority_urgent_audio);
    // 根据定义好的几个配置,来获取合适的缓冲大小
    final int minbuffersize = audiorecord.getminbuffersize(
      samplerate, audioformat.channel_in_mono,
      audioformat.encoding_pcm_16bit);
    if (minbuffersize < 0) {
     if (handler != null) {
      handler.sendemptymessage(msg_error_get_min_buffersize);
     }
     return;
    }
    audiorecord audiorecord = new audiorecord(
      mediarecorder.audiosource.mic, samplerate,
      audioformat.channel_in_mono,
      audioformat.encoding_pcm_16bit, minbuffersize * 2);
    // 5秒的缓冲
    short[] buffer = new short[samplerate * (16 / 8) * 1 * 5];
    byte[] mp3buffer = new byte[(int) (7200 + buffer.length * 2 * 1.25)];

    fileoutputstream output = null;
    try {
     file file = createsdfile(filepath);
     output = new fileoutputstream(file);
    } catch (filenotfoundexception e) {
     if (handler != null) {
      handler.sendemptymessage(msg_error_create_file);
     }
     return;
    } catch (ioexception e) {
     // todo auto-generated catch block
     e.printstacktrace();
    }
    mp3recorder.init(samplerate, 1, samplerate, 32);
    isrecording = true; // 录音状态
    ispause = false; // 录音状态
    try {
     try {
      audiorecord.startrecording(); // 开启录音获取音频数据

      if (mlistener != null) {
       mlistener.wellprepared();
      }
     } catch (illegalstateexception e) {
      // 不给录音...
      if (handler != null) {
       handler.sendemptymessage(msg_error_rec_start);
      }
      return;
     }

     try {
      // 开始录音
      if (handler != null) {
       handler.sendemptymessage(msg_rec_started);
      }

      int readsize = 0;
      boolean pause = false;
      while (isrecording) {
       /*--暂停--*/
       if (ispause) {
        if (!pause) {
         handler.sendemptymessage(msg_rec_pause);
         pause = true;
        }
        continue;
       }
       if (pause) {
        handler.sendemptymessage(msg_rec_restore);
        pause = false;
       }
       /*--end--*/
       /*--实时录音写数据--*/
       readsize = audiorecord.read(buffer, 0,
         minbuffersize);
       voicelevel = getvoicesize(readsize, buffer);
       if (readsize < 0) {
        if (handler != null) {
         handler.sendemptymessage(msg_error_audio_record);
        }
        break;
       } else if (readsize == 0) {
        ;
       } else {
        int encresult = mp3recorder.encode(buffer,
          buffer, readsize, mp3buffer);
        if (encresult < 0) {
         if (handler != null) {
          handler.sendemptymessage(msg_error_audio_encode);
         }
         break;
        }
        if (encresult != 0) {
         try {
          output.write(mp3buffer, 0, encresult);
         } catch (ioexception e) {
          if (handler != null) {
           handler.sendemptymessage(msg_error_write_file);
          }
          break;
         }
        }
       }
       /*--end--*/
      }
      /*--录音完--*/
      int flushresult = mp3recorder.flush(mp3buffer);
      if (flushresult < 0) {
       if (handler != null) {
        handler.sendemptymessage(msg_error_audio_encode);
       }
      }
      if (flushresult != 0) {
       try {
        output.write(mp3buffer, 0, flushresult);
       } catch (ioexception e) {
        if (handler != null) {
         handler.sendemptymessage(msg_error_write_file);
        }
       }
      }
      try {
       output.close();
      } catch (ioexception e) {
       if (handler != null) {
        handler.sendemptymessage(msg_error_close_file);
       }
      }
      /*--end--*/
     } finally {
      audiorecord.stop();
      audiorecord.release();
     }
    } finally {
     mp3recorder.close();
     isrecording = false;
    }
    if (handler != null) {
     handler.sendemptymessage(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;
 }

 // 获得声音的level
 public int getvoicesize(int r, short[] buffer) {
  if (isrecording) {
   try {
    long v = 0;
    // 将 buffer 内容取出,进行平方和运算
    for (int i = 0; i < buffer.length; i++) {
     v += buffer[i] * buffer[i];
    }
    // 平方和除以数据总长度,得到音量大小。
    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;
 }
 /**
  * 在sd卡上创建文件
  * 
  * @throws ioexception
  */
 public static file createsdfile(string filename) throws ioexception {
  file file = new file(filename);
  if (!isfileexists(file))
   if (file.isdirectory()) {
    file.mkdirs();
   } else {
    file.createnewfile();
   }
  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;
 }

 /**
  * 录音状态管理
  * 
  * @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) {
  this.handler = handler;
 }

 /*--以下为native部分--*/
 static {
  system.loadlibrary("mp3lame");
 }

 /**
  * 初始化录制参数
  */
 public static void init(int insamplerate, int outchannel,
   int outsamplerate, int outbitrate) {
  init(insamplerate, outchannel, outsamplerate, outbitrate, 7);
 }

 /**
  * 初始化录制参数 quality:0=很好很慢 9=很差很快
  */
 public native static void init(int insamplerate, int outchannel,
   int outsamplerate, int outbitrate, int quality);

 /**
  * 音频数据编码(pcm左进,pcm右进,mp3输出)
  */
 public native static int encode(short[] buffer_l, short[] buffer_r,
   int samples, byte[] mp3buf);

 /**
  * 据说录完之后要刷干净缓冲区
  */
 public native static int flush(byte[] mp3buf);

 /**
  * 结束编码
  */
 public native static void close();
}

创建c文件

在创建c文件,创建在jni下,命名就按你的java文件所在的包名命名”.”替换为“_”。例如:com_maxi_mp3record_mp3recorder.c。当然还得有头文件:com_maxi_mp3record_mp3recorder.h。

com_maxi_mp3record_mp3recorder.c

#include "lame-3.98.4_libmp3lame/lame.h"
#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 <jni.h>
/* 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

这俩文件别只复制不看内容啊,里面可都是连接java和c的接口,所以不能有差错。举个例子吧,#ifndef _included_com_maxi_mp3record_mp3recorder,这个就得替换成你对应的包名,里面所有都得替换成你自己的程序对应的包名。

编译ndk

创建application.mk在你的项目文件的jni下,在里面写入:
app_abi := all
如果不加,ndk只会编译“armeabi”,然而安卓有很多不同类型的处理器,所以我们不止需要arm。相信你们搞到现在了肯定ndk都配置好了,然后打开终端,进入到你的项目(找到你的ndk目录下的ndk-bulid,最好把它添加到环境变量里,对于以后编译比较方便,在此默认你没添加环境变量),执行ndk-bulid。稍等片刻你会发现你的项目里多了一个obj文件夹,obj文件夹下会生成”arm64-v8a”、”armeabi”、”armeabi-v7a”、”mips”、”mips64”、”x86”、”x86_64”。打开它,各个文件夹下会有一个libmp3lame.so。ok,没错那就是你要的“滑板鞋”。将它放入你的libs文件下,没有自行创建,各个平台便都可以加载了。

使用方法

mp3recorder recorder = new mp3recorder(8000);
recorder.setfilepath(voicepath);//录音保存目录
recorder.start();//开始录音
recorder.stop();//录音结束
recorder.getvoicelevel()//这是我封装的获取音频振幅接口,大家可以用来录音的时候显示声音大小,数据自行调节。

总结

之前一直用mediarecorder录音,发现录出来的只能是amr、acc等格式,用lame转mp3感觉是不可行的。我试了没能成功,不知道具体是什么原因,所以大家有时间可以研究研究,没时间就不要尝试了。

mp3lame录制出来的声音还是挺靠谱的(不过据听说ios就有些莎莎声),然后录制出来的大小还是可以接受的,五秒钟的音频大概在20k左右的样子吧。使用还是很方便的。如果有什么疑问或建议请留言哈。

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

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网