ref: b2f339168bf5b666f96067daf4de4c9f07f51c92
dir: /src/util.c/
/* * util.c. * Incorporate Jimen Ching's fixes for real library operation: Aug 3, 1994. * Redo all work from scratch, unfortunately. * Separate out all common variables used by effects & handlers, * and utility routines for other main programs to use. */ /* * July 5, 1991 * Copyright 1991 Lance Norskog And Sundry Contributors * This source code is freely redistributable and may be used for * any purpose. This copyright notice must be maintained. * Lance Norskog And Sundry Contributors are not responsible for * the consequences of using this software. */ #include "st_i.h" #include <stddef.h> #include <string.h> #include <ctype.h> #include <stdarg.h> struct st_output_message_s { char const * filename; char const * fmt; va_list ap; }; st_output_message_handler_t st_output_message_handler = NULL; int st_output_verbosity_level = 2; void st_output_message(FILE * file, st_output_message_t message) { char buffer[10]; char const * drivername; char const * dot_pos; drivername = strrchr(message->filename, '/'); if (drivername != NULL) { ++drivername; } else { drivername = strrchr(message->filename, '\\'); if (drivername != NULL) ++drivername; else drivername = message->filename; } dot_pos = strrchr(drivername, '.'); if (dot_pos != NULL && dot_pos - drivername <= (ptrdiff_t)(sizeof(buffer) - 1)) { strncpy(buffer, drivername, (size_t)(dot_pos - drivername)); buffer[dot_pos - drivername] = '\0'; drivername = buffer; } fprintf(file, "%s: ", drivername); vfprintf(file, message->fmt, message->ap); } /* This is a bit of a hack. It's useful to have the ST library * report which driver (i.e. format or effect handler) is outputing * the message. Using the filename for this purpose is only an * approximation, but it saves a lot of work. ;) */ char const * st_message_filename = 0; static void st_emit_message(int level, char const * fmt, va_list ap) { if (st_output_message_handler != NULL) { struct st_output_message_s m; m.filename = st_message_filename; m.fmt = fmt; m.ap = ap; (*st_output_message_handler)(level, &m); } } #undef st_fail #undef st_warn #undef st_report #undef st_debug #undef st_debug_more #undef st_debug_most #define ST_MESSAGE_FUNCTION(name,level) \ void name(char const * fmt, ...) \ { \ va_list args; \ \ va_start(args, fmt); \ st_emit_message(level, fmt, args); \ va_end(args); \ } ST_MESSAGE_FUNCTION(st_fail , 1) ST_MESSAGE_FUNCTION(st_warn , 2) ST_MESSAGE_FUNCTION(st_report, 3) ST_MESSAGE_FUNCTION(st_debug , 4) ST_MESSAGE_FUNCTION(st_debug_more , 5) ST_MESSAGE_FUNCTION(st_debug_most , 6) #undef ST_MESSAGE_FUNCTION void st_fail_errno(ft_t ft, int st_errno, const char *fmt, ...) { va_list args; ft->st_errno = st_errno; va_start(args, fmt); #ifdef _MSC_VER vsprintf(ft->st_errstr, fmt, args); #else vsnprintf(ft->st_errstr, sizeof(ft->st_errstr), fmt, args); #endif va_end(args); ft->st_errstr[255] = '\0'; } /* * Check that we have a known format suffix string. */ int st_gettype(ft_t formp, bool is_file_extension) { const char * const *list; int i; const st_format_t *f; if (!formp->filetype) { st_fail_errno(formp, ST_EFMT, "Filetype was not specified"); return ST_EFMT; } for (i = 0; st_format_fns[i]; i++) { f = st_format_fns[i](); if (is_file_extension && (f->flags & ST_FILE_DEVICE)) continue; /* don't match device names in real file names */ for (list = f->names; *list; list++) { const char *s1 = *list, *s2 = formp->filetype; if (!strcasecmp(s1, s2)) break; /* not a match */ } if (!*list) continue; /* Found it! */ formp->h = f; return ST_SUCCESS; } st_fail_errno(formp, ST_EFMT, "File type `%s' is not known", formp->filetype); return ST_EFMT; } /* * Check that we have a known effect name. If found, copy name of * effect into structure and place a pointer to internal data. * Returns -1 on error else it turns the total number of arguments * that should be passed to this effect's getopt() function. */ int st_geteffect_opt(eff_t effp, int argc, char **argv) { int i, optind; for (i = 0; st_effect_fns[i]; i++) { const st_effect_t *e = st_effect_fns[i](); if (e && e->name && strcasecmp(e->name, argv[0]) == 0) { effp->name = e->name; effp->h = e; for (optind = 1; optind < argc; optind++) { for (i = 0; st_effect_fns[i]; i++) { const st_effect_t *e = st_effect_fns[i](); if (e && e->name && strcasecmp(e->name, argv[optind]) == 0) return (optind - 1); } /* Didn't find a match, try the next argument. */ } /* * No matches found, all the following arguments are * for this effect passed in. */ return (optind - 1); } } return (ST_EOF); } /* * Check that we have a known effect name. If found, copy name of * effect into structure and place a pointer to internal data. * Returns -1 on on failure. */ int st_geteffect(eff_t effp, const char *effect_name) { int i; for(i = 0; st_effect_fns[i]; i++) { const st_effect_t *e = st_effect_fns[i](); if (e && e->name && strcasecmp(e->name, effect_name) == 0) { effp->name = e->name; effp->h = e; return ST_SUCCESS; } } return (ST_EOF); } /* * Check if we have a known effect name. */ bool is_effect_name(char const * text) { int i; for(i = 0; st_effect_fns[i]; i++) { const st_effect_t *e = st_effect_fns[i](); if (e && e->name && strcasecmp(e->name, text) == 0) return true; } return false; } /* * Copy input and output signal info into effect structures. * Must pass in a bitmask containing info of wheither ST_EFF_CHAN * or ST_EFF_RATE has been used previously on this effect stream. * If not running multiple effects then just pass in a value of 0. * * Return value is the same mask plus addition of ST_EFF_CHAN or * ST_EFF_RATE if it was used in this effect. That make this * return value can be passed back into this function in future * calls. */ int st_updateeffect(eff_t effp, const st_signalinfo_t *in, const st_signalinfo_t *out, int effect_mask) { effp->ininfo = *in; effp->outinfo = *out; if (in->channels != out->channels) { /* Only effects with ST_EFF_CHAN flag can actually handle * outputing a different number of channels then the input. */ if (!(effp->h->flags & ST_EFF_CHAN)) { /* If this effect is being ran before a ST_EFF_CHAN effect * then effect's output is the same as the input file. Else its * input contains same number of channels as the output * file. */ if (effect_mask & ST_EFF_CHAN) effp->ininfo.channels = out->channels; else effp->outinfo.channels = in->channels; } } if (in->rate != out->rate) { /* Only the ST_EFF_RATE effect can handle an input that * is a different sample rate then the output. */ if (!(effp->h->flags & ST_EFF_RATE)) { if (effect_mask & ST_EFF_RATE) effp->ininfo.rate = out->rate; else effp->outinfo.rate = in->rate; } } if (effp->h->flags & ST_EFF_CHAN) effect_mask |= ST_EFF_CHAN; if (effp->h->flags & ST_EFF_RATE) effect_mask |= ST_EFF_RATE; return effect_mask; } /* * st_parsesamples * * Parse a string for # of samples. If string ends with a 's' * then the string is interpreted as a user calculated # of samples. * If string contains ':' or '.' or if it ends with a 't' then its * treated as an amount of time. This is converted into seconds and * fraction of seconds and then use the sample rate to calculate * # of samples. * Returns NULL on error, pointer to next char to parse otherwise. */ char const * st_parsesamples(st_rate_t rate, const char *str, st_size_t *samples, char def) { int found_samples = 0, found_time = 0; int time = 0; long long_samples; float frac = 0; char const * end; char const * pos; bool found_colon, found_dot; for (end = str; *end && strchr("0123456789:.ts", *end); ++end); if (end == str) return NULL; pos = strchr(str, ':'); found_colon = pos && pos < end; pos = strchr(str, '.'); found_dot = pos && pos < end; if (found_colon || found_dot || *(end-1) == 't') found_time = 1; else if (*(end-1) == 's') found_samples = 1; if (found_time || (def == 't' && !found_samples)) { *samples = 0; while(1) { if (str[0] != '.' && sscanf(str, "%d", &time) != 1) return NULL; *samples += time; while (*str != ':' && *str != '.' && *str != 0) str++; if (*str == '.' || *str == 0) break; /* Skip past ':' */ str++; *samples *= 60; } if (*str == '.') { if (sscanf(str, "%f", &frac) != 1) return NULL; } *samples *= rate; *samples += (rate * frac) + 0.5; return end; } if (found_samples || (def == 's' && !found_time)) { if (sscanf(str, "%ld", &long_samples) != 1) return NULL; *samples = long_samples; return end; } return NULL; }