Fat R笔记……与减肥无关

Fat awful terrible Rubbish-bin

非常简单地……用esd播放PCM音频数据

,

网上关于esd的资料似乎不多,所以只好用google搜索函数名来看别人怎么用的……
esd本身提供了wav文件播放、sample播放和stream播放等相关的函数,可以在/usr/include/esd.h中看到所有的函数原型已经少得可怜的说明文字。下面要说的就是用它的stream播放功能来实现PCM音频数据流的播放。

首先程序要有#include <esd.h>,链接时也要加上-lesd开关。

获取esd服务器信息的方法:
  gint efd;
  esd_server_info_t *info;
  efd = esd_open_sound (NULL); //NULL表示本机的esd
  if (efd == -1)
    return;
  info = esd_get_server_info(efd);
  if (info)
    esd_print_server_info(info);
  esd_close(efd);

esd_server_info_t结构的成员可要在esd.h中看到,也就版本、数据格式、采样率三个信息。
数据格式的低4(0x000F)位标明数据是8位还是16位,0x00F0位标明是单声道还是立体声。在esd.h中定义了:
#define ESD_BITS8 	( 0x0000 )
#define ESD_BITS16	( 0x0001 )
#define ESD_MONO	( 0x0010 )
#define ESD_STEREO	( 0x0020 )

即format == 0x0021时,表示server的输出数据是16位有符号数,双声道。

嗯,播放音频流可以不用调用esd_open_sound(),用esd_play_stream_fallback()打开一个player,获取对应的file descriptor,对这个fd进行write()操作就可以了。打开一个player的代码:
  gint player; //player is a FD

  player = esd_play_stream_fallback(ESD_BITS16 | ESD_STEREO, 44100,
    NULL, "dummy");
  if (player == -1)
    return;

第一个参数指明了程序使用的pcm音频数据的格式为signed int16,立体声(即一个数据为左声道一个数据为右声道地交替),第二个参数44100为采样率。如果pcm数据是单声道,或者采样率不是44100,就要对参数做相应的修改。pcm数据的这些格式参数可以在wav文件的文件头里提取出来。第三个参数用于指定esd server,NULL表示local,第四个参数为player设置一个名称标示。

然后就是打开pcm数据文件并向player写数据:
  gint fd, rlen;
  gchar buf[2048];
  fd_set rfds;

  if ((fd = open("081.raw", O_RDONLY))<0){
    g_error("failed to open input file\n");
    return;
  }
  do {		
    FD_ZERO(&rfds);		
    FD_SET(player, &rfds);
    rlen = read (fd, buf, 530 );
    if (select(player + 1, NULL, &rfds, NULL, NULL) < 0) {
      perror("select");			
      break;
    }
    if (FD_ISSET(player, &rfds) ){
      write(player, buf, rlen);
    }
  } while (rlen > 0);

  esd_close(player);
  close (fd);

可以看到,跟前面说的socket操作类似,也是使用select检查fd是否可以写,当可以写的时候就把读到的数据写到player中,从而实现音频播放。因此如果fd是一个socket连接,就可以实现把网络上收到的音频流数据播放出来。同样,如果是蓝牙的sco音频连接,就可以把蓝牙耳机的mic收到的声音播放出来。
另外,可以参考一下xmms源码中的Output/esd目录,这是一个很好的esd音频输出范例。

datapipe.c笔记faint! gthread初始化后崩溃的问题

Comments

Unregistered user Friday, March 2, 2007 8:54:20 AM

Anonymous writes: 请问如果我是想把.wav里面的pcm音频数据提取出来,而不是播放,那个程序怎么编呢?网上关于这方面的信息太少了,望指点.还要请问你是中大的吗

Returner Friday, March 2, 2007 9:18:00 AM

提取?把wav文件头砍掉就可以了吧...wav文件的格式搜索一下就有了
参考:
http://www.china-askpro.com/msg48/qa81.shtml
http://topic.csdn.net/t/20030709/22/2010011.html

没错我是中大的:)

Unregistered user Saturday, March 3, 2007 9:02:50 AM

Anonymous writes: 多谢师兄,呵呵,我还有个问题,电脑不是以2进制储存数据的吗,为什么我打开的声音数据是显示16进制的??

Unregistered user Sunday, March 4, 2007 8:00:18 AM

Anonymous writes: 我是用ultraedit直接打开的,得到的是16进制的数,用网上的代码修改后,得到的是10进制的整数,电脑不是以2进制储存数据吗 ,究竟哪一种才是.wav里面的数据啊

Returner Tuesday, March 6, 2007 2:44:59 AM

这个。。。在物理上,数据的确是以2进制存储的,也就是一个个比特(bit)。但一般我们都是对字节(byte)进行操作,一个字节是8个比特,它可以用十六进制来表示,也可以用十进制表示,只是表示上的不一样而已,没有本质的区别。

Unregistered user Tuesday, March 6, 2007 8:44:09 AM

Anonymous writes: 那么提取数据时,应该隔多少位读一次,是i++吗,还是??间隔的位数不同,所读出的数据也不同吧,譬如我想要得到16进制的数,应该间隔多少位,十进制呢??

Returner Tuesday, March 6, 2007 9:24:22 AM

不太清楚你所谓的“提取”指的是什么。。。文件头之后就是完整的pcm数据了啊,逐个字节读出来就是了。。如果你是想从双声道的16位pcm数据中“提取”其中一个声道的数据,那就每读入2个字节(16位)就跳过2个字节。因为16位立体声的格式是:0声道(左)低字节 0声道(左)高字节 1声道(右)低字节 1声道(右)高字节

基本上,你能读写的最小单位是字节而不是位。也就是说你一次只能至少读写8个位(即一个字节)而不是一个位

另外,不要拘泥于16进制和10进制的问题了,本质上它们都是以2进制的方式存储的,不同数制之间的转换关系也可以在很多编程教科书的基础章节中查到。例如你读入的是11110000(二进制),表示成16进制就是0xF0,表示成10进制就是240

Unregistered user Wednesday, March 7, 2007 7:25:06 AM

Anonymous writes: 基本上明白了,thx~~

Write a comment

New comments have been disabled for this post.