之前有寫了一篇 ALSA API 實作amixer command: amixer -c0 set i2s_clock on
後來發現,有兩個 Bug 需要被修正,分別如下:
1. 第一次如果使用 i2s_clock off,會造成 kernel 端出現 core_dump 錯誤,導致執行時間會變很長 (原本大概只需要 20ms,結果時間拉長到 200ms)
2. 發現這個上層的API程式,呼叫Linux Kernel 層,去triger I2S,出現 non-blocking的現象(application 呼叫底層程式,但沒有等待底層做完,就直接繼續執行application層的事情)
因此加入底下一些程式,來解決這兩個問題 (用不同顏色標出)
#include <stdio.h>
#include <alsa/asoundlib.h>
int set_mixer_onoff(const char *control_name,
int option)
{
const char *device = "hw:0"; // 音訊設備的名稱或識別符號
//const char *control_name = "i2s_clock"; // 控制元素的名稱, 由API參數帶入
snd_ctl_t *handle;
snd_ctl_elem_id_t *elem_id;
snd_ctl_elem_value_t *elem_value;
int err;
int ori_value;
int i2s_is_set = 0;
// 打開音訊設備的控制界面
err = snd_ctl_open(&handle, device, 0);
if (err < 0) {
printf(":Cannot open device interface: %s\n", snd_strerror(err)); //無法打開音訊設備的控制界面
return err;
}
// 分配並初始化 snd_ctl_elem_id_t 結構
snd_ctl_elem_id_alloca(&elem_id);
snd_ctl_elem_id_set_name(elem_id, control_name);
// 獲取控制元素的索引
snd_ctl_elem_id_set_interface(elem_id, SND_CTL_ELEM_IFACE_MIXER);
// 獲取控制元素的數值
snd_ctl_elem_value_alloca(&elem_value);
snd_ctl_elem_value_set_id(elem_value, elem_id);
snd_ctl_elem_value_set_boolean(elem_value, 0, option); // 設置boolean值,由API參數option帶入
// This is for fixing the core_dump issue
// 先取得之前狀態,如果跟這次要設定的狀態相同,就不需要再做,直接 return.
err = snd_ctl_elem_read(handle, elem_value);
if (err < 0) {
printf("Cannot read value: %s\n", snd_strerror(err));
snd_ctl_close(handle);
return err;
}
ori_value = snd_ctl_elem_value_get_boolean(elem_value, 0);
if( option == ori_value )
{
printf("No need to Set, option equal to ori_value\n");
snd_ctl_close(handle);
return 0;
}
else
snd_ctl_elem_value_set_boolean(elem_value, 0, option); //設定boolean值,由API參數option帶入
/////////////////////////////////////////////
// 設置控制元素的數值
err = snd_ctl_elem_write(handle, elem_value);
if (err < 0) {
printf("Cannot set value: %s\n", snd_strerror(err)); //無法設置控制元素的數值
snd_ctl_close(handle);
return err;
}
// Poll the control value until it reaches the desired state
// For fix the non-blocking issue
while (!i2s_is_set) {
// Sleep for a short period to avoid busy-waiting
usleep(5000); // Sleep for 5 milliseconds
// Read the control value
err = snd_ctl_elem_read(handle, elem_value);
if (err < 0) {
printf("Cannot read value 2th: %s\n", snd_strerror(err));
snd_ctl_close(handle);
return err;
}
// Check if the control value is equal to the desired state
int cur_value = snd_ctl_elem_value_get_boolean(elem_value, 0);
if (cur_value == option) {
i2s_is_set = 1;
}
}
// 關閉音訊設備的控制界面
snd_ctl_close(handle);
printf("Setting %s %d success\n", control_name, option); //已成功設置 i2s_clock 為 on
return 0;
}