1 /* Copyright (C) 1998 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
3 /** \file mixer.c A volume fader and alarm clock for OSS. */
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
*, struct mixer_handle
*);
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 com_fade(const struct mixer
*m
, struct mixer_handle
*h
)
158 uint32_t new_vol
= OPT_UINT32_VAL(FADE
, FADE_VOL
);
159 uint32_t fade_time
= OPT_UINT32_VAL(FADE
, FADE_TIME
);
160 return fade(m
, h
, new_vol
, fade_time
);
164 static void client_cmd(const char *cmd
)
166 int ret
, status
, fds
[3] = {0, 0, 0};
168 char *cmdline
= make_message(BINDIR
"/para_client %s", cmd
);
170 PARA_NOTICE_LOG("%s\n", cmdline
);
171 ret
= para_exec_cmdline_pid(&pid
, cmdline
, fds
);
174 PARA_ERROR_LOG("%s\n", para_strerror(-ret
));
178 pid
= waitpid(pid
, &status
, 0);
179 while (pid
== -1 && errno
== EINTR
);
181 PARA_ERROR_LOG("%s\n", strerror(errno
));
184 if (!WIFEXITED(status
) || WEXITSTATUS(status
) != 0)
188 PARA_EMERG_LOG("command \"%s\" failed\n", cmd
);
192 static void change_afs_mode(const char *afs_mode
)
196 cmd
= make_message("select %s", afs_mode
);
201 static int set_initial_volume(const struct mixer
*m
, struct mixer_handle
*h
)
205 for (i
= 0; i
< OPT_GIVEN(SLEEP
, IVOL
); i
++) {
206 const char *val
= lls_string_val(i
, OPT_RESULT(SLEEP
, IVOL
));
207 char *p
, *ch
, *arg
= para_strdup(val
);
209 p
= strchr(arg
, ':');
218 ret
= para_atoi32(p
, &iv
);
223 ret
= set_channel(m
, h
, ch
);
227 PARA_WARNING_LOG("ignoring channel %s\n", ch
);
230 PARA_INFO_LOG("initial volume %s: %d\n", ch
, iv
);
240 static int com_sleep(const struct mixer
*m
, struct mixer_handle
*h
)
242 time_t t1
, wake_time_epoch
;
246 const char *wake_time
= OPT_STRING_VAL(SLEEP
, WAKE_TIME
);
247 const char *fo_mood
= OPT_STRING_VAL(SLEEP
, FO_MOOD
);
248 const char *fi_mood
= OPT_STRING_VAL(SLEEP
, FI_MOOD
);
249 const char *sleep_mood
= OPT_STRING_VAL(SLEEP
, SLEEP_MOOD
);
250 int fit
= OPT_UINT32_VAL(SLEEP
, FI_TIME
);
251 int fot
= OPT_UINT32_VAL(SLEEP
, FO_TIME
);
252 int fiv
= OPT_UINT32_VAL(SLEEP
, FI_VOL
);
253 int fov
= OPT_UINT32_VAL(SLEEP
, FO_VOL
);
254 int32_t hour
, min
= 0;
256 char *wt
= para_strdup(wake_time
+ (wake_time
[0] == '+'));
258 /* calculate wake time */
260 tmp
= strchr(wt
, ':');
264 ret
= para_atoi32(tmp
, &min
);
270 ret
= para_atoi32(wt
, &hour
);
274 if (wake_time
[0] == '+') { /* relative */
275 t1
+= hour
* 60 * 60 + min
* 60;
279 if (tm
->tm_hour
> hour
|| (tm
->tm_hour
== hour
&& tm
->tm_min
> min
)) {
280 t1
+= 86400; /* wake time is tomorrow */
287 wake_time_epoch
= mktime(tm
);
288 PARA_INFO_LOG("waketime: %d:%02d\n", tm
->tm_hour
, tm
->tm_min
);
291 if (fot
&& fo_mood
) {
292 ret
= set_initial_volume(m
, h
);
295 change_afs_mode(fo_mood
);
297 ret
= set_channel(m
, h
, OPT_STRING_VAL(PARA_MIXER
, MIXER_CHANNEL
));
300 ret
= fade(m
, h
, fov
, fot
);
304 ret
= m
->set(h
, fov
);
309 change_afs_mode(sleep_mood
);
310 if (!fot
|| !fo_mood
) /* currently stopped */
312 } else if (fot
&& fo_mood
) /* currently playing */
314 if (!fit
|| !fi_mood
) /* nothing to do */
316 change_afs_mode(fi_mood
);
319 if (wake_time_epoch
<= t1
+ fit
)
321 delay
= wake_time_epoch
- t1
- fit
;
322 PARA_INFO_LOG("sleeping %u seconds (%u:%02u)\n",
324 (delay
% 3600) / 60);
328 ret
= fade(m
, h
, fiv
, fit
);
329 PARA_INFO_LOG("fade complete, returning\n");
334 static int com_snooze(const struct mixer
*m
, struct mixer_handle
*h
)
338 if (OPT_UINT32_VAL(SNOOZE
, SO_TIME
) == 0)
344 if (val
< OPT_UINT32_VAL(SNOOZE
, SO_VOL
))
345 ret
= m
->set(h
, OPT_UINT32_VAL(SNOOZE
, SO_VOL
));
347 ret
= fade(m
, h
, OPT_UINT32_VAL(SNOOZE
, SO_VOL
),
348 OPT_UINT32_VAL(SNOOZE
, SO_TIME
));
352 PARA_NOTICE_LOG("%" PRIu32
" seconds snooze time...\n",
353 OPT_UINT32_VAL(SNOOZE
, SNOOZE_TIME
));
354 sleep(OPT_UINT32_VAL(SNOOZE
, SNOOZE_TIME
));
356 return fade(m
, h
, OPT_UINT32_VAL(SNOOZE
, SI_VOL
),
357 OPT_UINT32_VAL(SNOOZE
, SI_TIME
));
361 static int com_set(const struct mixer
*m
, struct mixer_handle
*h
)
363 return m
->set(h
, OPT_UINT32_VAL(SET
, VAL
));
367 static const struct mixer
*get_mixer_or_die(void)
371 if (!OPT_GIVEN(PARA_MIXER
, MIXER_API
))
372 i
= 0; /* default: use first mixer */
375 if (!strcmp(mixers
[i
]->name
,
376 OPT_STRING_VAL(PARA_MIXER
, MIXER_API
)))
378 if (i
< NUM_SUPPORTED_MIXERS
) {
379 PARA_NOTICE_LOG("using %s mixer API\n", mixers
[i
]->name
);
382 printf("available mixer APIs: ");
384 printf("%s%s%s ", i
== 0? "[" : "", mixers
[i
]->name
,
390 static void show_subcommands(void)
392 const struct lls_command
*cmd
;
394 printf("Subcommands:\n");
395 for (i
= 1; (cmd
= lls_cmd(i
, mixer_suite
)); i
++) {
396 const char *name
= lls_command_name(cmd
);
397 const char *purpose
= lls_purpose(cmd
);
398 printf("%-20s%s\n", name
, purpose
);
403 static int com_help(__a_unused
const struct mixer
*m
,
404 __a_unused
struct mixer_handle
*h
)
406 const struct lls_command
*cmd
;
407 const struct lls_opt_result
*r_l
= OPT_RESULT(HELP
, LONG
);
412 ret
= lls_check_arg_count(sub_lpr
, 0, 1, NULL
);
415 if (lls_num_inputs(sub_lpr
) == 0) {
419 name
= lls_input(0, sub_lpr
);
420 ret
= lls(lls_lookup_subcmd(name
, mixer_suite
, &errctx
));
423 PARA_ERROR_LOG("%s\n", errctx
);
427 cmd
= lls_cmd(ret
, mixer_suite
);
428 if (lls_opt_given(r_l
))
429 txt
= lls_long_help(cmd
);
431 txt
= lls_short_help(cmd
);
438 static void handle_help_flags(void)
442 if (OPT_GIVEN(PARA_MIXER
, DETAILED_HELP
))
443 help
= lls_long_help(CMD_PTR(PARA_MIXER
));
444 else if (OPT_GIVEN(PARA_MIXER
, HELP
))
445 help
= lls_short_help(CMD_PTR(PARA_MIXER
));
454 static int parse_and_merge_config_file(const struct lls_command
*cmd
)
457 struct lls_parse_result
**lprp
= (cmd
== lls_cmd(0, mixer_suite
))?
460 ret
= lsu_merge_config_file_options(OPT_STRING_VAL(PARA_MIXER
,
461 CONFIG_FILE
), "mixer.conf", lprp
, cmd
, mixer_suite
,
465 loglevel
= OPT_UINT32_VAL(PARA_MIXER
, LOGLEVEL
);
470 * The main function of para_mixer.
472 * The executable is linked with the alsa or the oss mixer API, or both. It has
473 * a custom log function which prefixes log messages with the current date.
475 * \param argc Argument counter.
476 * \param argv Argument vector.
478 * \return EXIT_SUCCESS or EXIT_FAILURE.
480 int main(int argc
, char *argv
[])
482 const struct lls_command
*cmd
= CMD_PTR(PARA_MIXER
);
486 const struct mixer
*m
;
487 struct mixer_handle
*h
;
490 ret
= lls(lls_parse(argc
, argv
, cmd
, &lpr
, &errctx
));
493 loglevel
= OPT_UINT32_VAL(PARA_MIXER
, LOGLEVEL
);
494 version_handle_flag("mixer", OPT_GIVEN(PARA_MIXER
, VERSION
));
497 n
= lls_num_inputs(lpr
);
503 ret
= parse_and_merge_config_file(cmd
);
506 subcmd
= lls_input(0, lpr
);
507 ret
= lls(lls_lookup_subcmd(subcmd
, mixer_suite
, &errctx
));
510 cmd
= lls_cmd(ret
, mixer_suite
);
511 ret
= lls(lls_parse(n
, argv
+ argc
- n
, cmd
, &sub_lpr
, &errctx
));
514 ret
= parse_and_merge_config_file(cmd
);
517 m
= get_mixer_or_die();
518 ret
= m
->open(OPT_STRING_VAL(PARA_MIXER
, MIXER_DEVICE
), &h
);
521 ret
= set_channel(m
, h
, OPT_STRING_VAL(PARA_MIXER
, MIXER_CHANNEL
));
522 if (ret
== -E_BAD_CHANNEL
) {
523 char *channels
= m
->get_channels(h
);
524 printf("Available channels: %s\n", channels
);
529 ret
= (*(mixer_subcommand_handler_t
*)(lls_user_data(cmd
)))(m
,h
);
533 lls_free_parse_result(sub_lpr
, cmd
);
535 lls_free_parse_result(lpr
, CMD_PTR(PARA_MIXER
));
540 PARA_ERROR_LOG("%s\n", errctx
);
542 PARA_EMERG_LOG("%s\n", para_strerror(-ret
));