您的位置:首页 > 运维架构 > 网站架构

Linux ALSA声卡驱动之一:ALSA架构简介

2013-06-19 17:18 351 查看
一. 概述

ALSA是Advanced Linux Sound Architecture 的缩写,目前已经成为了linux的主流音频体系结构,想了解更多的关于ALSA的这一开源项目的信息和知识,请查看以下网址:http://www.alsa-project.org/

在内核设备驱动层,ALSA提供了alsa-driver,同时在应用层,ALSA为我们提供了alsa-lib,应用程序只要调用alsa-lib提供的API,即可以完成对底层音频硬件的控制。



[align=left] [/align]

图 1.1 alsa的软件体系结构

由图1.1可以看出,用户空间的alsa-lib对应用程序提供统一的API接口,这样可以隐藏了驱动层的实现细节,简化了应用程序的实现难度。内核空间中,alsa-soc其实是对alsa-driver的进一步封装,他针对嵌入式设备提供了一些列增强的功能。本系列博文仅对嵌入式系统中的alsa-driver和alsa-soc进行讨论。

下面对录音的过程做简要描述“

第一步骤:

audio_io_handle_t AudioFlinger::openInput(audio_module_handle_tmodule,

audio_devices_t *pDevices,

uint32_t *pSamplingRate,

audio_format_t *pFormat,

audio_channel_mask_t *pChannelMask)

{

status_t status;

RecordThread *thread = NULL;

struct audio_config config = {

sample_rate: pSamplingRate ? *pSamplingRate : 0,

channel_mask: pChannelMask ? *pChannelMask : 0,

format: pFormat ? *pFormat : AUDIO_FORMAT_DEFAULT,

};

uint32_t reqSamplingRate = config.sample_rate;

audio_format_t reqFormat = config.format;

audio_channel_mask_t reqChannels = config.channel_mask;

audio_stream_in_t *inStream = NULL; // 1 audio_stream_in_t 结构体 audio_stream_in

AudioHwDevice *inHwDev; // 2 AudioHwDevice类在AudioFlinger.h文件中,处理设备的描述

if (pDevices == NULL || *pDevices == 0) {

return 0;

}

Mutex::Autolock _l(mLock);

inHwDev = findSuitableHwDev_l(module, *pDevices); // 3 根据线程号和设备号找到对应的AudioHwDevice

if (inHwDev == NULL)

return 0;

audio_hw_device_t *inHwHal = inHwDev->hwDevice(); // 4 根据线程号和设备号找到对应的设备结构体audio_hw_device

audio_io_handle_t id = nextUniqueId();

status = inHwHal->open_input_stream(inHwHal, id, *pDevices, &config,&inStream); // 5 根据inHwHal,pDevices,inStream等参数打开AudioHardwareALSA::openInputStream()方法,进而打开AudioStreamInALSA::set()方法。在执行inHwHal->open_input_stream()方法的过程中会有个转换的过程,见步骤二过程

ALOGV("openInput() openInputStream returned input %p, SamplingRate %d, Format %d, Channels %x, status %d",

inStream,

config.sample_rate,

config.format,

config.channel_mask,

status);

// If the input could not be opened with the requested parameters and we can handle the conversion internally,

// try to open again with the proposed parameters. The AudioFlinger can resample the input and do mono to stereo

// or stereo to mono conversions on 16 bit PCM inputs.

if (status == BAD_VALUE &&

reqFormat == config.format && config.format == AUDIO_FORMAT_PCM_16_BIT &&

(config.sample_rate <= 2 * reqSamplingRate) &&

(getInputChannelCount(config.channel_mask) <= FCC_2) && (getInputChannelCount(reqChannels) <= FCC_2)) {

ALOGV("openInput() reopening with proposed sampling rate and channel mask");

inStream = NULL;

status = inHwHal->open_input_stream(inHwHal, id, *pDevices, &config, &inStream);

}

if (status == NO_ERROR && inStream != NULL) {

AudioStreamIn *input = new AudioStreamIn(inHwDev,inStream); // 6 把inHwDev(设备结构体audio_hw_device)和inStream(流结构体 audio_stream_in)放入input

// Start record thread

// RecorThread require both input and output device indication to forward to audio

// pre processing modules

audio_devices_t device = (*pDevices) | primaryOutputDevice_l();

thread = new RecordThread(this, // 7 新建一个录音线程,参数包括input,id,device等

input,

reqSamplingRate,

reqChannels,

id,

device);

mRecordThreads.add(id, thread); // 8 把新建线程id和对应的线程加入到mRecordThreads数组中

ALOGV("openInput() created record thread: ID %d thread %p", id, thread);

if (pSamplingRate != NULL) *pSamplingRate = reqSamplingRate;

if (pFormat != NULL) *pFormat = config.format;

if (pChannelMask != NULL) *pChannelMask = reqChannels;

// notify client processes of the new input creation

thread->audioConfigChanged_l(AudioSystem::INPUT_OPENED); // 9 重新配置新建的线程信息,使之配置信息生效

return id;

}

return 0;

}

