1 /* Copyright (C) 1998 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
3 /** \file mixer.c A volume fader and alarm clock. */
18 /** Array of error strings. */
21 /* At least one of the two is defined if this file gets compiled. */
22 static const struct mixer
*mixers
[] = {
31 #define NUM_SUPPORTED_MIXERS (ARRAY_SIZE(mixers))
32 #define FOR_EACH_MIXER(i) for ((i) = 0; (i) < NUM_SUPPORTED_MIXERS; (i)++)
34 static struct lls_parse_result
*lpr
, *sub_lpr
;
36 #define CMD_PTR(_cmd) (lls_cmd(LSG_MIXER_CMD_ ## _cmd, mixer_suite))
37 #define OPT_RESULT(_cmd, _opt) (lls_opt_result( \
38 LSG_MIXER_ ## _cmd ## _OPT_ ## _opt, (LSG_MIXER_CMD_ ## _cmd == 0)? lpr : sub_lpr))
39 #define OPT_GIVEN(_cmd, _opt) (lls_opt_given(OPT_RESULT(_cmd, _opt)))
40 #define OPT_STRING_VAL(_cmd, _opt) (lls_string_val(0, OPT_RESULT(_cmd, _opt)))
41 #define OPT_UINT32_VAL(_cmd, _opt) (lls_uint32_val(0, OPT_RESULT(_cmd, _opt)))
43 typedef int (*mixer_subcommand_handler_t
)(const struct mixer
*);
45 #define EXPORT_CMD(_cmd) const mixer_subcommand_handler_t \
46 lsg_mixer_com_ ## _cmd ## _user_data = &com_ ## _cmd;
49 static __printf_2_3
void date_log(int ll
, const char *fmt
, ...)
59 fprintf(stderr
, "%d:%02d:%02d ", tm
->tm_hour
, tm
->tm_min
, tm
->tm_sec
);
64 __printf_2_3
void (*para_log
)(int, const char*, ...) = date_log
;
66 static int set_channel(const struct mixer
*m
, struct mixer_handle
*h
,
70 PARA_NOTICE_LOG("using %s mixer channel\n", channel
?
72 return m
->set_channel(h
, channel
);
75 static void millisleep(int ms
)
79 PARA_INFO_LOG("sleeping %dms\n", ms
);
82 ts
.tv_sec
= ms
/ 1000,
83 ts
.tv_nsec
= (ms
% 1000) * 1000 * 1000;
88 * This implements the inverse function of t -> t^alpha, scaled to the time
89 * interval [0,T] and the range given by old_vol and new_vol. It returns the
90 * amount of milliseconds until the given volume is reached.
92 static unsigned volume_time(double vol
, double old_vol
, double new_vol
,
93 double T
, double alpha
)
97 if (old_vol
< new_vol
) {
105 x
= T
* exp(log(((vol
- c
) / (d
- c
))) / alpha
);
107 if (old_vol
< new_vol
)
113 /* Fade to new volume in fade_time seconds. */
114 static int fade(const struct mixer
*m
, struct mixer_handle
*h
, uint32_t new_vol
,
117 int i
, T
, old_vol
, ret
, slept
, incr
;
119 uint32_t fe
= OPT_UINT32_VAL(PARA_MIXER
, FADE_EXPONENT
);
121 if (fade_time
<= 0 || fe
>= 100) {
122 ret
= m
->set(h
, new_vol
);
127 alpha
= (100 - fe
) / 100.0;
132 if (old_vol
== new_vol
)
134 PARA_NOTICE_LOG("fading %s from %d to %u in %u seconds\n",
135 OPT_STRING_VAL(PARA_MIXER
, MIXER_CHANNEL
), old_vol
,
137 incr
= old_vol
< new_vol
? 1 : -1;
138 T
= fade_time
* 1000;
142 ms
= volume_time(i
+ incr
, old_vol
, new_vol
, T
, alpha
);
143 millisleep(ms
- slept
);
149 } while (i
!= new_vol
);
156 static int open_mixer_and_set_channel(const struct mixer
*m
, struct mixer_handle
**h
)
160 ret
= m
->open(OPT_STRING_VAL(PARA_MIXER
, MIXER_DEVICE
), h
);
163 ret
= set_channel(m
, *h
, OPT_STRING_VAL(PARA_MIXER
, MIXER_CHANNEL
));
164 if (ret
== -E_BAD_CHANNEL
) {
165 char *channels
= m
->get_channels(*h
);
166 printf("Available channels: %s\n", channels
);
174 static int com_fade(const struct mixer
*m
)
176 uint32_t new_vol
= OPT_UINT32_VAL(FADE
, FADE_VOL
);
177 uint32_t fade_time
= OPT_UINT32_VAL(FADE
, FADE_TIME
);
178 struct mixer_handle
*h
;
181 ret
= open_mixer_and_set_channel(m
, &h
);
184 ret
= fade(m
, h
, new_vol
, fade_time
);
190 static void client_cmd(const char *cmd
)
192 int ret
, status
, fds
[3] = {0, 0, 0};
194 char *cmdline
= make_message(BINDIR
"/para_client %s", cmd
);
196 PARA_NOTICE_LOG("%s\n", cmdline
);
197 ret
= para_exec_cmdline_pid(&pid
, cmdline
, fds
);
200 PARA_ERROR_LOG("%s\n", para_strerror(-ret
));
204 pid
= waitpid(pid
, &status
, 0);
205 while (pid
== -1 && errno
== EINTR
);
207 PARA_ERROR_LOG("%s\n", strerror(errno
));
210 if (!WIFEXITED(status
) || WEXITSTATUS(status
) != 0)
214 PARA_EMERG_LOG("command \"%s\" failed\n", cmd
);
218 static void change_afs_mode(const char *afs_mode
)
222 cmd
= make_message("select %s", afs_mode
);
227 static int set_initial_volume(const struct mixer
*m
, struct mixer_handle
*h
)
231 for (i
= 0; i
< OPT_GIVEN(SLEEP
, IVOL
); i
++) {
232 const char *val
= lls_string_val(i
, OPT_RESULT(SLEEP
, IVOL
));
233 char *p
, *ch
, *arg
= para_strdup(val
);
235 p
= strchr(arg
, ':');
244 ret
= para_atoi32(p
, &iv
);
249 ret
= set_channel(m
, h
, ch
);
253 PARA_WARNING_LOG("ignoring channel %s\n", ch
);
256 PARA_INFO_LOG("initial volume %s: %d\n", ch
, iv
);
266 static int com_sleep(const struct mixer
*m
)
268 time_t t1
, wake_time_epoch
;
272 const char *wake_time
= OPT_STRING_VAL(SLEEP
, WAKE_TIME
);
273 const char *fo_mood
= OPT_STRING_VAL(SLEEP
, FO_MOOD
);
274 const char *fi_mood
= OPT_STRING_VAL(SLEEP
, FI_MOOD
);
275 const char *sleep_mood
= OPT_STRING_VAL(SLEEP
, SLEEP_MOOD
);
276 int fit
= OPT_UINT32_VAL(SLEEP
, FI_TIME
);
277 int fot
= OPT_UINT32_VAL(SLEEP
, FO_TIME
);
278 int fiv
= OPT_UINT32_VAL(SLEEP
, FI_VOL
);
279 int fov
= OPT_UINT32_VAL(SLEEP
, FO_VOL
);
280 int32_t hour
, min
= 0;
282 struct mixer_handle
*h
;
284 ret
= open_mixer_and_set_channel(m
, &h
);
287 wt
= para_strdup(wake_time
+ (wake_time
[0] == '+'));
288 /* calculate wake time */
290 tmp
= strchr(wt
, ':');
294 ret
= para_atoi32(tmp
, &min
);
300 ret
= para_atoi32(wt
, &hour
);
304 if (wake_time
[0] == '+') { /* relative */
305 t1
+= hour
* 60 * 60 + min
* 60;
309 if (tm
->tm_hour
> hour
|| (tm
->tm_hour
== hour
&& tm
->tm_min
> min
)) {
310 t1
+= 86400; /* wake time is tomorrow */
317 wake_time_epoch
= mktime(tm
);
318 PARA_INFO_LOG("waketime: %d:%02d\n", tm
->tm_hour
, tm
->tm_min
);
321 if (fot
&& fo_mood
&& *fo_mood
) {
322 ret
= set_initial_volume(m
, h
);
325 change_afs_mode(fo_mood
);
327 ret
= set_channel(m
, h
, OPT_STRING_VAL(PARA_MIXER
, MIXER_CHANNEL
));
330 ret
= fade(m
, h
, fov
, fot
);
334 ret
= m
->set(h
, fov
);
338 if (sleep_mood
&& *sleep_mood
) {
339 change_afs_mode(sleep_mood
);
340 if (!fot
|| !fo_mood
) /* currently stopped */
342 } else if (fot
&& fo_mood
&& *fo_mood
) /* currently playing */
345 if (!fit
|| !fi_mood
|| !*fi_mood
) /* nothing to do */
349 if (wake_time_epoch
<= t1
+ fit
)
351 delay
= wake_time_epoch
- t1
- fit
;
352 PARA_INFO_LOG("sleeping %u seconds (%u:%02u)\n",
354 (delay
% 3600) / 60);
357 change_afs_mode(fi_mood
);
358 if (sleep_mood
&& *sleep_mood
) /* currently playing */
360 else /* currently stopped */
362 ret
= open_mixer_and_set_channel(m
, &h
);
365 ret
= fade(m
, h
, fiv
, fit
);
372 static int com_snooze(const struct mixer
*m
)
375 struct mixer_handle
*h
;
377 ret
= open_mixer_and_set_channel(m
, &h
);
381 if (OPT_UINT32_VAL(SNOOZE
, SO_TIME
) == 0)
387 if (val
< OPT_UINT32_VAL(SNOOZE
, SO_VOL
))
388 ret
= m
->set(h
, OPT_UINT32_VAL(SNOOZE
, SO_VOL
));
390 ret
= fade(m
, h
, OPT_UINT32_VAL(SNOOZE
, SO_VOL
),
391 OPT_UINT32_VAL(SNOOZE
, SO_TIME
));
395 PARA_NOTICE_LOG("%" PRIu32
" seconds snooze time...\n",
396 OPT_UINT32_VAL(SNOOZE
, SNOOZE_TIME
));
398 sleep(OPT_UINT32_VAL(SNOOZE
, SNOOZE_TIME
));
400 ret
= open_mixer_and_set_channel(m
, &h
);
403 ret
= fade(m
, h
, OPT_UINT32_VAL(SNOOZE
, SI_VOL
),
404 OPT_UINT32_VAL(SNOOZE
, SI_TIME
));
411 static int com_set(const struct mixer
*m
)
413 struct mixer_handle
*h
;
416 ret
= open_mixer_and_set_channel(m
, &h
);
419 ret
= m
->set(h
, OPT_UINT32_VAL(SET
, VAL
));
425 static const struct mixer
*get_mixer_or_die(void)
429 if (!OPT_GIVEN(PARA_MIXER
, MIXER_API
))
430 i
= 0; /* default: use first mixer */
433 if (!strcmp(mixers
[i
]->name
,
434 OPT_STRING_VAL(PARA_MIXER
, MIXER_API
)))
436 if (i
< NUM_SUPPORTED_MIXERS
) {
437 PARA_NOTICE_LOG("using %s mixer API\n", mixers
[i
]->name
);
440 printf("available mixer APIs: ");
442 printf("%s%s%s ", i
== 0? "[" : "", mixers
[i
]->name
,
448 static void show_subcommands(void)
450 const struct lls_command
*cmd
;
452 printf("Subcommands:\n");
453 for (i
= 1; (cmd
= lls_cmd(i
, mixer_suite
)); i
++) {
454 const char *name
= lls_command_name(cmd
);
455 const char *purpose
= lls_purpose(cmd
);
456 printf("%-20s%s\n", name
, purpose
);
460 static int com_help(__a_unused
const struct mixer
*m
)
462 const struct lls_command
*cmd
;
463 const struct lls_opt_result
*r_l
= OPT_RESULT(HELP
, LONG
);
468 ret
= lls_check_arg_count(sub_lpr
, 0, 1, NULL
);
471 if (lls_num_inputs(sub_lpr
) == 0) {
475 name
= lls_input(0, sub_lpr
);
476 ret
= lls(lls_lookup_subcmd(name
, mixer_suite
, &errctx
));
479 PARA_ERROR_LOG("%s\n", errctx
);
483 cmd
= lls_cmd(ret
, mixer_suite
);
484 if (lls_opt_given(r_l
))
485 txt
= lls_long_help(cmd
);
487 txt
= lls_short_help(cmd
);
494 static void handle_help_flags(void)
498 if (OPT_GIVEN(PARA_MIXER
, DETAILED_HELP
))
499 help
= lls_long_help(CMD_PTR(PARA_MIXER
));
500 else if (OPT_GIVEN(PARA_MIXER
, HELP
))
501 help
= lls_short_help(CMD_PTR(PARA_MIXER
));
510 static int parse_and_merge_config_file(const struct lls_command
*cmd
)
513 struct lls_parse_result
**lprp
= (cmd
== lls_cmd(0, mixer_suite
))?
516 ret
= lsu_merge_config_file_options(OPT_STRING_VAL(PARA_MIXER
,
517 CONFIG_FILE
), "mixer.conf", lprp
, cmd
, mixer_suite
,
521 loglevel
= OPT_UINT32_VAL(PARA_MIXER
, LOGLEVEL
);
526 * The main function of para_mixer.
528 * The executable is linked with the alsa or the oss mixer API, or both. It has
529 * a custom log function which prefixes log messages with the current date.
531 * \param argc Argument counter.
532 * \param argv Argument vector.
534 * \return EXIT_SUCCESS or EXIT_FAILURE.
536 int main(int argc
, char *argv
[])
538 const struct lls_command
*cmd
= CMD_PTR(PARA_MIXER
);
542 const struct mixer
*m
;
545 ret
= lls(lls_parse(argc
, argv
, cmd
, &lpr
, &errctx
));
548 loglevel
= OPT_UINT32_VAL(PARA_MIXER
, LOGLEVEL
);
549 version_handle_flag("mixer", OPT_GIVEN(PARA_MIXER
, VERSION
));
552 n
= lls_num_inputs(lpr
);
558 ret
= parse_and_merge_config_file(cmd
);
561 subcmd
= lls_input(0, lpr
);
562 ret
= lls(lls_lookup_subcmd(subcmd
, mixer_suite
, &errctx
));
565 cmd
= lls_cmd(ret
, mixer_suite
);
566 ret
= lls(lls_parse(n
, argv
+ argc
- n
, cmd
, &sub_lpr
, &errctx
));
569 ret
= parse_and_merge_config_file(cmd
);
572 m
= get_mixer_or_die();
573 ret
= (*(mixer_subcommand_handler_t
*)(lls_user_data(cmd
)))(m
);
575 lls_free_parse_result(sub_lpr
, cmd
);
577 lls_free_parse_result(lpr
, CMD_PTR(PARA_MIXER
));
582 PARA_ERROR_LOG("%s\n", errctx
);
584 PARA_EMERG_LOG("%s\n", para_strerror(-ret
));