日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

【Linux系统编程应用】Linux音频编程实战(一)

發布時間:2024/4/21 linux 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【Linux系统编程应用】Linux音频编程实战(一) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在Linux下進行音頻編程時,重點在于如何正確地操作聲卡驅動程序所提供的各種設備文件,由于涉及到的概念和因素比較多,所以遵循一個通用的框架無疑將有助于簡化應用程序的設計。

1 DSP編程

對聲卡進行編程時首先要做的是打開與之對應的硬件設備,這是借助于open系統調用來完成的,并且一般情況下使用的是/dev/dsp文件。采用何種模式對聲卡進行操作也必須在打開設備時指定,對于不支持全雙工的聲卡來說,應該使用只讀或者只寫的方式打開,只有那些支持全雙工的聲卡,才能以讀寫的方式打開,并且還要依賴于驅動程序的具體實現。Linux允許應用程序多次打開或者關閉與聲卡對應的設備文件,從而能夠很方便地在放音狀態和錄音狀態之間進行切換,建議在進行音頻編程時只要有可能就盡量使用只讀或者只寫的方式打開設備文件,因為這樣不僅能夠充分利用聲卡的硬件資源,而且還有利于驅動程序的優化。下面的代碼示范了如何以只寫方式打開聲卡進行放音(playback)操作:

#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>int main(void) {int fd = -1;fd = open("/dev/dsp", O_RDWR);if (-1 == fd){perror("open"); goto err0;}close(fd);return 0; err0:return -1; }


運行在Linux內核中的聲卡驅動程序專門維護了一個緩沖區,其大小會影響到放音和錄音時的效果,使用ioctl系統調用可以對它的尺寸進行恰當的設置。調節驅動程序中緩沖區大小的操作不是必須的,如果沒有特殊的要求,一般采用默認的緩沖區大小也就可以了。但需要注意的是,緩沖區大小的設置通常應緊跟在設備文件打開之后,這是因為對聲卡的其它操作有可能會導致驅動程序無法再修改其緩沖區的大小。下面的代碼示范了怎樣設置聲卡驅動程序中的內核緩沖區的大小:
int setting = 0xnnnnssss; int result = ioctl(handle, SNDCTL_DSP_SETFRAGMENT, &setting); if (result == -1) {perror("ioctl buffer size");return -1; } // 檢查設置值的正確性
在設置緩沖區大小時,參數setting實際上由兩部分組成,其低16位標明緩沖區的尺寸,相應的計算公式為buffer_size = 2^ssss,即若參數setting低16位的值為16,那么相應的緩沖區的大小會被設置為65536字節。參數setting的高16位則用來標明分片(fragment)的最大序號,它的取值范圍從2一直到0x7FFF,其中0x7FFF表示沒有任何限制。


接下來要做的是設置聲卡工作時的聲道(channel)數目,根據硬件設備和驅動程序的具體情況,可以將其設置為0(單聲道,mono)或者1(立體聲,stereo)。下面的代碼示范了應該怎樣設置聲道數目:

int channels = 0; // 0=mono 1=stereo int result = ioctl(handle, SNDCTL_DSP_STEREO, &channels); if ( result == -1 ) {perror("ioctl channel number");return -1; } if (channels != 0) {// 只支持立體聲 }
采樣格式和采樣頻率是在進行音頻編程時需要考慮的另一個問題,聲卡支持的所有采樣格式可以在頭文件soundcard.h中找到,而通過ioctl系統調用則可以很方便地更改當前所使用的 采樣格式 。下面的代碼示范了如何設置聲卡的采樣格式:

int format = AFMT_U8; int result = ioctl(handle, SNDCTL_DSP_SETFMT, &format); if ( result == -1 ) {perror("ioctl sample format");return -1; } // 檢查設置值的正確性
聲卡采樣頻率的 設置也非常容易,只需在調用ioctl時將第二個參數的值設置為SNDCTL_DSP_SPEED,同時在第三個參數中指定采樣頻率的數值就行了。對于大多數聲卡來說,其支持的采樣頻率范圍一般為5kHz到44.1kHz或者48kHz,但并不意味著該范圍內的所有頻率都會被硬件支持,在Linux下進行音頻編程時最常用到的幾種采樣頻率是11025Hz、16000Hz、22050Hz、32000Hz和44100Hz。下面的代碼示范了如何設置聲卡的采樣頻率:

int rate = 22050; int result = ioctl(handle, SNDCTL_DSP_SPEED, &rate); if ( result == -1 ) {perror("ioctl sample format");return -1; } // 檢查設置值的正確性

2 Mixer編程

聲卡上的混音器由多個混音通道組成,它們可以通過驅動程序提供的設備文件/dev/mixer進行編程。對混音器的操作是通過ioctl系統調用來完成的,并且所有控制命令都由SOUND_MIXER或者MIXER開頭,表1列出了常用的幾個混音器控制命令:

