ALSA是Advanced Linux Sound Architecture 的缩写,目前已经成为了linux的主流音频体系结构,想了解更多的关于ALSA的这一开源项目的信息和知识,请查看以下网址:http://www.alsa-project.org/。
在内核设备驱动层,ALSA提供了alsa-driver,同时在应用层,ALSA为我们提供了alsa-lib,应用程序只要调用alsa-lib提供的API,即可以完成对底层音频硬件的控制。
图 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){}