'reclev', 'igain', 'ogain'. However, not all listed channels might be
supported on any particular hardware. The default channel is 'volume'.
[/help]
+ [option fade-exponent]
+ summary = set non-linear time scale for fading
+ arg_info = required_arg
+ arg_type = uint32
+ typestr = value
+ default_val = 0
+ [help]
+ This option affects the fade, snooze and sleep subcommands. It is
+ ignored in set mode.
+
+ The argument must be a number between 0 and 100. The default value
+ 0 corresponds to linear scaling. That is, the value of the mixer
+ channel is increased or decreased in fixed time intervals until the
+ destination value is reached. Exponents between 1 and 99 cause low
+ channel values to be increased more quickly than high channel values.
+ Large exponents cause greater differences. The special value 100 sets
+ the destination value immediately. The command then sleeps for the
+ configured fade time.
+ [/help]
[subcommand help]
purpose = print subcommand help
non-opts-name = [<subcommand>]
#include <regex.h>
#include <lopsub.h>
+#include <math.h>
#include "mixer.lsg.h"
#include "para.h"
return m->set_channel(h, channel);
}
+static void millisleep(int ms)
+{
+ struct timespec ts;
+
+ PARA_INFO_LOG("sleeping %dms\n", ms);
+ if (ms < 0)
+ return;
+ ts.tv_sec = ms / 1000,
+ ts.tv_nsec = (ms % 1000) * 1000 * 1000;
+ nanosleep(&ts, NULL);
+}
+
+/*
+ * This implements the inverse function of t -> t^alpha, scaled to the time
+ * interval [0,T] and the range given by old_vol and new_vol. It returns the
+ * amount of milliseconds until the given volume is reached.
+ */
+static unsigned volume_time(double vol, double old_vol, double new_vol,
+ double T, double alpha)
+{
+ double c, d, x;
+
+ if (old_vol < new_vol) {
+ c = old_vol;
+ d = new_vol;
+ } else {
+ c = new_vol;
+ d = old_vol;
+ }
+
+ x = T * exp(log(((vol - c) / (d - c))) / alpha);
+ assert(x <= T);
+ if (old_vol < new_vol)
+ return x;
+ else
+ return T - x;
+}
+
/* Fade to new volume in fade_time seconds. */
static int fade(struct mixer *m, struct mixer_handle *h, uint32_t new_vol,
uint32_t fade_time)
{
- int vol, diff, incr, ret;
- unsigned secs;
- struct timespec ts;
- unsigned long long tmp, tmp2; /* Careful with that axe, Eugene! */
+ int i, T, old_vol, ret, slept, incr;
+ double ms, alpha;
+ uint32_t fe = OPT_UINT32_VAL(PARA_MIXER, FADE_EXPONENT);
- if (fade_time <= 0)
- return m->set(h, new_vol);
- secs = fade_time;
+ if (fade_time <= 0 || fe >= 100) {
+ ret = m->set(h, new_vol);
+ if (ret < 0)
+ return ret;
+ goto sleep;
+ }
+ alpha = (100 - fe) / 100.0;
ret = m->get(h);
if (ret < 0)
- goto out;
- vol = ret;
+ return ret;
+ old_vol = ret;
+ if (old_vol == new_vol)
+ goto sleep;
PARA_NOTICE_LOG("fading %s from %d to %u in %u seconds\n",
- OPT_STRING_VAL(PARA_MIXER, MIXER_CHANNEL), vol, new_vol, secs);
- diff = new_vol - vol;
- if (!diff) {
- sleep(secs);
- goto out;
- }
- incr = diff > 0? 1: -1;
- diff = diff > 0? diff: -diff;
- tmp = secs * 1000 / diff;
- tmp2 = tmp % 1000;
- while ((new_vol - vol) * incr > 0) {
- ts.tv_nsec = tmp2 * 1000000; /* really nec ?*/
- ts.tv_sec = tmp / 1000; /* really nec ?*/
- //printf("ts.tv_sec: %i\n", ts.tv_nsec);
- vol += incr;
- ret = m->set(h, vol);
+ OPT_STRING_VAL(PARA_MIXER, MIXER_CHANNEL), old_vol,
+ new_vol, fade_time);
+ incr = old_vol < new_vol? 1 : -1;
+ T = fade_time * 1000;
+ i = old_vol;
+ slept = 0;
+ do {
+ ms = volume_time(i + incr, old_vol, new_vol, T, alpha);
+ millisleep(ms - slept);
+ slept = ms;
+ i += incr;
+ ret = m->set(h, i);
if (ret < 0)
- goto out;
- //printf("vol = %i\n", vol);
- nanosleep(&ts, NULL);
- }
-out:
+ return ret;
+ } while (i != new_vol);
+ return 1;
+sleep:
+ sleep(fade_time);
return ret;
}
+
static int com_fade(struct mixer *m, struct mixer_handle *h)
{
uint32_t new_vol = OPT_UINT32_VAL(FADE, FADE_VOL);