落鹤生 发布于 2012-02-22 10:13 点击:615次 |
默认的情况下,Android放音的采样 率固定为44.1khz,录音的采样率固定为8khz,因此底层的音频设备驱动只需设置好这两个固定的采样率。如果上层传过来的采样率与其不符的话,则 Android Framework层会对音频流做resample(重采样)处理。
Resample的大致流程如下:
图片1
AudioResample作为最基本的类,回放和录音resample最终都会调用到这个类;有兴趣可以研究下resample的算法和实现,这里不阐述。
AudioMixer仅仅提供给回放使用的,这个类功能不仅仅是resample了,混音、音量设置都由这个类实现的。
录音:
1、AudioFlinger::RecordThread是录音线程类,每当有录音请求时,进入AudioFlinger::openInput创建这个线程;
2、在创建这个线程的同时,调用readInputParameters,检查上层传过来的录音采样率是否与底层音频接口固定的录音采样率一致;如果不一致,则调用AudioResampler::create创建一个resampler;
3、关于 AudioFlinger::RecordThread::ThreadLoop,当录音工作线程启动后,会不断循环该ThreadLoop方法,主要 是:1)读取底层音频设备获取录音数据mBytesRead = mInput->read(mRsmpInBuffer, mInputBytes); 2)对录音数据做重采样 mResampler->resample(mRsmpOutBuffer, framesOut, this);
放音:
1、AudioFlinger::MixerThread是默认的放音线程,派生自PlaybackThread,由AudioFlinger::openOutput负责创建;
2、MixerThread创建时,1)调用readOutputParameters获取底层音频接口固定的放音采样率;2)创建一个AudioMixer;
3、关于AudioFlinger::MixerThread:: ThreadLoop,当放音工作线程启动后,会不断循环该TreadLoop方法,主要是:1)混合各track音频数据 mAudioMixer->process(); 2)将混合后的音频数据写到底层音频设备int bytesWritten = (int)mOutput->write(mMixBuffer, mixBufferSize);
之前有提及Android默认情况下,放音采样率是44.1khz,录音采样率是8khz,分别通过AudioStreamIn::sampleRate()和AudioStreamOut::sampleRate()获得。
一步一步跟下去,会发现是alsa_default.cpp里面固定好的:
- // 放音参数配置
- static alsa_handle_t _defaultsOut = {
- module : 0,
- devices : AudioSystem::DEVICE_OUT_ALL,
- curDev : 0,
- curMode : 0,
- handle : 0,
- format : SND_PCM_FORMAT_S16_LE, // AudioSystem::PCM_16_BIT
- channels : 2,
- sampleRate : DEFAULT_SAMPLE_RATE, // 放音采样率,固定为44.1khz
- latency : 200000, // Desired Delay in usec
- bufferSize : DEFAULT_SAMPLE_RATE / 5, // Desired Number of samples
- modPrivate : 0,
- };
- // 录音参数配置
- static alsa_handle_t _defaultsIn = {
- module : 0,
- devices : AudioSystem::DEVICE_IN_ALL,
- curDev : 0,
- curMode : 0,
- handle : 0,
- format : SND_PCM_FORMAT_S16_LE, // AudioSystem::PCM_16_BIT
- channels : 1,
- sampleRate : AudioRecord::DEFAULT_SAMPLE_RATE, // 录音采样率,固定为8khz
- latency : 250000, // Desired Delay in usec
- bufferSize : 2048, // Desired Number of samples
- modPrivate : 0,
- };
网上有谈到Android这种音频resample方式导致音质变差,我想修改起来应该也不是很难,以后找机会实践一下。思路:先尝试用上层传过来 的录音采样率来设置底层音频设备,如果设置成功则不需要resample,不成功才使用默认的采样率。放音暂不能这样改,因为放音可能要混合多个 track的数据,而各个track的采样率不一定是一样的。
这章节我是用问号的,因为在这里我的确困惑了,先细细道来。
一般来说,resample的流程是这样:
- +---------------------+ +-------------------------------+
- | app sample rate | | Android Framework sample rate |
- | 16khz | <--resample-- | 8khz | <--ALSA interface
- +---------------------+ +-------------------------------+
那么ALSA interface用8khz的采样率进行录音就好。
但事实上我发现底层音频设备用其他的采样率也是可以的。比如说44.1khz,我测量到I2S的ADCLRC是44.1khz并且CODEC的寄存器也是设置为44.1khz录音采样率,就这样Android应用录出来的声音也是正常的。
可明明在alsa_default.cpp中设置的采样率是8khz的,而且设置成功:
- err = snd_pcm_hw_params_set_rate_near(handle->handle, hardwareParams,
- &requestedRate, 0);
返回来的requestedRate是8000,这是Android Framework固定的录音采样率。
那么到底在哪里进行了44.1khz->8khz的resample处理?唯一是在alsa-lib或alsa-driver里面了,到目前为止我还未找到相关代码。
---------------------------------------------------
2011/10/21
近期琐事颇多,找房子租房子搬家一团糟,昨天终于全部搞定了。
回到正题,有关alsa的resample处理,应该是在alsa-lib中实现的。摘录别人的分析,如下:
snd_pcm_mmap_write_areas()函数循环写入数据,直到数据为空,首先将找到映射内存pcm->running_areas的地址,然后调用snd_pcm_areas_copy()进行数据转换,如sample rate、channels等(如果源数据和硬件支持格式一致,就简单地通过memcpy拷贝数据),转换成硬件支持格式对应的数据后,调用snd_pcm_mmap_commit()将转换后的数据写入映射内存。写入由snd_pcm_dmix_mmap_commit()完成,在对数据进行混音(do_mix_areas)同时,写入映射内存。
我不能保证以上分析是否完全正确(事实上我跟踪了一下,是有些出入)。由于alsa-lib太过臃肿复杂,我也懒得去细细分析,以后如果有空去研究的话,会对这里的说法作一些更正。
抛弃alsa-lib,其实我们还是有其他选择的,还记得ANDROID2.3音频系统HAL提到的mini alsa-lib(android2.3.1-gingerbread/device/samsung/crespo/libaudio)吗?我们可以从这里分析,由于篇幅可能比较长,就独立成章吧,待整理。。。