ifree64 发表于 2008-9-9 14:16:04

DIY wav播放器又进一步,WAV文件播放实验成功。

今天,看了下wav文件格式,在Linux下尝试了读取wav文件数据写往声卡文件/dev/dsp,播放成功。
没想到Linux下声卡编程竟然如此简单,就是把音频数据写到/dev/dsp文件即可。

源代码如下

/* filename: wavplay.c */

#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <linux/soundcard.h>

#define BUFSIZE 512

struct RIFF_Header{
char RIFF_ID;
uint32_t RIFF_Size;
char RIFF_Format;
};

struct Chunk_Header{
char Chunk_ID;
uint32_t Chunk_Size;
};

struct Wave_Format{
uint16_t AudioFormat;
uint16_t NumChannels;
uint32_t SampleRate;
uint32_t AvgBytesPerSec;
uint16_t BlockAlign;
uint16_t BitsPerSample;
};

int main(int argc, char *argv[])
{
struct RIFF_Header riff_header;
struct Chunk_Header fmt_chunk, data_chunk;
struct Wave_Format wavfmt;

char buf;
FILE * fwave;
int sndfd;       
int status;
int arg;
int readbytes;
int writebytes;
int writed;

if( argc < 2 ){
    fprintf(stderr, "Usage: wavplay <filename>\n");
    exit(-1);
}

fwave = fopen( argv, "r");
if( fwave == NULL ){
    fprintf(stderr, "Can't open file %s\n", argv);
    exit(-1);
}
       
fread(&riff_header, 1, sizeof(struct RIFF_Header), fwave);
if( strncmp(riff_header.RIFF_ID, "RIFF", 4) || strncmp(riff_header.RIFF_Format, "WAVE",4)){
    fprintf(stderr, "Unknown file format.\n");
    exit(-1);
}

sndfd = open("/dev/dsp", O_RDWR);
if (sndfd < 0) {
    perror("open of /dev/dsp failed");
    exit(-1);
}

fread(&fmt_chunk, 1, sizeof(struct Chunk_Header), fwave);
if( !strncmp(fmt_chunk.Chunk_ID, "fmt ", 4) ){
    /* this is a fmt chunk */
    fread(&wavfmt, 1, sizeof(struct Wave_Format), fwave);
   
    arg = wavfmt.BitsPerSample;
    status = ioctl(sndfd, SOUND_PCM_WRITE_BITS, &arg);
    if (status == -1)
      perror("SOUND_PCM_WRITE_BITS ioctl failed");
    if (arg != wavfmt.BitsPerSample)
      perror("unable to set sample size");
   
    arg = wavfmt.NumChannels;
    status = ioctl(sndfd, SOUND_PCM_WRITE_CHANNELS, &arg);
    if (status == -1)
      perror("SOUND_PCM_WRITE_CHANNELS ioctl failed");
    if (arg != wavfmt.NumChannels)
      perror("unable to set number of channels");

    arg = wavfmt.SampleRate;
    status = ioctl(sndfd, SOUND_PCM_WRITE_RATE, &arg);
    if (status == -1)
      perror("SOUND_PCM_WRITE_WRITE ioctl failed");

    /* skip extra bytes */
    fseek(fwave, fmt_chunk.Chunk_Size - 16 + fmt_chunk.Chunk_Size%2, SEEK_CUR);
}else{
    fprintf(stderr, "Can't find fmt chunk.\n");
    exit(-1);
}

while( fread(&data_chunk, 1, sizeof(struct Chunk_Header), fwave) != 0 )
    if( !strncmp(data_chunk.Chunk_ID, "data", 4) ){
      printf("Begin Play\n");
      /* this is a data chunk */
      writed = 0;
      while(writed < data_chunk.Chunk_Size){
        readbytes = fread(buf, 1, BUFSIZE, fwave);
        writebytes = write(sndfd, buf, readbytes);
        if( writebytes != readbytes )
          perror("wrote wrong number of bytes");
        writed += readbytes;
      }
    }else{
      /* skip unknown chunks */
      fseek(fwave, data_chunk.Chunk_Size + fmt_chunk.Chunk_Size%2, SEEK_CUR);
    }

fclose(fwave);
close(sndfd);

return 0;
}


哈哈,DIY wav播放器的路又向前进了一步。

1181zjf 发表于 2008-9-9 14:23:14

不错

ddb_21ic 发表于 2008-9-18 17:57:08

以前说的,cp到snd文件就能播放了

记不清了

ifree64 发表于 2008-9-18 18:14:14

据说这样就可以播放了

#cat&nbsp;haha.wav>&nbsp;/dev/dsp

这个命令在我的一个arm9的开发板实验可以播放wav文件,但在我的PC上不行。上面的程序在PC上可以播放wav在arm9上却又报dma2的一个什么错误。



我这个实验的目的主要是想看看如何解析wav文件,以便于以后用单片机diy一个wav播放器。

asdasd 发表于 2008-11-21 15:11:54

正是需要的&nbsp;谢谢

zhousd 发表于 2011-3-14 14:22:24

用linux 的好处多多。

lijinen 发表于 2012-5-27 18:32:54

收藏了,谢谢楼主分享!
页: [1]
查看完整版本: DIY wav播放器又进一步,WAV文件播放实验成功。