234 lines
5.1 KiB
C
234 lines
5.1 KiB
C
/*
|
|
* playwave.c
|
|
*
|
|
* Play sound files in wave format. Only MicroSoft PCM is supported.
|
|
*
|
|
* Michel R. Prevenier.
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/ioctl.h>
|
|
#include <minix/sound.h>
|
|
|
|
int main(int argc, char **argv);
|
|
void usage(void);
|
|
|
|
/******* Wave format definitions *********/
|
|
|
|
#define RIFF_ID 0x46464952
|
|
#define WAVE_ID1 0x45564157
|
|
#define WAVE_ID2 0x20746D66
|
|
#define DATA_ID 0x61746164
|
|
#define MS_PCM_FORMAT 0x0001
|
|
|
|
#define WORD short
|
|
#define DWORD unsigned long
|
|
|
|
struct RIFF_fields
|
|
{
|
|
DWORD RIFF_id;
|
|
DWORD RIFF_len;
|
|
DWORD WAVE_id1;
|
|
DWORD WAVE_id2;
|
|
DWORD data_ptr;
|
|
} r_fields;
|
|
|
|
struct common_fields
|
|
{
|
|
WORD FormatTag;
|
|
WORD Channels;
|
|
DWORD SamplesPerSec;
|
|
DWORD AvgBytesPerSec;
|
|
WORD BlockAlign;
|
|
} c_fields;
|
|
|
|
struct specific_fields
|
|
{
|
|
WORD BitsPerSample;
|
|
} s_fields;
|
|
|
|
DWORD data_id;
|
|
DWORD data_len;
|
|
|
|
/******** End of wave definitions *********/
|
|
|
|
|
|
void usage()
|
|
{
|
|
fprintf(stderr, "Usage: playwav [-i] file\n");
|
|
exit(-1);
|
|
}
|
|
|
|
int open_audio(unsigned int *fragment_size, unsigned int channels,
|
|
unsigned int samples_per_sec, unsigned int bits)
|
|
{
|
|
unsigned int sign;
|
|
int audio;
|
|
|
|
/* Open DSP */
|
|
if ((audio = open("/dev/audio", O_RDWR)) < 0)
|
|
{
|
|
printf("Cannot open /dev/audio: %s\n", strerror(errno));
|
|
exit(-1);
|
|
}
|
|
|
|
ioctl(audio, DSPIOMAX, fragment_size); /* Get maximum fragment size. */
|
|
|
|
/* Set DSP parameters (should check return values..) */
|
|
ioctl(audio, DSPIOSIZE, fragment_size); /* Use max. fragment size. */
|
|
ioctl(audio, DSPIOSTEREO, &channels);
|
|
ioctl(audio, DSPIORATE, &samples_per_sec);
|
|
ioctl(audio, DSPIOBITS, &bits);
|
|
sign = (bits == 16 ? 1 : 0);
|
|
ioctl(audio, DSPIOSIGN, &sign);
|
|
return audio;
|
|
}
|
|
|
|
int main ( int argc, char *argv[] )
|
|
{
|
|
int i, r, audio, file;
|
|
char *buffer, *file_name = NULL;
|
|
unsigned int fragment_size, fragment_size2;
|
|
long data_pos;
|
|
int showinfo = 0;
|
|
|
|
/* Check Parameters */
|
|
if (argc > 2)
|
|
{
|
|
if (strncmp(argv[1], "-i", 2) == 0)
|
|
{
|
|
showinfo = 1;
|
|
file_name = argv[2];
|
|
}
|
|
else
|
|
usage();
|
|
}
|
|
else file_name = argv[1];
|
|
|
|
/* Open wav file */
|
|
if((file = open(file_name, O_RDONLY)) < 0)
|
|
{
|
|
printf("Cannot open %s\n", file_name);
|
|
exit(-1);
|
|
}
|
|
|
|
/* Check for valid wave format */
|
|
read(file, &r_fields, 20);
|
|
if(r_fields.RIFF_id != RIFF_ID)
|
|
{
|
|
printf("%s not in RIFF format\n", file_name);
|
|
exit(1);
|
|
}
|
|
if(r_fields.WAVE_id1 != WAVE_ID1 || r_fields.WAVE_id2 != WAVE_ID2)
|
|
{
|
|
printf("%s not in WAVE format\n", file_name);
|
|
exit(1);
|
|
}
|
|
|
|
/* Store data_chunk position */
|
|
data_pos = lseek(file, 0L, 1) + r_fields.data_ptr;
|
|
|
|
/* Read the common and specific fields */
|
|
read(file, &c_fields, 14);
|
|
read(file, &s_fields, 2);
|
|
|
|
/* Check for valid wave format, we can only play MicroSoft PCM */
|
|
if(c_fields.FormatTag != MS_PCM_FORMAT)
|
|
{
|
|
printf("%s not in MicroSoft PCM format\n", file_name);
|
|
exit(1);
|
|
}
|
|
|
|
/* Open audio device and set DSP parameters */
|
|
audio = open_audio(&fragment_size, c_fields.Channels - 1,
|
|
c_fields.SamplesPerSec, s_fields.BitsPerSample);
|
|
|
|
if ((buffer = malloc(fragment_size)) == (char *)0)
|
|
{
|
|
fprintf(stderr, "Cannot allocate buffer\n");
|
|
exit(-1);
|
|
}
|
|
|
|
/* Goto data chunk */
|
|
lseek(file, data_pos, SEEK_SET);
|
|
|
|
/* Check for valid data chunk */
|
|
read(file, &data_id, sizeof(data_id));
|
|
if(data_id != DATA_ID)
|
|
{
|
|
printf("Invalid data chunk\n");
|
|
exit(1);
|
|
}
|
|
|
|
/* Get length of data */
|
|
read(file, &data_len, sizeof(data_len));
|
|
|
|
if (showinfo)
|
|
{
|
|
printf("\nBits per sample : %d \n", s_fields.BitsPerSample);
|
|
printf("Stereo : %s \n", (c_fields.Channels == 1 ? "yes" : "no"));
|
|
printf("Samples per second: %ld \n", c_fields.SamplesPerSec);
|
|
printf("Average bytes/sec : %ld \n", c_fields.AvgBytesPerSec);
|
|
printf("Block alignment : %d \n", c_fields.BlockAlign);
|
|
printf("Datalength (bytes): %ld \n\n", data_len);
|
|
}
|
|
|
|
/* Play data */
|
|
while(data_len > 0)
|
|
{
|
|
if (data_len > fragment_size)
|
|
{
|
|
/* Read next fragment */
|
|
read(file, buffer, fragment_size);
|
|
data_len-= fragment_size;
|
|
}
|
|
else
|
|
{
|
|
/* Read until end of file and fill rest of buffer with silence,
|
|
* in PCM this means: fill buffer with last played value
|
|
*/
|
|
read(file, buffer, data_len);
|
|
for (i = data_len; i< fragment_size; i++)
|
|
buffer[i] = buffer[(int)data_len-1];
|
|
data_len = 0;
|
|
}
|
|
|
|
/* Copy data to DSP */
|
|
r= write(audio, buffer, fragment_size);
|
|
if (r != fragment_size)
|
|
{
|
|
if (r < 0)
|
|
{
|
|
fprintf(stderr, "playwave: write to audio device failed: %s\n",
|
|
strerror(errno));
|
|
|
|
/* If we get EIO, the driver might have restarted. Reopen the
|
|
* audio device.
|
|
*/
|
|
if (errno == EIO) {
|
|
close(audio);
|
|
audio = open_audio(&fragment_size2,
|
|
c_fields.Channels - 1, c_fields.SamplesPerSec,
|
|
s_fields.BitsPerSample);
|
|
if (fragment_size2 != fragment_size) {
|
|
fprintf(stderr, "Fragment size has changed\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "playwave: partial write %d instead of %d\n",
|
|
r, fragment_size);
|
|
}
|
|
}
|
|
}
|
|
}
|