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(int s) 84 { 85 /* Restore terminal parameters */ 86 tcsetattr(0, TCSANOW, &old_tty); 87 (void) fcntl(0,F_SETFL,old_stdin); 88 close(audio); 89 close(file); 90 exit(0); 91 } 92 93 void write_wave_header() 94 { 95 /* RIFF fields */ 96 r_fields.RIFF_id = RIFF_ID; 97 r_fields.WAVE_id1 = WAVE_ID1; 98 r_fields.WAVE_id2 = WAVE_ID2; 99 r_fields.data_ptr = 16; 100 r_fields.RIFF_len = 20 + r_fields.data_ptr + data_len; 101 102 /* MicroSoft PCM specific fields */ 103 s_fields.BitsPerSample = bits; 104 105 /* Common fields */ 106 c_fields.FormatTag = MS_PCM_FORMAT; 107 c_fields.Channels = stereo + 1; 108 c_fields.SamplesPerSec = rate; 109 c_fields.AvgBytesPerSec = c_fields.Channels * rate * (bits / 8); 110 c_fields.BlockAlign = c_fields.Channels * (bits / 8); 111 112 /* Data chunk */ 113 data_id = DATA_ID; 114 115 /* Write wave-file header */ 116 lseek(file, 0L, SEEK_SET); 117 write(file, &r_fields, 20); 118 write(file, &c_fields, 14); 119 write(file, &s_fields, 2); 120 write(file, &data_id, sizeof(data_id)); 121 write(file, &data_len, sizeof(data_len)); 122 } 123 124 125 int main(int argc, char* argv[]) 126 { 127 unsigned int fragment_size; 128 char *buffer, *file_name; 129 char c; 130 int i; 131 132 /* Read parameters */ 133 if (argc < 2) usage(); 134 135 i = 1; 136 while ((i < argc) && (argv[i][0] == '-')) 137 { 138 if (strncmp(argv[i], "-b", 2) == 0) 139 bits = atoi(argv[i] + 2); 140 else if (strncmp(argv[i], "-s", 2) == 0) 141 stereo = atoi(argv[i] + 2); 142 else if (strncmp(argv[i], "-r", 2) == 0) 143 rate = (unsigned int) atol(argv[i] + 2); 144 else usage(); 145 i++; 146 } 147 if (i == argc) usage(); 148 149 file_name = argv[i]; 150 151 /* Some sanity checks */ 152 if ((bits != 8 && bits != 16) || 153 (rate < 4000 || rate > 44100) || 154 (stereo != 0 && stereo != 1)) 155 { 156 fprintf(stderr, "Invalid parameters\n"); 157 exit(-1); 158 } 159 160 /* Open DSP */ 161 if ((audio = open("/dev/rec", O_RDWR)) < 0) 162 { 163 fprintf(stderr, "Cannot open /dev/rec\n"); 164 exit(-1); 165 } 166 167 /* Get maximum fragment size and try to allocate a buffer */ 168 ioctl(audio, DSPIOMAX, &fragment_size); 169 if ((buffer = malloc(fragment_size)) == (char *) 0) 170 { 171 fprintf(stderr, "Cannot allocate buffer\n"); 172 exit(-1); 173 } 174 175 /* Set sample parameters */ 176 ioctl(audio, DSPIOSIZE, &fragment_size); 177 ioctl(audio, DSPIOSTEREO, &stereo); 178 ioctl(audio, DSPIORATE, &rate); 179 ioctl(audio, DSPIOBITS, &bits); 180 sign = (bits == 16 ? 1 : 0); 181 ioctl(audio, DSPIOSIGN, &sign); 182 183 /* Create sample file */ 184 if ((file = creat(file_name, 511)) < 0) 185 { 186 fprintf(stderr, "Cannot create %s\n", argv[1]); 187 exit(-1); 188 } 189 /* Skip wave header */ 190 lseek(file, (long)(sizeof(r_fields) + 191 sizeof(c_fields) + 192 sizeof(s_fields) + 193 sizeof(data_id) + 194 sizeof(data_len)), SEEK_SET); 195 196 printf("\nBits per sample : %u\n", bits); 197 printf("Stereo : %s\n", (stereo == 1 ? "yes" : "no")); 198 printf("Samples per second: %u\n", rate); 199 200 /* Set terminal parameters and remember the old ones */ 201 tcgetattr(0, &old_tty); 202 new_tty = old_tty; 203 new_tty.c_lflag &= ~(ICANON|ECHO); 204 old_stdin = fcntl(0, F_GETFL); 205 206 /* Catch break signal to be able to restore terminal parameters in case 207 * of a user interrupt 208 */ 209 signal(SIGINT, terminate); 210 211 /* Go to non-blocking mode */ 212 tcsetattr(0, TCSANOW, &new_tty); 213 (void) fcntl(0, F_SETFL, old_stdin | O_NONBLOCK); 214 215 printf("\nPress spacebar to start sampling...\n"); 216 while(!(read(0, &c, 1) == 1 && c == ' ')); 217 218 printf("Sampling, press spacebar to stop...\n"); 219 while(!(read(0, &c, 1) == 1 && c == ' ')) 220 { 221 /* Read sample fragment and write to sample file */ 222 read(audio, buffer, fragment_size); 223 write(file, buffer, fragment_size); 224 data_len+= fragment_size; 225 } 226 printf("%ld bytes sampled. \n\n", data_len); 227 228 /* Construct the wave header in front of the raw sample data */ 229 write_wave_header(); 230 231 /* Restore terminal parameters and exit */ 232 terminate(1); 233 } 234