diff --git a/Makefile b/Makefile index ad67b61..3cbb608 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ DEPFLAGS = -MMD WARNFLAGS = -Wall CFLAGS += ${DEPFLAGS} ${WARNFLAGS} -LDLIBS = -lespeak -lpthread +LDLIBS = -lespeak -lpthread -lasound -lm INSTALL = install BINMODE = 0755 diff --git a/cli.c b/cli.c index 31db4b0..b8a6543 100644 --- a/cli.c +++ b/cli.c @@ -30,11 +30,15 @@ extern char *pidPath; /* default voice */ extern char *defaultVoice; +/* Whether to drive ALSA volume */ +extern int alsaVolume; + /* command line options */ const char *shortOptions = "P:V:adhv"; const struct option longOptions[] = { {"pid-path", required_argument, NULL, 'P'}, {"default-voice", required_argument, NULL, 'V'}, + {"alsa-volume", no_argument, &alsaVolume, 1}, {"acsint", no_argument, NULL, 'a'}, {"debug", no_argument, NULL, 'd'}, {"help", no_argument, NULL, 'h'}, @@ -48,6 +52,7 @@ static void show_help() printf("Options are as follows:\n"); printf(" --pid-path=path, -P path\t\tSet path for pid file.\n"); printf(" --default-voice=voice, -V voice\tSet default voice.\n"); + printf(" --alsa-volume\t\t\t\tDrive the ALSA volume.\n"); printf(" --debug, -d\t\t\t\tDebug mode (stay in the foreground).\n"); printf(" --help, -h\t\t\t\tShow this help.\n"); printf(" --version, -v\t\t\t\tDisplay the software version.\n"); @@ -93,6 +98,7 @@ void process_cli(int argc, char **argv) show_version(); break; case -1: + case 0: break; default: show_help(); diff --git a/espeak.c b/espeak.c index 0ce680f..f9998fc 100644 --- a/espeak.c +++ b/espeak.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include "espeakup.h" @@ -32,6 +34,7 @@ const int defaultRange = 5; const int defaultRate = 2; const int defaultVolume = 5; char *defaultVoice = NULL; +int alsaVolume = 0; /* multipliers and offsets */ const int frequencyMultiplier = 11; @@ -152,6 +155,86 @@ static espeak_ERROR set_voice(struct synth_t *s, char *voice) return rc; } +static void set_alsa_volume(int vol) +{ + snd_mixer_t *m; + snd_mixer_elem_t *e; + int err; + + err = snd_mixer_open(&m, 0); + if (err < 0) + { + fprintf(stderr, "ALSA mixer open error: %s\n", snd_strerror(err)); + return; + } + + err = snd_mixer_attach(m, "default"); + if (err < 0) + { + fprintf(stderr, "ALSA mixer attach error: %s\n", snd_strerror(err)); + return; + } + err = snd_mixer_selem_register(m, NULL, NULL); + if (err < 0) + { + fprintf(stderr, "ALSA mixer load error: %s\n", snd_strerror(err)); + return; + } + err = snd_mixer_load(m); + if (err < 0) + { + fprintf(stderr, "ALSA mixer load error: %s\n", snd_strerror(err)); + return; + } + + /* Turn vol value to volume %. + * We do not want to soften that much with ALSA, espeak is already + * doing it. We want the default value (5) to be the usual default + * volume (80%), and make higher values increase ALSA volume, up to + * 100%. */ + + int volume = (vol+1) * 50 / 10 + 50; + + for (e = snd_mixer_first_elem(m); e; e = snd_mixer_elem_next(e)) + { + if (snd_mixer_elem_get_type(e) != SND_MIXER_ELEM_SIMPLE) + continue; + if (snd_mixer_selem_is_enumerated(e)) + continue; + + if (snd_mixer_selem_has_playback_switch(e)) { + snd_mixer_selem_set_playback_switch_all(e, 1); + } + + if (snd_mixer_selem_has_playback_volume(e)) { + long min, max, set; + + err = snd_mixer_selem_get_playback_dB_range(e, &min, &max); + if (err == 0 && min < max) { + if (max - min < 2400) { + /* 24dB amplitude is too small for using a logscale */ + set = min + volume * (max-min) / 100; + } else { + /* Use a logscale */ + double volf = volume / 100.; + if (min != SND_CTL_TLV_DB_GAIN_MUTE) + { + double minf = pow(10, (min-max) / 6000.); + volf = volf * (1 - minf) + minf; + } + set = 6000. * log10(volf) + max; + } + snd_mixer_selem_set_playback_dB_all(e, set, 0); + } else { + /* No dB setting, try a linear scale */ + snd_mixer_selem_get_playback_volume_range(e, &min, &max); + set = min + volume * (max-min) / 100; + snd_mixer_selem_set_playback_volume_all(e, set); + } + } + } +} + static espeak_ERROR set_volume(struct synth_t *s, int vol, enum adjust_t adj) { @@ -164,7 +247,11 @@ static espeak_ERROR set_volume(struct synth_t *s, int vol, rc = espeak_SetParameter(espeakVOLUME, (vol + 1) * volumeMultiplier, 0); if (rc == EE_OK) + { s->volume = vol; + if (alsaVolume) + set_alsa_volume(vol); + } return rc; }