| #include <stdio.h> |
| #include <unistd.h> |
| #include <string.h> |
| //#include <stdlib.h> |
| //#include "pthread.h" |
| //#include "alsatest.h" |
| //#include <stdbool.h> |
| #include <fcntl.h> |
| #include <signal.h> |
| #include <tinyalsa/asoundlib.h> |
| #include <sound/asound.h> |
| #include <stdint.h> |
| #include <stdbool.h> |
| #include <errno.h> |
| #include <stdlib.h> |
| #include <ctype.h> |
| |
| #define ID_RIFF 0x46464952 |
| #define ID_WAVE 0x45564157 |
| #define ID_FMT 0x20746d66 |
| #define ID_DATA 0x61746164 |
| |
| #define FORMAT_PCM 1 |
| #define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0])) |
| |
| |
| struct riff_wave_header { |
| uint32_t riff_id; |
| uint32_t riff_sz; |
| uint32_t wave_id; |
| }; |
| |
| struct chunk_header { |
| uint32_t id; |
| uint32_t sz; |
| }; |
| |
| struct chunk_fmt { |
| uint16_t audio_format; |
| uint16_t num_channels; |
| uint32_t sample_rate; |
| uint32_t byte_rate; |
| uint16_t block_align; |
| uint16_t bits_per_sample; |
| }; |
| struct wav_header { |
| uint32_t riff_id; |
| uint32_t riff_sz; |
| uint32_t riff_fmt; |
| uint32_t fmt_id; |
| uint32_t fmt_sz; |
| uint16_t audio_format; |
| uint16_t num_channels; |
| uint32_t sample_rate; |
| uint32_t byte_rate; |
| uint16_t block_align; |
| uint16_t bits_per_sample; |
| uint32_t data_id; |
| uint32_t data_sz; |
| }; |
| |
| enum t_output_path{ |
| T_OUTPUT_RECEIVER, |
| T_OUTPUT_SPEAKER, |
| T_OUTPUT_HEADSET, |
| }; |
| |
| enum t_input_path{ |
| T_INPUT_MICLP, |
| T_INPUT_MICRP, |
| }; |
| |
| enum t_output_vol{ |
| T_OUTPUT_VOL_MINUS_63_5DB = 0, |
| T_OUTPUT_VOL_MINUS_63DB = 1, |
| T_OUTPUT_VOL_MINUS_6DB = 115, |
| T_OUTPUT_VOL_MINUS_3DB = 121, |
| T_OUTPUT_VOL_MINUS_2DB = 123, |
| T_OUTPUT_VOL_MINUS_1DB = 125, |
| T_OUTPUT_VOL_0DB = 127, |
| T_OUTPUT_VOL_1DB = 129, |
| T_OUTPUT_VOL_2DB = 131, |
| T_OUTPUT_VOL_3DB = 133, |
| T_OUTPUT_VOL_6DB = 139, |
| T_OUTPUT_VOL_9DB = 145, |
| T_OUTPUT_VOL_12DB = 151, |
| T_OUTPUT_VOL_24DB = 175, |
| }; |
| |
| enum t_input_vol{ |
| T_INPUT_VOL_MINUS_12DB = 0, |
| T_INPUT_VOL_MINUS_3DB = 18, |
| T_INPUT_VOL_MINUS_2DB = 20, |
| T_INPUT_VOL_MINUS_1DB = 22, |
| T_INPUT_VOL_0DB = 24, |
| T_INPUT_VOL_1DB = 26, |
| T_INPUT_VOL_2DB = 28, |
| T_INPUT_VOL_3DB = 30, |
| T_INPUT_VOL_20DB = 64, |
| }; |
| |
| struct audio_para_conf { |
| const char *control; |
| char *values[2]; |
| }; |
| |
| static struct audio_para_conf common_out_para_audio[] = { //¶ÔÓ¦¹«¹²²¿·Ö |
| {"Speaker Analog Playback Volume",{"127"}}, |
| {"Speaker Driver Playback Switch",{"1"}}, |
| {"DAC Left Input",{"Mono"}}, |
| {"DAC Right Input",{"Mono"}}, |
| {"Output Right From Right DAC",{"1"}}, |
| {"Output Left From Left DAC",{"1"}}, |
| {"HP Analog Playback Volume",{"127","127"}}, |
| {"HP Driver Playback Switch",{"1","1"}}, //7dB |
| |
| }; |
| |
| static struct audio_para_conf common_in_para_audio[] = { //¶ÔÓ¦¹«¹²²¿·Ö |
| {"Mic PGA Capture Volume",{"40"}}, |
| {"ADC Capture Switch",{"1"}}, |
| {"ADC Fine Capture Volume",{"0"}}, |
| |
| }; |
| |
| static struct audio_para_conf hp_outpath_para_audio[] = { //¶ÔÓ¦ÓÚHpÊä³öͨµÀ |
| {"Speaker Switch",{"0"}}, |
| {"HP Left Switch",{"1"}}, |
| {"HP Right Switch",{"1"}}, |
| {"HP Driver Playback Volume",{"0","0"}}, |
| }; |
| |
| static struct audio_para_conf spk_outpath_para_audio[] = { //¶ÔÓ¦ÓÚSpkÊä³öͨµÀ |
| {"Speaker Switch",{"1"}}, |
| {"Speaker Driver Playback Volume",{"2"}}, //18dB |
| {"HP Left Switch",{"0"}}, |
| {"HP Right Switch",{"0"}}, |
| }; |
| |
| static struct audio_para_conf mic1lp_mc1lm_inpath_para_audio[] = { //¶ÔÓ¦ÓÚMic1lp_Mic1lmÊäÈëͨµÀ |
| {"MIC1LM P-Terminal",{"Off"}}, |
| {"MIC1RP P-Terminal",{"Off"}}, |
| {"MIC1LP P-Terminal",{"FFR 10 Ohm"}}, |
| {"MIC1LM M-Terminal",{"FFR 10 Ohm"}}, |
| |
| }; |
| |
| static struct audio_para_conf mic1rp_inpath_para_audio[] = {//¶ÔÓ¦ÓÚMic1rpÊäÈëͨµÀ |
| {"MIC1LM P-Terminal",{"Off"}}, |
| {"MIC1RP P-Terminal",{"FFR 10 Ohm"}}, |
| {"MIC1LP P-Terminal",{"Off"}}, |
| {"MIC1LM M-Terminal",{"Off"}}, |
| }; |
| |
| static struct audio_para_conf handset_inoutpath_para_voice[] = { //¶ÔÓ¦ÓÚHpÊä³öͨµÀMic1lp_Mic1lmÊäÈëͨµÀ |
| {"Speaker Switch",{"0"}}, |
| {"DAC Playback Volume",{"129","129"}}, |
| {"HP Left Switch",{"1"}}, |
| {"HP Right Switch",{"1"}}, |
| {"HP Driver Playback Volume",{"7","7"}}, |
| {"MIC1LM P-Terminal",{"Off"}}, |
| {"MIC1RP P-Terminal",{"Off"}}, |
| {"MIC1LP P-Terminal",{"FFR 10 Ohm"}}, |
| {"MIC1LM M-Terminal",{"FFR 10 Ohm"}}, |
| {"ADC Capture Volume",{"24"}}, |
| {"voice processing path select",{"handset"}}, |
| |
| }; |
| static struct audio_para_conf headset_inoutpath_para_voice[] = { //¶ÔÓ¦ÓÚHpÊä³öͨµÀ Mic1rpÊäÈëͨµÀ |
| {"Speaker Switch",{"0"}}, |
| {"DAC Playback Volume ",{"129","129"}}, |
| {"HP Left Switch",{"1"}}, |
| {"HP Right Switch",{"1"}}, |
| {"HP Driver Playback Switch",{"7","7"}}, //7dB |
| {"MIC1LM P-Terminal",{"Off"}}, |
| {"MIC1RP P-Terminal",{"FFR 10 Ohm"}}, |
| {"MIC1LP P-Terminal",{"Off"}}, |
| {"MIC1LM M-Terminal",{"Off"}}, |
| {"ADC Capture Volume",{"24"}}, |
| {"voice processing select",{"headset"}}, |
| }; |
| |
| static struct audio_para_conf spk_inoutpath_para_voice[] = { //¶ÔÓ¦ÓÚSpkÊä³öͨµÀMic1lp_Mic1lmÊäÈëͨµÀ |
| |
| {"DAC Playback Volume",{"124","124"}}, //-1.5dB |
| {"Speaker Switch",{"1"}}, |
| {"Speaker Driver Playback Volume",{"2"}}, //18dB |
| {"HP Left Switch",{"0"}}, |
| {"HP Right Switch",{"0"}}, |
| {"MIC1LM P-Terminal",{"Off"}}, |
| {"MIC1RP P-Terminal",{"Off"}}, |
| {"MIC1LP P-Terminal",{"FFR 10 Ohm"}}, |
| {"MIC1LM M-Terminal",{"FFR 10 Ohm"}}, |
| {"ADC Capture Volume",{"24"}}, |
| {"voice processing path select",{"speak"}}, |
| }; |
| |
| static struct audio_para_conf output_dac_vol = {"DAC Playback Volume",{"24"}}; |
| |
| static struct audio_para_conf input_adc_vol = {"ADC Capture Volume",{"129","129"}}; |
| |
| static int sig_close = 1; |
| |
| static int s_outpath = T_OUTPUT_SPEAKER; |
| static int s_inpath = T_INPUT_MICLP; |
| static int s_invol = T_INPUT_VOL_0DB; |
| static int s_outvol = T_OUTPUT_VOL_0DB; |
| |
| void stream_close(int sig) |
| { |
| /* allow the stream to be closed gracefully */ |
| signal(sig, SIG_IGN); |
| sig_close = 0; |
| } |
| |
| static void tinymix_set_value_test(struct mixer *mixer, const char *control, |
| char **values) |
| { |
| struct mixer_ctl *ctl; |
| enum mixer_ctl_type type; |
| unsigned int num_ctl_values; |
| unsigned int i; |
| |
| if (isdigit(control[0])) |
| ctl = mixer_get_ctl(mixer, atoi(control)); |
| else |
| ctl = mixer_get_ctl_by_name(mixer, control); |
| |
| if (!ctl) { |
| fprintf(stderr, "Invalid mixer control\n"); |
| return; |
| } |
| |
| type = mixer_ctl_get_type(ctl); |
| num_ctl_values = mixer_ctl_get_num_values(ctl); |
| |
| if (isdigit(values[0][0])) { |
| |
| for (i = 0; i < num_ctl_values; i++) { |
| if (mixer_ctl_set_value(ctl, i, atoi(values[i]))) { |
| fprintf(stderr, "Error: invalid value for index %d\n", i); |
| return; |
| } |
| } |
| |
| } else { |
| if (type == MIXER_CTL_TYPE_ENUM) { |
| /*if (num_values != 1) { |
| fprintf(stderr, "Enclose strings in quotes and try again\n"); |
| return; |
| }*/ |
| if (mixer_ctl_set_enum_by_string(ctl, values[0])) |
| fprintf(stderr, "Error: invalid enum value\n"); |
| } else { |
| fprintf(stderr, "Error: only enum types can be set with strings\n"); |
| } |
| } |
| } |
| |
| int mix_set_outputpath(struct mixer *mixer, int path) |
| { |
| int i; |
| for(i = 0;i<ARRAY_SIZE(common_out_para_audio);i++) |
| { |
| tinymix_set_value_test(mixer,common_out_para_audio[i].control,common_out_para_audio[i].values); |
| } |
| switch(path) |
| { |
| case T_OUTPUT_RECEIVER: |
| for(i = 0;i<ARRAY_SIZE(hp_outpath_para_audio);i++) |
| { |
| tinymix_set_value_test(mixer,hp_outpath_para_audio[i].control,hp_outpath_para_audio[i].values); |
| } |
| break; |
| case T_OUTPUT_SPEAKER: |
| for(i = 0;i<ARRAY_SIZE(spk_outpath_para_audio);i++) |
| { |
| tinymix_set_value_test(mixer,spk_outpath_para_audio[i].control,spk_outpath_para_audio[i].values); |
| } |
| break; |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| int mix_set_inputpath(struct mixer *mixer, int path) |
| { |
| int i; |
| for(i = 0;i<ARRAY_SIZE(common_in_para_audio);i++) |
| { |
| tinymix_set_value_test(mixer,common_in_para_audio[i].control,common_in_para_audio[i].values); |
| } |
| switch(path) |
| { |
| case T_INPUT_MICLP: |
| for(i = 0;i<ARRAY_SIZE(mic1lp_mc1lm_inpath_para_audio);i++) |
| { |
| tinymix_set_value_test(mixer,mic1lp_mc1lm_inpath_para_audio[i].control,mic1lp_mc1lm_inpath_para_audio[i].values); |
| } |
| break; |
| case T_INPUT_MICRP: |
| for(i = 0;i<ARRAY_SIZE(mic1rp_inpath_para_audio);i++) |
| { |
| tinymix_set_value_test(mixer,mic1rp_inpath_para_audio[i].control,mic1rp_inpath_para_audio[i].values); |
| } |
| break; |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| int mix_set_inputvol(struct mixer *mixer, int volume) |
| { |
| |
| struct mixer_ctl *ctl = input_adc_vol.control; |
| enum mixer_ctl_type type; |
| unsigned int num_ctl_values; |
| unsigned int i; |
| |
| ctl = mixer_get_ctl_by_name(mixer, ctl); |
| if (!ctl) { |
| fprintf(stderr, "Invalid mixer control\n"); |
| return; |
| } |
| |
| // int value = atoi(input_adc_vol.values[0]); |
| // mixer_ctl_set_value(ctl, 0, value); |
| mixer_ctl_set_value(ctl, 0, volume); |
| |
| return 0; |
| } |
| |
| int mix_set_outputvol(struct mixer *mixer, int volume) |
| { |
| struct mixer_ctl *ctl = output_dac_vol.control; |
| enum mixer_ctl_type type; |
| unsigned int i; |
| |
| ctl = mixer_get_ctl_by_name(mixer, ctl); |
| if (!ctl) { |
| fprintf(stderr, "Invalid mixer control\n"); |
| return; |
| } |
| |
| mixer_ctl_set_value(ctl, 0, volume); |
| mixer_ctl_set_value(ctl, 1, volume); |
| // int value = atoi(output_dac_vol.values[0]); |
| // mixer_ctl_set_value(ctl, 0, value); |
| // value = atoi(output_dac_vol.values[1]); |
| // mixer_ctl_set_value(ctl, 1, value); |
| |
| return 0; |
| } |
| |
| int mix_set_output_mute(struct mixer *mixer, bool enable) |
| { |
| struct mixer_ctl *ctl = "DAC Mute"; |
| enum mixer_ctl_type type; |
| unsigned int i; |
| printf("mix_set_output_mute %d\n",enable); |
| |
| ctl = mixer_get_ctl_by_name(mixer, ctl); |
| if (!ctl) { |
| fprintf(stderr, "Invalid mixer control\n"); |
| return; |
| } |
| mixer_ctl_set_value(ctl, 0, enable); |
| mixer_ctl_set_value(ctl, 1, enable); |
| } |
| |
| int mix_set_input_mute(struct mixer *mixer, bool enable) |
| { |
| struct mixer_ctl *ctl = "ADC Capture Switch"; |
| enum mixer_ctl_type type; |
| unsigned int i; |
| |
| ctl = mixer_get_ctl_by_name(mixer, ctl); |
| if (!ctl) { |
| fprintf(stderr, "Invalid mixer control\n"); |
| return; |
| } |
| |
| mixer_ctl_set_value(ctl, 0, !enable); |
| } |
| |
| int check_param(struct pcm_params *params, unsigned int param, unsigned int value, |
| char *param_name, char *param_unit) |
| { |
| unsigned int min; |
| unsigned int max; |
| int is_within_bounds = 1; |
| |
| min = pcm_params_get_min(params, param); |
| if (value < min) { |
| fprintf(stderr, "%s is %u%s, device only supports >= %u%s\n", param_name, value, |
| param_unit, min, param_unit); |
| is_within_bounds = 0; |
| } |
| |
| max = pcm_params_get_max(params, param); |
| if (value > max) { |
| fprintf(stderr, "%s is %u%s, device only supports <= %u%s\n", param_name, value, |
| param_unit, max, param_unit); |
| is_within_bounds = 0; |
| } |
| |
| return is_within_bounds; |
| } |
| |
| int sample_is_playable(unsigned int card, unsigned int device, unsigned int channels, |
| unsigned int rate, unsigned int bits, unsigned int period_size, |
| unsigned int period_count) |
| { |
| struct pcm_params *params; |
| int can_play; |
| |
| params = pcm_params_get(card, device, PCM_OUT); |
| if (params == NULL) { |
| fprintf(stderr, "Unable to open PCM device %u.\n", device); |
| return 0; |
| } |
| |
| can_play = check_param(params, PCM_PARAM_RATE, rate, "Sample rate", "Hz"); |
| can_play &= check_param(params, PCM_PARAM_CHANNELS, channels, "Sample", " channels"); |
| can_play &= check_param(params, PCM_PARAM_SAMPLE_BITS, bits, "Bitrate", " bits"); |
| can_play &= check_param(params, PCM_PARAM_PERIOD_SIZE, period_size, "Period size", "Hz"); |
| can_play &= check_param(params, PCM_PARAM_PERIODS, period_count, "Period count", "Hz"); |
| |
| pcm_params_free(params); |
| |
| return can_play; |
| } |
| |
| void play_sample(FILE *file, unsigned int card, unsigned int device, unsigned int channels, |
| unsigned int rate, unsigned int bits, unsigned int period_size, |
| unsigned int period_count) |
| { |
| struct pcm_config config; |
| struct pcm *pcm; |
| char *buffer; |
| int size; |
| int num_read; |
| |
| memset(&config, 0, sizeof(config)); |
| config.channels = channels; |
| config.rate = rate; |
| config.period_size = period_size; |
| config.period_count = period_count; |
| if (bits == 32) |
| config.format = PCM_FORMAT_S32_LE; |
| else if (bits == 16) |
| config.format = PCM_FORMAT_S16_LE; |
| config.start_threshold = 0; |
| config.stop_threshold = 0; |
| config.silence_threshold = 0; |
| |
| if (!sample_is_playable(card, device, channels, rate, bits, period_size, period_count)) { |
| return; |
| } |
| |
| pcm = pcm_open(card, device, PCM_OUT, &config); |
| if (!pcm || !pcm_is_ready(pcm)) { |
| fprintf(stderr, "Unable to open PCM device %u (%s)\n", |
| device, pcm_get_error(pcm)); |
| return; |
| } |
| |
| size = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm)); |
| buffer = malloc(size); |
| if (!buffer) { |
| fprintf(stderr, "Unable to allocate %d bytes\n", size); |
| free(buffer); |
| pcm_close(pcm); |
| return; |
| } |
| |
| printf("Playing sample: %u ch, %u hz, %u bit\n", channels, rate, bits); |
| |
| /* catch ctrl-c to shutdown cleanly */ |
| signal(SIGINT, stream_close); |
| |
| do { |
| num_read = fread(buffer, 1, size, file); |
| if (num_read > 0) { |
| if (pcm_write(pcm, buffer, num_read)) { |
| fprintf(stderr, "Error playing sample\n"); |
| break; |
| } |
| } |
| } while (sig_close && num_read > 0); |
| |
| free(buffer); |
| pcm_close(pcm); |
| } |
| |
| unsigned int capture_sample(FILE *file, unsigned int card, unsigned int device, |
| unsigned int channels, unsigned int rate, |
| enum pcm_format format, unsigned int period_size, |
| unsigned int period_count) |
| { |
| struct pcm_config config; |
| struct pcm *pcm; |
| char *buffer; |
| unsigned int size; |
| unsigned int bytes_read = 0; |
| |
| memset(&config, 0, sizeof(config)); |
| config.channels = channels; |
| config.rate = rate; |
| config.period_size = period_size; |
| config.period_count = period_count; |
| config.format = format; |
| config.start_threshold = 0; |
| config.stop_threshold = 0; |
| config.silence_threshold = 0; |
| |
| pcm = pcm_open(card, device, PCM_IN, &config); |
| if (!pcm || !pcm_is_ready(pcm)) { |
| fprintf(stderr, "Unable to open PCM device (%s)\n", |
| pcm_get_error(pcm)); |
| return 0; |
| } |
| |
| size = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm)); |
| buffer = malloc(size); |
| if (!buffer) { |
| fprintf(stderr, "Unable to allocate %d bytes\n", size); |
| free(buffer); |
| pcm_close(pcm); |
| return 0; |
| } |
| |
| printf("Capturing sample: %u ch, %u hz, %u bit\n", channels, rate, |
| pcm_format_to_bits(format)); |
| |
| while (sig_close && !pcm_read(pcm, buffer, size)) { |
| if (fwrite(buffer, 1, size, file) != size) { |
| fprintf(stderr,"Error capturing sample\n"); |
| break; |
| } |
| bytes_read += size; |
| if (bytes_read>=300000) |
| break; |
| } |
| |
| free(buffer); |
| pcm_close(pcm); |
| return pcm_bytes_to_frames(pcm, bytes_read); |
| } |
| |
| int main(int argc, char **argv) |
| { |
| FILE *file; |
| struct wav_header header; |
| unsigned int card = 0; |
| unsigned int device = 0; |
| unsigned int channels = 2; |
| unsigned int rate = 44100; |
| unsigned int bits = 16; |
| unsigned int frames; |
| unsigned int period_size = 1024; |
| unsigned int period_count = 4; |
| enum pcm_format format; |
| |
| struct riff_wave_header riff_wave_header; |
| struct chunk_header chunk_header; |
| struct chunk_fmt chunk_fmt; |
| char *filename; |
| int more_chunks = 1; |
| struct mixer *mixer; |
| |
| |
| if (argc < 2) { |
| fprintf(stderr, "Usage: %s cap file.wav [-D card] [-d device] [-c channels] " |
| "[-r rate] [-b bits] [-p period_size] [-n n_periods]\n", argv[0]); |
| fprintf(stderr, "Usage: %s play file.wav [-D card] [-d device] [-p period_size]" |
| " [-n n_periods] \n", argv[0]); |
| return 1; |
| } |
| |
| if (strcmp(argv[1], "play") == 0) { |
| mixer = mixer_open(card); |
| if (!mixer) { |
| fprintf(stderr, "Failed to open mixer\n"); |
| return -1; |
| } |
| mix_set_outputpath(mixer, T_OUTPUT_SPEAKER); |
| mix_set_outputvol(mixer, T_OUTPUT_VOL_0DB); |
| // mix_set_output_mute(mixer,false); |
| mixer_close(mixer); |
| |
| filename = argv[2]; |
| file = fopen(filename, "rb"); |
| if (!file) { |
| fprintf(stderr, "Unable to open file '%s'\n", filename); |
| return 1; |
| } |
| |
| fread(&riff_wave_header, sizeof(riff_wave_header), 1, file); |
| if ((riff_wave_header.riff_id != ID_RIFF) || |
| (riff_wave_header.wave_id != ID_WAVE)) { |
| fprintf(stderr, "Error: '%s' is not a riff/wave file\n", filename); |
| fclose(file); |
| return 1; |
| } |
| |
| do { |
| fread(&chunk_header, sizeof(chunk_header), 1, file); |
| |
| switch (chunk_header.id) { |
| case ID_FMT: |
| fread(&chunk_fmt, sizeof(chunk_fmt), 1, file); |
| /* If the format header is larger, skip the rest */ |
| if (chunk_header.sz > sizeof(chunk_fmt)) |
| fseek(file, chunk_header.sz - sizeof(chunk_fmt), SEEK_CUR); |
| break; |
| case ID_DATA: |
| /* Stop looking for chunks */ |
| more_chunks = 0; |
| break; |
| default: |
| /* Unknown chunk, skip bytes */ |
| fseek(file, chunk_header.sz, SEEK_CUR); |
| } |
| } while (more_chunks); |
| |
| /* parse command line arguments */ |
| argv += 3; |
| while (*argv) { |
| if (strcmp(*argv, "-d") == 0) { |
| argv++; |
| if (*argv) |
| device = atoi(*argv); |
| } |
| if (strcmp(*argv, "-p") == 0) { |
| argv++; |
| if (*argv) |
| period_size = atoi(*argv); |
| } |
| if (strcmp(*argv, "-n") == 0) { |
| argv++; |
| if (*argv) |
| period_count = atoi(*argv); |
| } |
| if (strcmp(*argv, "-D") == 0) { |
| argv++; |
| if (*argv) |
| card = atoi(*argv); |
| } |
| if (*argv) |
| argv++; |
| } |
| |
| play_sample(file, card, device, chunk_fmt.num_channels, chunk_fmt.sample_rate, |
| chunk_fmt.bits_per_sample, period_size, period_count); |
| |
| fclose(file); |
| |
| }else if(strcmp(argv[1], "cap") == 0) { |
| mixer = mixer_open(card); |
| if (!mixer) { |
| fprintf(stderr, "Failed to open mixer\n"); |
| return -1; |
| } |
| mix_set_inputpath(mixer, T_INPUT_MICLP); |
| mix_set_inputvol(mixer, T_INPUT_VOL_0DB); |
| mixer_close(mixer); |
| |
| file = fopen(argv[2], "wb"); |
| if (!file) { |
| fprintf(stderr, "Unable to create file '%s'\n", argv[1]); |
| return 1; |
| } |
| |
| /* parse command line arguments */ |
| argv += 3; |
| while (*argv) { |
| if (strcmp(*argv, "-d") == 0) { |
| argv++; |
| if (*argv) |
| device = atoi(*argv); |
| } else if (strcmp(*argv, "-c") == 0) { |
| argv++; |
| if (*argv) |
| channels = atoi(*argv); |
| } else if (strcmp(*argv, "-r") == 0) { |
| argv++; |
| if (*argv) |
| rate = atoi(*argv); |
| } else if (strcmp(*argv, "-b") == 0) { |
| argv++; |
| if (*argv) |
| bits = atoi(*argv); |
| } else if (strcmp(*argv, "-D") == 0) { |
| argv++; |
| if (*argv) |
| card = atoi(*argv); |
| } else if (strcmp(*argv, "-p") == 0) { |
| argv++; |
| if (*argv) |
| period_size = atoi(*argv); |
| } else if (strcmp(*argv, "-n") == 0) { |
| argv++; |
| if (*argv) |
| period_count = atoi(*argv); |
| } |
| if (*argv) |
| argv++; |
| } |
| |
| header.riff_id = ID_RIFF; |
| header.riff_sz = 0; |
| header.riff_fmt = ID_WAVE; |
| header.fmt_id = ID_FMT; |
| header.fmt_sz = 16; |
| header.audio_format = FORMAT_PCM; |
| header.num_channels = channels; |
| header.sample_rate = rate; |
| |
| switch (bits) { |
| case 32: |
| format = PCM_FORMAT_S32_LE; |
| break; |
| case 24: |
| format = PCM_FORMAT_S24_LE; |
| break; |
| case 16: |
| format = PCM_FORMAT_S16_LE; |
| break; |
| default: |
| fprintf(stderr, "%d bits is not supported.\n", bits); |
| return 1; |
| } |
| |
| header.bits_per_sample = pcm_format_to_bits(format); |
| header.byte_rate = (header.bits_per_sample / 8) * channels * rate; |
| header.block_align = channels * (header.bits_per_sample / 8); |
| header.data_id = ID_DATA; |
| |
| /* leave enough room for header */ |
| fseek(file, sizeof(struct wav_header), SEEK_SET); |
| |
| /* install signal handler and begin capturing */ |
| signal(SIGINT, stream_close); |
| frames = capture_sample(file, card, device, header.num_channels, |
| header.sample_rate, format, |
| period_size, period_count); |
| printf("Captured %d frames\n", frames); |
| |
| /* write header now all information is known */ |
| header.data_sz = frames * header.block_align; |
| header.riff_sz = header.data_sz + sizeof(header) - 8; |
| fseek(file, 0, SEEK_SET); |
| fwrite(&header, sizeof(struct wav_header), 1, file); |
| |
| fclose(file); |
| } |
| |
| return 0; |
| } |