|  | #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; | 
|  | } |