您的位置:首页 > 移动开发 > Android开发

Android 语音遥控器的整体分析-HAL层的AudioFlinger

2015-07-14 22:16 701 查看
上篇说到语音部分最后会通过AudioFlinger来操作HAL层。

一、首先我们看下硬件接口层的接口(奇怪为什么只有Audio的hardwareinterface):

(1)hardware\libhardware_legacy\include\hardware_legacy\AudioHardwareInterface.h

其中定义了AudioStreamOut和AudioStreamIn,二者被AudioHardwareInterface类中的openOutputStream和openInputStream操作以对硬件音频的输入和输出进行管理。

类中定义的都是抽象函数,具体要实现Audio系统时去实现。

(2)hardware\libhardware_legacy\include\hardware_legacy\AudioPolicyInterface.h

这里定义了Android的策略类。

二、然后看下HAL层的实现

Android的HAL中提供三种示例:AudioHardwareStub AudioDumpInterface AudioHardwareGeneric这三种各自代表一种Audio硬件抽象层的实现

(1)AudioHardwareStub是一个空实现,不表述,可以自己去看代码

(2)AudioDumpInterface是一个用文件来模拟输入输出的示例

这个要先介绍AudioDumpInterface:



分析代码是上面的类关系,可以知道,在AudioDumpInterface中是new了AudioStreamInDump和AudioStreamOutDump两个对象来进行输入输出操作。

其中的AudioStreamInDump.write会生成一个PCM文件,而read函数能够将一个一个指定的Audio文件读入,比如Android5.0中是打开一个/sdcard/music/目录下的.wav文件

怎么命名参见程序实现:

ssize_t AudioStreamInDump::read(void* buffer, ssize_t bytes)
{
ssize_t ret;

if (mFinalStream) {
ret = mFinalStream->read(buffer, bytes);
if(!mFile) {
if (mInterface->fileName() != "") {
char name[255];
sprintf(name, "%s_in_%d_%d.pcm", mInterface->fileName().string(), mId, ++mFileCount);
mFile = fopen(name, "wb");
ALOGV("Opening input dump file %s, fh %p", name, mFile);
}
}
if (mFile) {
fwrite(buffer, bytes, 1, mFile);
}
} else {
usleep((((bytes * 1000) / frameSize()) / sampleRate()) * 1000);
ret = bytes;
if(!mFile) {
char name[255];
strcpy(name, "/sdcard/music/sine440");
if (channels() == AudioSystem::CHANNEL_IN_MONO) {
strcat(name, "_mo");
} else {
strcat(name, "_st");
}
if (format() == AudioSystem::PCM_16_BIT) {
strcat(name, "_16b");
} else {
strcat(name, "_8b");
}
if (sampleRate() < 16000) {
strcat(name, "_8k");
} else if (sampleRate() < 32000) {
strcat(name, "_22k");
} else if (sampleRate() < 48000) {
strcat(name, "_44k");
} else {
strcat(name, "_48k");
}
strcat(name, ".wav");
mFile = fopen(name, "rb");
ALOGV("Opening input read file %s, fh %p", name, mFile);
if (mFile) {
fseek(mFile, AUDIO_DUMP_WAVE_HDR_SIZE, SEEK_SET);
}
}
if (mFile) {
ssize_t bytesRead = fread(buffer, bytes, 1, mFile);
if (bytesRead >=0 && bytesRead < bytes) {
fseek(mFile, AUDIO_DUMP_WAVE_HDR_SIZE, SEEK_SET);
fread((uint8_t *)buffer+bytesRead, bytes-bytesRead, 1, mFile);
}
}
}

return ret;
}
(3)AudioHardwareGeneric是一个真正和硬件交互的实现

3.1先在构造函数中指定了一个音频设备节点:kAudioDeviceName = "/dev/eac"

AudioHardwareGeneric::AudioHardwareGeneric()
: mOutput(0), mInput(0),  mFd(-1), mMicMute(false)
{
mFd = ::open(kAudioDeviceName, O_RDWR);
}
3.2然后在openInputStream和openOutStream中创建输入输出对象的时候会将文件句柄传进去,

// create new output stream
AudioStreamInGeneric* in = new AudioStreamInGeneric();
status_t lStatus = in->set(this, mFd, devices, format, channels, sampleRate, acoustics);
3.3最后就是在write和read中去读写文件节点了。

ssize_t AudioStreamOutGeneric::write(const void* buffer, size_t bytes)
{
Mutex::Autolock _l(mLock);
return ssize_t(::write(mFd, buffer, bytes));
}


好了,现在整个Android主机端的实现方式就分析完了。

实际使用的时候,可以由Android层的系统工程师和驱动工程师配合工作,驱动工程师完成音频驱动后,给出音频数据的节点,Android系统层去对这个设备节点进行读写。或者与其他网络模块比如蓝牙模块工作的时候,可以不通过内核。遥控器端将数据加密,编码压缩后,通过蓝牙部分接受裸数据,然后蓝牙模块通过socket传给语音模块进行解码,然后给上层使用。

后面会进行音频编解码以及蓝牙无线传输音频这两个部分的总结。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: