blob: e599fffb056b25f08f2e1b4a99a2055ec8bab4ca [file] [log] [blame]
lh9ed821d2023-04-07 01:36:19 -07001#include <stdio.h>
2#include <unistd.h>
3#include <string.h>
4//#include <stdlib.h>
5//#include "pthread.h"
6//#include "alsatest.h"
7//#include <stdbool.h>
8#include <fcntl.h>
9#include <signal.h>
10#include <tinyalsa/asoundlib.h>
11#include <sound/asound.h>
12#include <stdint.h>
13#include <stdbool.h>
14#include <errno.h>
15#include <stdlib.h>
16#include <ctype.h>
17
18#define ID_RIFF 0x46464952
19#define ID_WAVE 0x45564157
20#define ID_FMT 0x20746d66
21#define ID_DATA 0x61746164
22
23#define FORMAT_PCM 1
24#define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
25
26
27struct riff_wave_header {
28 uint32_t riff_id;
29 uint32_t riff_sz;
30 uint32_t wave_id;
31};
32
33struct chunk_header {
34 uint32_t id;
35 uint32_t sz;
36};
37
38struct chunk_fmt {
39 uint16_t audio_format;
40 uint16_t num_channels;
41 uint32_t sample_rate;
42 uint32_t byte_rate;
43 uint16_t block_align;
44 uint16_t bits_per_sample;
45};
46struct wav_header {
47 uint32_t riff_id;
48 uint32_t riff_sz;
49 uint32_t riff_fmt;
50 uint32_t fmt_id;
51 uint32_t fmt_sz;
52 uint16_t audio_format;
53 uint16_t num_channels;
54 uint32_t sample_rate;
55 uint32_t byte_rate;
56 uint16_t block_align;
57 uint16_t bits_per_sample;
58 uint32_t data_id;
59 uint32_t data_sz;
60};
61
62enum t_output_path{
63 T_OUTPUT_RECEIVER,
64 T_OUTPUT_SPEAKER,
65 T_OUTPUT_HEADSET,
66};
67
68enum t_input_path{
69 T_INPUT_MICLP,
70 T_INPUT_MICRP,
71};
72
73enum t_output_vol{
74 T_OUTPUT_VOL_MINUS_63_5DB = 0,
75 T_OUTPUT_VOL_MINUS_63DB = 1,
76 T_OUTPUT_VOL_MINUS_6DB = 115,
77 T_OUTPUT_VOL_MINUS_3DB = 121,
78 T_OUTPUT_VOL_MINUS_2DB = 123,
79 T_OUTPUT_VOL_MINUS_1DB = 125,
80 T_OUTPUT_VOL_0DB = 127,
81 T_OUTPUT_VOL_1DB = 129,
82 T_OUTPUT_VOL_2DB = 131,
83 T_OUTPUT_VOL_3DB = 133,
84 T_OUTPUT_VOL_6DB = 139,
85 T_OUTPUT_VOL_9DB = 145,
86 T_OUTPUT_VOL_12DB = 151,
87 T_OUTPUT_VOL_24DB = 175,
88};
89
90enum t_input_vol{
91 T_INPUT_VOL_MINUS_12DB = 0,
92 T_INPUT_VOL_MINUS_3DB = 18,
93 T_INPUT_VOL_MINUS_2DB = 20,
94 T_INPUT_VOL_MINUS_1DB = 22,
95 T_INPUT_VOL_0DB = 24,
96 T_INPUT_VOL_1DB = 26,
97 T_INPUT_VOL_2DB = 28,
98 T_INPUT_VOL_3DB = 30,
99 T_INPUT_VOL_20DB = 64,
100};
101
102struct audio_para_conf {
103 const char *control;
104 char *values[2];
105};
106
107static struct audio_para_conf common_out_para_audio[] = { //¶ÔÓ¦¹«¹²²¿·Ö
108 {"Speaker Analog Playback Volume",{"127"}},
109 {"Speaker Driver Playback Switch",{"1"}},
110 {"DAC Left Input",{"Mono"}},
111 {"DAC Right Input",{"Mono"}},
112 {"Output Right From Right DAC",{"1"}},
113 {"Output Left From Left DAC",{"1"}},
114 {"HP Analog Playback Volume",{"127","127"}},
115 {"HP Driver Playback Switch",{"1","1"}}, //7dB
116
117};
118
119static struct audio_para_conf common_in_para_audio[] = { //¶ÔÓ¦¹«¹²²¿·Ö
120 {"Mic PGA Capture Volume",{"40"}},
121 {"ADC Capture Switch",{"1"}},
122 {"ADC Fine Capture Volume",{"0"}},
123
124};
125
126static struct audio_para_conf hp_outpath_para_audio[] = { //¶ÔÓ¦ÓÚHpÊä³öͨµÀ
127 {"Speaker Switch",{"0"}},
128 {"HP Left Switch",{"1"}},
129 {"HP Right Switch",{"1"}},
130 {"HP Driver Playback Volume",{"0","0"}},
131};
132
133static struct audio_para_conf spk_outpath_para_audio[] = { //¶ÔÓ¦ÓÚSpkÊä³öͨµÀ
134 {"Speaker Switch",{"1"}},
135 {"Speaker Driver Playback Volume",{"2"}}, //18dB
136 {"HP Left Switch",{"0"}},
137 {"HP Right Switch",{"0"}},
138};
139
140static struct audio_para_conf mic1lp_mc1lm_inpath_para_audio[] = { //¶ÔÓ¦ÓÚMic1lp_Mic1lmÊäÈëͨµÀ
141 {"MIC1LM P-Terminal",{"Off"}},
142 {"MIC1RP P-Terminal",{"Off"}},
143 {"MIC1LP P-Terminal",{"FFR 10 Ohm"}},
144 {"MIC1LM M-Terminal",{"FFR 10 Ohm"}},
145
146};
147
148static struct audio_para_conf mic1rp_inpath_para_audio[] = {//¶ÔÓ¦ÓÚMic1rpÊäÈëͨµÀ
149 {"MIC1LM P-Terminal",{"Off"}},
150 {"MIC1RP P-Terminal",{"FFR 10 Ohm"}},
151 {"MIC1LP P-Terminal",{"Off"}},
152 {"MIC1LM M-Terminal",{"Off"}},
153};
154
155static struct audio_para_conf handset_inoutpath_para_voice[] = { //¶ÔÓ¦ÓÚHpÊä³öͨµÀMic1lp_Mic1lmÊäÈëͨµÀ
156 {"Speaker Switch",{"0"}},
157 {"DAC Playback Volume",{"129","129"}},
158 {"HP Left Switch",{"1"}},
159 {"HP Right Switch",{"1"}},
160 {"HP Driver Playback Volume",{"7","7"}},
161 {"MIC1LM P-Terminal",{"Off"}},
162 {"MIC1RP P-Terminal",{"Off"}},
163 {"MIC1LP P-Terminal",{"FFR 10 Ohm"}},
164 {"MIC1LM M-Terminal",{"FFR 10 Ohm"}},
165 {"ADC Capture Volume",{"24"}},
166 {"voice processing path select",{"handset"}},
167
168};
169static struct audio_para_conf headset_inoutpath_para_voice[] = { //¶ÔÓ¦ÓÚHpÊä³öͨµÀ Mic1rpÊäÈëͨµÀ
170 {"Speaker Switch",{"0"}},
171 {"DAC Playback Volume ",{"129","129"}},
172 {"HP Left Switch",{"1"}},
173 {"HP Right Switch",{"1"}},
174 {"HP Driver Playback Switch",{"7","7"}}, //7dB
175 {"MIC1LM P-Terminal",{"Off"}},
176 {"MIC1RP P-Terminal",{"FFR 10 Ohm"}},
177 {"MIC1LP P-Terminal",{"Off"}},
178 {"MIC1LM M-Terminal",{"Off"}},
179 {"ADC Capture Volume",{"24"}},
180 {"voice processing select",{"headset"}},
181};
182
183static struct audio_para_conf spk_inoutpath_para_voice[] = { //¶ÔÓ¦ÓÚSpkÊä³öͨµÀMic1lp_Mic1lmÊäÈëͨµÀ
184
185 {"DAC Playback Volume",{"124","124"}}, //-1.5dB
186 {"Speaker Switch",{"1"}},
187 {"Speaker Driver Playback Volume",{"2"}}, //18dB
188 {"HP Left Switch",{"0"}},
189 {"HP Right Switch",{"0"}},
190 {"MIC1LM P-Terminal",{"Off"}},
191 {"MIC1RP P-Terminal",{"Off"}},
192 {"MIC1LP P-Terminal",{"FFR 10 Ohm"}},
193 {"MIC1LM M-Terminal",{"FFR 10 Ohm"}},
194 {"ADC Capture Volume",{"24"}},
195 {"voice processing path select",{"speak"}},
196};
197
198static struct audio_para_conf output_dac_vol = {"DAC Playback Volume",{"24"}};
199
200static struct audio_para_conf input_adc_vol = {"ADC Capture Volume",{"129","129"}};
201
202static int sig_close = 1;
203
204static int s_outpath = T_OUTPUT_SPEAKER;
205static int s_inpath = T_INPUT_MICLP;
206static int s_invol = T_INPUT_VOL_0DB;
207static int s_outvol = T_OUTPUT_VOL_0DB;
208
209void stream_close(int sig)
210{
211 /* allow the stream to be closed gracefully */
212 signal(sig, SIG_IGN);
213 sig_close = 0;
214}
215
216static void tinymix_set_value_test(struct mixer *mixer, const char *control,
217 char **values)
218{
219 struct mixer_ctl *ctl;
220 enum mixer_ctl_type type;
221 unsigned int num_ctl_values;
222 unsigned int i;
223
224 if (isdigit(control[0]))
225 ctl = mixer_get_ctl(mixer, atoi(control));
226 else
227 ctl = mixer_get_ctl_by_name(mixer, control);
228
229 if (!ctl) {
230 fprintf(stderr, "Invalid mixer control\n");
231 return;
232 }
233
234 type = mixer_ctl_get_type(ctl);
235 num_ctl_values = mixer_ctl_get_num_values(ctl);
236
237 if (isdigit(values[0][0])) {
238
239 for (i = 0; i < num_ctl_values; i++) {
240 if (mixer_ctl_set_value(ctl, i, atoi(values[i]))) {
241 fprintf(stderr, "Error: invalid value for index %d\n", i);
242 return;
243 }
244 }
245
246 } else {
247 if (type == MIXER_CTL_TYPE_ENUM) {
248 /*if (num_values != 1) {
249 fprintf(stderr, "Enclose strings in quotes and try again\n");
250 return;
251 }*/
252 if (mixer_ctl_set_enum_by_string(ctl, values[0]))
253 fprintf(stderr, "Error: invalid enum value\n");
254 } else {
255 fprintf(stderr, "Error: only enum types can be set with strings\n");
256 }
257 }
258}
259
260int mix_set_outputpath(struct mixer *mixer, int path)
261{
262 int i;
263 for(i = 0;i<ARRAY_SIZE(common_out_para_audio);i++)
264 {
265 tinymix_set_value_test(mixer,common_out_para_audio[i].control,common_out_para_audio[i].values);
266 }
267 switch(path)
268 {
269 case T_OUTPUT_RECEIVER:
270 for(i = 0;i<ARRAY_SIZE(hp_outpath_para_audio);i++)
271 {
272 tinymix_set_value_test(mixer,hp_outpath_para_audio[i].control,hp_outpath_para_audio[i].values);
273 }
274 break;
275 case T_OUTPUT_SPEAKER:
276 for(i = 0;i<ARRAY_SIZE(spk_outpath_para_audio);i++)
277 {
278 tinymix_set_value_test(mixer,spk_outpath_para_audio[i].control,spk_outpath_para_audio[i].values);
279 }
280 break;
281 default:
282 break;
283 }
284
285 return 0;
286}
287
288int mix_set_inputpath(struct mixer *mixer, int path)
289{
290 int i;
291 for(i = 0;i<ARRAY_SIZE(common_in_para_audio);i++)
292 {
293 tinymix_set_value_test(mixer,common_in_para_audio[i].control,common_in_para_audio[i].values);
294 }
295 switch(path)
296 {
297 case T_INPUT_MICLP:
298 for(i = 0;i<ARRAY_SIZE(mic1lp_mc1lm_inpath_para_audio);i++)
299 {
300 tinymix_set_value_test(mixer,mic1lp_mc1lm_inpath_para_audio[i].control,mic1lp_mc1lm_inpath_para_audio[i].values);
301 }
302 break;
303 case T_INPUT_MICRP:
304 for(i = 0;i<ARRAY_SIZE(mic1rp_inpath_para_audio);i++)
305 {
306 tinymix_set_value_test(mixer,mic1rp_inpath_para_audio[i].control,mic1rp_inpath_para_audio[i].values);
307 }
308 break;
309 default:
310 break;
311 }
312
313 return 0;
314}
315
316int mix_set_inputvol(struct mixer *mixer, int volume)
317{
318
319 struct mixer_ctl *ctl = input_adc_vol.control;
320 enum mixer_ctl_type type;
321 unsigned int num_ctl_values;
322 unsigned int i;
323
324 ctl = mixer_get_ctl_by_name(mixer, ctl);
325 if (!ctl) {
326 fprintf(stderr, "Invalid mixer control\n");
327 return;
328 }
329
330// int value = atoi(input_adc_vol.values[0]);
331// mixer_ctl_set_value(ctl, 0, value);
332 mixer_ctl_set_value(ctl, 0, volume);
333
334 return 0;
335}
336
337int mix_set_outputvol(struct mixer *mixer, int volume)
338{
339 struct mixer_ctl *ctl = output_dac_vol.control;
340 enum mixer_ctl_type type;
341 unsigned int i;
342
343 ctl = mixer_get_ctl_by_name(mixer, ctl);
344 if (!ctl) {
345 fprintf(stderr, "Invalid mixer control\n");
346 return;
347 }
348
349 mixer_ctl_set_value(ctl, 0, volume);
350 mixer_ctl_set_value(ctl, 1, volume);
351// int value = atoi(output_dac_vol.values[0]);
352// mixer_ctl_set_value(ctl, 0, value);
353// value = atoi(output_dac_vol.values[1]);
354// mixer_ctl_set_value(ctl, 1, value);
355
356 return 0;
357}
358
359int mix_set_output_mute(struct mixer *mixer, bool enable)
360{
361 struct mixer_ctl *ctl = "DAC Mute";
362 enum mixer_ctl_type type;
363 unsigned int i;
364 printf("mix_set_output_mute %d\n",enable);
365
366 ctl = mixer_get_ctl_by_name(mixer, ctl);
367 if (!ctl) {
368 fprintf(stderr, "Invalid mixer control\n");
369 return;
370 }
371 mixer_ctl_set_value(ctl, 0, enable);
372 mixer_ctl_set_value(ctl, 1, enable);
373}
374
375int mix_set_input_mute(struct mixer *mixer, bool enable)
376{
377 struct mixer_ctl *ctl = "ADC Capture Switch";
378 enum mixer_ctl_type type;
379 unsigned int i;
380
381 ctl = mixer_get_ctl_by_name(mixer, ctl);
382 if (!ctl) {
383 fprintf(stderr, "Invalid mixer control\n");
384 return;
385 }
386
387 mixer_ctl_set_value(ctl, 0, !enable);
388}
389
390int check_param(struct pcm_params *params, unsigned int param, unsigned int value,
391 char *param_name, char *param_unit)
392{
393 unsigned int min;
394 unsigned int max;
395 int is_within_bounds = 1;
396
397 min = pcm_params_get_min(params, param);
398 if (value < min) {
399 fprintf(stderr, "%s is %u%s, device only supports >= %u%s\n", param_name, value,
400 param_unit, min, param_unit);
401 is_within_bounds = 0;
402 }
403
404 max = pcm_params_get_max(params, param);
405 if (value > max) {
406 fprintf(stderr, "%s is %u%s, device only supports <= %u%s\n", param_name, value,
407 param_unit, max, param_unit);
408 is_within_bounds = 0;
409 }
410
411 return is_within_bounds;
412}
413
414int sample_is_playable(unsigned int card, unsigned int device, unsigned int channels,
415 unsigned int rate, unsigned int bits, unsigned int period_size,
416 unsigned int period_count)
417{
418 struct pcm_params *params;
419 int can_play;
420
421 params = pcm_params_get(card, device, PCM_OUT);
422 if (params == NULL) {
423 fprintf(stderr, "Unable to open PCM device %u.\n", device);
424 return 0;
425 }
426
427 can_play = check_param(params, PCM_PARAM_RATE, rate, "Sample rate", "Hz");
428 can_play &= check_param(params, PCM_PARAM_CHANNELS, channels, "Sample", " channels");
429 can_play &= check_param(params, PCM_PARAM_SAMPLE_BITS, bits, "Bitrate", " bits");
430 can_play &= check_param(params, PCM_PARAM_PERIOD_SIZE, period_size, "Period size", "Hz");
431 can_play &= check_param(params, PCM_PARAM_PERIODS, period_count, "Period count", "Hz");
432
433 pcm_params_free(params);
434
435 return can_play;
436}
437
438void play_sample(FILE *file, unsigned int card, unsigned int device, unsigned int channels,
439 unsigned int rate, unsigned int bits, unsigned int period_size,
440 unsigned int period_count)
441{
442 struct pcm_config config;
443 struct pcm *pcm;
444 char *buffer;
445 int size;
446 int num_read;
447
448 memset(&config, 0, sizeof(config));
449 config.channels = channels;
450 config.rate = rate;
451 config.period_size = period_size;
452 config.period_count = period_count;
453 if (bits == 32)
454 config.format = PCM_FORMAT_S32_LE;
455 else if (bits == 16)
456 config.format = PCM_FORMAT_S16_LE;
457 config.start_threshold = 0;
458 config.stop_threshold = 0;
459 config.silence_threshold = 0;
460
461 if (!sample_is_playable(card, device, channels, rate, bits, period_size, period_count)) {
462 return;
463 }
464
465 pcm = pcm_open(card, device, PCM_OUT, &config);
466 if (!pcm || !pcm_is_ready(pcm)) {
467 fprintf(stderr, "Unable to open PCM device %u (%s)\n",
468 device, pcm_get_error(pcm));
469 return;
470 }
471
472 size = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
473 buffer = malloc(size);
474 if (!buffer) {
475 fprintf(stderr, "Unable to allocate %d bytes\n", size);
476 free(buffer);
477 pcm_close(pcm);
478 return;
479 }
480
481 printf("Playing sample: %u ch, %u hz, %u bit\n", channels, rate, bits);
482
483 /* catch ctrl-c to shutdown cleanly */
484 signal(SIGINT, stream_close);
485
486 do {
487 num_read = fread(buffer, 1, size, file);
488 if (num_read > 0) {
489 if (pcm_write(pcm, buffer, num_read)) {
490 fprintf(stderr, "Error playing sample\n");
491 break;
492 }
493 }
494 } while (sig_close && num_read > 0);
495
496 free(buffer);
497 pcm_close(pcm);
498}
499
500unsigned int capture_sample(FILE *file, unsigned int card, unsigned int device,
501 unsigned int channels, unsigned int rate,
502 enum pcm_format format, unsigned int period_size,
503 unsigned int period_count)
504{
505 struct pcm_config config;
506 struct pcm *pcm;
507 char *buffer;
508 unsigned int size;
509 unsigned int bytes_read = 0;
510
511 memset(&config, 0, sizeof(config));
512 config.channels = channels;
513 config.rate = rate;
514 config.period_size = period_size;
515 config.period_count = period_count;
516 config.format = format;
517 config.start_threshold = 0;
518 config.stop_threshold = 0;
519 config.silence_threshold = 0;
520
521 pcm = pcm_open(card, device, PCM_IN, &config);
522 if (!pcm || !pcm_is_ready(pcm)) {
523 fprintf(stderr, "Unable to open PCM device (%s)\n",
524 pcm_get_error(pcm));
525 return 0;
526 }
527
528 size = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
529 buffer = malloc(size);
530 if (!buffer) {
531 fprintf(stderr, "Unable to allocate %d bytes\n", size);
532 free(buffer);
533 pcm_close(pcm);
534 return 0;
535 }
536
537 printf("Capturing sample: %u ch, %u hz, %u bit\n", channels, rate,
538 pcm_format_to_bits(format));
539
540 while (sig_close && !pcm_read(pcm, buffer, size)) {
541 if (fwrite(buffer, 1, size, file) != size) {
542 fprintf(stderr,"Error capturing sample\n");
543 break;
544 }
545 bytes_read += size;
546 if (bytes_read>=300000)
547 break;
548 }
549
550 free(buffer);
551 pcm_close(pcm);
552 return pcm_bytes_to_frames(pcm, bytes_read);
553}
554
555int main(int argc, char **argv)
556{
557 FILE *file;
558 struct wav_header header;
559 unsigned int card = 0;
560 unsigned int device = 0;
561 unsigned int channels = 2;
562 unsigned int rate = 44100;
563 unsigned int bits = 16;
564 unsigned int frames;
565 unsigned int period_size = 1024;
566 unsigned int period_count = 4;
567 enum pcm_format format;
568
569 struct riff_wave_header riff_wave_header;
570 struct chunk_header chunk_header;
571 struct chunk_fmt chunk_fmt;
572 char *filename;
573 int more_chunks = 1;
574 struct mixer *mixer;
575
576
577 if (argc < 2) {
578 fprintf(stderr, "Usage: %s cap file.wav [-D card] [-d device] [-c channels] "
579 "[-r rate] [-b bits] [-p period_size] [-n n_periods]\n", argv[0]);
580 fprintf(stderr, "Usage: %s play file.wav [-D card] [-d device] [-p period_size]"
581 " [-n n_periods] \n", argv[0]);
582 return 1;
583 }
584
585 if (strcmp(argv[1], "play") == 0) {
586 mixer = mixer_open(card);
587 if (!mixer) {
588 fprintf(stderr, "Failed to open mixer\n");
589 return -1;
590 }
591 mix_set_outputpath(mixer, T_OUTPUT_SPEAKER);
592 mix_set_outputvol(mixer, T_OUTPUT_VOL_0DB);
593 // mix_set_output_mute(mixer,false);
594 mixer_close(mixer);
595
596 filename = argv[2];
597 file = fopen(filename, "rb");
598 if (!file) {
599 fprintf(stderr, "Unable to open file '%s'\n", filename);
600 return 1;
601 }
602
603 fread(&riff_wave_header, sizeof(riff_wave_header), 1, file);
604 if ((riff_wave_header.riff_id != ID_RIFF) ||
605 (riff_wave_header.wave_id != ID_WAVE)) {
606 fprintf(stderr, "Error: '%s' is not a riff/wave file\n", filename);
607 fclose(file);
608 return 1;
609 }
610
611 do {
612 fread(&chunk_header, sizeof(chunk_header), 1, file);
613
614 switch (chunk_header.id) {
615 case ID_FMT:
616 fread(&chunk_fmt, sizeof(chunk_fmt), 1, file);
617 /* If the format header is larger, skip the rest */
618 if (chunk_header.sz > sizeof(chunk_fmt))
619 fseek(file, chunk_header.sz - sizeof(chunk_fmt), SEEK_CUR);
620 break;
621 case ID_DATA:
622 /* Stop looking for chunks */
623 more_chunks = 0;
624 break;
625 default:
626 /* Unknown chunk, skip bytes */
627 fseek(file, chunk_header.sz, SEEK_CUR);
628 }
629 } while (more_chunks);
630
631 /* parse command line arguments */
632 argv += 3;
633 while (*argv) {
634 if (strcmp(*argv, "-d") == 0) {
635 argv++;
636 if (*argv)
637 device = atoi(*argv);
638 }
639 if (strcmp(*argv, "-p") == 0) {
640 argv++;
641 if (*argv)
642 period_size = atoi(*argv);
643 }
644 if (strcmp(*argv, "-n") == 0) {
645 argv++;
646 if (*argv)
647 period_count = atoi(*argv);
648 }
649 if (strcmp(*argv, "-D") == 0) {
650 argv++;
651 if (*argv)
652 card = atoi(*argv);
653 }
654 if (*argv)
655 argv++;
656 }
657
658 play_sample(file, card, device, chunk_fmt.num_channels, chunk_fmt.sample_rate,
659 chunk_fmt.bits_per_sample, period_size, period_count);
660
661 fclose(file);
662
663 }else if(strcmp(argv[1], "cap") == 0) {
664 mixer = mixer_open(card);
665 if (!mixer) {
666 fprintf(stderr, "Failed to open mixer\n");
667 return -1;
668 }
669 mix_set_inputpath(mixer, T_INPUT_MICLP);
670 mix_set_inputvol(mixer, T_INPUT_VOL_0DB);
671 mixer_close(mixer);
672
673 file = fopen(argv[2], "wb");
674 if (!file) {
675 fprintf(stderr, "Unable to create file '%s'\n", argv[1]);
676 return 1;
677 }
678
679 /* parse command line arguments */
680 argv += 3;
681 while (*argv) {
682 if (strcmp(*argv, "-d") == 0) {
683 argv++;
684 if (*argv)
685 device = atoi(*argv);
686 } else if (strcmp(*argv, "-c") == 0) {
687 argv++;
688 if (*argv)
689 channels = atoi(*argv);
690 } else if (strcmp(*argv, "-r") == 0) {
691 argv++;
692 if (*argv)
693 rate = atoi(*argv);
694 } else if (strcmp(*argv, "-b") == 0) {
695 argv++;
696 if (*argv)
697 bits = atoi(*argv);
698 } else if (strcmp(*argv, "-D") == 0) {
699 argv++;
700 if (*argv)
701 card = atoi(*argv);
702 } else if (strcmp(*argv, "-p") == 0) {
703 argv++;
704 if (*argv)
705 period_size = atoi(*argv);
706 } else if (strcmp(*argv, "-n") == 0) {
707 argv++;
708 if (*argv)
709 period_count = atoi(*argv);
710 }
711 if (*argv)
712 argv++;
713 }
714
715 header.riff_id = ID_RIFF;
716 header.riff_sz = 0;
717 header.riff_fmt = ID_WAVE;
718 header.fmt_id = ID_FMT;
719 header.fmt_sz = 16;
720 header.audio_format = FORMAT_PCM;
721 header.num_channels = channels;
722 header.sample_rate = rate;
723
724 switch (bits) {
725 case 32:
726 format = PCM_FORMAT_S32_LE;
727 break;
728 case 24:
729 format = PCM_FORMAT_S24_LE;
730 break;
731 case 16:
732 format = PCM_FORMAT_S16_LE;
733 break;
734 default:
735 fprintf(stderr, "%d bits is not supported.\n", bits);
736 return 1;
737 }
738
739 header.bits_per_sample = pcm_format_to_bits(format);
740 header.byte_rate = (header.bits_per_sample / 8) * channels * rate;
741 header.block_align = channels * (header.bits_per_sample / 8);
742 header.data_id = ID_DATA;
743
744 /* leave enough room for header */
745 fseek(file, sizeof(struct wav_header), SEEK_SET);
746
747 /* install signal handler and begin capturing */
748 signal(SIGINT, stream_close);
749 frames = capture_sample(file, card, device, header.num_channels,
750 header.sample_rate, format,
751 period_size, period_count);
752 printf("Captured %d frames\n", frames);
753
754 /* write header now all information is known */
755 header.data_sz = frames * header.block_align;
756 header.riff_sz = header.data_sz + sizeof(header) - 8;
757 fseek(file, 0, SEEK_SET);
758 fwrite(&header, sizeof(struct wav_header), 1, file);
759
760 fclose(file);
761 }
762
763 return 0;
764}