第二步骤:

// 5 根据inHwHal,pDevices,inStream等参数打开AudioHardwareALSA::openInputStream()方法,进而打开AudioStreamInALSA::set()方法。在执inHwHal->open_input_stream()方法的过程中会有个转换的过程,见步骤二过程。

2.1 hardware\libhardware\include\hardware\audio.h Audio.h中struct audio_hw_device {

struct hw_device_t common;

/** This method creates and opens the audio hardware output stream */

int (*open_output_stream)(struct audio_hw_device *dev,

audio_io_handle_t handle,

audio_devices_t devices,

audio_output_flags_t flags,

struct audio_config *config,

struct audio_stream_out **stream_out);

void (*close_output_stream)(struct audio_hw_device *dev,

struct audio_stream_out* stream_out);

/** This method creates and opens the audio hardware input stream */

int (*open_input_stream)(struct audio_hw_device *dev,

audio_io_handle_t handle,

audio_devices_t devices,

struct audio_config *config,

struct audio_stream_in **stream_in);

void (*close_input_stream)(struct audio_hw_device *dev,

struct audio_stream_in *stream_in);

}

2.2 \hardware\qcom\audio\alsa_sound\audio_hw_hal.cpp中

static int qcom_adev_open(const hw_module_t* module, const char* name, hw_device_t** device)

{

struct qcom_audio_device *qadev;

int ret;

if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0)

return -EINVAL;

qadev = (struct qcom_audio_device *)calloc(1, sizeof(*qadev));

if (!qadev)

return -ENOMEM;

qadev->device.common.tag = HARDWARE_DEVICE_TAG;

qadev->device.common.version = AUDIO_DEVICE_API_VERSION_2_0;

qadev->device.common.module = const_cast<hw_module_t*>(module);

qadev->device.common.close = qcom_adev_close;

qadev->device.get_supported_devices = adev_get_supported_devices;

qadev->device.init_check = adev_init_check;

qadev->device.set_voice_volume = adev_set_voice_volume;

qadev->device.set_master_volume = adev_set_master_volume;

qadev->device.get_master_volume = adev_get_master_volume;

#ifdef QCOM_FM_ENABLED

qadev->device.set_fm_volume = adev_set_fm_volume;

#endif

qadev->device.set_mode = adev_set_mode;

qadev->device.set_mic_mute = adev_set_mic_mute;

qadev->device.get_mic_mute = adev_get_mic_mute;

qadev->device.set_parameters = adev_set_parameters;

qadev->device.get_parameters = adev_get_parameters;

qadev->device.get_input_buffer_size = adev_get_input_buffer_size;

qadev->device.open_output_stream = adev_open_output_stream;

qadev->device.close_output_stream = adev_close_output_stream;

qadev->device.open_input_stream = adev_open_input_stream; //qadev->device 代表的就是hardware\libhardware\include\hardware\audio.h中struct
audio_hw_device的方法open_input_stream,见说下说明1

qadev->device.close_input_stream = adev_close_input_stream;

qadev->device.dump = adev_dump;

qadev->hwif = createAudioHardware(); //
qadev->hwif 代表的是结构体中struct qcom_audio_device的结构体struct AudioHardwareInterface *hwif;也即是AudioHardwareALSA

if (!qadev->hwif) {

ret = -EIO;

goto err_create_audio_hw;

}

*device = &qadev->device.common;

return 0;

err_create_audio_hw:

free(qadev);

return ret;

}

2.3 \hardware\qcom\audio\alsa_sound\audio_hw_hal.cpp中

/** This method creates and opens the audio hardware input stream */

static int adev_open_input_stream(struct audio_hw_device *dev,

audio_io_handle_t handle,

audio_devices_t devices,

audio_config *config,

audio_stream_in **stream_in)

