Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
tree: 9fd7763a2a
Fetching contributors…

Cannot retrieve contributors at this time

executable file 5779 lines (5420 sloc) 150.769 kB
/*
TiMidity++ -- MIDI to WAVE converter and player
Copyright (C) 1999-2008 Masanao Izumo <iz@onicos.co.jp>
Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#ifdef __POCC__
#include <sys/types.h>
#endif //for off_t
#include <stdio.h>
#ifdef STDC_HEADERS
#include <stdlib.h>
#include <ctype.h>
#include <stddef.h>
#endif
#ifndef NO_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif
#ifdef __W32__
#include <windows.h>
#include <io.h>
#include <shlobj.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif /* HAVE_UNISTD_H */
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif /* TIME_WITH_SYS_TIME */
#include <fcntl.h> /* for open */
#ifdef BORLANDC_EXCEPTION
#include <excpt.h>
#endif /* BORLANDC_EXCEPTION */
#include <signal.h>
#if defined(__FreeBSD__) && !defined(__alpha__)
#include <floatingpoint.h> /* For FP exceptions */
#endif
#if defined(__NetBSD__) || defined(__OpenBSD__)
#include <ieeefp.h> /* For FP exceptions */
#endif
#include "interface.h"
#include "timidity.h"
#include "tmdy_getopt.h"
#include "common.h"
#include "instrum.h"
#include "playmidi.h"
#include "readmidi.h"
#include "output.h"
#include "controls.h"
#include "tables.h"
#include "miditrace.h"
#include "reverb.h"
#ifdef SUPPORT_SOUNDSPEC
#include "soundspec.h"
#endif /* SUPPORT_SOUNDSPEC */
#include "resample.h"
#include "recache.h"
#include "arc.h"
#include "strtab.h"
#include "wrd.h"
#define DEFINE_GLOBALS
#include "mid.defs"
#include "aq.h"
#include "mix.h"
//#include "unimod.h"
#include "quantity.h"
//#include "rtsyn.h"
#ifdef __BORLANDC__
#undef inline
#define inline
#endif
#ifdef IA_W32GUI
#include "w32g.h"
#include "w32g_utl.h"
#endif
#ifdef WINDRV
/* supress std outputs */
#define fputs(a, b)
#endif
#ifndef __GNUC__
#define __attribute__(x) /* ignore */
#endif
/* option enums */
enum {
TIM_OPT_FIRST = 256,
/* first entry */
TIM_OPT_VOLUME = TIM_OPT_FIRST,
TIM_OPT_DRUM_POWER,
TIM_OPT_VOLUME_COMP,
TIM_OPT_ANTI_ALIAS,
TIM_OPT_BUFFER_FRAGS,
TIM_OPT_CONTROL_RATIO,
TIM_OPT_CONFIG_FILE,
TIM_OPT_DRUM_CHANNEL,
TIM_OPT_IFACE_PATH,
TIM_OPT_EXT,
TIM_OPT_MOD_WHEEL,
TIM_OPT_PORTAMENTO,
TIM_OPT_VIBRATO,
TIM_OPT_CH_PRESS,
TIM_OPT_MOD_ENV,
TIM_OPT_TRACE_TEXT,
TIM_OPT_OVERLAP,
TIM_OPT_TEMPER_CTRL,
TIM_OPT_DEFAULT_MID,
TIM_OPT_SYSTEM_MID,
TIM_OPT_DEFAULT_BANK,
TIM_OPT_FORCE_BANK,
TIM_OPT_DEFAULT_PGM,
TIM_OPT_FORCE_PGM,
TIM_OPT_DELAY,
TIM_OPT_CHORUS,
TIM_OPT_REVERB,
TIM_OPT_VOICE_LPF,
TIM_OPT_NS,
TIM_OPT_RESAMPLE,
TIM_OPT_EVIL,
TIM_OPT_FAST_PAN,
TIM_OPT_FAST_DECAY,
TIM_OPT_SPECTROGRAM,
TIM_OPT_KEYSIG,
TIM_OPT_HELP,
TIM_OPT_INTERFACE,
TIM_OPT_VERBOSE,
TIM_OPT_QUIET,
TIM_OPT_TRACE,
TIM_OPT_LOOP,
TIM_OPT_RANDOM,
TIM_OPT_SORT,
TIM_OPT_BACKGROUND,
TIM_OPT_RT_PRIO,
TIM_OPT_SEQ_PORTS,
TIM_OPT_RTSYN_LATENCY,
TIM_OPT_REALTIME_LOAD,
TIM_OPT_ADJUST_KEY,
TIM_OPT_VOICE_QUEUE,
TIM_OPT_PATCH_PATH,
TIM_OPT_PCM_FILE,
TIM_OPT_DECAY_TIME,
TIM_OPT_INTERPOLATION,
TIM_OPT_OUTPUT_MODE,
TIM_OPT_OUTPUT_STEREO,
TIM_OPT_OUTPUT_SIGNED,
TIM_OPT_OUTPUT_BITWIDTH,
TIM_OPT_OUTPUT_FORMAT,
TIM_OPT_OUTPUT_SWAB,
TIM_OPT_FLAC_VERIFY,
TIM_OPT_FLAC_PADDING,
TIM_OPT_FLAC_COMPLEVEL,
TIM_OPT_FLAC_OGGFLAC,
TIM_OPT_SPEEX_QUALITY,
TIM_OPT_SPEEX_VBR,
TIM_OPT_SPEEX_ABR,
TIM_OPT_SPEEX_VAD,
TIM_OPT_SPEEX_DTX,
TIM_OPT_SPEEX_COMPLEXITY,
TIM_OPT_SPEEX_NFRAMES,
TIM_OPT_OUTPUT_FILE,
TIM_OPT_PATCH_FILE,
TIM_OPT_POLYPHONY,
TIM_OPT_POLY_REDUCE,
TIM_OPT_MUTE,
TIM_OPT_TEMPER_MUTE,
TIM_OPT_PRESERVE_SILENCE,
TIM_OPT_AUDIO_BUFFER,
TIM_OPT_CACHE_SIZE,
TIM_OPT_SAMPLE_FREQ,
TIM_OPT_ADJUST_TEMPO,
TIM_OPT_CHARSET,
TIM_OPT_UNLOAD_INST,
TIM_OPT_VOLUME_CURVE,
TIM_OPT_VERSION,
TIM_OPT_WRD,
TIM_OPT_RCPCV_DLL,
TIM_OPT_CONFIG_STR,
TIM_OPT_FREQ_TABLE,
TIM_OPT_PURE_INT,
TIM_OPT_MODULE,
/* last entry */
TIM_OPT_LAST = TIM_OPT_MODULE
};
#ifdef IA_WINSYN
const char *optcommands =
#else
static const char *optcommands =
#endif
"4A:aB:b:C:c:D:d:E:eFfg:H:hI:i:jK:k:L:M:m:N:"
"O:o:P:p:Q:q:R:S:s:T:t:UV:vW:"
#ifdef __W32__
"w:"
#endif
"x:Z:"; /* Only GJlnruXYyz are remain... */
#ifdef IA_WINSYN
const struct option longopts[] = {
#else
static const struct option longopts[] = {
#endif
{ "volume", required_argument, NULL, TIM_OPT_VOLUME },
{ "drum-power", required_argument, NULL, TIM_OPT_DRUM_POWER },
{ "no-volume-compensation", no_argument, NULL, TIM_OPT_DRUM_POWER },
{ "volume-compensation", optional_argument, NULL, TIM_OPT_VOLUME_COMP },
{ "no-anti-alias", no_argument, NULL, TIM_OPT_ANTI_ALIAS },
{ "anti-alias", optional_argument, NULL, TIM_OPT_ANTI_ALIAS },
{ "buffer-fragments", required_argument, NULL, TIM_OPT_BUFFER_FRAGS },
{ "control-ratio", required_argument, NULL, TIM_OPT_CONTROL_RATIO },
{ "config-file", required_argument, NULL, TIM_OPT_CONFIG_FILE },
{ "drum-channel", required_argument, NULL, TIM_OPT_DRUM_CHANNEL },
{ "interface-path", required_argument, NULL, TIM_OPT_IFACE_PATH },
{ "ext", required_argument, NULL, TIM_OPT_EXT },
{ "no-mod-wheel", no_argument, NULL, TIM_OPT_MOD_WHEEL },
{ "mod-wheel", optional_argument, NULL, TIM_OPT_MOD_WHEEL },
{ "no-portamento", no_argument, NULL, TIM_OPT_PORTAMENTO },
{ "portamento", optional_argument, NULL, TIM_OPT_PORTAMENTO },
{ "no-vibrato", no_argument, NULL, TIM_OPT_VIBRATO },
{ "vibrato", optional_argument, NULL, TIM_OPT_VIBRATO },
{ "no-ch-pressure", no_argument, NULL, TIM_OPT_CH_PRESS },
{ "ch-pressure", optional_argument, NULL, TIM_OPT_CH_PRESS },
{ "no-mod-envelope", no_argument, NULL, TIM_OPT_MOD_ENV },
{ "mod-envelope", optional_argument, NULL, TIM_OPT_MOD_ENV },
{ "no-trace-text-meta", no_argument, NULL, TIM_OPT_TRACE_TEXT },
{ "trace-text-meta", optional_argument, NULL, TIM_OPT_TRACE_TEXT },
{ "no-overlap-voice", no_argument, NULL, TIM_OPT_OVERLAP },
{ "overlap-voice", optional_argument, NULL, TIM_OPT_OVERLAP },
{ "no-temper-control", no_argument, NULL, TIM_OPT_TEMPER_CTRL },
{ "temper-control", optional_argument, NULL, TIM_OPT_TEMPER_CTRL },
{ "default-mid", required_argument, NULL, TIM_OPT_DEFAULT_MID },
{ "system-mid", required_argument, NULL, TIM_OPT_SYSTEM_MID },
{ "default-bank", required_argument, NULL, TIM_OPT_DEFAULT_BANK },
{ "force-bank", required_argument, NULL, TIM_OPT_FORCE_BANK },
{ "default-program", required_argument, NULL, TIM_OPT_DEFAULT_PGM },
{ "force-program", required_argument, NULL, TIM_OPT_FORCE_PGM },
{ "delay", required_argument, NULL, TIM_OPT_DELAY },
{ "chorus", required_argument, NULL, TIM_OPT_CHORUS },
{ "reverb", required_argument, NULL, TIM_OPT_REVERB },
{ "voice-lpf", required_argument, NULL, TIM_OPT_VOICE_LPF },
{ "noise-shaping", required_argument, NULL, TIM_OPT_NS },
#ifndef FIXED_RESAMPLATION
{ "resample", required_argument, NULL, TIM_OPT_RESAMPLE },
#endif
{ "evil", required_argument, NULL, TIM_OPT_EVIL },
{ "no-fast-panning", no_argument, NULL, TIM_OPT_FAST_PAN },
{ "fast-panning", optional_argument, NULL, TIM_OPT_FAST_PAN },
{ "no-fast-decay", no_argument, NULL, TIM_OPT_FAST_DECAY },
{ "fast-decay", optional_argument, NULL, TIM_OPT_FAST_DECAY },
{ "spectrogram", required_argument, NULL, TIM_OPT_SPECTROGRAM },
{ "force-keysig", required_argument, NULL, TIM_OPT_KEYSIG },
{ "help", optional_argument, NULL, TIM_OPT_HELP },
{ "interface", required_argument, NULL, TIM_OPT_INTERFACE },
{ "verbose", optional_argument, NULL, TIM_OPT_VERBOSE },
{ "quiet", optional_argument, NULL, TIM_OPT_QUIET },
{ "no-trace", no_argument, NULL, TIM_OPT_TRACE },
{ "trace", optional_argument, NULL, TIM_OPT_TRACE },
{ "no-loop", no_argument, NULL, TIM_OPT_LOOP },
{ "loop", optional_argument, NULL, TIM_OPT_LOOP },
{ "no-random", no_argument, NULL, TIM_OPT_RANDOM },
{ "random", optional_argument, NULL, TIM_OPT_RANDOM },
{ "no-sort", no_argument, NULL, TIM_OPT_SORT },
{ "sort", optional_argument, NULL, TIM_OPT_SORT },
#ifdef IA_ALSASEQ
{ "no-background", no_argument, NULL, TIM_OPT_BACKGROUND },
{ "background", optional_argument, NULL, TIM_OPT_BACKGROUND },
{ "realtime-priority", required_argument, NULL, TIM_OPT_RT_PRIO },
{ "sequencer-ports", required_argument, NULL, TIM_OPT_SEQ_PORTS },
#endif
#if defined(IA_WINSYN) || defined(IA_PORTMIDISYN) || defined(IA_NPSYN) || defined(IA_W32G_SYN) || defined(IA_W32GUI)
{ "rtsyn-latency", required_argument, NULL, TIM_OPT_RTSYN_LATENCY },
#endif
{ "no-realtime-load", no_argument, NULL, TIM_OPT_REALTIME_LOAD },
{ "realtime-load", optional_argument, NULL, TIM_OPT_REALTIME_LOAD },
{ "adjust-key", required_argument, NULL, TIM_OPT_ADJUST_KEY },
{ "voice-queue", required_argument, NULL, TIM_OPT_VOICE_QUEUE },
{ "patch-path", required_argument, NULL, TIM_OPT_PATCH_PATH },
{ "pcm-file", required_argument, NULL, TIM_OPT_PCM_FILE },
{ "decay-time", required_argument, NULL, TIM_OPT_DECAY_TIME },
{ "interpolation", required_argument, NULL, TIM_OPT_INTERPOLATION },
{ "output-mode", required_argument, NULL, TIM_OPT_OUTPUT_MODE },
{ "output-stereo", no_argument, NULL, TIM_OPT_OUTPUT_STEREO },
{ "output-mono", no_argument, NULL, TIM_OPT_OUTPUT_STEREO },
{ "output-signed", no_argument, NULL, TIM_OPT_OUTPUT_SIGNED },
{ "output-unsigned", no_argument, NULL, TIM_OPT_OUTPUT_SIGNED },
{ "output-16bit", no_argument, NULL, TIM_OPT_OUTPUT_BITWIDTH },
{ "output-24bit", no_argument, NULL, TIM_OPT_OUTPUT_BITWIDTH },
{ "output-8bit", no_argument, NULL, TIM_OPT_OUTPUT_BITWIDTH },
{ "output-linear", no_argument, NULL, TIM_OPT_OUTPUT_FORMAT },
{ "output-ulaw", no_argument, NULL, TIM_OPT_OUTPUT_FORMAT },
{ "output-alaw", no_argument, NULL, TIM_OPT_OUTPUT_FORMAT },
{ "no-output-swab", no_argument, NULL, TIM_OPT_OUTPUT_SWAB },
{ "output-swab", optional_argument, NULL, TIM_OPT_OUTPUT_SWAB },
#ifdef AU_FLAC
{ "flac-verify", no_argument, NULL, TIM_OPT_FLAC_VERIFY },
{ "flac-padding", required_argument, NULL, TIM_OPT_FLAC_PADDING },
{ "flac-complevel", required_argument, NULL, TIM_OPT_FLAC_COMPLEVEL },
#ifdef AU_OGGFLAC
{ "oggflac", no_argument, NULL, TIM_OPT_FLAC_OGGFLAC },
#endif /* AU_OGGFLAC */
#endif /* AU_FLAC */
#ifdef AU_SPEEX
{ "speex-quality", required_argument, NULL, TIM_OPT_SPEEX_QUALITY },
{ "speex-vbr", no_argument, NULL, TIM_OPT_SPEEX_VBR },
{ "speex-abr", required_argument, NULL, TIM_OPT_SPEEX_ABR },
{ "speex-vad", no_argument, NULL, TIM_OPT_SPEEX_VAD },
{ "speex-dtx", no_argument, NULL, TIM_OPT_SPEEX_DTX },
{ "speex-complexity", required_argument, NULL, TIM_OPT_SPEEX_COMPLEXITY },
{ "speex-nframes", required_argument, NULL, TIM_OPT_SPEEX_NFRAMES },
#endif /* AU_SPEEX */
{ "output-file", required_argument, NULL, TIM_OPT_OUTPUT_FILE },
{ "patch-file", required_argument, NULL, TIM_OPT_PATCH_FILE },
{ "polyphony", required_argument, NULL, TIM_OPT_POLYPHONY },
{ "no-polyphony-reduction", no_argument, NULL, TIM_OPT_POLY_REDUCE },
{ "polyphony-reduction", optional_argument, NULL, TIM_OPT_POLY_REDUCE },
{ "mute", required_argument, NULL, TIM_OPT_MUTE },
{ "temper-mute", required_argument, NULL, TIM_OPT_TEMPER_MUTE },
{ "preserve-silence", no_argument, NULL, TIM_OPT_PRESERVE_SILENCE },
{ "audio-buffer", required_argument, NULL, TIM_OPT_AUDIO_BUFFER },
{ "cache-size", required_argument, NULL, TIM_OPT_CACHE_SIZE },
{ "sampling-freq", required_argument, NULL, TIM_OPT_SAMPLE_FREQ },
{ "adjust-tempo", required_argument, NULL, TIM_OPT_ADJUST_TEMPO },
{ "output-charset", required_argument, NULL, TIM_OPT_CHARSET },
{ "no-unload-instruments", no_argument, NULL, TIM_OPT_UNLOAD_INST },
{ "unload-instruments", optional_argument, NULL, TIM_OPT_UNLOAD_INST },
{ "volume-curve", required_argument, NULL, TIM_OPT_VOLUME_CURVE },
{ "version", no_argument, NULL, TIM_OPT_VERSION },
{ "wrd", required_argument, NULL, TIM_OPT_WRD },
#ifdef __W32__
{ "rcpcv-dll", required_argument, NULL, TIM_OPT_RCPCV_DLL },
#endif
{ "config-string", required_argument, NULL, TIM_OPT_CONFIG_STR },
{ "freq-table", required_argument, NULL, TIM_OPT_FREQ_TABLE },
{ "pure-intonation", optional_argument, NULL, TIM_OPT_PURE_INT },
{ "module", required_argument, NULL, TIM_OPT_MODULE },
{ NULL, no_argument, NULL, '\0' }
};
#define INTERACTIVE_INTERFACE_IDS "kmqagrwAWNP"
/* main interfaces (To be used another main) */
#if defined(main) || defined(ANOTHER_MAIN) || defined ( IA_W32GUI ) || defined ( IA_W32G_SYN )
#define MAIN_INTERFACE
#else
#define MAIN_INTERFACE static
#endif /* main */
MAIN_INTERFACE void timidity_start_initialize(void);
MAIN_INTERFACE int timidity_pre_load_configuration(void);
MAIN_INTERFACE int timidity_post_load_configuration(void);
MAIN_INTERFACE void timidity_init_player(void);
MAIN_INTERFACE int timidity_play_main(int nfiles, char **files);
MAIN_INTERFACE int got_a_configuration;
char *wrdt_open_opts = NULL;
char *opt_aq_max_buff = NULL,
*opt_aq_fill_buff = NULL;
void timidity_init_aq_buff(void);
int opt_control_ratio = 0; /* Save -C option */
#ifdef AU_PORTAUDIO
extern int opt_pa_device_id;
#endif
#ifdef AU_W32
extern int opt_wmme_device_id;
#endif
int set_extension_modes(char *);
int set_ctl(char *);
int set_play_mode(char *);
int set_wrd(char *);
MAIN_INTERFACE int set_tim_opt_short(int, char *);
MAIN_INTERFACE int set_tim_opt_long(int, char *, int);
static inline int parse_opt_A(const char *);
static inline int parse_opt_drum_power(const char *);
static inline int parse_opt_volume_comp(const char *);
static inline int parse_opt_a(const char *);
static inline int parse_opt_B(const char *);
static inline int parse_opt_C(const char *);
static inline int parse_opt_c(char *);
static inline int parse_opt_D(const char *);
static inline int parse_opt_d(const char *);
static inline int parse_opt_E(char *);
static inline int parse_opt_mod_wheel(const char *);
static inline int parse_opt_portamento(const char *);
static inline int parse_opt_vibrato(const char *);
static inline int parse_opt_ch_pressure(const char *);
static inline int parse_opt_mod_env(const char *);
static inline int parse_opt_trace_text(const char *);
static inline int parse_opt_overlap_voice(const char *);
static inline int parse_opt_temper_control(const char *);
static inline int parse_opt_default_mid(char *);
static inline int parse_opt_system_mid(char *);
static inline int parse_opt_default_bank(const char *);
static inline int parse_opt_force_bank(const char *);
static inline int parse_opt_default_program(const char *);
static inline int parse_opt_force_program(const char *);
static inline int set_default_program(int);
static inline int parse_opt_delay(const char *);
static inline int parse_opt_chorus(const char *);
static inline int parse_opt_reverb(const char *);
static int parse_opt_reverb_freeverb(const char *arg, char type);
static inline int parse_opt_voice_lpf(const char *);
static inline int parse_opt_noise_shaping(const char *);
static inline int parse_opt_resample(const char *);
static inline int parse_opt_e(const char *);
static inline int parse_opt_F(const char *);
static inline int parse_opt_f(const char *);
static inline int parse_opt_g(const char *);
static inline int parse_opt_H(const char *);
__attribute__((noreturn))
static inline int parse_opt_h(const char *);
#ifdef IA_DYNAMIC
static inline void list_dyna_interface(FILE *, char *, char *);
ControlMode *dynamic_interface_module(int);
#endif
static inline int parse_opt_i(const char *);
static inline int parse_opt_verbose(const char *);
static inline int parse_opt_quiet(const char *);
static inline int parse_opt_trace(const char *);
static inline int parse_opt_loop(const char *);
static inline int parse_opt_random(const char *);
static inline int parse_opt_sort(const char *);
#ifdef IA_ALSASEQ
static inline int parse_opt_background(const char *);
static inline int parse_opt_rt_prio(const char *);
static inline int parse_opt_seq_ports(const char *);
#endif
#if defined(IA_WINSYN) || defined(IA_PORTMIDISYN) || defined(IA_NPSYN) || defined(IA_W32G_SYN) || defined(IA_W32GUI)
static inline int parse_opt_rtsyn_latency(const char *);
#endif
static inline int parse_opt_j(const char *);
static inline int parse_opt_K(const char *);
static inline int parse_opt_k(const char *);
static inline int parse_opt_L(char *);
static inline int parse_opt_M(const char *);
static inline int parse_opt_m(const char *);
static inline int parse_opt_N(const char *);
static inline int parse_opt_O(const char *);
static inline int parse_opt_output_stereo(const char *);
static inline int parse_opt_output_signed(const char *);
static inline int parse_opt_output_bitwidth(const char *);
static inline int parse_opt_output_format(const char *);
static inline int parse_opt_output_swab(const char *);
#if defined(AU_PORTAUDIO) || defined(AU_W32)
static inline int parse_opt_output_device(const char *);
#endif
#ifdef AU_FLAC
static inline int parse_opt_flac_verify(const char *);
static inline int parse_opt_flac_padding(const char *);
static inline int parse_opt_flac_complevel(const char *);
#ifdef AU_OGGFLAC
static inline int parse_opt_flac_oggflac(const char *);
#endif /* AU_OGGFLAC */
#endif /* AU_FLAC */
#ifdef AU_SPEEX
static inline int parse_opt_speex_quality(const char *);
static inline int parse_opt_speex_vbr(const char *);
static inline int parse_opt_speex_abr(const char *);
static inline int parse_opt_speex_vad(const char *);
static inline int parse_opt_speex_dtx(const char *);
static inline int parse_opt_speex_complexity(const char *);
static inline int parse_opt_speex_nframes(const char *);
#endif /* AU_SPEEX */
static inline int parse_opt_o(char *);
static inline int parse_opt_P(const char *);
static inline int parse_opt_p(const char *);
static inline int parse_opt_p1(const char *);
static inline int parse_opt_Q(const char *);
static inline int parse_opt_Q1(const char *);
static inline int parse_opt_preserve_silence(const char *);
static inline int parse_opt_q(const char *);
static inline int parse_opt_R(const char *);
static inline int parse_opt_S(const char *);
static inline int parse_opt_s(const char *);
static inline int parse_opt_T(const char *);
static inline int parse_opt_t(const char *);
static inline int parse_opt_U(const char *);
static inline int parse_opt_volume_curve(char *);
__attribute__((noreturn))
static inline int parse_opt_v(const char *);
static inline int parse_opt_W(char *);
#ifdef __W32__
static inline int parse_opt_w(const char *);
#endif
static inline int parse_opt_x(char *);
static inline void expand_escape_string(char *);
static inline int parse_opt_Z(char *);
static inline int parse_opt_Z1(const char *);
static inline int parse_opt_default_module(const char *);
__attribute__((noreturn))
static inline int parse_opt_fail(const char *);
static inline int set_value(int *, int, int, int, char *);
static inline int set_val_i32(int32 *, int32, int32, int32, char *);
static int parse_val_float_t(FLOAT_T *param, const char *arg, FLOAT_T low, FLOAT_T high, const char *name);
static inline int set_val_float_t(FLOAT_T *param, FLOAT_T i, FLOAT_T low, FLOAT_T high, const char *name);
static inline int set_channel_flag(ChannelBitMask *, int32, char *);
static inline int y_or_n_p(const char *);
static inline int set_flag(int32 *, int32, const char *);
static inline FILE *open_pager(void);
static inline void close_pager(FILE *);
static void interesting_message(void);
#ifdef IA_DYNAMIC
MAIN_INTERFACE char dynamic_interface_id;
#endif /* IA_DYNAMIC */
extern StringTable wrd_read_opts;
extern int SecondMode;
extern struct URL_module URL_module_file;
#ifndef __MACOS__
extern struct URL_module URL_module_dir;
#endif /* __MACOS__ */
#ifdef SUPPORT_SOCKET
extern struct URL_module URL_module_http;
extern struct URL_module URL_module_ftp;
extern struct URL_module URL_module_news;
extern struct URL_module URL_module_newsgroup;
#endif /* SUPPORT_SOCKET */
#ifdef HAVE_POPEN
extern struct URL_module URL_module_pipe;
#endif /* HAVE_POPEN */
MAIN_INTERFACE struct URL_module *url_module_list[] =
{
&URL_module_file,
#ifndef __MACOS__
&URL_module_dir,
#endif /* __MACOS__ */
#ifdef SUPPORT_SOCKET
&URL_module_http,
&URL_module_ftp,
&URL_module_news,
&URL_module_newsgroup,
#endif /* SUPPORT_SOCKET */
#if !defined(__MACOS__) && defined(HAVE_POPEN)
&URL_module_pipe,
#endif
#if defined(main) || defined(ANOTHER_MAIN)
/* You can put some other modules */
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
#endif /* main */
NULL
};
#ifdef IA_DYNAMIC
#include "dlutils.h"
#ifndef SHARED_LIB_PATH
#define SHARED_LIB_PATH PKGLIBDIR
#endif /* SHARED_LIB_PATH */
static char *dynamic_lib_root = NULL;
#endif /* IA_DYNAMIC */
#ifndef MAXPATHLEN
#define MAXPATHLEN 1024
#endif /* MAXPATHLEN */
int free_instruments_afterwards=0;
int def_prog = -1;
char def_instr_name[256]="";
VOLATILE int intr = 0;
#ifdef __W32__
CRITICAL_SECTION critSect;
#pragma argsused
static BOOL WINAPI handler(DWORD dw)
{
#if defined(IA_WINSYN) || defined(IA_PORTMIDISYN)
if (ctl->id_character == 'W' || ctl->id_character == 'P')
rtsyn_midiports_close();
#endif
#if 0
#if defined(IA_NPSYN)
if (ctl->id_character == 'N')
return FALSE; /* why FALSE need? It must close by intr++; */
#endif
#endif
printf ("***BREAK" NLS);
fflush(stdout);
intr++;
return TRUE;
}
#endif
int effect_lr_mode = -1;
/* 0: left delay
* 1: right delay
* 2: rotate
* -1: not use
*/
int effect_lr_delay_msec = 25;
extern char* pcm_alternate_file;
/* NULL, "none": disabled (default)
* "auto": automatically selected
* filename: use the one.
*/
#ifndef atof
extern double atof(const char *);
#endif
/*! copy bank and, if necessary, map appropriately */
static void copybank(ToneBank *to, ToneBank *from, int mapid, int bankmapfrom, int bankno)
{
ToneBankElement *toelm, *fromelm;
int i;
if (from == NULL)
return;
for(i = 0; i < 128; i++)
{
toelm = &to->tone[i];
fromelm = &from->tone[i];
if (fromelm->name == NULL)
continue;
copy_tone_bank_element(toelm, fromelm);
toelm->instrument = NULL;
if (mapid != INST_NO_MAP)
set_instrument_map(mapid, bankmapfrom, i, bankno, i);
}
}
/*! copy the whole mapped bank. returns 0 if no error. */
static int copymap(int mapto, int mapfrom, int isdrum)
{
ToneBank **tb = isdrum ? drumset : tonebank;
int i, bankfrom, bankto;
for(i = 0; i < 128; i++)
{
bankfrom = find_instrument_map_bank(isdrum, mapfrom, i);
if (bankfrom <= 0) /* not mapped */
continue;
bankto = alloc_instrument_map_bank(isdrum, mapto, i);
if (bankto == -1) /* failed */
return 1;
copybank(tb[bankto], tb[bankfrom], mapto, i, bankto);
}
return 0;
}
static float *config_parse_tune(const char *cp, int *num)
{
const char *p;
float *tune_list;
int i;
/* count num */
*num = 1, p = cp;
while ((p = strchr(p, ',')) != NULL)
(*num)++, p++;
/* alloc */
tune_list = (float *) safe_malloc((*num) * sizeof(float));
/* regist */
for (i = 0, p = cp; i < *num; i++, p++) {
tune_list[i] = atof(p);
if (! (p = strchr(p, ',')))
break;
}
return tune_list;
}
static int16 *config_parse_int16(const char *cp, int *num)
{
const char *p;
int16 *list;
int i;
/* count num */
*num = 1, p = cp;
while ((p = strchr(p, ',')) != NULL)
(*num)++, p++;
/* alloc */
list = (int16 *) safe_malloc((*num) * sizeof(int16));
/* regist */
for (i = 0, p = cp; i < *num; i++, p++) {
list[i] = atoi(p);
if (! (p = strchr(p, ',')))
break;
}
return list;
}
static int **config_parse_envelope(const char *cp, int *num)
{
const char *p, *px;
int **env_list;
int i, j;
/* count num */
*num = 1, p = cp;
while ((p = strchr(p, ',')) != NULL)
(*num)++, p++;
/* alloc */
env_list = (int **) safe_malloc((*num) * sizeof(int *));
for (i = 0; i < *num; i++)
env_list[i] = (int *) safe_malloc(6 * sizeof(int));
/* init */
for (i = 0; i < *num; i++)
for (j = 0; j < 6; j++)
env_list[i][j] = -1;
/* regist */
for (i = 0, p = cp; i < *num; i++, p++) {
px = strchr(p, ',');
for (j = 0; j < 6; j++, p++) {
if (*p == ':')
continue;
env_list[i][j] = atoi(p);
if (! (p = strchr(p, ':')))
break;
if (px && p > px)
break;
}
if (! (p = px))
break;
}
return env_list;
}
static Quantity **config_parse_modulation(const char *name, int line, const char *cp, int *num, int mod_type)
{
const char *p, *px, *err;
char buf[128], *delim;
Quantity **mod_list;
int i, j;
static const char * qtypestr[] = {"tremolo", "vibrato"};
static const uint16 qtypes[] = {
QUANTITY_UNIT_TYPE(TREMOLO_SWEEP), QUANTITY_UNIT_TYPE(TREMOLO_RATE), QUANTITY_UNIT_TYPE(DIRECT_INT),
QUANTITY_UNIT_TYPE(VIBRATO_SWEEP), QUANTITY_UNIT_TYPE(VIBRATO_RATE), QUANTITY_UNIT_TYPE(DIRECT_INT)
};
/* count num */
*num = 1, p = cp;
while ((p = strchr(p, ',')) != NULL)
(*num)++, p++;
/* alloc */
mod_list = (Quantity **) safe_malloc((*num) * sizeof(Quantity *));
for (i = 0; i < *num; i++)
mod_list[i] = (Quantity *) safe_malloc(3 * sizeof(Quantity));
/* init */
for (i = 0; i < *num; i++)
for (j = 0; j < 3; j++)
INIT_QUANTITY(mod_list[i][j]);
buf[sizeof buf - 1] = '\0';
/* regist */
for (i = 0, p = cp; i < *num; i++, p++) {
px = strchr(p, ',');
for (j = 0; j < 3; j++, p++) {
if (*p == ':')
continue;
if ((delim = strpbrk(strncpy(buf, p, sizeof buf - 1), ":,")) != NULL)
*delim = '\0';
if (*buf != '\0' && (err = string_to_quantity(buf, &mod_list[i][j], qtypes[mod_type * 3 + j])) != NULL) {
ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: line %d: %s: parameter %d of item %d: %s (%s)",
name, line, qtypestr[mod_type], j+1, i+1, err, buf);
free_ptr_list(mod_list, *num);
mod_list = NULL;
*num = 0;
return NULL;
}
if (! (p = strchr(p, ':')))
break;
if (px && p > px)
break;
}
if (! (p = px))
break;
}
return mod_list;
}
static int set_gus_patchconf_opts(char *name,
int line, char *opts, ToneBankElement *tone)
{
char *cp;
int k;
if (! (cp = strchr(opts, '='))) {
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: bad patch option %s", name, line, opts);
return 1;
}
*cp++ = 0;
if (! strcmp(opts, "amp")) {
k = atoi(cp);
if ((k < 0 || k > MAX_AMPLIFICATION) || (*cp < '0' || *cp > '9')) {
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: amplification must be between 0 and %d",
name, line, MAX_AMPLIFICATION);
return 1;
}
tone->amp = k;
} else if (! strcmp(opts, "note")) {
k = atoi(cp);
if ((k < 0 || k > 127) || (*cp < '0' || *cp > '9')) {
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: note must be between 0 and 127",
name, line);
return 1;
}
tone->note = k;
tone->scltune = config_parse_int16("100", &tone->scltunenum);
} else if (! strcmp(opts, "pan")) {
if (! strcmp(cp, "center"))
k = 64;
else if (! strcmp(cp, "left"))
k = 0;
else if (! strcmp(cp, "right"))
k = 127;
else {
k = ((atoi(cp) + 100) * 100) / 157;
if ((k < 0 || k > 127)
|| (k == 0 && *cp != '-' && (*cp < '0' || *cp > '9'))) {
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: panning must be left, right, "
"center, or between -100 and 100",
name, line);
return 1;
}
}
tone->pan = k;
} else if (! strcmp(opts, "tune"))
tone->tune = config_parse_tune(cp, &tone->tunenum);
else if (! strcmp(opts, "rate"))
tone->envrate = config_parse_envelope(cp, &tone->envratenum);
else if (! strcmp(opts, "offset"))
tone->envofs = config_parse_envelope(cp, &tone->envofsnum);
else if (! strcmp(opts, "keep")) {
if (! strcmp(cp, "env"))
tone->strip_envelope = 0;
else if (! strcmp(cp, "loop"))
tone->strip_loop = 0;
else {
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: keep must be env or loop", name, line);
return 1;
}
} else if (! strcmp(opts, "strip")) {
if (! strcmp(cp, "env"))
tone->strip_envelope = 1;
else if (! strcmp(cp, "loop"))
tone->strip_loop = 1;
else if (! strcmp(cp, "tail"))
tone->strip_tail = 1;
else {
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: strip must be env, loop, or tail",
name, line);
return 1;
}
} else if (! strcmp(opts, "tremolo")) {
if ((tone->trem = config_parse_modulation(name,
line, cp, &tone->tremnum, 0)) == NULL)
return 1;
} else if (! strcmp(opts, "vibrato")) {
if ((tone->vib = config_parse_modulation(name,
line, cp, &tone->vibnum, 1)) == NULL)
return 1;
} else if (! strcmp(opts, "sclnote"))
tone->sclnote = config_parse_int16(cp, &tone->sclnotenum);
else if (! strcmp(opts, "scltune"))
tone->scltune = config_parse_int16(cp, &tone->scltunenum);
else if (! strcmp(opts, "comm")) {
char *p;
if (tone->comment)
free(tone->comment);
p = tone->comment = safe_strdup(cp);
while (*p) {
if (*p == ',')
*p = ' ';
p++;
}
} else if (! strcmp(opts, "modrate"))
tone->modenvrate = config_parse_envelope(cp, &tone->modenvratenum);
else if (! strcmp(opts, "modoffset"))
tone->modenvofs = config_parse_envelope(cp, &tone->modenvofsnum);
else if (! strcmp(opts, "envkeyf"))
tone->envkeyf = config_parse_envelope(cp, &tone->envkeyfnum);
else if (! strcmp(opts, "envvelf"))
tone->envvelf = config_parse_envelope(cp, &tone->envvelfnum);
else if (! strcmp(opts, "modkeyf"))
tone->modenvkeyf = config_parse_envelope(cp, &tone->modenvkeyfnum);
else if (! strcmp(opts, "modvelf"))
tone->modenvvelf = config_parse_envelope(cp, &tone->modenvvelfnum);
else if (! strcmp(opts, "trempitch"))
tone->trempitch = config_parse_int16(cp, &tone->trempitchnum);
else if (! strcmp(opts, "tremfc"))
tone->tremfc = config_parse_int16(cp, &tone->tremfcnum);
else if (! strcmp(opts, "modpitch"))
tone->modpitch = config_parse_int16(cp, &tone->modpitchnum);
else if (! strcmp(opts, "modfc"))
tone->modfc = config_parse_int16(cp, &tone->modfcnum);
else if (! strcmp(opts, "fc"))
tone->fc = config_parse_int16(cp, &tone->fcnum);
else if (! strcmp(opts, "q"))
tone->reso = config_parse_int16(cp, &tone->resonum);
else if (! strcmp(opts, "fckeyf")) /* filter key-follow */
tone->key_to_fc = atoi(cp);
else if (! strcmp(opts, "fcvelf")) /* filter velocity-follow */
tone->vel_to_fc = atoi(cp);
else if (! strcmp(opts, "qvelf")) /* resonance velocity-follow */
tone->vel_to_resonance = atoi(cp);
else {
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: bad patch option %s",
name, line, opts);
return 1;
}
return 0;
}
static void reinit_tone_bank_element(ToneBankElement *tone)
{
free_tone_bank_element(tone);
tone->note = tone->pan = -1;
tone->strip_loop = tone->strip_envelope = tone->strip_tail = -1;
tone->amp = -1;
tone->rnddelay = 0;
tone->loop_timeout = 0;
tone->legato = tone->damper_mode = tone->key_to_fc = tone->vel_to_fc = 0;
tone->reverb_send = tone->chorus_send = tone->delay_send = -1;
tone->tva_level = -1;
tone->play_note = -1;
}
#define SET_GUS_PATCHCONF_COMMENT
static int set_gus_patchconf(char *name, int line,
ToneBankElement *tone, char *pat, char **opts)
{
int j;
#ifdef SET_GUS_PATCHCONF_COMMENT
char *old_name = NULL;
if(tone != NULL && tone->name != NULL)
old_name = safe_strdup(tone->name);
#endif
reinit_tone_bank_element(tone);
if(strcmp(pat, "%font") == 0) /* Font extention */
{
/* %font filename bank prog [note-to-use]
* %font filename 128 bank key
*/
if(opts[0] == NULL || opts[1] == NULL || opts[2] == NULL ||
(atoi(opts[1]) == 128 && opts[3] == NULL))
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Syntax error", name, line);
return 1;
}
tone->name = safe_strdup(opts[0]);
tone->instype = 1;
if(atoi(opts[1]) == 128) /* drum */
{
tone->font_bank = 128;
tone->font_preset = atoi(opts[2]);
tone->font_keynote = atoi(opts[3]);
opts += 4;
}
else
{
tone->font_bank = atoi(opts[1]);
tone->font_preset = atoi(opts[2]);
if(opts[3] && isdigit(opts[3][0]))
{
tone->font_keynote = atoi(opts[3]);
opts += 4;
}
else
{
tone->font_keynote = -1;
opts += 3;
}
}
}
else if(strcmp(pat, "%sample") == 0) /* Sample extention */
{
/* %sample filename */
if(opts[0] == NULL)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Syntax error", name, line);
return 1;
}
tone->name = safe_strdup(opts[0]);
tone->instype = 2;
opts++;
}
else
{
tone->instype = 0;
tone->name = safe_strdup(pat);
}
for(j = 0; opts[j] != NULL; j++)
{
int err;
if((err = set_gus_patchconf_opts(name, line, opts[j], tone)) != 0)
return err;
}
#ifdef SET_GUS_PATCHCONF_COMMENT
if(tone->comment == NULL ||
(old_name != NULL && strcmp(old_name,tone->comment) == 0))
{
if(tone->comment != NULL )
free(tone->comment);
tone->comment = safe_strdup(tone->name);
}
if(old_name != NULL)
free(old_name);
#else
if(tone->comment == NULL)
tone->comment = safe_strdup(tone->name);
#endif
return 0;
}
static int set_patchconf(char *name, int line, ToneBank *bank, char *w[], int dr, int mapid, int bankmapfrom, int bankno)
{
int i;
i = atoi(w[0]);
if(!dr)
i -= progbase;
if(i < 0 || i > 127)
{
if(dr)
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Drum number must be between "
"0 and 127",
name, line);
else
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Program must be between "
"%d and %d",
name, line, progbase, 127 + progbase);
return 1;
}
if(!bank)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Must specify tone bank or drum set "
"before assignment", name, line);
return 1;
}
if(set_gus_patchconf(name, line, &bank->tone[i], w[1], w + 2))
return 1;
if (mapid != INST_NO_MAP)
set_instrument_map(mapid, bankmapfrom, i, bankno, i);
return 0;
}
typedef struct {
const char *name;
int mapid, isdrum;
} MapNameEntry;
static int mapnamecompare(const void *name, const void *entry)
{
return strcmp((const char *)name, ((const MapNameEntry *)entry)->name);
}
static int mapname2id(char *name, int *isdrum)
{
static const MapNameEntry data[] = {
/* sorted in alphabetical order */
{"gm2", GM2_TONE_MAP, 0},
{"gm2drum", GM2_DRUM_MAP, 1},
{"sc55", SC_55_TONE_MAP, 0},
{"sc55drum", SC_55_DRUM_MAP, 1},
{"sc88", SC_88_TONE_MAP, 0},
{"sc8850", SC_8850_TONE_MAP, 0},
{"sc8850drum", SC_8850_DRUM_MAP, 1},
{"sc88drum", SC_88_DRUM_MAP, 1},
{"sc88pro", SC_88PRO_TONE_MAP, 0},
{"sc88prodrum", SC_88PRO_DRUM_MAP, 1},
{"xg", XG_NORMAL_MAP, 0},
{"xgdrum", XG_DRUM_MAP, 1},
{"xgsfx126", XG_SFX126_MAP, 1},
{"xgsfx64", XG_SFX64_MAP, 0}
};
const MapNameEntry *found;
found = (MapNameEntry *)bsearch(name, data, sizeof data / sizeof data[0], sizeof data[0], mapnamecompare);
if (found != NULL)
{
*isdrum = found->isdrum;
return found->mapid;
}
return -1;
}
/* string[0] should not be '#' */
static int strip_trailing_comment(char *string, int next_token_index)
{
if (string[next_token_index - 1] == '#' /* strip \1 in /^\S+(#*[ \t].*)/ */
&& (string[next_token_index] == ' ' || string[next_token_index] == '\t'))
{
string[next_token_index] = '\0'; /* new c-string terminator */
while(string[--next_token_index - 1] == '#')
;
}
return next_token_index;
}
static char *expand_variables(char *string, MBlockList *varbuf, const char *basedir)
{
char *p, *expstr;
const char *copystr;
int limlen, copylen, explen, varlen, braced;
if ((p = strchr(string, '$')) == NULL)
return string;
varlen = strlen(basedir);
explen = limlen = 0;
expstr = NULL;
copystr = string;
copylen = p - string;
string = p;
for(;;)
{
if (explen + copylen + 1 > limlen)
{
limlen += copylen + 128;
expstr = memcpy(new_segment(varbuf, limlen), expstr, explen);
}
memcpy(&expstr[explen], copystr, copylen);
explen += copylen;
if (*string == '\0')
break;
else if (*string == '$')
{
braced = *++string == '{';
if (braced)
{
if ((p = strchr(string + 1, '}')) == NULL)
p = string; /* no closing brace */
else
string++;
}
else
for(p = string; isalnum(*p) || *p == '_'; p++) ;
if (p == string) /* empty */
{
copystr = "${";
copylen = 1 + braced;
}
else
{
if (p - string == 7 && memcmp(string, "basedir", 7) == 0)
{
copystr = basedir;
copylen = varlen;
}
else /* undefined variable */
copylen = 0;
string = p + braced;
}
}
else /* search next */
{
p = strchr(string, '$');
if (p == NULL)
copylen = strlen(string);
else
copylen = p - string;
copystr = string;
string += copylen;
}
}
expstr[explen] = '\0';
return expstr;
}
#define MAXWORDS 130
#define CHECKERRLIMIT \
if(++errcnt >= 10) { \
ctl->cmsg(CMSG_ERROR, VERB_NORMAL, \
"Too many errors... Give up read %s", name); \
reuse_mblock(&varbuf); \
close_file(tf); return 1; }
MAIN_INTERFACE int read_config_file(char *name, int self)
{
struct timidity_file *tf;
char buf[1024], *tmp, *w[MAXWORDS + 1], *cp;
ToneBank *bank = NULL;
int i, j, k, line = 0, words, errcnt = 0;
static int rcf_count = 0;
int dr = 0, bankno = 0, mapid = INST_NO_MAP, origbankno = 0x7FFFFFFF;
int extension_flag, param_parse_err;
MBlockList varbuf;
char *basedir, *sep;
if(rcf_count > 50)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"Probable source loop in configuration files");
return 2;
}
if(self)
{
tf = open_with_mem(name, (int32)strlen(name), OF_VERBOSE);
name = "(configuration)";
}
else
tf = open_file(name, 1, OF_VERBOSE);
if(tf == NULL)
return 1;
init_mblock(&varbuf);
if (!self)
{
basedir = strdup_mblock(&varbuf, current_filename);
if (is_url_prefix(basedir))
sep = strrchr(basedir, '/');
else
sep = pathsep_strrchr(basedir);
}
else
sep = NULL;
if (sep == NULL)
{
#ifndef __MACOS__
basedir = ".";
#else
basedir = "";
#endif
}
else
{
if ((cp = strchr(sep, '#')) != NULL)
sep = cp + 1; /* inclusive of '#' */
*sep = '\0';
}
errno = 0;
while(tf_gets(buf, sizeof(buf), tf))
{
line++;
if(strncmp(buf, "#extension", 10) == 0) {
extension_flag = 1;
i = 10;
}
else
{
extension_flag = 0;
i = 0;
}
while(isspace(buf[i])) /* skip /^\s*(?#)/ */
i++;
if (buf[i] == '#' || buf[i] == '\0') /* /^#|^$/ */
continue;
tmp = expand_variables(buf, &varbuf, basedir);
j = strcspn(tmp + i, " \t\r\n\240");
if (j == 0)
j = strlen(tmp + i);
j = strip_trailing_comment(tmp + i, j);
tmp[i + j] = '\0'; /* terminate the first token */
w[0] = tmp + i;
i += j + 1;
words = param_parse_err = 0;
while(words < MAXWORDS - 1) /* -1 : next arg */
{
char *terminator;
while(isspace(tmp[i])) /* skip /^\s*(?#)/ */
i++;
if (tmp[i] == '\0'
|| tmp[i] == '#') /* /\s#/ */
break;
if ((tmp[i] == '"' || tmp[i] == '\'')
&& (terminator = strchr(tmp + i + 1, tmp[i])) != NULL)
{
if (!isspace(terminator[1]) && terminator[1] != '\0')
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: there must be at least one whitespace between "
"string terminator (%c) and the next parameter", name, line, tmp[i]);
CHECKERRLIMIT;
param_parse_err = 1;
break;
}
w[++words] = tmp + i + 1;
i = terminator - tmp + 1;
*terminator = '\0';
}
else /* not terminated */
{
j = strcspn(tmp + i, " \t\r\n\240");
if (j > 0)
j = strip_trailing_comment(tmp + i, j);
w[++words] = tmp + i;
i += j;
if (tmp[i] != '\0') /* unless at the end-of-string (i.e. EOF) */
tmp[i++] = '\0'; /* terminate the token */
}
}
if (param_parse_err)
continue;
w[++words] = NULL;
/*
* #extension [something...]
*/
/* #extension comm program comment */
if(strcmp(w[0], "comm") == 0)
{
char *p;
if(words != 3)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: syntax error", name, line);
CHECKERRLIMIT;
continue;
}
if(!bank)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Must specify tone bank or drum "
"set before assignment", name, line);
CHECKERRLIMIT;
continue;
}
i = atoi(w[1]);
if(i < 0 || i > 127)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: extension comm must be "
"between 0 and 127", name, line);
CHECKERRLIMIT;
continue;
}
if(bank->tone[i].comment)
free(bank->tone[i].comment);
p = bank->tone[i].comment = safe_strdup(w[2]);
while(*p)
{
if(*p == ',') *p = ' ';
p++;
}
}
/* #extension timeout program sec */
else if(strcmp(w[0], "timeout") == 0)
{
if(words != 3)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: syntax error", name, line);
CHECKERRLIMIT;
continue;
}
if(!bank)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Must specify tone bank or drum set "
"before assignment", name, line);
CHECKERRLIMIT;
continue;
}
i = atoi(w[1]);
if(i < 0 || i > 127)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: extension timeout "
"must be between 0 and 127", name, line);
CHECKERRLIMIT;
continue;
}
bank->tone[i].loop_timeout = atoi(w[2]);
}
/* #extension copydrumset drumset */
else if(strcmp(w[0], "copydrumset") == 0)
{
if(words < 2)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: No copydrumset number given",
name, line);
CHECKERRLIMIT;
continue;
}
i = atoi(w[1]);
if(i < 0 || i > 127)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: extension copydrumset "
"must be between 0 and 127", name, line);
CHECKERRLIMIT;
continue;
}
if(!bank)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Must specify tone bank or "
"drum set before assignment", name, line);
CHECKERRLIMIT;
continue;
}
copybank(bank, drumset[i], mapid, origbankno, bankno);
}
/* #extension copybank bank */
else if(strcmp(w[0], "copybank") == 0)
{
if(words < 2)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: No copybank number given",
name, line);
CHECKERRLIMIT;
continue;
}
i = atoi(w[1]);
if(i < 0 || i > 127)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: extension copybank "
"must be between 0 and 127", name, line);
CHECKERRLIMIT;
continue;
}
if(!bank)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Must specify tone bank or "
"drum set before assignment", name, line);
CHECKERRLIMIT;
continue;
}
copybank(bank, tonebank[i], mapid, origbankno, bankno);
}
/* #extension copymap tomapid frommapid */
else if(strcmp(w[0], "copymap") == 0)
{
int mapto, mapfrom;
int toisdrum, fromisdrum;
if(words != 3)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: syntax error", name, line);
CHECKERRLIMIT;
continue;
}
if ((mapto = mapname2id(w[1], &toisdrum)) == -1)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Invalid map name: %s", name, line, w[1]);
CHECKERRLIMIT;
continue;
}
if ((mapfrom = mapname2id(w[2], &fromisdrum)) == -1)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Invalid map name: %s", name, line, w[2]);
CHECKERRLIMIT;
continue;
}
if (toisdrum != fromisdrum)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Map type should be matched", name, line);
CHECKERRLIMIT;
continue;
}
if (copymap(mapto, mapfrom, toisdrum))
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: No free %s available to map",
name, line, toisdrum ? "drum set" : "tone bank");
CHECKERRLIMIT;
continue;
}
}
/* #extension HTTPproxy hostname:port */
else if(strcmp(w[0], "HTTPproxy") == 0)
{
char r_bracket, l_bracket;
if(words < 2)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: No proxy name given",
name, line);
CHECKERRLIMIT;
continue;
}
/* If network is not supported, this extension is ignored. */
#ifdef SUPPORT_SOCKET
url_http_proxy_host = safe_strdup(w[1]);
if((cp = strrchr(url_http_proxy_host, ':')) == NULL)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Syntax error", name, line);
CHECKERRLIMIT;
continue;
}
*cp++ = '\0';
if((url_http_proxy_port = atoi(cp)) <= 0)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Port number must be "
"positive number", name, line);
CHECKERRLIMIT;
continue;
}
l_bracket = url_http_proxy_host[0];
r_bracket = url_http_proxy_host[strlen(url_http_proxy_host) - 1];
if (l_bracket == '[' || r_bracket == ']')
{
if (l_bracket != '[' || r_bracket != ']')
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Malformed IPv6 address",
name, line);
CHECKERRLIMIT;
continue;
}
url_http_proxy_host++;
url_http_proxy_host[strlen(url_http_proxy_host) - 1] = '\0';
}
#endif
}
/* #extension FTPproxy hostname:port */
else if(strcmp(w[0], "FTPproxy") == 0)
{
char l_bracket, r_bracket;
if(words < 2)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: No proxy name given",
name, line);
CHECKERRLIMIT;
continue;
}
/* If network is not supported, this extension is ignored. */
#ifdef SUPPORT_SOCKET
url_ftp_proxy_host = safe_strdup(w[1]);
if((cp = strrchr(url_ftp_proxy_host, ':')) == NULL)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Syntax error", name, line);
CHECKERRLIMIT;
continue;
}
*cp++ = '\0';
if((url_ftp_proxy_port = atoi(cp)) <= 0)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Port number "
"must be positive number", name, line);
CHECKERRLIMIT;
continue;
}
l_bracket = url_ftp_proxy_host[0];
r_bracket = url_ftp_proxy_host[strlen(url_ftp_proxy_host) - 1];
if (l_bracket == '[' || r_bracket == ']')
{
if (l_bracket != '[' || r_bracket != ']')
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Malformed IPv6 address",
name, line);
CHECKERRLIMIT;
continue;
}
url_ftp_proxy_host++;
url_ftp_proxy_host[strlen(url_ftp_proxy_host) - 1] = '\0';
}
#endif
}
/* #extension mailaddr somebody@someware.domain.com */
else if(strcmp(w[0], "mailaddr") == 0)
{
if(words < 2)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: No mail address given",
name, line);
CHECKERRLIMIT;
continue;
}
if(strchr(w[1], '@') == NULL) {
ctl->cmsg(CMSG_WARNING, VERB_NOISY,
"%s: line %d: Warning: Mail address %s is not valid",
name, line);
}
/* If network is not supported, this extension is ignored. */
#ifdef SUPPORT_SOCKET
user_mailaddr = safe_strdup(w[1]);
#endif /* SUPPORT_SOCKET */
}
/* #extension opt [-]{option}[optarg] */
else if (strcmp(w[0], "opt") == 0) {
int c, longind, err;
char *p, *cmd, *arg;
if (words != 2 && words != 3) {
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Syntax error", name, line);
CHECKERRLIMIT;
continue;
}
if (*w[1] == '-') {
int optind_save = optind;
optind = 0;
#if defined(__MINGW32__) || defined(__CYGWIN__)
optreset = 1;
#endif
c = getopt_long(words, w, optcommands, longopts, &longind);
err = set_tim_opt_long(c, optarg, longind);
optind = optind_save;
} else {
/* backward compatibility */
if ((p = strchr(optcommands, c = *(cmd = w[1]))) == NULL)
err = 1;
else {
if (*(p + 1) == ':')
arg = (words == 2) ? cmd + 1 : w[2];
else
arg = "";
err = set_tim_opt_short(c, arg);
}
}
if (err) {
/* error */
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Invalid command line option",
name, line);
errcnt += err - 1;
CHECKERRLIMIT;
continue;
}
}
/* #extension undef program */
else if(strcmp(w[0], "undef") == 0)
{
if(words < 2)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: No undef number given",
name, line);
CHECKERRLIMIT;
continue;
}
i = atoi(w[1]);
if(i < 0 || i > 127)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: extension undef "
"must be between 0 and 127", name, line);
CHECKERRLIMIT;
continue;
}
if(!bank)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Must specify tone bank or "
"drum set before assignment", name, line);
CHECKERRLIMIT;
continue;
}
free_tone_bank_element(&bank->tone[i]);
}
/* #extension altassign numbers... */
else if(strcmp(w[0], "altassign") == 0)
{
ToneBank *bk;
if(!bank)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Must specify tone bank or drum set "
"before altassign", name, line);
CHECKERRLIMIT;
continue;
}
if(words < 2)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: No alternate assignment", name, line);
CHECKERRLIMIT;
continue;
}
if(!dr) {
ctl->cmsg(CMSG_WARNING, VERB_NORMAL,
"%s: line %d: Warning: Not a drumset altassign"
" (ignored)",
name, line);
continue;
}
bk = drumset[bankno];
bk->alt = add_altassign_string(bk->alt, w + 1, words - 1);
} /* #extension legato [program] [0 or 1] */
else if(strcmp(w[0], "legato") == 0)
{
if(words != 3)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: syntax error", name, line);
CHECKERRLIMIT;
continue;
}
if(!bank)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Must specify tone bank or drum set "
"before assignment", name, line);
CHECKERRLIMIT;
continue;
}
i = atoi(w[1]);
if(i < 0 || i > 127)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: extension legato "
"must be between 0 and 127", name, line);
CHECKERRLIMIT;
continue;
}
bank->tone[i].legato = atoi(w[2]);
} /* #extension damper [program] [0 or 1] */
else if(strcmp(w[0], "damper") == 0)
{
if(words != 3)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: syntax error", name, line);
CHECKERRLIMIT;
continue;
}
if(!bank)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Must specify tone bank or drum set "
"before assignment", name, line);
CHECKERRLIMIT;
continue;
}
i = atoi(w[1]);
if(i < 0 || i > 127)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: extension damper "
"must be between 0 and 127", name, line);
CHECKERRLIMIT;
continue;
}
bank->tone[i].damper_mode = atoi(w[2]);
} /* #extension rnddelay [program] [0 or 1] */
else if(strcmp(w[0], "rnddelay") == 0)
{
if(words != 3)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: syntax error", name, line);
CHECKERRLIMIT;
continue;
}
if(!bank)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Must specify tone bank or drum set "
"before assignment", name, line);
CHECKERRLIMIT;
continue;
}
i = atoi(w[1]);
if(i < 0 || i > 127)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: extension rnddelay "
"must be between 0 and 127", name, line);
CHECKERRLIMIT;
continue;
}
bank->tone[i].rnddelay = atoi(w[2]);
} /* #extension level program tva_level */
else if(strcmp(w[0], "level") == 0)
{
if(words != 3)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: line %d: syntax error", name, line);
CHECKERRLIMIT;
continue;
}
if(!bank)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Must specify tone bank or drum set "
"before assignment", name, line);
CHECKERRLIMIT;
continue;
}
i = atoi(w[2]);
if(i < 0 || i > 127)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: extension level "
"must be between 0 and 127", name, line);
CHECKERRLIMIT;
continue;
}
cp = w[1];
do {
if (string_to_7bit_range(cp, &j, &k))
{
while (j <= k)
bank->tone[j++].tva_level = i;
}
cp = strchr(cp, ',');
} while(cp++ != NULL);
} /* #extension reverbsend */
else if(strcmp(w[0], "reverbsend") == 0)
{
if(words != 3)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: line %d: syntax error", name, line);
CHECKERRLIMIT;
continue;
}
if(!bank)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Must specify tone bank or drum set "
"before assignment", name, line);
CHECKERRLIMIT;
continue;
}
i = atoi(w[2]);
if(i < 0 || i > 127)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: extension reverbsend "
"must be between 0 and 127", name, line);
CHECKERRLIMIT;
continue;
}
cp = w[1];
do {
if (string_to_7bit_range(cp, &j, &k))
{
while (j <= k)
bank->tone[j++].reverb_send = i;
}
cp = strchr(cp, ',');
} while(cp++ != NULL);
} /* #extension chorussend */
else if(strcmp(w[0], "chorussend") == 0)
{
if(words != 3)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: line %d: syntax error", name, line);
CHECKERRLIMIT;
continue;
}
if(!bank)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Must specify tone bank or drum set "
"before assignment", name, line);
CHECKERRLIMIT;
continue;
}
i = atoi(w[2]);
if(i < 0 || i > 127)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: extension chorussend "
"must be between 0 and 127", name, line);
CHECKERRLIMIT;
continue;
}
cp = w[1];
do {
if (string_to_7bit_range(cp, &j, &k))
{
while (j <= k)
bank->tone[j++].chorus_send = i;
}
cp = strchr(cp, ',');
} while(cp++ != NULL);
} /* #extension delaysend */
else if(strcmp(w[0], "delaysend") == 0)
{
if(words != 3)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: line %d: syntax error", name, line);
CHECKERRLIMIT;
continue;
}
if(!bank)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Must specify tone bank or drum set "
"before assignment", name, line);
CHECKERRLIMIT;
continue;
}
i = atoi(w[2]);
if(i < 0 || i > 127)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: extension delaysend "
"must be between 0 and 127", name, line);
CHECKERRLIMIT;
continue;
}
cp = w[1];
do {
if (string_to_7bit_range(cp, &j, &k))
{
while (j <= k)
bank->tone[j++].delay_send = i;
}
cp = strchr(cp, ',');
} while(cp++ != NULL);
} /* #extension playnote */
else if(strcmp(w[0], "playnote") == 0)
{
if(words != 3)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: line %d: syntax error", name, line);
CHECKERRLIMIT;
continue;
}
if(!bank)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Must specify tone bank or drum set "
"before assignment", name, line);
CHECKERRLIMIT;
continue;
}
i = atoi(w[2]);
if(i < 0 || i > 127)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: extension playnote"
"must be between 0 and 127", name, line);
CHECKERRLIMIT;
continue;
}
cp = w[1];
do {
if (string_to_7bit_range(cp, &j, &k))
{
while (j <= k)
bank->tone[j++].play_note = i;
}
cp = strchr(cp, ',');
} while(cp++ != NULL);
}
else if(!strcmp(w[0], "soundfont"))
{
int order, cutoff, isremove, reso, amp;
char *sf_file;
if(words < 2)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: No soundfont file given",
name, line);
CHECKERRLIMIT;
continue;
}
sf_file = w[1];
order = cutoff = reso = amp = -1;
isremove = 0;
for(j = 2; j < words; j++)
{
if(strcmp(w[j], "remove") == 0)
{
isremove = 1;
break;
}
if(!(cp = strchr(w[j], '=')))
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: bad patch option %s",
name, line, w[j]);
CHECKERRLIMIT;
break;
}
*cp++=0;
k = atoi(cp);
if(!strcmp(w[j], "order"))
{
if(k < 0 || (*cp < '0' || *cp > '9'))
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: order must be a digit",
name, line);
CHECKERRLIMIT;
break;
}
order = k;
}
else if(!strcmp(w[j], "cutoff"))
{
if(k < 0 || (*cp < '0' || *cp > '9'))
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: cutoff must be a digit",
name, line);
CHECKERRLIMIT;
break;
}
cutoff = k;
}
else if(!strcmp(w[j], "reso"))
{
if(k < 0 || (*cp < '0' || *cp > '9'))
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: reso must be a digit",
name, line);
CHECKERRLIMIT;
break;
}
reso = k;
}
else if(!strcmp(w[j], "amp"))
{
amp = k;
}
}
if(isremove)
remove_soundfont(sf_file);
else
add_soundfont(sf_file, order, cutoff, reso, amp);
}
else if(!strcmp(w[0], "font"))
{
int bank, preset, keynote;
if(words < 2)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: no font command", name, line);
CHECKERRLIMIT;
continue;
}
if(!strcmp(w[1], "exclude"))
{
if(words < 3)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: No bank/preset/key is given",
name, line);
CHECKERRLIMIT;
continue;
}
bank = atoi(w[2]);
if(words >= 4)
preset = atoi(w[3]) - progbase;
else
preset = -1;
if(words >= 5)
keynote = atoi(w[4]);
else
keynote = -1;
if(exclude_soundfont(bank, preset, keynote))
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: No soundfont is given",
name, line);
CHECKERRLIMIT;
}
}
else if(!strcmp(w[1], "order"))
{
int order;
if(words < 4)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: No order/bank is given",
name, line);
CHECKERRLIMIT;
continue;
}
order = atoi(w[2]);
bank = atoi(w[3]);
if(words >= 5)
preset = atoi(w[4]) - progbase;
else
preset = -1;
if(words >= 6)
keynote = atoi(w[5]);
else
keynote = -1;
if(order_soundfont(bank, preset, keynote, order))
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: No soundfont is given",
name, line);
CHECKERRLIMIT;
}
}
}
else if(!strcmp(w[0], "progbase"))
{
if(words < 2 || *w[1] < '0' || *w[1] > '9')
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: syntax error", name, line);
CHECKERRLIMIT;
continue;
}
progbase = atoi(w[1]);
}
else if(!strcmp(w[0], "map")) /* map <name> set1 elem1 set2 elem2 */
{
int arg[5], isdrum;
if(words != 6)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: syntax error", name, line);
CHECKERRLIMIT;
continue;
}
if((arg[0] = mapname2id(w[1], &isdrum)) == -1)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Invalid map name: %s", name, line, w[1]);
CHECKERRLIMIT;
continue;
}
for(i = 2; i < 6; i++)
arg[i - 1] = atoi(w[i]);
if(isdrum)
{
arg[1] -= progbase;
arg[3] -= progbase;
}
else
{
arg[2] -= progbase;
arg[4] -= progbase;
}
for(i = 1; i < 5; i++)
if(arg[i] < 0 || arg[i] > 127)
break;
if(i != 5)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Invalid parameter", name, line);
CHECKERRLIMIT;
continue;
}
set_instrument_map(arg[0], arg[1], arg[2], arg[3], arg[4]);
}
/*
* Standard configurations
*/
else if(!strcmp(w[0], "dir"))
{
if(words < 2)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: No directory given", name, line);
CHECKERRLIMIT;
continue;
}
for(i = 1; i < words; i++)
add_to_pathlist(w[i]);
}
else if(!strcmp(w[0], "source"))
{
if(words < 2)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: No file name given", name, line);
CHECKERRLIMIT;
continue;
}
for(i = 1; i < words; i++)
{
int status;
rcf_count++;
status = read_config_file(w[i], 0);
rcf_count--;
if(status == 2)
{
reuse_mblock(&varbuf);
close_file(tf);
return 2;
}
else if(status != 0)
{
CHECKERRLIMIT;
continue;
}
}
}
else if(!strcmp(w[0], "default"))
{
if(words != 2)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Must specify exactly one patch name",
name, line);
CHECKERRLIMIT;
continue;
}
strncpy(def_instr_name, w[1], 255);
def_instr_name[255] = '\0';
default_instrument_name = def_instr_name;
}
/* drumset [mapid] num */
else if(!strcmp(w[0], "drumset"))
{
int newmapid, isdrum, newbankno;
if(words < 2)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: No drum set number given", name, line);
CHECKERRLIMIT;
continue;
}
if (words != 2 && !isdigit(*w[1]))
{
if ((newmapid = mapname2id(w[1], &isdrum)) == -1 || !isdrum)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Invalid drum set map name: %s", name, line, w[1]);
CHECKERRLIMIT;
continue;
}
words--;
memmove(&w[1], &w[2], sizeof w[0] * words);
}
else
newmapid = INST_NO_MAP;
i = atoi(w[1]) - progbase;
if(i < 0 || i > 127)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Drum set must be between %d and %d",
name, line,
progbase, progbase + 127);
CHECKERRLIMIT;
continue;
}
newbankno = i;
i = alloc_instrument_map_bank(1, newmapid, i);
if (i == -1)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: No free drum set available to map",
name, line);
CHECKERRLIMIT;
continue;
}
if(words == 2)
{
bank = drumset[i];
bankno = i;
mapid = newmapid;
origbankno = newbankno;
dr = 1;
}
else
{
if(words < 4 || *w[2] < '0' || *w[2] > '9')
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: syntax error", name, line);
CHECKERRLIMIT;
continue;
}
if (set_patchconf(name, line, drumset[i], &w[2], 1, newmapid, newbankno, i))
{
CHECKERRLIMIT;
continue;
}
}
}
/* bank [mapid] num */
else if(!strcmp(w[0], "bank"))
{
int newmapid, isdrum, newbankno;
if(words < 2)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: No bank number given", name, line);
CHECKERRLIMIT;
continue;
}
if (words != 2 && !isdigit(*w[1]))
{
if ((newmapid = mapname2id(w[1], &isdrum)) == -1 || isdrum)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Invalid bank map name: %s", name, line, w[1]);
CHECKERRLIMIT;
continue;
}
words--;
memmove(&w[1], &w[2], sizeof w[0] * words);
}
else
newmapid = INST_NO_MAP;
i = atoi(w[1]);
if(i < 0 || i > 127)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: Tone bank must be between 0 and 127",
name, line);
CHECKERRLIMIT;
continue;
}
newbankno = i;
i = alloc_instrument_map_bank(0, newmapid, i);
if (i == -1)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: No free tone bank available to map",
name, line);
CHECKERRLIMIT;
continue;
}
if(words == 2)
{
bank = tonebank[i];
bankno = i;
mapid = newmapid;
origbankno = newbankno;
dr = 0;
}
else
{
if(words < 4 || *w[2] < '0' || *w[2] > '9')
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: syntax error", name, line);
CHECKERRLIMIT;
continue;
}
if (set_patchconf(name, line, tonebank[i], &w[2], 0, newmapid, newbankno, i))
{
CHECKERRLIMIT;
continue;
}
}
}
else
{
if(words < 2 || *w[0] < '0' || *w[0] > '9')
{
if(extension_flag)
continue;
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"%s: line %d: syntax error", name, line);
CHECKERRLIMIT;
continue;
}
if (set_patchconf(name, line, bank, w, dr, mapid, origbankno, bankno))
{
CHECKERRLIMIT;
continue;
}
}
}
if(errno)
{
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"Can't read %s: %s", name, strerror(errno));
errcnt++;
}
reuse_mblock(&varbuf);
close_file(tf);
return errcnt != 0;
}
#ifdef SUPPORT_SOCKET
#if defined(__W32__) && !defined(MAIL_NAME)
#define MAIL_NAME "anonymous"
#endif /* __W32__ */
#ifdef MAIL_NAME
#define get_username() MAIL_NAME
#else /* MAIL_NAME */
#include <pwd.h>
static char *get_username(void)
{
char *p;
struct passwd *pass;
/* USER
* LOGIN
* LOGNAME
* getpwnam()
*/
if((p = getenv("USER")) != NULL)
return p;
if((p = getenv("LOGIN")) != NULL)
return p;
if((p = getenv("LOGNAME")) != NULL)
return p;
pass = getpwuid(getuid());
if(pass == NULL)
return "nobody";
return pass->pw_name;
}
#endif /* MAIL_NAME */
static void init_mail_addr(void)
{
char addr[BUFSIZ];
sprintf(addr, "%s%s", get_username(), MAIL_DOMAIN);
user_mailaddr = safe_strdup(addr);
}
#endif /* SUPPORT_SOCKET */
static int read_user_config_file(void)
{
char *home;
char path[BUFSIZ];
int opencheck;
#ifdef __W32__
/* HOME or home */
home = getenv("HOME");
if(home == NULL)
home = getenv("home");
if(home == NULL)
{
ctl->cmsg(CMSG_INFO, VERB_NOISY,
"Warning: HOME environment is not defined.");
return 0;
}
/* .timidity.cfg or timidity.cfg */
sprintf(path, "%s" PATH_STRING "timidity.cfg", home);
if((opencheck = open(path, 0)) < 0)
{
sprintf(path, "%s" PATH_STRING "_timidity.cfg", home);
if((opencheck = open(path, 0)) < 0)
{
sprintf(path, "%s" PATH_STRING ".timidity.cfg", home);
if((opencheck = open(path, 0)) < 0)
{
ctl->cmsg(CMSG_INFO, VERB_NOISY, "%s: %s",
path, strerror(errno));
return 0;
}
}
}
close(opencheck);
return read_config_file(path, 0);
#else
home = getenv("HOME");
if(home == NULL)
{
ctl->cmsg(CMSG_INFO, VERB_NOISY,
"Warning: HOME environment is not defined.");
return -1;
}
sprintf(path, "%s" PATH_STRING "Documents/timidity.cfg", home);
if((opencheck = open(path, 0)) < 0)
{
ctl->cmsg(CMSG_INFO, VERB_NOISY, "%s: %s",
path, strerror(errno));
return -2;
}
close(opencheck);
return read_config_file(path, 0);
#endif /* __W32__ */
}
MAIN_INTERFACE void tmdy_free_config(void)
{
free_tone_bank();
free_instrument_map();
clean_up_pathlist();
}
int set_extension_modes(char *flag)
{
return parse_opt_E(flag);
}
int set_ctl(char *cp)
{
return parse_opt_i(cp);
}
int set_play_mode(char *cp)
{
return parse_opt_O(cp);
}
int set_wrd(char *w)
{
return parse_opt_W(w);
}
#ifdef __W32__
int opt_evil_mode = 0;
#ifdef SMFCONV
int opt_rcpcv_dll = 0;
#endif /* SMFCONV */
#endif /* __W32__ */
static int try_config_again = 0;
int32 opt_output_rate = 0;
static char *opt_output_name = NULL;
static StringTable opt_config_string;
#ifdef SUPPORT_SOUNDSPEC
static double spectrogram_update_sec = 0.0;
#endif /* SUPPORT_SOUNDSPEC */
int opt_buffer_fragments = -1;
MAIN_INTERFACE int set_tim_opt_short(int c, char *optarg)
{
int err = 0;
switch (c) {
case '4':
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"-4 option is obsoleted. Please use -N");
return 1;
case 'A':
if (*optarg != ',' && *optarg != 'a')
err += parse_opt_A(optarg);
if (strchr(optarg, ','))
err += parse_opt_drum_power(strchr(optarg, ',') + 1);
if (strchr(optarg, 'a'))
opt_amp_compensation = 1;
return err;
case 'a':
antialiasing_allowed = 1;
break;
case 'B':
return parse_opt_B(optarg);
case 'C':
return parse_opt_C(optarg);
case 'c':
return parse_opt_c(optarg);
case 'D':
return parse_opt_D(optarg);
case 'd':
return parse_opt_d(optarg);
case 'E':
return parse_opt_E(optarg);
case 'e':
return parse_opt_e(optarg);
case 'F':
adjust_panning_immediately = (adjust_panning_immediately) ? 0 : 1;
break;
case 'f':
fast_decay = (fast_decay) ? 0 : 1;
break;
case 'g':
return parse_opt_g(optarg);
case 'H':
return parse_opt_H(optarg);
case 'h':
return parse_opt_h(optarg);
case 'I':
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"-I option is obsoleted. Please use -Ei");
return 1;
case 'i':
return parse_opt_i(optarg);
case 'j':
opt_realtime_playing = (opt_realtime_playing) ? 0 : 1;
break;
case 'K':
return parse_opt_K(optarg);
case 'k':
return parse_opt_k(optarg);
case 'L':
return parse_opt_L(optarg);
case 'M':
return parse_opt_M(optarg);
case 'm':
return parse_opt_m(optarg);
case 'N':
return parse_opt_N(optarg);
case 'O':
return parse_opt_O(optarg);
case 'o':
return parse_opt_o(optarg);
case 'P':
return parse_opt_P(optarg);
case 'p':
if (*optarg != 'a')
err += parse_opt_p(optarg);
if (strchr(optarg, 'a'))
auto_reduce_polyphony = (auto_reduce_polyphony) ? 0 : 1;
return err;
case 'Q':
return parse_opt_Q(optarg);
case 'q':
return parse_opt_q(optarg);
case 'R':
return parse_opt_R(optarg);
case 'S':
return parse_opt_S(optarg);
case 's':
return parse_opt_s(optarg);
case 'T':
return parse_opt_T(optarg);
case 't':
return parse_opt_t(optarg);
case 'U':
free_instruments_afterwards = 1;
break;
case 'V':
return parse_opt_volume_curve(optarg);
case 'v':
return parse_opt_v(optarg);
case 'W':
return parse_opt_W(optarg);
#ifdef __W32__
case 'w':
return parse_opt_w(optarg);
#endif
case 'x':
return parse_opt_x(optarg);
case 'Z':
if (strncmp(optarg, "pure", 4))
return parse_opt_Z(optarg);
else
return parse_opt_Z1(optarg + 4);
default:
return 1;
}
return 0;
}
MAIN_INTERFACE int set_tim_opt_short_cfg(int c, char *optarg)
{
int err = 0;
switch (c) {
case 'c':
return parse_opt_c(optarg);
}
return 0;
}
/* -------- getopt_long -------- */
MAIN_INTERFACE int set_tim_opt_long(int c, char *optarg, int index)
{
const struct option *the_option = &(longopts[index]);
char *arg;
if (c == '?') /* getopt_long failed parsing */
parse_opt_fail(optarg);
else if (c < TIM_OPT_FIRST)
return set_tim_opt_short(c, optarg);
if (! strncmp(the_option->name, "no-", 3))
arg = "no"; /* `reverse' switch */
else
arg = optarg;
switch (c) {
case TIM_OPT_VOLUME:
return parse_opt_A(arg);
case TIM_OPT_DRUM_POWER:
return parse_opt_drum_power(arg);
case TIM_OPT_VOLUME_COMP:
return parse_opt_volume_comp(arg);
case TIM_OPT_ANTI_ALIAS:
return parse_opt_a(arg);
case TIM_OPT_BUFFER_FRAGS:
return parse_opt_B(arg);
case TIM_OPT_CONTROL_RATIO:
return parse_opt_C(arg);
case TIM_OPT_CONFIG_FILE:
return parse_opt_c(arg);
case TIM_OPT_DRUM_CHANNEL:
return parse_opt_D(arg);
case TIM_OPT_IFACE_PATH:
return parse_opt_d(arg);
case TIM_OPT_EXT:
return parse_opt_E(arg);
case TIM_OPT_MOD_WHEEL:
return parse_opt_mod_wheel(arg);
case TIM_OPT_PORTAMENTO:
return parse_opt_portamento(arg);
case TIM_OPT_VIBRATO:
return parse_opt_vibrato(arg);
case TIM_OPT_CH_PRESS:
return parse_opt_ch_pressure(arg);
case TIM_OPT_MOD_ENV:
return parse_opt_mod_env(arg);
case TIM_OPT_TRACE_TEXT:
return parse_opt_trace_text(arg);
case TIM_OPT_OVERLAP:
return parse_opt_overlap_voice(arg);
case TIM_OPT_TEMPER_CTRL:
return parse_opt_temper_control(arg);
case TIM_OPT_DEFAULT_MID:
return parse_opt_default_mid(arg);
case TIM_OPT_SYSTEM_MID:
return parse_opt_system_mid(arg);
case TIM_OPT_DEFAULT_BANK:
return parse_opt_default_bank(arg);
case TIM_OPT_FORCE_BANK:
return parse_opt_force_bank(arg);
case TIM_OPT_DEFAULT_PGM:
return parse_opt_default_program(arg);
case TIM_OPT_FORCE_PGM:
return parse_opt_force_program(arg);
case TIM_OPT_DELAY:
return parse_opt_delay(arg);
case TIM_OPT_CHORUS:
return parse_opt_chorus(arg);
case TIM_OPT_REVERB:
return parse_opt_reverb(arg);
case TIM_OPT_VOICE_LPF:
return parse_opt_voice_lpf(arg);
case TIM_OPT_NS:
return parse_opt_noise_shaping(arg);
#ifndef FIXED_RESAMPLATION
case TIM_OPT_RESAMPLE:
return parse_opt_resample(arg);
#endif
case TIM_OPT_EVIL:
return parse_opt_e(arg);
case TIM_OPT_FAST_PAN:
return parse_opt_F(arg);
case TIM_OPT_FAST_DECAY:
return parse_opt_f(arg);
case TIM_OPT_SPECTROGRAM:
return parse_opt_g(arg);
case TIM_OPT_KEYSIG:
return parse_opt_H(arg);
case TIM_OPT_HELP:
return parse_opt_h(arg);
case TIM_OPT_INTERFACE:
return parse_opt_i(arg);
case TIM_OPT_VERBOSE:
return parse_opt_verbose(arg);
case TIM_OPT_QUIET:
return parse_opt_quiet(arg);
case TIM_OPT_TRACE:
return parse_opt_trace(arg);
case TIM_OPT_LOOP:
return parse_opt_loop(arg);
case TIM_OPT_RANDOM:
return parse_opt_random(arg);
case TIM_OPT_SORT:
return parse_opt_sort(arg);
#ifdef IA_ALSASEQ
case TIM_OPT_BACKGROUND:
return parse_opt_background(arg);
case TIM_OPT_RT_PRIO:
return parse_opt_rt_prio(arg);
case TIM_OPT_SEQ_PORTS:
return parse_opt_seq_ports(arg);
#endif
#if defined(IA_WINSYN) || defined(IA_PORTMIDISYN) || defined(IA_NPSYN) || defined(IA_W32G_SYN) || defined(IA_W32GUI)
case TIM_OPT_RTSYN_LATENCY:
return parse_opt_rtsyn_latency(arg);
#endif
case TIM_OPT_REALTIME_LOAD:
return parse_opt_j(arg);
case TIM_OPT_ADJUST_KEY:
return parse_opt_K(arg);
case TIM_OPT_VOICE_QUEUE:
return parse_opt_k(arg);
case TIM_OPT_PATCH_PATH:
return parse_opt_L(arg);
case TIM_OPT_PCM_FILE:
return parse_opt_M(arg);
case TIM_OPT_DECAY_TIME:
return parse_opt_m(arg);
case TIM_OPT_INTERPOLATION:
return parse_opt_N(arg);
case TIM_OPT_OUTPUT_MODE:
return parse_opt_O(arg);
case TIM_OPT_OUTPUT_STEREO:
if (! strcmp(the_option->name, "output-mono"))
/* --output-mono == --output-stereo=no */
arg = "no";
return parse_opt_output_stereo(arg);
case TIM_OPT_OUTPUT_SIGNED:
if (! strcmp(the_option->name, "output-unsigned"))
/* --output-unsigned == --output-signed=no */
arg = "no";
return parse_opt_output_signed(arg);
case TIM_OPT_OUTPUT_BITWIDTH:
if (! strcmp(the_option->name, "output-16bit"))
arg = "16bit";
else if (! strcmp(the_option->name, "output-24bit"))
arg = "24bit";
else if (! strcmp(the_option->name, "output-8bit"))
arg = "8bit";
return parse_opt_output_bitwidth(arg);
case TIM_OPT_OUTPUT_FORMAT:
if (! strcmp(the_option->name, "output-linear"))
arg = "linear";
else if (! strcmp(the_option->name, "output-ulaw"))
arg = "ulaw";
else if (! strcmp(the_option->name, "output-alaw"))
arg = "alaw";
return parse_opt_output_format(arg);
case TIM_OPT_OUTPUT_SWAB:
return parse_opt_output_swab(arg);
#ifdef AU_FLAC
case TIM_OPT_FLAC_VERIFY:
return parse_opt_flac_verify(arg);
case TIM_OPT_FLAC_PADDING:
return parse_opt_flac_padding(arg);
case TIM_OPT_FLAC_COMPLEVEL:
return parse_opt_flac_complevel(arg);
#ifdef AU_OGGFLAC
case TIM_OPT_FLAC_OGGFLAC:
return parse_opt_flac_oggflac(arg);
#endif /* AU_OGGFLAC */
#endif /* AU_FLAC */
#ifdef AU_SPEEX
case TIM_OPT_SPEEX_QUALITY:
return parse_opt_speex_quality(arg);
case TIM_OPT_SPEEX_VBR:
return parse_opt_speex_vbr(arg);
case TIM_OPT_SPEEX_ABR:
return parse_opt_speex_abr(arg);
case TIM_OPT_SPEEX_VAD:
return parse_opt_speex_vad(arg);
case TIM_OPT_SPEEX_DTX:
return parse_opt_speex_dtx(arg);
case TIM_OPT_SPEEX_COMPLEXITY:
return parse_opt_speex_complexity(arg);
case TIM_OPT_SPEEX_NFRAMES:
return parse_opt_speex_nframes(arg);
#endif /* AU_SPEEX */
case TIM_OPT_OUTPUT_FILE:
return parse_opt_o(arg);
case TIM_OPT_PATCH_FILE:
return parse_opt_P(arg);
case TIM_OPT_POLYPHONY:
return parse_opt_p(arg);
case TIM_OPT_POLY_REDUCE:
return parse_opt_p1(arg);
case TIM_OPT_MUTE:
return parse_opt_Q(arg);
case TIM_OPT_TEMPER_MUTE:
return parse_opt_Q1(arg);
case TIM_OPT_PRESERVE_SILENCE:
return parse_opt_preserve_silence(arg);
case TIM_OPT_AUDIO_BUFFER:
return parse_opt_q(arg);
case TIM_OPT_CACHE_SIZE:
return parse_opt_S(arg);
case TIM_OPT_SAMPLE_FREQ:
return parse_opt_s(arg);
case TIM_OPT_ADJUST_TEMPO:
return parse_opt_T(arg);
case TIM_OPT_CHARSET:
return parse_opt_t(arg);
case TIM_OPT_UNLOAD_INST:
return parse_opt_U(arg);
case TIM_OPT_VOLUME_CURVE:
return parse_opt_volume_curve(arg);
case TIM_OPT_VERSION:
return parse_opt_v(arg);
case TIM_OPT_WRD:
return parse_opt_W(arg);
#ifdef __W32__
case TIM_OPT_RCPCV_DLL:
return parse_opt_w(arg);
#endif
case TIM_OPT_CONFIG_STR:
return parse_opt_x(arg);
case TIM_OPT_FREQ_TABLE:
return parse_opt_Z(arg);
case TIM_OPT_PURE_INT:
return parse_opt_Z1(arg);
case TIM_OPT_MODULE:
return parse_opt_default_module(arg);
default:
ctl->cmsg(CMSG_FATAL, VERB_NORMAL,
"[BUG] Inconceivable case branch %d", c);
abort();
}
}
MAIN_INTERFACE int set_tim_opt_long_cfg(int c, char *optarg, int index)
{
const struct option *the_option = &(longopts[index]);
char *arg;
if (c == '?') /* getopt_long failed parsing */
parse_opt_fail(optarg);
else if (c < TIM_OPT_FIRST)
return set_tim_opt_short_cfg(c, optarg);
if (! strncmp(the_option->name, "no-", 3))
arg = "no"; /* `reverse' switch */
else
arg = optarg;
switch (c) {
case TIM_OPT_CONFIG_FILE:
return parse_opt_c(arg);
}
}
static inline int parse_opt_A(const char *arg)
{
/* amplify volume by n percent */
return set_val_i32(&amplification, atoi(arg), 0, MAX_AMPLIFICATION,
"Amplification");
}
static inline int parse_opt_drum_power(const char *arg)
{
/* --drum-power */
return set_val_i32(&opt_drum_power, atoi(arg), 0, MAX_AMPLIFICATION,
"Drum power");
}
static inline int parse_opt_volume_comp(const char *arg)
{
/* --[no-]volume-compensation */
opt_amp_compensation = y_or_n_p(arg);
return 0;
}
static inline int parse_opt_a(const char *arg)
{
antialiasing_allowed = y_or_n_p(arg);
return 0;
}
static inline int parse_opt_B(const char *arg)
{
/* --buffer-fragments */
const char *p;
/* num */
if (*arg != ',') {
if (set_value(&opt_buffer_fragments, atoi(arg), 0, 1000,
"Buffer Fragments (num)"))
return 1;
}
/* bits */
if ((p = strchr(arg, ',')) != NULL) {
if (set_value(&audio_buffer_bits, atoi(++p), 1, AUDIO_BUFFER_BITS,
"Buffer Fragments (bit)"))
return 1;
}
return 0;
}
static inline int parse_opt_C(const char *arg)
{
if (set_val_i32(&control_ratio, atoi(arg), 0, MAX_CONTROL_RATIO,
"Control ratio"))
return 1;
opt_control_ratio = control_ratio;
return 0;
}
static inline int parse_opt_c(char *arg)
{
#ifdef __W32__
if (got_a_configuration == 1)
return 0;
#endif
if (read_config_file(arg, 0))
return 1;
got_a_configuration = 1;
return 0;
}
static inline int parse_opt_D(const char *arg)
{
return set_channel_flag(&default_drumchannels, atoi(arg), "Drum channel");
}
static inline int parse_opt_d(const char *arg)
{
/* dynamic lib root */
#ifdef IA_DYNAMIC
if (dynamic_lib_root)
free(dynamic_lib_root);
dynamic_lib_root = safe_strdup(arg);
return 0;
#else
ctl->cmsg(CMSG_WARNING, VERB_NOISY, "-d option is not supported");
return 1;
#endif /* IA_DYNAMIC */
}
static inline int parse_opt_E(char *arg)
{
/* undocumented option --ext */
int err = 0;
while (*arg) {
switch (*arg) {
case 'w':
opt_modulation_wheel = 1;
break;
case 'W':
opt_modulation_wheel = 0;
break;
case 'p':
opt_portamento = 1;
break;
case 'P':
opt_portamento = 0;
break;
case 'v':
opt_nrpn_vibrato = 1;
break;
case 'V':
opt_nrpn_vibrato = 0;
break;
case 's':
opt_channel_pressure = 1;
break;
case 'S':
opt_channel_pressure = 0;
break;
case 'e':
opt_modulation_envelope = 1;
break;
case 'E':
opt_modulation_envelope = 0;
break;
case 't':
opt_trace_text_meta_event = 1;
break;
case 'T':
opt_trace_text_meta_event = 0;
break;
case 'o':
opt_overlap_voice_allow = 1;
break;
case 'O':
opt_overlap_voice_allow = 0;
break;
case 'z':
opt_temper_control = 1;
break;
case 'Z':
opt_temper_control = 0;
break;
case 'm':
if (parse_opt_default_mid(arg + 1))
err++;
arg += 2;
break;
case 'M':
if (parse_opt_system_mid(arg + 1))
err++;
arg += 2;
break;
case 'b':
if (parse_opt_default_bank(arg + 1))
err++;
while (isdigit(*(arg + 1)))
arg++;
break;
case 'B':
if (parse_opt_force_bank(arg + 1))
err++;
while (isdigit(*(arg + 1)))
arg++;
break;
case 'i':
if (parse_opt_default_program(arg + 1))
err++;
while (isdigit(*(arg + 1)) || *(arg + 1) == '/')
arg++;
break;
case 'I':
if (parse_opt_force_program(arg + 1))
err++;
while (isdigit(*(arg + 1)) || *(arg + 1) == '/')
arg++;
break;
case 'F':
if (strncmp(arg + 1, "delay=", 6) == 0) {
if (parse_opt_delay(arg + 7))
err++;
} else if (strncmp(arg + 1, "chorus=", 7) == 0) {
if (parse_opt_chorus(arg + 8))
err++;
} else if (strncmp(arg + 1, "reverb=", 7) == 0) {
if (parse_opt_reverb(arg + 8))
err++;
} else if (strncmp(arg + 1, "ns=", 3) == 0) {
if (parse_opt_noise_shaping(arg + 4))
err++;
#ifndef FIXED_RESAMPLATION
} else if (strncmp(arg + 1, "resamp=", 7) == 0) {
if (parse_opt_resample(arg + 8))
err++;
#endif
}
if (err) {
ctl->cmsg(CMSG_ERROR,
VERB_NORMAL, "-E%s: unsupported effect", arg);
return err;
}
return err;
default:
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"-E: Illegal mode `%c'", *arg);
err++;
break;
}
arg++;
}
return err;
}
static inline int parse_opt_mod_wheel(const char *arg)
{
/* --[no-]mod-wheel */
opt_modulation_wheel = y_or_n_p(arg);
return 0;
}
static inline int parse_opt_portamento(const char *arg)
{
/* --[no-]portamento */
opt_portamento = y_or_n_p(arg);
return 0;
}
static inline int parse_opt_vibrato(const char *arg)
{
/* --[no-]vibrato */
opt_nrpn_vibrato = y_or_n_p(arg);
return 0;
}
static inline int parse_opt_ch_pressure(const char *arg)
{
/* --[no-]ch-pressure */
opt_channel_pressure = y_or_n_p(arg);
return 0;
}
static inline int parse_opt_mod_env(const char *arg)
{
/* --[no-]mod-envelope */
opt_modulation_envelope = y_or_n_p(arg);
return 0;
}
static inline int parse_opt_trace_text(const char *arg)
{
/* --[no-]trace-text-meta */
opt_trace_text_meta_event = y_or_n_p(arg);
return 0;
}
static inline int parse_opt_overlap_voice(const char *arg)
{
/* --[no-]overlap-voice */
opt_overlap_voice_allow = y_or_n_p(arg);
return 0;
}
static inline int parse_opt_temper_control(const char *arg)
{
/* --[no-]temper-control */
opt_temper_control = y_or_n_p(arg);
return 0;
}
static inline int parse_opt_default_mid(char *arg)
{
/* --default-mid */
int val = str2mID(arg);
if (! val) {
ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Manufacture ID: Illegal value");
return 1;
}
opt_default_mid = val;
return 0;
}
static inline int parse_opt_system_mid(char *arg)
{
/* --system-mid */
int val = str2mID(arg);
if (! val) {
ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Manufacture ID: Illegal value");
return 1;
}
opt_system_mid = val;
return 0;
}
static inline int parse_opt_default_bank(const char *arg)
{
/* --default-bank */
if (set_value(&default_tonebank, atoi(arg), 0, 0x7f, "Bank number"))
return 1;
special_tonebank = -1;
return 0;
}
static inline int parse_opt_force_bank(const char *arg)
{
/* --force-bank */
if (set_value(&special_tonebank, atoi(arg), 0, 0x7f, "Bank number"))
return 1;
return 0;
}
static inline int parse_opt_default_program(const char *arg)
{
/* --default-program */
int prog, i;
const char *p;
if (set_value(&prog, atoi(arg), 0, 0x7f, "Program number"))
return 1;
if ((p = strchr(arg, '/')) != NULL) {
if (set_value(&i, atoi(++p), 1, MAX_CHANNELS, "Program channel"))
return 1;
default_program[i - 1] = prog;
} else
for (i = 0; i < MAX_CHANNELS; i++)
default_program[i] = prog;
return 0;
}
static inline int parse_opt_force_program(const char *arg)
{
/* --force-program */
const char *p;
int i;
if (set_value(&def_prog, atoi(arg), 0, 0x7f, "Program number"))
return 1;
if (ctl->opened)
set_default_program(def_prog);
if ((p = strchr(arg, '/')) != NULL) {
if (set_value(&i, atoi(++p), 1, MAX_CHANNELS, "Program channel"))
return 1;
default_program[i - 1] = SPECIAL_PROGRAM;
} else
for (i = 0; i < MAX_CHANNELS; i++)
default_program[i] = SPECIAL_PROGRAM;
return 0;
}
static inline int set_default_program(int prog)
{
int bank;
Instrument *ip;
bank = (special_tonebank >= 0) ? special_tonebank : default_tonebank;
if ((ip = play_midi_load_instrument(0, bank, prog)) == NULL)
return 1;
default_instrument = ip;
return 0;
}
static inline int parse_opt_delay(const char *arg)
{
/* --delay */
const char *p;
switch (*arg) {
case '0':
case 'd': /* disable */
effect_lr_mode = -1;
return 0;
case 'l': /* left */
effect_lr_mode = 0;
break;
case 'r': /* right */
effect_lr_mode = 1;
break;
case 'b': /* both */
effect_lr_mode = 2;
break;
}
if ((p = strchr(arg, ',')) != NULL)
if ((effect_lr_delay_msec = atoi(++p)) < 0) {
effect_lr_delay_msec = 0;
effect_lr_mode = -1;
ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Invalid delay parameter.");
return 1;
}
return 0;
}
static inline int parse_opt_chorus(const char *arg)
{
/* --chorus */
const char *p;
switch (*arg) {
case '0':
case 'd': /* disable */
opt_chorus_control = 0;
opt_surround_chorus = 0;
break;
case '1':
case 'n': /* normal */
case '2':
case 's': /* surround */
opt_surround_chorus = (*arg == '2' || *arg == 's') ? 1 : 0;
if ((p = strchr(arg, ',')) != NULL) {
if (set_value(&opt_chorus_control, atoi(++p), 0, 0x7f,
"Chorus level"))
return 1;
opt_chorus_control = -opt_chorus_control;
} else
opt_chorus_control = 1;
break;
default:
ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Invalid chorus parameter.");
return 1;
}
return 0;
}
static inline int parse_opt_reverb(const char *arg)
{
/* --reverb */
const char *p;
/* option action opt_reverb_control
* reverb=0 no reverb 0
* reverb=1 old reverb 1
* reverb=1,n set reverb level to n (-1 to -127)
* reverb=2 "global" old reverb 2
* reverb=2,n set reverb level to n (-1 to -127) - 128
* reverb=3 new reverb 3
* reverb=3,n set reverb level to n (-1 to -127) - 256
* reverb=4 "global" new reverb 4
* reverb=4,n set reverb level to n (-1 to -127) - 384
*
* I think "global" was meant to apply a single global reverb,
* without applying any reverb to the channels. The do_effects()
* function in effects.c looks like a good way to do this.
*
* This is NOT the "correct" way to implement global reverb, we should
* really make a new variable just for that. But if opt_reverb_control
* is already used in a similar fashion, rather than creating a new
* variable for setting the channel reverb levels, then I guess
* maybe this isn't so bad.... It would be nice to create new
* variables for both global reverb and channel reverb level settings
* in the future, but this will do for now.
*/
switch (*arg) {
case '0':
case 'd': /* disable */
opt_reverb_control = 0;
break;
case '1':
case 'n': /* normal */
if ((p = strchr(arg, ',')) != NULL) {
if (set_value(&opt_reverb_control, atoi(++p), 1, 0x7f,
"Reverb level"))
return 1;
opt_reverb_control = -opt_reverb_control;
} else
opt_reverb_control = 1;
break;
case '2':
case 'g': /* global */
if ((p = strchr(arg, ',')) != NULL) {
if (set_value(&opt_reverb_control, atoi(++p), 1, 0x7f,
"Reverb level"))
return 1;
opt_reverb_control = -opt_reverb_control - 128;
} else
opt_reverb_control = 2;
break;
case '3':
case 'f': /* freeverb */
return parse_opt_reverb_freeverb(arg, 'f');
case '4':
case 'G': /* global freeverb */
return parse_opt_reverb_freeverb(arg, 'G');
default:
ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Invalid reverb parameter.");
return 1;
}
return 0;
}
static int parse_opt_reverb_freeverb(const char *arg, char type)
{
const char *p;
if ((p = strchr(arg, ',')) != NULL)
p++;
else
p = "";
/* reverb level */
if (*p && *p != ',') {
if (set_value(&opt_reverb_control, atoi(p), 1, 0x7f,
"Reverb level"))
return 1;
if (type == 'f')
opt_reverb_control = -opt_reverb_control - 256;
else
opt_reverb_control = -opt_reverb_control - 384;
} else
opt_reverb_control = (type == 'f') ? 3 : 4;
if ((p = strchr(p, ',')) == NULL)
return 0;
p++;
/* ranges 0..10 below determined just to reject an extreme value */
/* scaleroom */
if (*p && *p != ',') {
if (parse_val_float_t(&freeverb_scaleroom, p, 0, 10,
"Freeverb scaleroom"))
return 1;
}
if ((p = strchr(p, ',')) == NULL)
return 0;
p++;
/* offsetroom */
if (*p && *p != ',') {
if (parse_val_float_t(&freeverb_offsetroom, p, 0, 10,
"Freeverb offsetroom"))
return 1;
}
if ((p = strchr(p, ',')) == NULL)
return 0;
p++;
/* predelay factor */
if (*p && *p != ',') {
int value;
if (set_val_i32(&value, atoi(p), 0, 1000,
"Freeverb predelay factor"))
return 1;
reverb_predelay_factor = value / 100.0;
}
return 0;
}
static inline int parse_opt_voice_lpf(const char *arg)
{
/* --voice-lpf */
switch (*arg) {
case '0':
case 'd': /* disable */
opt_lpf_def = 0;
break;
case '1':
case 'c': /* chamberlin */
opt_lpf_def = 1;
break;
case '2':
case 'm': /* moog */
opt_lpf_def = 2;
break;
default:
ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Invalid voice LPF type %s", arg);
return 1;
}
return 0;
}
/* Noise Shaping filter from
* Kunihiko IMAI <imai@leo.ec.t.kanazawa-u.ac.jp>
*/
static inline int parse_opt_noise_shaping(const char *arg)
{
/* --noise-shaping */
if (set_value(&noise_sharp_type, atoi(arg), 0, 4, "Noise shaping type"))
return 1;
return 0;
}
static inline int parse_opt_resample(const char *arg)
{
/* --resample */
switch (*arg) {
case '0':
case 'd': /* disable */
set_current_resampler(RESAMPLE_NONE);
break;
case '1':
case 'l': /* linear */
set_current_resampler(RESAMPLE_LINEAR);
break;
case '2':
case 'c': /* cspline */
set_current_resampler(RESAMPLE_CSPLINE);
break;
case '3':
case 'L': /* lagrange */
set_current_resampler(RESAMPLE_LAGRANGE);
break;
case '4':
case 'n': /* newton */
set_current_resampler(RESAMPLE_NEWTON);
break;
case '5':
case 'g': /* guass */
set_current_resampler(RESAMPLE_GAUSS);
break;
default:
ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Invalid resample type %s", arg);
return 1;
}
return 0;
}
static inline int parse_opt_e(const char *arg)
{
/* evil */
#ifdef __W32__
opt_evil_mode = 1;
return 0;
#else
ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "-e option is not supported");
return 1;
#endif /* __W32__ */
}
static inline int parse_opt_F(const char *arg)
{
adjust_panning_immediately = y_or_n_p(arg);
return 0;
}
static inline int parse_opt_f(const char *arg)
{
fast_decay = y_or_n_p(arg);
return 0;
}
static inline int parse_opt_g(const char *arg)
{
#ifdef SUPPORT_SOUNDSPEC
spectrogram_update_sec = atof(arg);
if (spectrogram_update_sec <= 0) {
ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"Invalid -g argument: `%s'", arg);
return 1;
}
view_soundspec_flag = 1;
return 0;
#else
ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "-g option is not supported");
return 1;
#endif /* SUPPORT_SOUNDSPEC */
}
static inline int parse_opt_H(const char *arg)
{
/* force keysig (number of sharp/flat) */
int keysig;
if (set_value(&keysig, atoi(arg), -7, 7,
"Force keysig (number of sHarp(+)/flat(-))"))
return 1;
opt_force_keysig = keysig;
return 0;
}
__attribute__((noreturn))
#ifndef __BORLANDC__
static inline int parse_opt_h(const char *arg)
#else
static int parse_opt_h(const char *arg)
#endif
{
static char *help_list[] = {
"TiMidity++ %s (C) 1999-2004 Masanao Izumo <iz@onicos.co.jp>",
"The original version (C) 1995 Tuukka Toivonen <tt@cgs.fi>",
"TiMidity is free software and comes with ABSOLUTELY NO WARRANTY.",
"",
#ifdef __W32__
"Win32 version by Davide Moretti <dave@rimini.com>",
" and Daisuke Aoki <dai@y7.net>",
"",
#endif /* __W32__ */
"Usage:",
" %s [options] filename [...]",
"",
#ifndef __W32__ /*does not work in Windows */
" Use \"-\" as filename to read a MIDI file from stdin",
"",
#endif
"Options:",
" -A n,m --volume=n, --drum-power=m",
" Amplify volume by n percent (may cause clipping),",
" and amplify drum power by m percent",
" (a) --[no-]volume-compensation",
" Toggle amplify compensation (disabled by default)",
" -a --[no-]anti-alias",
" Enable the anti-aliasing filter",
" -B n,m --buffer-fragments=n,m",
" Set number of buffer fragments(n), and buffer size(2^m)",
" -C n --control-ratio=n",
" Set ratio of sampling and control frequencies",
" -c file --config-file=file",
" Read extra configuration file",
" -D n --drum-channel=n",
" Play drums on channel n",
#ifdef IA_DYNAMIC
" -d path --interface-path=path",
" Set dynamic interface module directory",
#endif /* IA_DYNAMIC */
" -E mode --ext=mode",
" TiMidity sequencer extensional modes:",
" mode = w/W : Enable/Disable Modulation wheel",
" p/P : Enable/Disable Portamento",
" v/V : Enable/Disable NRPN Vibrato",
" s/S : Enable/Disable Channel pressure",
" e/E : Enable/Disable Modulation Envelope",
" t/T : Enable/Disable Trace Text Meta Event at playing",
" o/O : Enable/Disable Overlapped voice",
" z/Z : Enable/Disable Temperament control",
" m<HH>: Define default Manufacture ID <HH> in two hex",
" M<HH>: Define system Manufacture ID <HH> in two hex",
" b<n>: Use tone bank <n> as the default",
" B<n>: Always use tone bank <n>",
" i<n/m>: Use program <n> on channel <m> as the default",
" I<n/m>: Always use program <n> on channel <m>",
" F<args>: For effect. See below for effect options",
" default: -E "
#ifdef MODULATION_WHEEL_ALLOW
"w"
#else
"W"
#endif /* MODULATION_WHEEL_ALLOW */
#ifdef PORTAMENTO_ALLOW
"p"
#else
"P"
#endif /* PORTAMENTO_ALLOW */
#ifdef NRPN_VIBRATO_ALLOW
"v"
#else
"V"
#endif /* NRPN_VIBRATO_ALLOW */
#ifdef GM_CHANNEL_PRESSURE_ALLOW
"s"
#else
"S"
#endif /* GM_CHANNEL_PRESSURE_ALLOW */
#ifdef MODULATION_ENVELOPE_ALLOW
"e"
#else
"E"
#endif /* MODULATION_ENVELOPE_ALLOW */
#ifdef ALWAYS_TRACE_TEXT_META_EVENT
"t"
#else
"T"
#endif /* ALWAYS_TRACE_TEXT_META_EVENT */
#ifdef OVERLAP_VOICE_ALLOW
"o"
#else
"O"
#endif /* OVERLAP_VOICE_ALLOW */
#ifdef TEMPER_CONTROL_ALLOW
"z"
#else
"Z"
#endif /* TEMPER_CONTROL_ALLOW */
,
#ifdef __W32__
" -e --evil",
" Increase thread priority (evil) - be careful!",
#endif
" -F --[no-]fast-panning",
" Disable/Enable fast panning (toggle on/off, default is on)",
" -f --[no-]fast-decay",
" "
#ifdef FAST_DECAY
"Disable "
#else
"Enable "
#endif
"fast decay mode (toggle)",
#ifdef SUPPORT_SOUNDSPEC
" -g sec --spectrogram=sec",
" Open Sound-Spectrogram Window",
#endif /* SUPPORT_SOUNDSPEC */
" -H n --force-keysig=n",
" Force keysig number of sHarp(+)/flat(-) (-7..7)",
" -h --help",
" Display this help message",
" -i mode --interface=mode",
" Select user interface (see below for list)",
#ifdef IA_ALSASEQ
" --realtime-priority=n (for alsaseq only)",
" Set the realtime priority (0-100)",
" --sequencer-ports=n (for alsaseq only)",
" Set the number of opened sequencer ports (default is 4)",
#endif
#if defined(IA_WINSYN) || defined(IA_PORTMIDISYN) || defined(IA_NPSYN) || defined(IA_W32G_SYN)
" --rtsyn-latency=sec (for rtsyn only)",
" Set the realtime latency (sec)",
" (default is 0.2 sec, minimum is 0.04 sec)",
#endif
" -j --[no-]realtime-load",
" Realtime load instrument (toggle on/off)",
" -K n --adjust-key=n",
" Adjust key by n half tone (-24..24)",
" -k msec --voice-queue=msec",
" Specify audio queue time limit to reduce voice",
" -L path --patch-path=path",
" Append dir to search path",
" -M name --pcm-file=name",
" Specify PCM filename (*.wav or *.aiff) to be played or:",
" \"auto\" : Play *.mid.wav or *.mid.aiff",
" \"none\" : Disable this feature (default)",
" -m msec --decay-time=msec",
" Minimum time for a full volume sustained note to decay,",
" 0 disables",
" -N n --interpolation=n",
" Set the interpolation parameter (depends on -EFresamp option)",
" Linear interpolation is used if audio queue < 99%%",
" cspline, lagrange:",
" Toggle 4-point interpolation (default on)",
" newton:",
" n'th order Newton polynomial interpolation, n=1-57 odd",
" gauss:",
" n+1 point Gauss-like interpolation, n=1-34 (default 25)",
" -O mode --output-mode=mode",
" Select output mode and format (see below for list)",
#ifdef AU_FLAC
" --flac-verify (for Ogg FLAC only)",
" Verify a correct encoding",
" --flac-padding=n (for Ogg FLAC only)",
" Write a PADDING block of length n",
" --flac-complevel=n (for Ogg FLAC only)",
" Set compression level n:[0..8]",
#ifdef AU_OGGFLAC
" --oggflac (for Ogg FLAC only)",
" Output OggFLAC stream (experimental)",
#endif /* AU_OGGFLAC */
#endif /* AU_FLAC */
#ifdef AU_SPEEX
" --speex-quality=n (for Ogg Speex only)",
" Encoding quality n:[0..10]",
" --speex-vbr (for Ogg Speex only)",
" Enable variable bit-rate (VBR)",
" --speex-abr=n (for Ogg Speex only)",
" Enable average bit-rate (ABR) at rate bps",
" --speex-vad (for Ogg Speex only)",
" Enable voice activity detection (VAD)",
" --speex-dtx (for Ogg Speex only)",
" Enable file-based discontinuous transmission (DTX)",
" --speex-complexity=n (for Ogg Speex only)",
" Set encoding complexity n:[0-10]",
" --speex-nframes=n (for Ogg Speex only)",
" Number of frames per Ogg packet n:[0-10]",
#endif
" -o file --output-file=file",
" Output to another file (or device/server) (Use \"-\" for stdout)",
#if defined(AU_PORTAUDIO) || defined(AU_WIN32)
" Set the output device no. (-1 shows available device no. list)",
#endif
" -P file --patch-file=file",
" Use patch file for all programs",
" -p n --polyphony=n",
" Allow n-voice polyphony. Optional auto polyphony reduction",
" (a) --[no-]polyphony-reduction",
" Toggle automatic polyphony reduction. Enabled by default",
" -Q n[,...] --mute=n[,...]",
" Ignore channel n (0: ignore all, -n: resume channel n)",
" (t) --temper-mute=n[,...]",
" Quiet temperament type n (0..3: preset, 4..7: user-defined)",
" --preserve-silence",
" Do not drop initial silence. Default: drop initial silence",
" -q sec/n --audio-buffer=sec/n",
" Specify audio buffer in seconds",
" sec: Maxmum buffer, n: Filled to start (default is 5.0/100%%)",
" (size of 100%% equals device buffer size)",
" -R msec Pseudo reveb effect (set every instrument's release to msec)",
" if n=0, n is set to 800",
" -S n --cache-size=n",
" Cache size (0 means no cache)",
" -s freq --sampling-freq=freq",
" Set sampling frequency to freq (Hz or kHz)",
" -T n --adjust-tempo=n",
" Adjust tempo to n%%,",
" 120=play MOD files with an NTSC Amiga's timing",
" -t code --output-charset=code",
" Output text language code:",
" code=auto : Auto conversion by `LANG' environment variable",
" (UNIX only)",
"