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. */
17 /** Array of error strings. */
20 /* At least one of the two is defined if this file gets compiled. */
21 static const struct mixer
*mixers
[] = {
30 #define NUM_SUPPORTED_MIXERS (ARRAY_SIZE(mixers))
31 #define FOR_EACH_MIXER(i) for ((i) = 0; (i) < NUM_SUPPORTED_MIXERS; (i)++)
33 static struct lls_parse_result
*lpr
, *sub_lpr
;
35 #define CMD_PTR(_cmd) (lls_cmd(LSG_MIXER_CMD_ ## _cmd, mixer_suite))
36 #define OPT_RESULT(_cmd, _opt) (lls_opt_result( \
37 LSG_MIXER_ ## _cmd ## _OPT_ ## _opt, (LSG_MIXER_CMD_ ## _cmd == 0)? lpr : sub_lpr))
38 #define OPT_GIVEN(_cmd, _opt) (lls_opt_given(OPT_RESULT(_cmd, _opt)))
39 #define OPT_STRING_VAL(_cmd, _opt) (lls_string_val(0, OPT_RESULT(_cmd, _opt)))
40 #define OPT_UINT32_VAL(_cmd, _opt) (lls_uint32_val(0, OPT_RESULT(_cmd, _opt)))
42 typedef int (*mixer_subcommand_handler_t
)(const struct mixer
*, struct mixer_handle
*);
44 #define EXPORT_CMD(_cmd) const mixer_subcommand_handler_t \
45 lsg_mixer_com_ ## _cmd ## _user_data = &com_ ## _cmd;
48 static __printf_2_3
void date_log(int ll
, const char *fmt
, ...)
58 fprintf(stderr
, "%d:%02d:%02d ", tm
->tm_hour
, tm
->tm_min
, tm
->tm_sec
);
63 __printf_2_3
void (*para_log
)(int, const char*, ...) = date_log
;
65 static int set_channel(const struct mixer
*m
, struct mixer_handle
*h
,
69 PARA_NOTICE_LOG("using %s mixer channel\n", channel
?
71 return m
->set_channel(h
, channel
);
74 static void millisleep(int ms
)
78 PARA_INFO_LOG("sleeping %dms\n", ms
);
81 ts
.tv_sec
= ms
/ 1000,
82 ts
.tv_nsec
= (ms
% 1000) * 1000 * 1000;
87 * This implements the inverse function of t -> t^alpha, scaled to the time
88 * interval [0,T] and the range given by old_vol and new_vol. It returns the
89 * amount of milliseconds until the given volume is reached.
91 static unsigned volume_time(double vol
, double old_vol
, double new_vol
,
92 double T
, double alpha
)
96 if (old_vol
< new_vol
) {
104 x
= T
* exp(log(((vol
- c
) / (d
- c
))) / alpha
);
106 if (old_vol
< new_vol
)
112 /* Fade to new volume in fade_time seconds. */
113 static int fade(const struct mixer
*m
, struct mixer_handle
*h
, uint32_t new_vol
,
116 int i
, T
, old_vol
, ret
, slept
, incr
;
118 uint32_t fe
= OPT_UINT32_VAL(PARA_MIXER
, FADE_EXPONENT
);
120 if (fade_time
<= 0 || fe
>= 100) {
121 ret
= m
->set(h
, new_vol
);
126 alpha
= (100 - fe
) / 100.0;
131 if (old_vol
== new_vol
)
133 PARA_NOTICE_LOG("fading %s from %d to %u in %u seconds\n",
134 OPT_STRING_VAL(PARA_MIXER
, MIXER_CHANNEL
), old_vol
,
136 incr
= old_vol
< new_vol
? 1 : -1;
137 T
= fade_time
* 1000;
141 ms
= volume_time(i
+ incr
, old_vol
, new_vol
, T
, alpha
);
142 millisleep(ms
- slept
);
148 } while (i
!= new_vol
);
155 static int com_fade(const struct mixer
*m
, struct mixer_handle
*h
)
157 uint32_t new_vol
= OPT_UINT32_VAL(FADE
, FADE_VOL
);
158 uint32_t fade_time
= OPT_UINT32_VAL(FADE
, FADE_TIME
);
159 return fade(m
, h
, new_vol
, fade_time
);
163 static void client_cmd(const char *cmd
)
165 int ret
, status
, fds
[3] = {0, 0, 0};
167 char *cmdline
= make_message(BINDIR
"/para_client %s", cmd
);
169 PARA_NOTICE_LOG("%s\n", cmdline
);
170 ret
= para_exec_cmdline_pid(&pid
, cmdline
, fds
);
173 PARA_ERROR_LOG("%s\n", para_strerror(-ret
));
177 pid
= waitpid(pid
, &status
, 0);
178 while (pid
== -1 && errno
== EINTR
);
180 PARA_ERROR_LOG("%s\n", strerror(errno
));
183 if (!WIFEXITED(status
) || WEXITSTATUS(status
) != 0)
187 PARA_EMERG_LOG("command \"%s\" failed\n", cmd
);
191 static void change_afs_mode(const char *afs_mode
)
198 cmd
= make_message("select %s", afs_mode
);
203 static int set_initial_volume(const struct mixer
*m
, struct mixer_handle
*h
)
207 for (i
= 0; i
< OPT_GIVEN(SLEEP
, IVOL
); i
++) {
208 const char *val
= lls_string_val(i
, OPT_RESULT(SLEEP
, IVOL
));
209 char *p
, *ch
, *arg
= para_strdup(val
);
211 p
= strchr(arg
, ':');
220 ret
= para_atoi32(p
, &iv
);
225 ret
= set_channel(m
, h
, ch
);
229 PARA_WARNING_LOG("ignoring channel %s\n", ch
);
232 PARA_INFO_LOG("initial volume %s: %d\n", ch
, iv
);
242 static int com_sleep(const struct mixer
*m
, struct mixer_handle
*h
)
244 time_t t1
, wake_time_epoch
;
248 const char *wake_time
= OPT_STRING_VAL(SLEEP
, WAKE_TIME
);
249 const char *fo_mood
= OPT_STRING_VAL(SLEEP
, FO_MOOD
);
250 const char *fi_mood
= OPT_STRING_VAL(SLEEP
, FI_MOOD
);
251 const char *sleep_mood
= OPT_STRING_VAL(SLEEP
, SLEEP_MOOD
);
252 int fit
= OPT_UINT32_VAL(SLEEP
, FI_TIME
);
253 int fot
= OPT_UINT32_VAL(SLEEP
, FO_TIME
);
254 int fiv
= OPT_UINT32_VAL(SLEEP
, FI_VOL
);
255 int fov
= OPT_UINT32_VAL(SLEEP
, FO_VOL
);
256 int32_t hour
, min
= 0;
258 char *wt
= para_strdup(wake_time
+ (wake_time
[0] == '+'));
260 /* calculate wake time */
262 tmp
= strchr(wt
, ':');
266 ret
= para_atoi32(tmp
, &min
);
272 ret
= para_atoi32(wt
, &hour
);
276 if (wake_time
[0] == '+') { /* relative */
277 t1
+= hour
* 60 * 60 + min
* 60;
281 if (tm
->tm_hour
> hour
|| (tm
->tm_hour
== hour
&& tm
->tm_min
> min
)) {
282 t1
+= 86400; /* wake time is tomorrow */
289 wake_time_epoch
= mktime(tm
);
290 PARA_INFO_LOG("waketime: %d:%02d\n", tm
->tm_hour
, tm
->tm_min
);
294 ret
= set_initial_volume(m
, h
);
297 change_afs_mode(fo_mood
);
299 ret
= set_channel(m
, h
, OPT_STRING_VAL(PARA_MIXER
, MIXER_CHANNEL
));
302 ret
= fade(m
, h
, fov
, fot
);
306 ret
= m
->set(h
, fov
);
310 if (OPT_GIVEN(SLEEP
, SLEEP_MOOD
)) {
311 change_afs_mode(sleep_mood
);
317 change_afs_mode(fi_mood
);
320 if (wake_time_epoch
<= t1
+ fit
)
322 delay
= wake_time_epoch
- t1
- fit
;
323 PARA_INFO_LOG("sleeping %u seconds (%u:%02u)\n",
325 (delay
% 3600) / 60);
329 ret
= fade(m
, h
, fiv
, fit
);
330 PARA_INFO_LOG("fade complete, returning\n");
335 static int com_snooze(const struct mixer
*m
, struct mixer_handle
*h
)
339 if (OPT_UINT32_VAL(SNOOZE
, SO_TIME
) == 0)
345 if (val
< OPT_UINT32_VAL(SNOOZE
, SO_VOL
))
346 ret
= m
->set(h
, OPT_UINT32_VAL(SNOOZE
, SO_VOL
));
348 ret
= fade(m
, h
, OPT_UINT32_VAL(SNOOZE
, SO_VOL
),
349 OPT_UINT32_VAL(SNOOZE
, SO_TIME
));
353 PARA_NOTICE_LOG("%" PRIu32
" seconds snooze time...\n",
354 OPT_UINT32_VAL(SNOOZE
, SNOOZE_TIME
));
355 sleep(OPT_UINT32_VAL(SNOOZE
, SNOOZE_TIME
));
357 return fade(m
, h
, OPT_UINT32_VAL(SNOOZE
, SI_VOL
),
358 OPT_UINT32_VAL(SNOOZE
, SI_TIME
));
362 static int com_set(const struct mixer
*m
, struct mixer_handle
*h
)
364 return m
->set(h
, OPT_UINT32_VAL(SET
, VAL
));
368 static const struct mixer
*get_mixer_or_die(void)
372 if (!OPT_GIVEN(PARA_MIXER
, MIXER_API
))
373 i
= 0; /* default: use first mixer */
376 if (!strcmp(mixers
[i
]->name
,
377 OPT_STRING_VAL(PARA_MIXER
, MIXER_API
)))
379 if (i
< NUM_SUPPORTED_MIXERS
) {
380 PARA_NOTICE_LOG("using %s mixer API\n", mixers
[i
]->name
);
383 printf("available mixer APIs: ");
385 printf("%s%s%s ", i
== 0? "[" : "", mixers
[i
]->name
,
391 static void show_subcommands(void)
393 const struct lls_command
*cmd
;
395 printf("Subcommands:\n");
396 for (i
= 1; (cmd
= lls_cmd(i
, mixer_suite
)); i
++) {
397 const char *name
= lls_command_name(cmd
);
398 const char *purpose
= lls_purpose(cmd
);
399 printf("%-20s%s\n", name
, purpose
);
404 static int com_help(__a_unused
const struct mixer
*m
,
405 __a_unused
struct mixer_handle
*h
)
407 const struct lls_command
*cmd
;
408 const struct lls_opt_result
*r_l
= OPT_RESULT(HELP
, LONG
);
413 ret
= lls_check_arg_count(sub_lpr
, 0, 1, NULL
);
416 if (lls_num_inputs(sub_lpr
) == 0) {
420 name
= lls_input(0, sub_lpr
);
421 ret
= lls(lls_lookup_subcmd(name
, mixer_suite
, &errctx
));
424 PARA_ERROR_LOG("%s\n", errctx
);
428 cmd
= lls_cmd(ret
, mixer_suite
);
429 if (lls_opt_given(r_l
))
430 txt
= lls_long_help(cmd
);
432 txt
= lls_short_help(cmd
);
439 static void handle_help_flags(void)
443 if (OPT_GIVEN(PARA_MIXER
, DETAILED_HELP
))
444 help
= lls_long_help(CMD_PTR(PARA_MIXER
));
445 else if (OPT_GIVEN(PARA_MIXER
, HELP
))
446 help
= lls_short_help(CMD_PTR(PARA_MIXER
));
455 static int parse_and_merge_config_file(const struct lls_command
*cmd
)
460 char *cf
, *errctx
= NULL
;
461 struct lls_parse_result
**lprp
, *cf_lpr
, *merged_lpr
;
464 const char *subcmd_name
;
466 if (cmd
== lls_cmd(0, mixer_suite
)) {
471 subcmd_name
= lls_command_name(cmd
);
473 if (OPT_GIVEN(PARA_MIXER
, CONFIG_FILE
))
474 cf
= para_strdup(OPT_STRING_VAL(PARA_MIXER
, CONFIG_FILE
));
476 char *home
= para_homedir();
477 cf
= make_message("%s/.paraslash/mixer.conf", home
);
480 ret
= mmap_full_file(cf
, O_RDONLY
, &map
, &sz
, NULL
);
482 if (ret
!= -E_EMPTY
&& ret
!= -ERRNO_TO_PARA_ERROR(ENOENT
))
484 if (ret
== -ERRNO_TO_PARA_ERROR(ENOENT
) &&
485 OPT_GIVEN(PARA_MIXER
, CONFIG_FILE
))
488 ret
= lls(lls_convert_config(map
, sz
, subcmd_name
, &cf_argv
, &errctx
));
489 para_munmap(map
, sz
);
493 ret
= lls(lls_parse(cf_argc
, cf_argv
, cmd
, &cf_lpr
, &errctx
));
494 lls_free_argv(cf_argv
);
497 ret
= lls(lls_merge(*lprp
, cf_lpr
, cmd
, &merged_lpr
, &errctx
));
498 lls_free_parse_result(cf_lpr
, cmd
);
501 lls_free_parse_result(*lprp
, cmd
);
503 loglevel
= OPT_UINT32_VAL(PARA_MIXER
, LOGLEVEL
);
509 PARA_ERROR_LOG("%s\n", errctx
);
515 * The main function of para_mixer.
517 * The executable is linked with the alsa or the oss mixer API, or both. It has
518 * a custom log function which prefixes log messages with the current date.
520 * \param argc Argument counter.
521 * \param argv Argument vector.
523 * \return EXIT_SUCCESS or EXIT_FAILURE.
525 int main(int argc
, char *argv
[])
527 const struct lls_command
*cmd
= CMD_PTR(PARA_MIXER
);
531 const struct mixer
*m
;
532 struct mixer_handle
*h
;
535 ret
= lls(lls_parse(argc
, argv
, cmd
, &lpr
, &errctx
));
538 loglevel
= OPT_UINT32_VAL(PARA_MIXER
, LOGLEVEL
);
539 version_handle_flag("mixer", OPT_GIVEN(PARA_MIXER
, VERSION
));
542 n
= lls_num_inputs(lpr
);
548 ret
= parse_and_merge_config_file(cmd
);
551 subcmd
= lls_input(0, lpr
);
552 ret
= lls(lls_lookup_subcmd(subcmd
, mixer_suite
, &errctx
));
555 cmd
= lls_cmd(ret
, mixer_suite
);
556 ret
= lls(lls_parse(n
, argv
+ argc
- n
, cmd
, &sub_lpr
, &errctx
));
559 ret
= parse_and_merge_config_file(cmd
);
562 m
= get_mixer_or_die();
563 ret
= m
->open(OPT_STRING_VAL(PARA_MIXER
, MIXER_DEVICE
), &h
);
566 ret
= set_channel(m
, h
, OPT_STRING_VAL(PARA_MIXER
, MIXER_CHANNEL
));
567 if (ret
== -E_BAD_CHANNEL
) {
568 char *channels
= m
->get_channels(h
);
569 printf("Available channels: %s\n", channels
);
574 ret
= (*(mixer_subcommand_handler_t
*)(lls_user_data(cmd
)))(m
,h
);
578 lls_free_parse_result(sub_lpr
, cmd
);
580 lls_free_parse_result(lpr
, CMD_PTR(PARA_MIXER
));
585 PARA_ERROR_LOG("%s\n", errctx
);
587 PARA_EMERG_LOG("%s\n", para_strerror(-ret
));