{

struct qcom_audio_device *qadev = to_ladev(dev);

status_t status;

struct qcom_stream_in *in;

int ret;

in = (struct qcom_stream_in *)calloc(1, sizeof(*in));

if (!in)

return -ENOMEM;

devices = convert_audio_device(devices, HAL_API_REV_2_0, HAL_API_REV_1_0);

in->qcom_in = qadev->hwif->openInputStream(devices, (int *)&config->format,

&config->channel_mask,

&config->sample_rate,

&status,

(AudioSystem::audio_in_acoustics)0);

if (!in->qcom_in) {

ret = status;

goto err_open;

}

in->stream.common.get_sample_rate = in_get_sample_rate;

in->stream.common.set_sample_rate = in_set_sample_rate;

in->stream.common.get_buffer_size = in_get_buffer_size;

in->stream.common.get_channels = in_get_channels;

in->stream.common.get_format = in_get_format;

in->stream.common.set_format = in_set_format;

in->stream.common.standby = in_standby;

in->stream.common.dump = in_dump;

in->stream.common.set_parameters = in_set_parameters;

in->stream.common.get_parameters = in_get_parameters;

in->stream.common.add_audio_effect = in_add_audio_effect;

in->stream.common.remove_audio_effect = in_remove_audio_effect;

in->stream.set_gain = in_set_gain;

in->stream.read = in_read;

in->stream.get_input_frames_lost = in_get_input_frames_lost;

*stream_in = &in->stream;

return 0;

err_open:

free(in);

*stream_in = NULL;

return ret;

}

2.4 \hardware\qcom\audio\alsa_sound\audio_hw_hal.cpp中

static void adev_close_input_stream(struct audio_hw_device *dev,

struct audio_stream_in *stream)

{

struct qcom_audio_device *qadev = to_ladev(dev);

struct qcom_stream_in *in =reinterpret_cast<struct qcom_stream_in *>(stream);

qadev->hwif->closeInputStream(in->qcom_in);

free(in);

}

2.5 \hardware\qcom\audio\alsa_sound\audio_hw_hal.cpp中

/** audio_hw_device implementation **/

static inline struct qcom_audio_device * to_ladev(struct
audio_hw_device *dev)

{

return reinterpret_cast<struct qcom_audio_device *>(dev);

}

第三步骤:ALSA-lib中录音的open的过程如下:

3.1 audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module,audio_devices_t *pDevices,uint32_t *pSamplingRate,audio_format_t *pFormat, audio_channel_mask_t *pChannelMask){ status =
inHwHal->open_input_stream(inHwHal, id, *pDevices, &config, &inStream);}

3.2 \hardware\qcom\audio\alsa_sound\audio_hw_hal.cpp中static int qcom_adev_open(const hw_module_t* module, const char* name, hw_device_t** device){

qadev->device.open_input_stream = adev_open_input_stream;

qadev->hwif = createAudioHardware();

*device = &qadev->device.common; }

3.3 \hardware\qcom\audio\alsa_sound\audio_hw_hal.cpp中static int
adev_open_input_stream(struct audio_hw_device *dev, audio_io_handle_t handle, audio_devices_t devices, audio_config *config,audio_stream_in **stream_in){

in->qcom_in = qadev->hwif->openInputStream(devices, (int *)&config->format, &config->channel_mask,&config->sample_rate, &status,(AudioSystem::audio_in_acoustics)0);

in->stream.read = in_read;

*stream_in = &in->stream; }

3.4 \hardware\libhardware_legacy\include\hardware_legacy\AudioHardwareInterface.h 中virtual AudioStreamIn* openInputStream(){}

3.5 \hardware\qcom\audio\alsa_sound\AudioHardwareALSA.cpp
AudioStreamIn *AudioHardwareALSA::openInputStream(){

AudioStreamInALSA *in = 0;

in = new AudioStreamInALSA(this, &(*it), acoustics);

err = in->set(format, channels, sampleRate, devices); }

说明1

extern "C" {

struct qcom_audio_module {

struct audio_module module;

};

struct qcom_audio_device {

struct audio_hw_device device;

struct AudioHardwareInterface *hwif;

};

struct qcom_stream_out {

struct audio_stream_out stream;

AudioStreamOut *qcom_out;

};

struct qcom_stream_in {

struct audio_stream_in stream;

AudioStreamIn *qcom_in;

};

说明2:

status_t ALSAStreamOps::set(int *format,

uint32_t *channels,

uint32_t *rate,

uint32_t device){}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: