1 /* 2 * playwave.c 3 * 4 * Play sound files in wave format. Only MicroSoft PCM is supported. 5 * 6 * Michel R. Prevenier. 7 */ 8 9 #include <sys/types.h> 10 #include <errno.h> 11 #include <signal.h> 12 #include <stdlib.h> 13 #include <unistd.h> 14 #include <fcntl.h> 15 #include <stdio.h> 16 #include <string.h> 17 #include <sys/ioctl.h> 18 #include <minix/sound.h> 19 20 int main(int argc, char **argv); 21 void usage(void); 22 23 /******* Wave format definitions *********/ 24 25 #define RIFF_ID 0x46464952 26 #define WAVE_ID1 0x45564157 27 #define WAVE_ID2 0x20746D66 28 #define DATA_ID 0x61746164 29 #define MS_PCM_FORMAT 0x0001 30 31 #define WORD short 32 #define DWORD unsigned long 33 34 struct RIFF_fields 35 { 36 DWORD RIFF_id; 37 DWORD RIFF_len; 38 DWORD WAVE_id1; 39 DWORD WAVE_id2; 40 DWORD data_ptr; 41 } r_fields; 42 43 struct common_fields 44 { 45 WORD FormatTag; 46 WORD Channels; 47 DWORD SamplesPerSec; 48 DWORD AvgBytesPerSec; 49 WORD BlockAlign; 50 } c_fields; 51 52 struct specific_fields 53 { 54 WORD BitsPerSample; 55 } s_fields; 56 57 DWORD data_id; 58 DWORD data_len; 59 60 /******** End of wave definitions *********/ 61 62 63 void usage() 64 { 65 fprintf(stderr, "Usage: playwav [-i] file\n"); 66 exit(-1); 67 } 68 69 int open_audio(unsigned int *fragment_size, unsigned int channels, 70 unsigned int samples_per_sec, unsigned int bits) 71 { 72 unsigned int sign; 73 int audio; 74 75 /* Open DSP */ 76 if ((audio = open("/dev/audio", O_RDWR)) < 0) 77 { 78 printf("Cannot open /dev/audio: %s\n", strerror(errno)); 79 exit(-1); 80 } 81 82 ioctl(audio, DSPIOMAX, fragment_size); /* Get maximum fragment size. */ 83 84 /* Set DSP parameters (should check return values..) */ 85 ioctl(audio, DSPIOSIZE, fragment_size); /* Use max. fragment size. */ 86 ioctl(audio, DSPIOSTEREO, &channels); 87 ioctl(audio, DSPIORATE, &samples_per_sec); 88 ioctl(audio, DSPIOBITS, &bits); 89 sign = (bits == 16 ? 1 : 0); 90 ioctl(audio, DSPIOSIGN, &sign); 91 return audio; 92 } 93 94 int main ( int argc, char *argv[] ) 95 { 96 int i, r, audio, file; 97 char *buffer, *file_name = NULL; 98 unsigned int fragment_size, fragment_size2; 99 long data_pos; 100 int showinfo = 0; 101 102 /* Check Parameters */ 103 if (argc > 2) 104 { 105 if (strncmp(argv[1], "-i", 2) == 0) 106 { 107 showinfo = 1; 108 file_name = argv[2]; 109 } 110 else 111 usage(); 112 } 113 else file_name = argv[1]; 114 115 /* Open wav file */ 116 if((file = open(file_name, O_RDONLY)) < 0) 117 { 118 printf("Cannot open %s\n", file_name); 119 exit(-1); 120 } 121 122 /* Check for valid wave format */ 123 read(file, &r_fields, 20); 124 if(r_fields.RIFF_id != RIFF_ID) 125 { 126 printf("%s not in RIFF format\n", file_name); 127 exit(1); 128 } 129 if(r_fields.WAVE_id1 != WAVE_ID1 || r_fields.WAVE_id2 != WAVE_ID2) 130 { 131 printf("%s not in WAVE format\n", file_name); 132 exit(1); 133 } 134 135 /* Store data_chunk position */ 136 data_pos = lseek(file, 0L, 1) + r_fields.data_ptr; 137 138 /* Read the common and specific fields */ 139 read(file, &c_fields, 14); 140 read(file, &s_fields, 2); 141 142 /* Check for valid wave format, we can only play MicroSoft PCM */ 143 if(c_fields.FormatTag != MS_PCM_FORMAT) 144 { 145 printf("%s not in MicroSoft PCM format\n", file_name); 146 exit(1); 147 } 148 149 /* Open audio device and set DSP parameters */ 150 audio = open_audio(&fragment_size, c_fields.Channels - 1, 151 c_fields.SamplesPerSec, s_fields.BitsPerSample); 152 153 if ((buffer = malloc(fragment_size)) == (char *)0) 154 { 155 fprintf(stderr, "Cannot allocate buffer\n"); 156 exit(-1); 157 } 158 159 /* Goto data chunk */ 160 lseek(file, data_pos, SEEK_SET); 161 162 /* Check for valid data chunk */ 163 read(file, &data_id, sizeof(data_id)); 164 if(data_id != DATA_ID) 165 { 166 printf("Invalid data chunk\n"); 167 exit(1); 168 } 169 170 /* Get length of data */ 171 read(file, &data_len, sizeof(data_len)); 172 173 if (showinfo) 174 { 175 printf("\nBits per sample : %d \n", s_fields.BitsPerSample); 176 printf("Stereo : %s \n", (c_fields.Channels == 1 ? "yes" : "no")); 177 printf("Samples per second: %ld \n", c_fields.SamplesPerSec); 178 printf("Average bytes/sec : %ld \n", c_fields.AvgBytesPerSec); 179 printf("Block alignment : %d \n", c_fields.BlockAlign); 180 printf("Datalength (bytes): %ld \n\n", data_len); 181 } 182 183 /* Play data */ 184 while(data_len > 0) 185 { 186 if (data_len > fragment_size) 187 { 188 /* Read next fragment */ 189 read(file, buffer, fragment_size); 190 data_len-= fragment_size; 191 } 192 else 193 { 194 /* Read until end of file and fill rest of buffer with silence, 195 * in PCM this means: fill buffer with last played value 196 */ 197 read(file, buffer, data_len); 198 for (i = data_len; i< fragment_size; i++) 199 buffer[i] = buffer[(int)data_len-1]; 200 data_len = 0; 201 } 202 203 /* Copy data to DSP */ 204 r= write(audio, buffer, fragment_size); 205 if (r != fragment_size) 206 { 207 if (r < 0) 208 { 209 fprintf(stderr, "playwave: write to audio device failed: %s\n", 210 strerror(errno)); 211 212 /* If we get EIO, the driver might have restarted. Reopen the 213 * audio device. 214 */ 215 if (errno == EIO) { 216 close(audio); 217 audio = open_audio(&fragment_size2, 218 c_fields.Channels - 1, c_fields.SamplesPerSec, 219 s_fields.BitsPerSample); 220 if (fragment_size2 != fragment_size) { 221 fprintf(stderr, "Fragment size has changed\n"); 222 exit(1); 223 } 224 } 225 } 226 else 227 { 228 fprintf(stderr, "playwave: partial write %d instead of %d\n", 229 r, fragment_size); 230 } 231 } 232 } 233 } 234