a我考网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 81|回复: 0

[综合辅导] Linux认证:Linux操作系统的声音设备编程

[复制链接]
发表于 2012-8-4 12:07:07 | 显示全部楼层 |阅读模式
Linux认证:Linux操作系统的声音设备编程- ]4 H1 [. V: ]+ F2 V; {
Linux下的声音设备编程比大多数人想象的要简单得多。一般说来,我们常用的声音设备是内部扬声器和声卡,它们都对应/dev目录下的一个或多个设备文件,我们象打开普通文件一样打开它们,用ioctl()函数设置一些参数,然后对这些打开的特殊文件进写操作。
7 H! ~/ D5 U: S4 t由于这些文件不是普通的文件,所以我们不能用ANSI C(标准C)的fopen、fclose等来操作文件,而应该使用系统文件I/O处理函数(open、read、write、lseek和close)来处理这些设备文件。ioctl()或许是Linux下最庞杂的函数,它可以控制各种文件的属性,在Linux声音设备编程中,最重要的就是使用此函数正确设置必要的参数。
# x% a: I" t" Y下面我们举两个实际的例子来说明如何实现Linux下的声音编程。由于此类编程涉及到系统设备的读写,所以,很多时候需要你有root权限,如果你将下面的例子编译后不能正确执行,那么,首先请你检查是否是因为没有操纵某个设备的权限。
- }5 b3 b+ J5 t; A2 x对内部扬声器编程内部扬声器是控制台的一部分,所以它对应的设备文件为/dev/console。变量KIOCSOUND在头文件 /usr /include /linux /kd.h中声明,ioctl函数使用它可以来控制扬声器的发声,使用规则为:
0 B/ f- T4 i" W9 B- Uioctl ( fd, KIOCSOUND, (int) tone);
( R# r* o: L+ Y, |" q/ `0 C; E" _fd为文件设备号,tone 是音频值。当tone为0时,终止发声。必须一提的是它所理解的音频和我们平常以为的音频是不同的,由于计算机主板定时器的时钟频率为1.19MHZ,所以要进行正确的发声,必须进行如下的转换:扬声器音频值=1190000/我们期望的音频值。/ [- e% Q, n" Y* i4 O
扬声器发声时间的长短我们通过函数usleep(unsigned long usec)来控制。它是在头文件/usr /include /unistd.h中定义的,让程序睡眠usec微秒。下面即是让扬声器按指定的长度和音频发声的程序的完整清单:
, E1 I/ f+ X" q2 Q& N8 U7 j6 S( h  P! r" J8 t  O1 t

1 L4 f! k7 _+ a2 v! D% Y#include 《 fcntl.h 》( r6 x- s8 o9 n( E- u4 U- s+ }
#include 《 stdio.h 》" c5 G3 i' |- b& B. a. ~! a2 q" e
#include 《 stdlib.h 》4 h8 h; b3 b, Y
#include 《 string.h 》; f7 i; p' x/ @/ I# E3 O
#include 《 unistd.h 》
/ _& w! i4 U8 @' m$ F5 z+ A+ }#include 《 sys/ioctl.h 》
# B8 ^: Q) G+ a% h  |#include 《 sys/types.h 》
1 M- W5 w: Y1 G3 l8 L$ Y4 p7 H$ M#include 《 linux/kd.h 》3 @% w  f9 G9 c3 X* F$ `* K
/* 设定默认值 */3 }. S& s  d* c+ @' p5 I6 p4 v
#define DEFAULT_FREQ 440 /* 设定一个合适的频率 */
1 u! g3 X. v9 H: m9 V! c#define DEFAULT_LENGTH 200 /* 200 微秒,发声的长度是以微秒为单位的*/$ f6 r3 d' S6 i; `! ?! h* V
#define DEFAULT_REPS 1 /* 默认不重复发声 */4 \* U( ^. d5 r9 p
#define DEFAULT_DELAY 100 /* 同样以微秒为单位*/( e4 j( t% G( g7 g
/* 定义一个结构,存储所需的数据*/
& }, W/ T! @7 @( v. Ytypedef struct {/ J8 X9 S2 P* q4 G- ^( K8 H1 i' H
int freq; /* 我们期望输出的频率,单位为Hz */
7 a& p3 j1 \( J7 M/ r) r) gint length; /* 发声长度,以微秒为单位*/
, a3 \% z9 C2 |. ~9 zint reps; /* 重复的次数*/3 ~0 Y' E6 M" K5 b6 {0 ]4 L
int delay; /* 两次发声间隔,以微秒为单位*/! a% [" O* |5 T, E
} beep_parms_t;
. a" j2 [  m) P' c& v0 l9 L. Y/* 打印帮助信息并退出*/
4 t+ A9 }6 K7 _" {0 \void usage_bail ( const char *executable_name ) {- \; @4 Z( _2 y: n/ R- [
printf ( “Usage: \n \t%s [-f frequency] [-l length] [-r reps] [-d delay] \n ”,
: q' y# l: ?- B" y; G; mexecutable_name );( ^/ G2 B" Y; @# j$ ~
exit(1);& f4 w4 o3 U* Y$ Z' w2 F4 J& r
}
( Z3 t/ Z* q( X* L/ * 分析运行参数,各项意义如下:* r( \, ^* k; D3 h$ P- z/ `& R
* “-f 《以HZ为单位的频率值 》”
+ J( w& L- B3 g% h* “-l 《以毫秒为单位的发声时长 》”
% g+ f: a. u5 S3 K* “-r 《重复次数 》”
. a+ U$ C* d) ^# N# x* “-d 《以毫秒为单位的间歇时长 》”
6 x1 S* _- p) U* @, a*/
" J, }% r' a: W9 E+ {8 c2 {. n0 Avoid parse_command_line(char **argv, beep_parms_t *result) {
8 }+ u4 f2 g/ E7 z- Achar *arg0 = *(argv++);
+ [" t: ]% D! b* U2 }while ( *argv ) {
9 N, \! D0 P. B& o; {& w" Iif ( !strcmp( *argv,“-f” )) { /*频率*/
! O3 q/ u* k$ ~# O2 ~int freq = atoi ( *( ++argv ) );, K) m2 X% A/ B
if ( ( freq 《= 0 ) | | ( freq 》 10000 ) ) {
9 g4 U- f8 }  ]2 nfprintf ( stderr, “Bad parameter: frequency must be from 1..10000\n” );
/ N- C4 R# f  I4 Wexit (1) ;7 S( Z4 }! x. d. d7 F
} else {
- d9 B2 _& z/ a" P# P5 Dresult-》freq = freq;" Y  r) I, k1 D* V, o' t" D, G
argv++;* T2 S- V" r7 O0 M6 t( x0 n
}$ G# z: b' W0 d! p7 M. Y
$ b) o3 b$ [- c/ N, W, @

/ e+ s0 S9 m/ n} else if ( ! strcmp ( *argv, “-l” ) ) { /*时长*/
& H$ P6 g" T0 bint length = atoi ( *(++argv ) );+ S! v; |) M% }. |
if (length 《 0) {  d) A+ H& t; g, [- N2 z7 `+ L
fprintf(stderr, “Bad parameter: length must be 》= 0\n”);$ B9 p9 @: v. j. w+ u: G$ F- t* C. m
exit(1);# @, P) @2 a/ J9 W/ ?$ w
} else {
# }( I6 z# w0 e4 s0 _3 yresult-》length = length;
" j4 v1 {; I; M6 Q8 Q& z$ l6 Hargv++;3 ]1 e' Q1 n3 u0 x
}
* N0 P& Z' {2 ^. B5 I, s} else if (!strcmp(*argv, “-r”)) { /*重复次数*/! y6 r7 S: m- ~- C6 }" x, \' N3 F4 f# q
int reps = atoi(*(++argv));
6 t  p+ `" o( J) O  _7 `. l0 e4 Dif (reps 《 0) {# P1 v7 g$ q1 o; @
fprintf(stderr, “Bad parameter: reps must be 》= 0\n”);4 G. o% S0 G% l. O
exit(1);& I2 Q( _- J4 O1 p
} else {
# o& J& j" n, o0 @7 |9 Wresult-》reps = reps;% J; |. A1 C- C4 i2 {
argv++;; x  e  o& _! Q/ v# n0 C
}
& M$ A) X1 [  f  V& T! h+ I: M} else if (!strcmp(*argv, “-d”)) { /* 延时 */0 P# N; E' r: a- B( j. [0 ]
int delay = atoi(*(++argv));
+ h3 h  r- R4 w9 ~3 Qif (delay 《 0) {
" y  T4 a) x3 _$ i: R: s9 M3 B, Kfprintf(stderr, “Bad parameter: delay must be 》= 0\n”);
; {' u% e9 n$ L8 `2 I( ]exit(1);3 {8 U' U4 e0 G2 ^
} else {
7 [( ~8 x; k# ^9 u- Tresult-》delay = delay;5 t9 I4 O0 `( r( B
argv++;- ^. t+ W7 J* [: H: b4 b$ f
}; M, i: e8 E4 m
} else {
( `( e: c  o1 z5 {$ l( jfprintf(stderr, “Bad parameter: %s\n”, *argv);
; o# S. @+ f$ Gusage_bail(arg0);$ _( U% D) D3 b6 R+ g( M( H
}5 g5 z: C& C) m3 y
}) N' w3 t% m% ^" K
}5 F1 T6 h9 Z1 {& Z9 t' X
int main(int argc, char **argv) {! r7 J+ `4 R; W. t
int console_fd;
- @/ b0 z" F* X7 q4 G$ @' Q2 mint i; /* 循环计数器 */
4 ]4 j6 J9 g( E, v4 o* }8 o/ P/* 设发声参数为默认值*/# Y: u( A! |7 \1 M4 m' s+ x# t
beep_parms_t parms = {DEFAULT_FREQ, DEFAULT_LENGTH, DEFAULT_REPS,
/ S; y) y; i. _- p: v  j# D; iDEFAULT_DELAY};; A3 }: m6 O% A* c: ~8 M* `- z. Q
/* 分析参数,可能的话更新发声参数*/
* ?9 B4 a' w( W5 Rparse_command_line(argv, &parms);. n- M9 Z( X+ P' |# X% F& V( F
/* 打开控制台,失败则结束程序*/
! A* y: F1 l0 e  S6 X: Pif ( ( console_fd = open ( “/dev/console”, O_WRONLY ) ) == -1 ) {6 |9 x. g; |; Y
fprintf(stderr, “Failed to open console.\n”);% v8 i' Q! c  w# H
perror(“open”);- Y7 y& G; ]+ _! c) b7 X1 \
exit(1);
5 r/ \; s' t- c& Z4 T}3 @- c# h) }& {! @! I
/* 真正开始让扬声器发声*/
' }' g% O3 }0 l. w. h8 xfor (i = 0; i 《 parms.reps; i++) {! K% y" o- U" i, T
/* 数字1190000从何而来,不得而知*/5 a. G3 i6 G$ X7 w! Z( I' @/ Z, G
int magical_fairy_number = 1190000/parms.freq;( o8 p! e1 {. C9 Q* ^- X! ^
ioctl(console_fd, KIOCSOUND, magical_fairy_number); /* 开始发声 */
; @4 f' Y( w9 y: d  ^' c/ Musleep(1000*parms.length); /*等待。.. */. \6 \  F( l9 z7 G6 @
ioctl(console_fd, KIOCSOUND, 0); /* 停止发声*/- r# Y0 n1 q5 A; v6 f' y
usleep(1000*parms.delay); /* 等待。.. */
' h$ t8 H  I7 h2 A6 }& m} /* 重复播放*/
# o- v+ z  n+ A2 b( v% [; H' u( nreturn EXIT_SUCCESS;
0 g! Y' T% t# W" @}1 H7 J1 I) G5 \  y1 D; s
5 _, o- Z+ X0 C/ U  d( L: m/ r9 ^
/ Q1 Z) ~# @# E. Y
将上面的例子稍作扩展,用户即可以让扬声器唱歌。只要找到五线谱或简谱的音阶、音长、节拍和频率、发声时长、间隔的对应关系就可以了。我现在还记得以前在DOS下编写出《世上只有妈妈好》时的兴奋。最后,说一些提外话,这其实是一个很简单的程序,但是我们却用了很长的篇幅,希望读者从以上的代码里能体会到写好的程序的一些方法,或许最重要的是添加注释吧。一个程序的注释永远不会嫌多,即便你写的时候觉得它根本是多余,但相信我,相信曾这样告诉我们的许多优秀的程序员:养成写很多注释的习惯。
6 z$ x2 i0 V) u) i对声卡编程
& D- W8 X" E6 L& |( O' e只要我们不是进行诸如驱动设备开发之类的工作,对声卡的编程和上面对扬声器的编程没有什么本质的区别。当你试图来编写诸如CD播放器、MP3播放器之类的复杂的程序时,你的工作是取获得与CDROM控制、MP3解码之类的信息,而读写系统设备的这一步在Linux下超互想象的简单。例如,Linux下最简单的播放wav的程序只有一行:cp $《 》/dev/audio。将它写成一个shell文件,同样是一个程序(shell 编程)。
5 l+ l8 j5 }, p' v我们首先需要知道一台机器上是否有声卡,一个检查的办法是检查文件/dev/sndstat文件,如果打开此文件错误,并且错误号是ENODEV,则说明此机器没有安装声卡。除此之外,试着去打开文件/dev/dsp也可以来检查是否安装了声卡。6 k: w" v( Z: z) e5 {
Linux下和声卡相关的文件有许多,如采集数字样本的/dev/dsp文件,针对混音器的/dev/mixer文件以及用于音序器的/dev/sequencer等。文件/dev/audio是一个基于兼容性考虑的声音设备文件,它实际是到上述数字设备的一个映射,它最大的特色或许是对诸如wav这类文件格式的直接支持。我们下面的例子即使用了此设备文件实现了一个简单的录音机:我们从声卡设备(当然要用麦克风)读取音频数据,并将它存放到文件test.wav中去。要播放这个wav文件,只要如前面所述,使用命令cp test.wav 》/dev/audio即可,当然你也可以用Linux下其他的多媒体软件来播放这个文件。
, G/ {" k) @3 ]; u7 P9 U5 ]2 \下面即是完整的程序清单:
" o2 D3 y7 F& U$ D" Y6 J. O/* 此文件中定义了下面所有形如SND_的变量*/
  a1 Q$ @5 h) r. K4 S5 H2 e( [#include; W: B6 d) h- e
#include$ d! Y+ G- C( D( q3 t; H8 W2 [
#include; l( m3 w! a3 ?& n
#include
- i. S' H7 }; k, Q#include- N) \( x! ~9 n  n( ^5 L
main()
: B, e5 p  f; X* `1 A$ l- Y{
* N6 N: |! s9 |# L* U8 d  C/* id:读取音频文件描述符;fd:写入的文件描述符。i,j为临时变量*/
6 V, ^+ M5 \1 Hint id,fd,i,j;
2 a: g8 b. |. N( C0 g  |2 v/* 存储音频数据的缓冲区,可以调整*/
- P' R, d9 F/ v: q0 \char testbuf[4096];
% `& p  }$ e. H" n$ N" h5 e) p: H/* 打开声卡设备,失败则退出*/
% ^2 T  i. Q& X& b0 E/ Cif ( ( id = open ( “/dev/audio”, O_RDWR ) ) 《 0 ) {
  x1 b2 H( I5 X* z5 Jfprintf (stderr, “ Can‘t open sound device!\n”);
/ F5 k! L8 k5 kexit ( -1 ) ;
# \7 h1 x! b' ]}/ m, H- s" G+ A7 Y+ D" }. ]
/* 打开输出文件,失败则退出*/
- [2 V/ W* I" o9 d( @7 Uif ( ( fd = open (“test.wav”,O_RDWR))《0){7 n2 Y3 K- z! N6 f
fprintf ( stderr, “ Can’t open output file!\n”);# f- u# `! i3 u: z
exit (-1 );9 M8 `1 [2 _( ]  K2 n
}# y/ ^% G5 Q0 g- P3 X! e
/* 设置适当的参数,使得声音设备工作正常*/
0 I& K% {# @& F, `/* 详细情况请参考Linux关于声卡编程的文档*/- G" {+ S/ w2 a6 w" D7 A! c
i=0;7 z& M% u. l# L3 P1 @% s
ioctl (id,SNDCTL_DSP_RESET,(char *)&i) ;
# i! m- W. @$ F4 Zioctl (id,SNDCTL_DSP_SYNC,(char *)&i);
& h6 P- \5 K* b3 g' S0 ei=1;
: C! l& ]5 v, ^+ V0 vioctl (id,SNDCTL_DSP_NONBLOCK,(char *)&i);
5 N0 D# D3 r* H  \i=8000;' N" F# `) N6 V; s& ?
ioctl (id,SNDCTL_DSP_SPEED,(char *)&i);+ G) W8 B; j. J7 |' c
i=1;
/ I: y5 }; R. G! |& Iioctl (id,SNDCTL_DSP_CHANNELS,(char *)&i);$ j' d$ A0 y- V
i=8;, e% k2 [6 i8 T1 y
ioctl (id,SNDCTL_DSP_SETFMT,(char *)&i);- V* e6 v2 M2 p% D  q: |. [4 ]
i=3;
7 u& K: m: v0 M" H  B% Kioctl (id,SNDCTL_DSP_SETTRIGGER,(char *)&i);$ x0 a/ ^) O& N5 i  T7 e' w
i=3;3 ?. K7 l7 ~9 F9 H5 n3 E
ioctl (id,SNDCTL_DSP_SETFRAGMENT,(char *)&i);
" C' w. `% T8 M" Ui=1;
9 {7 Q' y! @4 i% X0 [0 Q9 W" wioctl (id,SNDCTL_DSP_PROFILE,(char *)&i);
  O9 e3 t) Q' O/* 读取一定数量的音频数据,并将之写到输出文件中去*/
- d9 L+ ~. l3 l" k2 J  ?/ N1 r  vfor ( j=0; j《10;){
" y( p! X; a8 w6 Ai=read(id,testbuf,4096);
  S0 k; L1 H0 I7 j* \3 b1 j; t6 Iif(i》0){
/ U0 C6 D( U" j: n, G+ V1 b( W) }write(fd,filebuf,i);
, [- C* {: s6 r0 g- T5 A+ A( yj++;
' o4 I6 D0 y" w4 P+ @2 \$ q& E0 [}$ b' o- X2 U( ^4 J4 X6 m2 g
}
4 m% I% M& G. h! u/* 关闭输入、输出文件*/8 m( _9 X" d1 X/ v
close(fd);( T9 O2 ^1 x$ {, r
close(id);
# w+ ?1 k9 r- G6 r  c6 B1 v" F}
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|Woexam.Com ( 湘ICP备18023104号 )

GMT+8, 2024-5-21 13:16 , Processed in 0.161713 second(s), 21 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表