blob: e599fffb056b25f08f2e1b4a99a2055ec8bab4ca [file] [log] [blame]
#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;
}