Chinaunix首页 | 论坛 | 博客
  • 博客访问: 538272
  • 博文数量: 86
  • 博客积分: 1076
  • 博客等级: 准尉
  • 技术积分: 1018
  • 用 户 组: 普通用户
  • 注册时间: 2011-11-02 19:15
文章分类

全部博文(86)

文章存档

2013年(15)

2012年(69)

2011年(2)

分类: LINUX

2012-04-07 11:15:05

介绍一个小型的基于ALSA的播放wav音乐的小程序,对于wav文件,其头部一般有44字节的头部信息,里面标示了音频文件的采样率,量化位数,通道数,以及音频文件的数据大小(文件大小+44字节的头)。
1、playback.c

点击(此处)折叠或打开

  1. /*

  2. This example reads from the default PCM device
  3. and writes to standard output for 5 seconds of data.

  4. */

  5. /* Use the newer ALSA API */
  6. #define ALSA_PCM_NEW_HW_PARAMS_API

  7. #include <alsa/asoundlib.h>

  8. /**************************************************************/
  9. #define ID_RIFF 0x46464952
  10. #define ID_WAVE 0x45564157
  11. #define ID_FMT 0x20746d66
  12. #define ID_DATA 0x61746164

  13. typedef unsigned long uint32_t;
  14. typedef unsigned short uint16_t;

  15. #define FORMAT_PCM 1

  16. static uint32_t totle_size = 0;

  17. struct wav_header {
  18.     /* RIFF WAVE Chunk */
  19.     uint32_t riff_id;
  20.     uint32_t riff_sz;
  21.     uint32_t riff_fmt;
  22.     /* Format Chunk */
  23.     uint32_t fmt_id;
  24.     uint32_t fmt_sz;
  25.     uint16_t audio_format;
  26.     uint16_t num_channels;
  27.     uint32_t sample_rate;
  28.     uint32_t byte_rate; /* sample_rate * num_channels * bps / 8 */
  29.     uint16_t block_align; /* num_channels * bps / 8 */
  30.     uint16_t bits_per_sample;
  31.     /* Data Chunk */
  32.     uint32_t data_id;
  33.     uint32_t data_sz;
  34. }__attribute__((packed));

  35. static struct wav_header hdr;

  36. /**************************************************************/
  37. int playback_file(unsigned rate, uint16_t channels, int fd, uint32_t total_count)
  38. {
  39.     int rc;
  40.     int size;
  41.     uint32_t left_size;
  42.     snd_pcm_t *handle;
  43.     snd_pcm_hw_params_t *params;
  44.     int dir;
  45.     snd_pcm_uframes_t frames;
  46.     char *buffer;                    /* TODO */

  47.     /* Open PCM device for playbacking. */
  48.     rc = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
  49.     if (rc < 0) {
  50.         fprintf(stderr, "unable to open pcm device: %s\n", snd_strerror(rc));
  51.         exit(1);
  52.     }

  53.     /* Allocate a hardware parameters object. */
  54.     snd_pcm_hw_params_alloca(&params);

  55.     /* Fill it in with default values. */
  56.     snd_pcm_hw_params_any(handle, params);

  57.     /* Set the desired hardware parameters. */

  58.     /* Interleaved mode */
  59.     snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);

  60.     /* Signed 16-bit little-endian format */
  61.     snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);

  62.     /* Two channels (stereo) */
  63.     snd_pcm_hw_params_set_channels(handle, params, channels);

  64.     /* 44100 bits/second sampling rate (CD quality) */
  65.     snd_pcm_hw_params_set_rate_near(handle, params, &rate, &dir);

  66.     /* Set period size to 32 frames. */
  67.     frames = 32;
  68.     snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir);

  69.     /* Write the parameters to the driver */
  70.     rc = snd_pcm_hw_params(handle, params);
  71.     if (rc < 0) {
  72.         fprintf(stderr, "unable to set hw parameters: %s\n", snd_strerror(rc));
  73.         exit(1);
  74.     }

  75.     /* Use a buffer large enough to hold one period */
  76.     snd_pcm_hw_params_get_period_size(params, &frames, &dir);
  77.     
  78.     size = frames * 2 * channels; /* 2 bytes/sample(16bit), 2 channels */
  79.     buffer = (char *) malloc(size);
  80.     
  81.     while (left_size > 0) {
  82.         rc = read(fd, buffer, size);
  83.         totle_size += rc;                        /* totle data size */
  84.         left_size = total_count - totle_size;
  85.         if (rc != size)
  86.          fprintf(stderr, "short read: read %d bytes\n", rc);
  87.         
  88.         rc = snd_pcm_writei(handle, buffer, frames);
  89.         if (rc == -EPIPE) {
  90.          /* EPIPE means overrun */
  91.          fprintf(stderr, "overrun occurred\n");
  92.          snd_pcm_prepare(handle);
  93.         } else if (rc < 0) {
  94.          fprintf(stderr, "error from write: %s\n", snd_strerror(rc));
  95.         } else if (rc != (int)frames) {
  96.          fprintf(stderr, "short write, write %d frames\n", rc);
  97.         }

  98.     }

  99.     snd_pcm_drain(handle);
  100.     snd_pcm_close(handle);
  101.     free(buffer);

  102.     return 0;
  103. }

  104. /**************************************************************/
  105. int play_wav(const char *fn)
  106. {
  107.     unsigned rate, channels;
  108.     int fd;

  109.     fd = open(fn, O_RDONLY, 0777);
  110.     if (fd < 0) {
  111.         fprintf(stderr, "playback: cannot open '%s'\n", fn);
  112.         return -1;
  113.     }

  114.     if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
  115.         fprintf(stderr, "playback: cannot read header\n");
  116.         return -1;
  117.     }
  118.     fprintf(stderr,"playback: %d ch, %d hz, %d bit, %s, file_size %ld\n",
  119.             hdr.num_channels, hdr.sample_rate, hdr.bits_per_sample,
  120.             hdr.audio_format == FORMAT_PCM ? "PCM" : "unknown", hdr.data_sz);

  121.     return playback_file(hdr.sample_rate, hdr.num_channels, fd, hdr.data_sz);
  122. }

  123. int main(int argc, char **argv)
  124. {
  125.     if (argc != 2) {
  126.         fprintf(stderr,"usage: playback \n");
  127.         return -1;
  128.     }

  129.     return play_wav(argv[1]);
  130. }
程序默认为16bit放音,可以支持不同采样率和通道数。

2、Makefile

点击(此处)折叠或打开

  1. exe = playback
  2. src = playback.c
  3. CC = unicore32-linux-gcc
  4. INC = -I/nfs/usr/local/mplayer/include
  5. LDFLAGS = -L/nfs/usr/local/mplayer/lib -lasound

  6. $(exe) : $(src) FORCE
  7.     $(CC) -o $@ $(src) $(LDFLAGS) $(INC)


  8. FORCE:

  9. clean:
  10.     rm -f ./*.o $(exe)

我是基于嵌入式的编译环境,我的alsa-lib库在我的nfs下,故链接库指定在那里。

阅读(1788) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~