名 稱 作 用
SOUND_MIXER_VOLUME 主音量調節
SOUND_MIXER_BASS 低音控制
SOUND_MIXER_TREBLE 高音控制
SOUND_MIXER_SYNTH FM合成器
SOUND_MIXER_PCM 主D/A轉換器
SOUND_MIXER_SPEAKER PC喇叭
SOUND_MIXER_LINE 音頻線輸入
SOUND_MIXER_MIC 麥克風輸入
SOUND_MIXER_CD CD輸入
SOUND_MIXER_IMIX 回放音量
SOUND_MIXER_ALTPCM 從D/A 轉換器
SOUND_MIXER_RECLEV 錄音音量
SOUND_MIXER_IGAIN 輸入增益
SOUND_MIXER_OGAIN 輸出增益
SOUND_MIXER_LINE1 聲卡的第1輸入
SOUND_MIXER_LINE2 聲卡的第2輸入
SOUND_MIXER_LINE3 聲卡的第3輸入

表1 混音器命令


對聲卡的輸入增益和輸出增益進行調節是混音器的一個主要作用,目前大部分聲卡采用的是8位或者16位的增益控制器,但作為程序員來講并不需要關心這些,因為聲卡驅動程序會負責將它們變換成百分比的形式,也就是說無論是輸入增益還是輸出增益,其取值范圍都是從0到100。在進行混音器編程時,可以使用SOUND_MIXER_READ宏來讀取混音通道的增益大小,例如在獲取麥克風的輸入增益時,可以使用如下的代碼:

int vol; ioctl(fd, SOUND_MIXER_READ(SOUND_MIXER_MIC), &vol); printf("Mic gain is at %d %%\n", vol);

對于只有一個混音通道的單聲道設備來說,返回的增益大小保存在低位字節中。而對于支持多個混音通道的雙聲道設備來說,返回的增益大小實際上包括兩個部分,分別代表左、右兩個聲道的值,其中低位字節保存左聲道的音量,而高位字節則保存右聲道的音量。下面的代碼可以從返回值中依次提取左右聲道的增益大小:

int left, right; left = vol & 0xff; right = (vol & 0xff00) >> 8; printf("Left gain is %d %%, Right gain is %d %%\n", left, right);


類似地,如果想設置混音通道的增益大小,則可以通過SOUND_MIXER_WRITE宏來實現,此時遵循的原則與獲取增益值時的原則基本相同,例如下面的語句可以用來設置麥克風的輸入增益:
vol = (right << 8) + left; ioctl(fd, SOUND_MIXER_WRITE(SOUND_MIXER_MIC), &vol);
在編寫實用的音頻程序時,混音器是在涉及到兼容性時需要重點考慮的一個對象,這是因為不同的聲卡所提供的混音器資源是有所區別的。聲卡驅動程序提供了多個ioctl系統調用來獲得混音器的信息,它們通常返回一個整型的位掩碼(bitmask),其中每一位分別代表一個特定的混音通道,如果相應的位為1,則說明與之對應的混音通道是可用的。例如通過SOUND_MIXER_READ_DEVMASK返回的位掩碼,可以查詢出能夠被聲卡支持的每一個混音通道,而通過SOUND_MIXER_READ_RECMAS返回的位掩碼,則可以查詢出能夠被當作錄音源的每一個通道。下面的代碼可以用來檢查CD輸入是否是一個有效的混音通道:

ioctl(fd, SOUND_MIXER_READ_DEVMASK, &devmask); if (devmask & SOUND_MIXER_CD) printf("The CD input is supported");
如果進一步還想知道其是否是一個有效的錄音源,則可以使用如下語句:

ioctl(fd, SOUND_MIXER_READ_RECMASK, &recmask); if (recmask & SOUND_MIXER_CD) printf("The CD input can be a recording source");
目前大多數聲卡提供多個錄音源,通過SOUND_MIXER_READ_RECSRC可以查詢出當前正在使用的錄音源,同一時刻能夠使用幾個錄音源是由聲卡硬件決定的。類似地,使用SOUND_MIXER_WRITE_RECSRC可以設置聲卡當前使用的錄音源,例如下面的代碼可以將CD輸入作為聲卡的錄音源使用:

devmask = SOUND_MIXER_CD; ioctl(fd, SOUND_MIXER_WRITE_DEVMASK, &devmask);


此外,所有的混音通道都有單聲道和雙聲道的區別,如果需要知道哪些混音通道提供了對立體聲的支持,可以通過SOUND_MIXER_READ_STEREODEVS來獲得。

3 DSP錄音和放音程序

下面給出一個利用聲卡上的DSP設備進行聲音錄制和回放的基本框架,它的功能是先錄制幾秒種音頻數據,將其存放在內存緩沖區中,然后再進行回放,其所有的功能都是通過讀寫/dev/dsp設備文件來完成的:

