简介
在一家专注于AI音频公司做了一年,最近正处于预离职状态,正好刚刚给客户写了个关于android音频方面的demo,花了我足足一天赶出来的,感觉挺全面的决定再努力一点写个总结。 公司虽小,是和中科院声学所合作,也和讯飞一样也有自己关于音频的一系列语音识别/语音转写等引擎,麻雀虽小五脏俱全的感觉。 Android 音频这块其实也没那么神秘,神秘的地方有专门的C++/算法工程师等为我们负责,大家都懂得,我只是搬搬砖。
主要涉及3点
- SpeechToText(音频转文本:STT): AudioRecord 录制音频 并用本地和Socket2中方式上传 。
- TextToSpeech (文本转语音:TTS) API获取音频流并用AudioTrack 播放。
- Speex 加密
这里不讲TTS/STT底层原理,怎么实现的呆了这么久我也只是一点点,一点点而已,涉及人耳听声相关函数/声波/傅里叶分析/一系列复杂函数, 这里不敢班门弄斧了 感兴趣请大家自行Google 。,
AudioRecord 介绍
AudioRecord 过程是一个IPC过程,Java层通过JNI调用到native层的AudioRecord,后者通过IAudioRecord接口跨进程调用到 AudioFlinger,AudioFlinger负责启动录音线程,将从录音数据源里采集的音频数据填充到共享内存缓冲区,然后应用程序侧从其里面拷贝数据到自己的缓冲区。
public AudioRecord(int audioSource, //指定声音源 MediaRecorder.AudioSource.MIC;
int sampleRateInHz,//指定采样率 这里8000
int channelConfig,//指定声道数,单声道
int audioFormat, //指定8/16pcm 这里16bit 模拟信号转化为数字信号时的量化单位
int bufferSizeInBytes)//缓冲区大小 根据采样率 通道 量化参数决定
复制代码
1. STT 之本地录完之后文件形式上传
第二步再与socket 上传比较 //参数初始化 // 音频输入-麦克风
public final static int AUDIO_INPUT = MediaRecorder.AudioSource.MIC;
public final static int AUDIO_SAMPLE_RATE = 8000; // 44.1KHz,普遍使用的频率
public final static int CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_MONO;
public final static int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
private int bufferSizeInBytes = 0;//缓冲区字节大小
private AudioRecord audioRecord;
private volatile boolean isRecord = false;// volatile 可见性 设置正在录制的状态
复制代码
//创建AudioRecord
private void creatAudioRecord() {
// 获得缓冲区字节大小
bufferSizeInBytes = AudioRecord.getMinBufferSize(AudioFileUtils.AUDIO_SAMPLE_RATE,
AudioFileUtils.CHANNEL_CONFIG, AudioFileUtils.AUDIO_FORMAT);
// MONO单声道
audioRecord = new AudioRecord(AudioFileUtils.AUDIO_INPUT, AudioFileUtils.AUDIO_SAMPLE_RATE,
AudioFileUtils.CHANNEL_CONFIG, AudioFileUtils.AUDIO_FORMAT, bufferSizeInBytes);
}
//
@Override
public boolean onTouch(View v, MotionEvent event) {
AudioRecordUtils utils = AudioRecordUtils.getInstance();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
utils.startRecordAndFile();
break;
case MotionEvent.ACTION_UP:
utils.stopRecordAndFile();
Log.d(TAG, "stopRecordAndFile");
stt();
break;
}
return false;
}
//开始录音
public int startRecordAndFile() {
Log.d("NLPService", "startRecordAndFile");
// 判断是否有外部存储设备sdcard
if (AudioFileUtils.isSdcardExit()) {
if (isRecord) {
return ErrorCode.E_STATE_RECODING;
} else {
if (audioRecord =