1 /* 2 * recwave.c 3 * 4 * Record sound files in wave format. Only MicroSoft PCM is supported. 5 * 6 * Michel R. Prevenier. 7 */ 8 9 #include <errno.h> 10 #include <sys/types.h> 11 #include <sys/stat.h> 12 #include <termios.h> 13 #include <stdlib.h> 14 #include <fcntl.h> 15 #include <stdio.h> 16 #include <unistd.h> 17 #include <string.h> 18 #include <signal.h> 19 #include <sys/ioctl.h> 20 #include <minix/sound.h> 21 22 int main(int argc, char **argv); 23 void usage(void); 24 void write_wave_header(void); 25 void terminate(int s); 26 27 28 /******* Wave format definitions *********/ 29 30 #define RIFF_ID 0x46464952 31 #define WAVE_ID1 0x45564157 32 #define WAVE_ID2 0x20746D66 33 #define DATA_ID 0x61746164 34 #define MS_PCM_FORMAT 0x0001 35 36 #define WORD short 37 #define DWORD unsigned long 38 39 struct RIFF_fields 40 { 41 DWORD RIFF_id; 42 DWORD RIFF_len; 43 DWORD WAVE_id1; 44 DWORD WAVE_id2; 45 DWORD data_ptr; 46 } r_fields; 47 48 struct common_fields 49 { 50 WORD FormatTag; 51 WORD Channels; 52 DWORD SamplesPerSec; 53 DWORD AvgBytesPerSec; 54 WORD BlockAlign; 55 } c_fields; 56 57 struct specific_fields 58 { 59 WORD BitsPerSample; 60 } s_fields; 61 62 DWORD data_id; 63 DWORD data_len; 64 65 /******** End of wave format definitions *********/ 66 67 /* Default recording values */ 68 unsigned int sign = 0; 69 unsigned int bits = 8; 70 unsigned int stereo = 0; 71 unsigned int rate = 22050; 72 73 int old_stdin; 74 struct termios old_tty, new_tty; 75 int audio, file; 76 77 void usage() 78 { 79 fprintf(stderr, "Usage: recwav [-b -s -r] file_name\n"); 80 exit(-1); 81 } 82 83 void terminate(s) 84 int s; 85 { 86 /* Restore terminal parameters */ 87 tcsetattr(0, TCSANOW, &old_tty); 88 (void) fcntl(0,F_SETFL,old_stdin); 89 close(audio); 90 close(file); 91 exit(0); 92 } 93 94 void write_wave_header() 95 { 96 /* RIFF fields */ 97 r_fields.RIFF_id = RIFF_ID; 98 r_fields.WAVE_id1 = WAVE_ID1; 99 r_fields.WAVE_id2 = WAVE_ID2; 100 r_fields.data_ptr = 16; 101 r_fields.RIFF_len = 20 + r_fields.data_ptr + data_len; 102 103 /* MicroSoft PCM specific fields */ 104 s_fields.BitsPerSample = bits; 105 106 /* Common fields */ 107 c_fields.FormatTag = MS_PCM_FORMAT; 108 c_fields.Channels = stereo + 1; 109 c_fields.SamplesPerSec = rate; 110 c_fields.AvgBytesPerSec = c_fields.Channels * rate * (bits / 8); 111 c_fields.BlockAlign = c_fields.Channels * (bits / 8); 112 113 /* Data chunk */ 114 data_id = DATA_ID; 115 116 /* Write wave-file header */ 117 lseek(file, 0L, SEEK_SET); 118 write(file, &r_fields, 20); 119 write(file, &c_fields, 14); 120 write(file, &s_fields, 2); 121 write(file, &data_id, sizeof(data_id)); 122 write(file, &data_len, sizeof(data_len)); 123 } 124 125 126 int main(argc, argv) 127 int argc; 128 char **argv; 129 { 130 unsigned int fragment_size; 131 char *buffer, *file_name; 132 char c; 133 int i; 134 135 /* Read parameters */ 136 if (argc < 2) usage(); 137 138 i = 1; 139 while (argv[i][0] == '-' && i < argc) 140 { 141 if (strncmp(argv[i], "-b", 2) == 0) 142 bits = atoi(argv[i] + 2); 143 else if (strncmp(argv[i], "-s", 2) == 0) 144 stereo = atoi(argv[i] + 2); 145 else if (strncmp(argv[i], "-r", 2) == 0) 146 rate = (unsigned int) atol(argv[i] + 2); 147 else usage(); 148 i++; 149 } 150 if (i == argc) usage(); 151 152 file_name = argv[i]; 153 154 /* Some sanity checks */ 155 if ((bits != 8 && bits != 16) || 156 (rate < 4000 || rate > 44100) || 157 (stereo != 0 && stereo != 1)) 158 { 159 fprintf(stderr, "Invalid parameters\n"); 160 exit(-1); 161 } 162 163 /* Open DSP */ 164 if ((audio = open("/dev/rec", O_RDWR)) < 0) 165 { 166 fprintf(stderr, "Cannot open /dev/rec\n"); 167 exit(-1); 168 } 169 170 /* Get maximum fragment size and try to allocate a buffer */ 171 ioctl(audio, DSPIOMAX, &fragment_size); 172 if ((buffer = malloc(fragment_size)) == (char *) 0) 173 { 174 fprintf(stderr, "Cannot allocate buffer\n"); 175 exit(-1); 176 } 177 178 /* Set sample parameters */ 179 ioctl(audio, DSPIOSIZE, &fragment_size); 180 ioctl(audio, DSPIOSTEREO, &stereo); 181 ioctl(audio, DSPIORATE, &rate); 182 ioctl(audio, DSPIOBITS, &bits); 183 sign = (bits == 16 ? 1 : 0); 184 ioctl(audio, DSPIOSIGN, &sign); 185 186 /* Create sample file */ 187 if ((file = creat(file_name, 511)) < 0) 188 { 189 fprintf(stderr, "Cannot create %s\n", argv[1]); 190 exit(-1); 191 } 192 /* Skip wave header */ 193 lseek(file, (long)(sizeof(r_fields) + 194 sizeof(c_fields) + 195 sizeof(s_fields) + 196 sizeof(data_id) + 197 sizeof(data_len)), SEEK_SET); 198 199 printf("\nBits per sample : %u\n", bits); 200 printf("Stereo : %s\n", (stereo == 1 ? "yes" : "no")); 201 printf("Samples per second: %u\n", rate); 202 203 /* Set terminal parameters and remember the old ones */ 204 tcgetattr(0, &old_tty); 205 new_tty = old_tty; 206 new_tty.c_lflag &= ~(ICANON|ECHO); 207 old_stdin = fcntl(0, F_GETFL); 208 209 /* Catch break signal to be able to restore terminal parameters in case 210 * of a user interrupt 211 */ 212 signal(SIGINT, terminate); 213 214 /* Go to non-blocking mode */ 215 tcsetattr(0, TCSANOW, &new_tty); 216 (void) fcntl(0, F_SETFL, old_stdin | O_NONBLOCK); 217 218 printf("\nPress spacebar to start sampling...\n"); 219 while(!(read(0, &c, 1) == 1 && c == ' ')); 220 221 printf("Sampling, press spacebar to stop...\n"); 222 while(!(read(0, &c, 1) == 1 && c == ' ')) 223 { 224 /* Read sample fragment and write to sample file */ 225 read(audio, buffer, fragment_size); 226 write(file, buffer, fragment_size); 227 data_len+= fragment_size; 228 } 229 printf("%ld bytes sampled. \n\n", data_len); 230 231 /* Construct the wave header in front of the raw sample data */ 232 write_wave_header(); 233 234 /* Restore terminal parameters and exit */ 235 terminate(1); 236 } 237