#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/ioctl.h> #include <linux/soundcard.h>#define LEN 3 /*存儲秒數*/ #define RATE 8000 /*采樣頻率*/ #define SIZE 8 /*量化位數*/ #define CHANNELS 1 /*聲道數目*/unsigned char buf[LEN * RATE * SIZE * CHANNELS / 8];int main(void) {int fd = -1;int ret = -1;int setting = 0x10000;int args;fd = open("/dev/dsp", O_RDWR);if (-1 == fd){perror("open"); goto err0;}//設置量化位數args = SIZE;ret = ioctl(fd, SOUND_PCM_WRITE_BITS, &args);if (-1 == ret){perror("ioctl"); goto err0;}//設置聲道數args = CHANNELS;ret = ioctl(fd, SOUND_PCM_WRITE_CHANNELS, &args);if (-1 == ret){perror("ioctl"); goto err0;}//設置采樣頻率args = RATE;ret = ioctl(fd, SOUND_PCM_WRITE_RATE, &args);if (-1 == ret){perror("ioctl"); goto err0;}while(1){printf("開始錄音.....\n"); //錄音ret = read(fd, buf, sizeof(buf));if (ret != sizeof(buf)){perror("read"); }//放音printf("you say: \n");ret = write(fd, buf, sizeof(buf));{perror("write"); }//在繼續錄音之前等待回放結束 ret = ioctl(fd, SOUND_PCM_SYNC, 0);if (-1 == ret){perror("ioctl"); }}close(fd);return 0; err0:return -1; }

4 Mixer程序

下面再給出一個對混音器進行編程的基本框架,利用它可以對各種混音通道的增益進行調節,其所有的功能都是通過讀寫/dev/mixer設備文件來完成的:

/** mixer.c*/ #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <sys/ioctl.h> #include <fcntl.h> #include <linux/soundcard.h> /* 用來存儲所有可用混音設備的名稱 */ const char *sound_device_names[] = SOUND_DEVICE_NAMES; int fd; /* 混音設備所對應的文件描述符 */ int devmask, stereodevs; /* 混音器信息對應的位圖掩碼 */ char *name; /* 顯示命令的使用方法及所有可用的混音設備 */ void usage() {int i;fprintf(stderr, "usage: %s <device> <left-gain%%> <right-gain%%>\n"" %s <device> <gain%%>\n\n""Where <device> is one of:\n", name, name);for (i = 0 ; i < SOUND_MIXER_NRDEVICES ; i++)if ((1 << i) & devmask) /* 只顯示有效的混音設備 */fprintf(stderr, "%s ", sound_device_names[i]);fprintf(stderr, "\n");exit(1); } int main(int argc, char *argv[]) {int left, right, level; /* 增益設置 */int status; /* 系統調用的返回值 */int device; /* 選用的混音設備 */char *dev; /* 混音設備的名稱 */int i;name = argv[0];/* 以只讀方式打開混音設備 */fd = open("/dev/mixer", O_RDONLY);if (fd == -1) {perror("unable to open /dev/mixer");exit(1);}/* 獲得所需要的信息 */status = ioctl(fd, SOUND_MIXER_READ_DEVMASK, &devmask);if (status == -1)perror("SOUND_MIXER_READ_DEVMASK ioctl failed");status = ioctl(fd, SOUND_MIXER_READ_STEREODEVS, &stereodevs);if (status == -1)perror("SOUND_MIXER_READ_STEREODEVS ioctl failed");/* 檢查用戶輸入 */if (argc != 3 && argc != 4)usage();/* 保存用戶輸入的混音器名稱 */dev = argv[1];/* 確定即將用到的混音設備 */for (i = 0 ; i < SOUND_MIXER_NRDEVICES ; i++)if (((1 << i) & devmask) && !strcmp(dev, sound_device_names[i]))break;if (i == SOUND_MIXER_NRDEVICES) { /* 沒有找到匹配項 */fprintf(stderr, "%s is not a valid mixer device\n", dev);usage();}/* 查找到有效的混音設備 */device = i;/* 獲取增益值 */if (argc == 4) {/* 左、右聲道均給定 */left = atoi(argv[2]);right = atoi(argv[3]);} else {/* 左、右聲道設為相等 */left = atoi(argv[2]);right = atoi(argv[2]);}/* 對非立體聲設備給出警告信息 */if ((left != right) && !((1 << i) & stereodevs)) {fprintf(stderr, "warning: %s is not a stereo device\n", dev);}/* 將兩個聲道的值合到同一變量中 */level = (right << 8) + left;/* 設置增益 */status = ioctl(fd, MIXER_WRITE(device), &level);if (status == -1) {perror("MIXER_WRITE ioctl failed");exit(1);}/* 獲得從驅動返回的左右聲道的增益 */left = level & 0xff;right = (level & 0xff00) >> 8;/* 顯示實際設置的增益 */fprintf(stderr, "%s gain set to %d%% / %d%%\n", dev, left, right);/* 關閉混音設備 */close(fd);return 0; }


總結

以上是生活随笔為你收集整理的【Linux系统编程应用】Linux音频编程实战(一)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。