commit 3e1d8617b47bf41a6bd284f4e98583e00ea561bc Author: Claudio Matsuoka Date: Sat Feb 11 11:43:29 2012 -0200 Change directory layout Make the layout more appropriate for a library-only package, move front-ends and other things not library-related out of src/ and into players/. Signed-off-by: Claudio Matsuoka diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d2b189d --- /dev/null +++ b/Makefile @@ -0,0 +1,26 @@ + +#MAIN_OBJS = getopt.o getopt1.o options.o drivers.o main.o +MAIN_OBJS = sound_alsa.o terminal.o info.o options.o main.o +MAIN_DFILES = Makefile $(MAIN_OBJS:.o=.c) +MAIN_PATH = players/xmp + +dist-main: + mkdir -p $(DIST)/$(MAIN_PATH) + cp -RPp $(addprefix $(MAIN_PATH)/,$(MAIN_DFILES)) $(DIST)/$(MAIN_PATH) + +$(MAIN_PATH)/options.o: Makefile + +M_OBJS = $(addprefix $(MAIN_PATH)/,$(MAIN_OBJS)) + +$(MAIN_PATH)/xmp: $(M_OBJS) $(LIB) + @CMD='$(LD) -o $@ $(LDFLAGS) $(M_OBJS) -Llib -lxmp -lm -lasound'; \ + if [ "$(V)" -gt 0 ]; then echo $$CMD; else echo LD $@ ; fi; \ + eval $$CMD + +$(M_OBJS): $(LIB) + +install-xmp: $(MAIN_PATH)/xmp + @echo Installing xmp in $(DESTDIR)$(BINDIR) + @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR) + @$(INSTALL_PROGRAM) $+ $(DESTDIR)$(BINDIR) + diff --git a/common.h b/common.h new file mode 100644 index 0000000..a2cc83e --- /dev/null +++ b/common.h @@ -0,0 +1,19 @@ + +struct options { + int start; /* start order */ + int amplify; /* amplification factor */ + int freq; /* sampling rate */ + int format; /* sample format */ + int time; /* max. replay time */ + int mix; /* channel separation */ + int loop; /* loop module */ + int random; /* play in random order */ + int load_only; /* load module and exit */ + char *out_file; /* output file name */ + char *ins_path; /* instrument path */ + char mute[XMP_MAX_CHANNELS]; +}; + +int set_tty(void); +int reset_tty(void); + diff --git a/drivers.c b/drivers.c new file mode 100644 index 0000000..33c3c61 --- /dev/null +++ b/drivers.c @@ -0,0 +1,106 @@ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "xmp.h" + +extern struct xmp_drv_info drv_file; +extern struct xmp_drv_info drv_wav; +extern struct xmp_drv_info drv_osx; +extern struct xmp_drv_info drv_solaris; +extern struct xmp_drv_info drv_hpux; +extern struct xmp_drv_info drv_bsd; +extern struct xmp_drv_info drv_netbsd; +extern struct xmp_drv_info drv_openbsd; +extern struct xmp_drv_info drv_sndio; +extern struct xmp_drv_info drv_sgi; +extern struct xmp_drv_info drv_aix; +extern struct xmp_drv_info drv_oss_seq; +extern struct xmp_drv_info drv_oss; +extern struct xmp_drv_info drv_alsa; +extern struct xmp_drv_info drv_alsa05; +extern struct xmp_drv_info drv_net; +extern struct xmp_drv_info drv_esd; +extern struct xmp_drv_info drv_arts; +extern struct xmp_drv_info drv_nas; +extern struct xmp_drv_info drv_pulseaudio; +extern struct xmp_drv_info drv_os2dart; +extern struct xmp_drv_info drv_qnx; +extern struct xmp_drv_info drv_beos; +extern struct xmp_drv_info drv_amiga; +extern struct xmp_drv_info drv_win32; + + +void init_drivers() +{ +#ifdef DRIVER_OSX + xmp_drv_register(&drv_osx); +#endif +#ifdef DRIVER_WIN32 + xmp_drv_register(&drv_win32); +#endif +#ifdef DRIVER_SOLARIS + xmp_drv_register(&drv_solaris); +#endif +#ifdef DRIVER_HPUX + xmp_drv_register(&drv_hpux); +#endif +#ifdef DRIVER_BSD + xmp_drv_register(&drv_bsd); +#endif +#ifdef DRIVER_NETBSD + xmp_drv_register(&drv_netbsd); +#endif +#ifdef DRIVER_SNDIO + xmp_drv_register(&drv_sndio); +#endif +#ifdef DRIVER_OPENBSD + xmp_drv_register(&drv_openbsd); +#endif +#ifdef DRIVER_SGI + xmp_drv_register(&drv_sgi); +#endif +#ifdef DRIVER_OSS_SEQ + xmp_drv_register(&drv_oss_seq); +#endif +#ifdef DRIVER_OSS + xmp_drv_register(&drv_oss); +#endif +#ifdef DRIVER_ALSA + xmp_drv_register(&drv_alsa); +#endif +#ifdef DRIVER_ALSA05 + xmp_drv_register(&drv_alsa05); +#endif +#ifdef DRIVER_QNX + xmp_drv_register(&drv_qnx); +#endif +#ifdef DRIVER_BEOS + xmp_drv_register(&drv_beos); +#endif +#ifdef DRIVER_AMIGA + xmp_drv_register(&drv_amiga); +#endif +#ifdef DRIVER_NET + xmp_drv_register(&drv_net); +#endif +#ifdef DRIVER_OS2DART + xmp_drv_register(&drv_os2dart); +#endif +#ifdef DRIVER_PULSEAUDIO + xmp_drv_register(&drv_pulseaudio); +#endif +#ifdef DRIVER_ARTS + xmp_drv_register(&drv_arts); +#endif +#ifdef DRIVER_ESD + xmp_drv_register(&drv_esd); +#endif +#ifdef DRIVER_NAS + xmp_drv_register(&drv_nas); +#endif + xmp_drv_register(&drv_file); + xmp_drv_register(&drv_wav); +} + diff --git a/drivers/Makefile b/drivers/Makefile new file mode 100644 index 0000000..6954a3d --- /dev/null +++ b/drivers/Makefile @@ -0,0 +1,26 @@ + +DRV = file.c oss.c hpux.c bsd.c solaris.c alsa.c alsa05.c \ + sgi.c os2_dart.c netbsd.c openbsd.c aix.c qnx.c \ + win32.c smix.c wav.c osx.c beos.c pulseaudio.c amiga.c sndio.c + +DRIVERS_OBJS = file.o wav.o +DRIVERS_LOBJS = smix.lo +DRIVERS_DFILES = Makefile $(DRV) debug.c +DRIVERS_PATH = src/drivers + +# Don't include platform-specific drivers in DRIVERS_OBJS so we can generate +# foreign makefiles from it (such as MSVC .dsp files, etc) + +D_OBJS = $(addprefix $(DRIVERS_PATH)/,$(DRIVERS_OBJS) $(DRIVERS)) +D_LOBJS = $(addprefix $(DRIVERS_PATH)/,$(DRIVERS_LOBJS)) + +dist-drivers: + mkdir -p $(DIST)/$(DRIVERS_PATH) + cp -RPp $(addprefix $(DRIVERS_PATH)/,$(DRIVERS_DFILES)) $(DIST)/$(DRIVERS_PATH) + +$(DRIVERS_PATH)/beos.o: src/drivers/beos.c + g++ -c $(XCFLAGS) -o $@ $+ + +$(DRIVERS_PATH)/arts.o: src/drivers/arts.c + $(CC) $(CFLAGS) $(XCFLAGS) `artsc-config --cflags` -o $@ $+ + diff --git a/drivers/aix.c b/drivers/aix.c new file mode 100644 index 0000000..2e0d46a --- /dev/null +++ b/drivers/aix.c @@ -0,0 +1,153 @@ +/* Extended Module Player + * Copyright (C) 1996-2012 Claudio Matsuoka and Hipolito Carraro Jr + * + * This file is part of the Extended Module Player and is distributed + * under the terms of the GNU General Public License. See doc/COPYING + * for more information. + */ + +/* + * Based on the AIX XMMS output plugin by Peter Alm, Thomas Nilsson + * and Olle Hallnas. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "common.h" +#include "driver.h" +#include "mixer.h" + +static int audio_fd; +static audio_control control; +static audio_change change; + +static int init(struct context_data *); +static int setaudio(struct xmp_options *); +static void bufdump(struct context_data *, void *, int); +static void shutdown(struct context_data *); + +static void dummy() +{ +} + +static char *help[] = { + "gain=val", "Audio output gain (0 to 255)", + /* "buffer=val", "Audio buffer size (default is 32768)", */ + NULL +}; + +struct xmp_drv_info drv_bsd = { + "aix", /* driver ID */ + "AIX PCM audio", /* driver description */ + help, /* help */ + init, /* init */ + shutdown, /* shutdown */ + dummy, /* starttimer */ + dummy, /* stoptimer */ + bufdump, /* bufdump */ + NULL +}; + +#define AUDIO_MIN_GAIN 0 +#define AUDIO_MAX_GAIN 100 + +static int setaudio(struct xmp_options *o) +{ + audio_init ainit; + int gain = 128; + int bsize = 32 * 1024; + char *token, **parm; + + parm_init(); + chkparm1("gain", gain = strtoul(token, NULL, 0)); + /* chkparm1 ("buffer", bsize = strtoul(token, NULL, 0)); */ + parm_end(); + + if (gain < AUDIO_MIN_GAIN) + gain = AUDIO_MIN_GAIN; + if (gain > AUDIO_MAX_GAIN) + gain = AUDIO_MAX_GAIN; + + init.mode = PCM; /* audio format */ + init.srate = o->freq; /* sample rate */ + init.operation = PLAY; /* PLAY or RECORD */ + init.channels = o->outfmt & XMP_FORMAT_MONO ? 1 : 2; + init.bits_per_sample = o->resol; /* bits per sample */ + init.flags = BIG_ENDIAN | TWOS_COMPLEMENT; + + if (ioctl(audio_fd, AUDIO_INIT, &init) < 0) { + close(audio_fd); + return XMP_ERR_DINIT; + } + + /* full blast; range: 0-0x7fffffff */ + change.volume = 0x7fffffff * (1.0 * gain / 200.0); + change.monitor = AUDIO_IGNORE; /* monitor what's recorded ? */ + change.input = AUDIO_IGNORE; /* input to record from */ + change.output = OUTPUT_1; /* line-out */ + change.balance = 0x3FFFFFFF; + + control.ioctl_request = AUDIO_CHANGE; + control.request_info = (char *)&change; + if (ioctl(audio_fd, AUDIO_CONTROL, &control) < 0) { + close(audio_fd); + return XMP_ERR_DINIT; + } + + /* start playback - won't actually start until write() calls occur */ + control.ioctl_request = AUDIO_START; + control.position = 0; + if (ioctl(audio_fd, AUDIO_CONTROL, &control) < 0) { + close(audio_fd); + return XMP_ERR_DINIT; + } + + return 0; +} + +static int init(struct context_data *ctx) +{ + if ((audio_fd = open("/dev/paud0/1", O_WRONLY)) == -1) + return XMP_ERR_DINIT; + + if (setaudio(&ctx->o) != 0) + return XMP_ERR_DINIT; + + return 0; +} + +/* Build and write one tick (one PAL frame or 1/50 s in standard vblank + * timed mods) of audio data to the output device. + */ +static void bufdump(struct context_data *ctx, void *b, int i) +{ + int j; + + while (i) { + if ((j = write(audio_fd, b, i)) > 0) { + i -= j; + (char *)b += j; + } else + break; + }; +} + +static void shutdown(struct context_data *ctx) +{ + control.ioctl_request = AUDIO_STOP; + ioctl(audio_fd, AUDIO_CONTROL, &control); + close(audio_fd); +} diff --git a/drivers/alsa.c b/drivers/alsa.c new file mode 100644 index 0000000..e454a2e --- /dev/null +++ b/drivers/alsa.c @@ -0,0 +1,175 @@ +/* ALSA driver for xmp + * Copyright (C) 2005-2007 Claudio Matsuoka and Hipolito Carraro Jr + * Based on the ALSA 0.5 driver for xmp, Copyright (C) 2000 Tijs + * van Bakel and Rob Adamson. + * + * This file is part of the Extended Module Player and is distributed + * under the terms of the GNU General Public License. See doc/COPYING + * for more information. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "driver.h" + +static int init (struct context_data *); +static int prepare_driver (void); +static void dshutdown (struct context_data *); +static int to_fmt (struct xmp_options *); +static void bufdump (struct context_data *, void *, int); +static void flush (void); + +static void dummy () { } + +static char *help[] = { + "buffer=num", "Set the ALSA buffer time in milliseconds", + "period=num", "Set the ALSA period time in milliseconds", + "card ", "Select sound card to use", + NULL +}; + +struct xmp_drv_info drv_alsa = { + "alsa", /* driver ID */ + "ALSA PCM audio", /* driver description */ + help, /* help */ + init, /* init */ + dshutdown, /* shutdown */ + dummy, /* starttimer */ + flush, /* stoptimer */ + bufdump, /* bufdump */ +}; + +static snd_pcm_t *pcm_handle; + + +static int init(struct context_data *ctx) +{ + snd_pcm_hw_params_t *hwparams; + int ret; + char *token, **parm; + unsigned int channels, rate; + unsigned int btime = 250000; /* 250ms */ + unsigned int ptime = 50000; /* 50ms */ + char *card_name = "default"; + struct xmp_options *o = &ctx->o; + + parm_init(); + chkparm1("buffer", btime = 1000 * strtoul(token, NULL, 0)); + chkparm1("period", btime = 1000 * strtoul(token, NULL, 0)); + chkparm1("card", card_name = token); + parm_end(); + + if ((ret = snd_pcm_open(&pcm_handle, card_name, + SND_PCM_STREAM_PLAYBACK, 0)) < 0) { + fprintf(stderr, "Unable to initialize ALSA pcm device: %s\n", + snd_strerror(ret)); + return XMP_ERR_DINIT; + } + + channels = (o->outfmt & XMP_FORMAT_MONO) ? 1 : 2; + rate = o->freq; + + snd_pcm_hw_params_alloca(&hwparams); + snd_pcm_hw_params_any(pcm_handle, hwparams); + snd_pcm_hw_params_set_access(pcm_handle, hwparams, + SND_PCM_ACCESS_RW_INTERLEAVED); + snd_pcm_hw_params_set_format(pcm_handle, hwparams, to_fmt(o)); + snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &rate, 0); + snd_pcm_hw_params_set_channels_near(pcm_handle, hwparams, &channels); + snd_pcm_hw_params_set_buffer_time_near(pcm_handle, hwparams, &btime, 0); + snd_pcm_hw_params_set_period_time_near(pcm_handle, hwparams, &ptime, 0); + snd_pcm_nonblock(pcm_handle, 0); + + if ((ret = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) { + fprintf(stderr, "Unable to set ALSA output parameters: %s\n", + snd_strerror(ret)); + return XMP_ERR_DINIT; + } + + if (prepare_driver() < 0) + return XMP_ERR_DINIT; + + o->freq = rate; + + return 0; +} + +static int prepare_driver() +{ + int ret; + + if ((ret = snd_pcm_prepare(pcm_handle)) < 0 ) { + fprintf(stderr, "Unable to prepare ALSA: %s\n", + snd_strerror(ret)); + return ret; + } + + return 0; +} + +static int to_fmt(struct xmp_options *o) +{ + int fmt; + + switch (o->resol) { + case 0: + return SND_PCM_FORMAT_MU_LAW; + case 8: + fmt = SND_PCM_FORMAT_U8 | SND_PCM_FORMAT_S8; + break; + default: + if (o->big_endian) { + fmt = SND_PCM_FORMAT_S16_BE | SND_PCM_FORMAT_U16_BE; + } else { + fmt = SND_PCM_FORMAT_S16_LE | SND_PCM_FORMAT_U16_LE; + } + } + + if (o->outfmt & XMP_FMT_UNS) { + fmt &= SND_PCM_FORMAT_U8 | SND_PCM_FORMAT_U16_LE | + SND_PCM_FORMAT_U16_BE; + } else { + fmt &= SND_PCM_FORMAT_S8 | SND_PCM_FORMAT_S16_LE | + SND_PCM_FORMAT_S16_BE; + } + + return fmt; +} + +/* Build and write one tick (one PAL frame or 1/50 s in standard vblank + * timed mods) of audio data to the output device. + */ +static void bufdump(struct context_data *ctx, void *b, int i) +{ + int frames; + + frames = snd_pcm_bytes_to_frames(pcm_handle, i); + if (snd_pcm_writei(pcm_handle, b, frames) < 0) { + snd_pcm_prepare(pcm_handle); + } +} + +static void dshutdown(struct context_data *ctx) +{ + snd_pcm_close(pcm_handle); +} + +static void flush() +{ + snd_pcm_drain(pcm_handle); + prepare_driver(); +} diff --git a/drivers/alsa05.c b/drivers/alsa05.c new file mode 100644 index 0000000..17381f2 --- /dev/null +++ b/drivers/alsa05.c @@ -0,0 +1,234 @@ +/* ALSA 0.5 driver for xmp + * Copyright (C) 2000 Tijs van Bakel and Rob Adamson + * + * This file is part of the Extended Module Player and is distributed + * under the terms of the GNU General Public License. See doc/COPYING + * for more information. + * + * Fixed for ALSA 0.5 by Rob Adamson + * Sat, 29 Apr 2000 17:10:46 +0100 (BST) + */ + +/* preliminary alsa 0.5 support, Tijs van Bakel, 02-03-2000. + * only default values are supported and music sounds chunky + */ + +/* Better ALSA 0.5 support, Rob Adamson, 16 Mar 2000. + * Again, hard-wired fragment size & number and sample rate, + * but it plays smoothly now. + */ + +/* Now uses specified options - Rob Adamson, 20 Mar 2000 */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "driver.h" +#include "mixer.h" + +static int init (struct context_data *ctx); +static void dshutdown (struct context_data *); +static void bufdump (struct context_data *, void *, int); +static void bufwipe (void); +static void flush (void); + +static void dummy() +{ +} + +static char *help[] = { + "frag=num,size", "Set the number and size (bytes) of fragments", + "card ", "Select sound card to use", + NULL +}; + +struct xmp_drv_info drv_alsa05 = { + "alsa05", /* driver ID */ + "ALSA 0.5 PCM audio", /* driver description */ + help, /* help */ + init, /* init */ + dshutdown, /* shutdown */ + dummy, /* starttimer */ + flush, /* stoptimer */ + bufdump, /* bufdump */ + NULL +}; + +static snd_pcm_t *pcm_handle; + +static int frag_num = 4; +static size_t frag_size = 4096; +static char *mybuffer = NULL; +static char *mybuffer_nextfree = NULL; +static char *card_name; + + +static int prepare_driver(void) +{ + int rc; + + rc = snd_pcm_plugin_prepare(pcm_handle, SND_PCM_CHANNEL_PLAYBACK); + if (rc < 0) { + printf("Unable to prepare plugin: %s\n", snd_strerror(rc)); + return rc; + } + + return 0; +} + +static int to_fmt(struct xmp_options *o) +{ + int fmt; + + if (o->resol == 0) + return SND_PCM_SFMT_MU_LAW; + + if (o->resol == 8) { + fmt = SND_PCM_SFMT_U8 | SND_PCM_SFMT_S8; + } else { + fmt = SND_PCM_SFMT_S16_LE | SND_PCM_SFMT_S16_BE | + SND_PCM_SFMT_U16_LE | SND_PCM_SFMT_U16_BE; + + if (o->big_endian) { + fmt &= SND_PCM_SFMT_S16_BE | SND_PCM_SFMT_U16_BE; + } else { + fmt &= SND_PCM_SFMT_S16_LE | SND_PCM_SFMT_U16_LE; + } + } + + if (o->outfmt & XMP_FMT_UNS) { + fmt &= SND_PCM_SFMT_U8|SND_PCM_SFMT_U16_LE|SND_PCM_SFMT_U16_BE; + } else { + fmt &= SND_PCM_SFMT_S8|SND_PCM_SFMT_S16_LE|SND_PCM_SFMT_S16_BE; + } + + return fmt; +} + +static int init(struct context_data *ctx) +{ + struct xmp_options *o = &ctx->o; + snd_pcm_channel_params_t params; + snd_pcm_channel_setup_t setup; + int card = 0; + int dev = 0; + int rc; + char *token, **parm; + + parm_init(); + chkparm2("frag", "%d,%d", &frag_num, &frag_size); + if (frag_num > 8) + frag_num = 8; + if (frag_num < 4) + frag_num = 4; + if (frag_size > 65536) + frag_size = 65536; + if (frag_size < 16) + frag_size = 16; + chkparm1("card", card_name = token); + // card = snd_card_name(card_name); /* ? */ + // dev = snd_defaults_pcm_device(); /* ? */ + parm_end(); + + mybuffer = malloc(frag_size); + if (mybuffer) { + mybuffer_nextfree = mybuffer; + } else { + printf("Unable to allocate memory for mixer buffer\n"); + return XMP_ERR_DINIT; + } + + if ((rc = + snd_pcm_open(&pcm_handle, card, dev, SND_PCM_OPEN_PLAYBACK)) < 0) { + printf("Unable to initialize pcm device: %s\n", + snd_strerror(rc)); + return XMP_ERR_DINIT; + } + + memset(¶ms, 0, sizeof(snd_pcm_channel_params_t)); + + params.mode = SND_PCM_MODE_BLOCK; + params.buf.block.frag_size = frag_size; + params.buf.block.frags_min = 1; + params.buf.block.frags_max = frag_num; + + //params.mode = SND_PCM_MODE_STREAM; + //params.buf.stream.queue_size = 16384; + //params.buf.stream.fill = SND_PCM_FILL_NONE; + //params.buf.stream.max_fill = 0; + + params.channel = SND_PCM_CHANNEL_PLAYBACK; + params.start_mode = SND_PCM_START_FULL; + params.stop_mode = SND_PCM_STOP_ROLLOVER; + + params.format.interleave = 1; + params.format.format = to_fmt(o); + params.format.rate = o->freq; + params.format.voices = (o->outfmt & XMP_FORMAT_MONO) ? 1 : 2; + + if ((rc = snd_pcm_plugin_params(pcm_handle, ¶ms)) < 0) { + printf("Unable to set output parameters: %s\n", + snd_strerror(rc)); + return XMP_ERR_DINIT; + } + + if (prepare_driver() < 0) + return XMP_ERR_DINIT; + + memset(&setup, 0, sizeof(setup)); + setup.mode = SND_PCM_MODE_STREAM; + setup.channel = SND_PCM_CHANNEL_PLAYBACK; + + if ((rc = snd_pcm_channel_setup(pcm_handle, &setup)) < 0) { + printf("Unable to setup channel: %s\n", snd_strerror(rc)); + return XMP_ERR_DINIT; + } + + return 0; +} + +/* Build and write one tick (one PAL frame or 1/50 s in standard vblank + * timed mods) of audio data to the output device. + */ +static void bufdump(struct context_data *ctx, void *b, int i) +{ + /* Note this assumes a fragment size of (frag_size) */ + while (i > 0) { + size_t f = (frag_size) - (mybuffer_nextfree - mybuffer); + size_t to_copy = (f < i) ? f : i; + + memcpy(mybuffer_nextfree, b, to_copy); + b += to_copy; + mybuffer_nextfree += to_copy; + f -= to_copy; + i -= to_copy; + if (f == 0) { + snd_pcm_plugin_write(pcm_handle, mybuffer, frag_size); + mybuffer_nextfree = mybuffer; + } + } +} + +static void dshutdown(struct context_data *ctx) +{ + snd_pcm_close(pcm_handle); + free(mybuffer); +} + +static void flush() +{ + snd_pcm_plugin_flush(pcm_handle, SND_PCM_CHANNEL_PLAYBACK); + prepare_driver(); +} diff --git a/drivers/amiga.c b/drivers/amiga.c new file mode 100644 index 0000000..d5b2d7d --- /dev/null +++ b/drivers/amiga.c @@ -0,0 +1,90 @@ +/* Amiga AHI driver for Extended Module Player + * Copyright (C) 2007 Lorence Lombardo + * + * This file is part of the Extended Module Player and is distributed + * under the terms of the GNU General Public License. See doc/COPYING + * for more information. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "driver.h" +#include "mixer.h" +#include "convert.h" + +static int fd; + +static int init(struct context_data *); +static void bufdump(struct context_data *, void *, int); +static void shutdown(struct context_data *); + +static void dummy() +{ +} + +static char *help[] = { + "buffer=val", "Audio buffer size", + NULL +}; + +struct xmp_drv_info drv_amiga = { + "ahi", /* driver ID */ + "Amiga AHI audio", /* driver description */ + help, /* help */ + init, /* init */ + shutdown, /* shutdown */ + dummy, /* starttimer */ + dummy, /* stoptimer */ + dummy, /* resetvoices */ + bufdump, /* bufdump */ + NULL +}; + +static int init(struct context_data *ctx) +{ + struct xmp_options *o = &ctx->o; + char outfile[256]; + int nch = o->outfmt & XMP_FORMAT_MONO ? 1 : 2; + int bsize = o->freq * nch * o->resol / 4; + char *token, **parm; + + parm_init(); + chkparm1("buffer", bsize = strtoul(token, NULL, 0)); + parm_end(); + + sprintf(outfile, "AUDIO:B/%d/F/%d/C/%d/BUFFER/%d", + o->resol, o->freq, nch, bsize); + + fd = open(outfile, O_WRONLY); + + return 0; +} + +static void bufdump(struct context_data *ctx, void *b, int i) +{ + int j; + + while (i) { + if ((j = write(fd, b, i)) > 0) { + i -= j; + b = (char *)b + j; + } else + break; + } +} + +static void shutdown(struct context_data *ctx) +{ + if (fd) + close(fd); +} diff --git a/drivers/beos.c b/drivers/beos.c new file mode 100644 index 0000000..95dd0ca --- /dev/null +++ b/drivers/beos.c @@ -0,0 +1,230 @@ +/* Extended Module Player + * Copyright (C) 1996-2012 Claudio Matsuoka and Hipolito Carraro Jr + * + * This file is part of the Extended Module Player and is distributed + * under the terms of the GNU General Public License. See doc/COPYING + * for more information. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#define B_AUDIO_CHAR 1 +#define B_AUDIO_SHORT 2 + +extern "C" { +#include +#include + +#include "xmp.h" + +/* g++ doesn't like typedef xmp_context and struct context_data */ +#define xmp_context _xmp_context +#include "common.h" + +#include "driver.h" +#include "mixer.h" +} + +static int init (struct context_data *ctx); +static void bufdump (struct context_data *, void *, int); +static void myshutdown (struct context_data *); + +static void dummy() +{ +} + +static char *help[] = { + "buffer=num,size", "set the number and size of buffer fragments", + NULL +}; + +struct xmp_drv_info drv_beos = { + "beos", /* driver ID */ + "BeOS PCM audio", /* driver description */ + NULL, /* help */ + (int (*)())init, /* init */ + (void (*)())myshutdown, /* shutdown */ + dummy, /* starttimer */ + dummy, /* flush */ + (void (*)())bufdump, /* bufdump */ +}; + +static media_raw_audio_format fmt; +static BSoundPlayer *player; + + +/* + * CoreAudio helpers from mplayer/libao + * The player fills a ring buffer, BSP retrieves data from the buffer + */ + +static int paused; +static uint8 *buffer; +static int buffer_len; +static int buf_write_pos; +static int buf_read_pos; +static int chunk_size; +static int chunk_num; +static int packet_size; + + +/* return minimum number of free bytes in buffer, value may change between + * two immediately following calls, and the real number of free bytes + * might actually be larger! */ +static int buf_free() +{ + int free = buf_read_pos - buf_write_pos - chunk_size; + if (free < 0) + free += buffer_len; + return free; +} + +/* return minimum number of buffered bytes, value may change between + * two immediately following calls, and the real number of buffered bytes + * might actually be larger! */ +static int buf_used() +{ + int used = buf_write_pos - buf_read_pos; + if (used < 0) + used += buffer_len; + return used; +} + +/* add data to ringbuffer */ +static int write_buffer(unsigned char *data, int len) +{ + int first_len = buffer_len - buf_write_pos; + int free = buf_free(); + + if (len > free) + len = free; + if (first_len > len) + first_len = len; + + /* till end of buffer */ + memcpy(buffer + buf_write_pos, data, first_len); + if (len > first_len) { /* we have to wrap around */ + /* remaining part from beginning of buffer */ + memcpy(buffer, data + first_len, len - first_len); + } + buf_write_pos = (buf_write_pos + len) % buffer_len; + + return len; +} + +/* remove data from ringbuffer */ +static int read_buffer(unsigned char *data, int len) +{ + int first_len = buffer_len - buf_read_pos; + int buffered = buf_used(); + + if (len > buffered) + len = buffered; + if (first_len > len) + first_len = len; + + /* till end of buffer */ + memcpy(data, buffer + buf_read_pos, first_len); + if (len > first_len) { /* we have to wrap around */ + /* remaining part from beginning of buffer */ + memcpy(data + first_len, buffer, len - first_len); + } + buf_read_pos = (buf_read_pos + len) % buffer_len; + + return len; +} + +/* + * end of CoreAudio helpers + */ + + +void render_proc(void *theCookie, void *buffer, size_t req, + const media_raw_audio_format &format) +{ + int amt; + + while ((amt = buf_used()) < req) + snooze(100000); + + read_buffer((unsigned char *)buffer, req); +} + + +static int init(struct context_data *ctx) +{ + struct xmp_options *o = &ctx->o; + char *dev; + char *token, **parm; + static char desc[80]; + + be_app = new BApplication("application/x-vnd.cm-xmp"); + + chunk_size = 4096; + chunk_num = 20; + + parm_init(); + chkparm2("buffer", "%d,%d", &chunk_num, &chunk_size); + parm_end(); + + snprintf(desc, 80, "%s [%d fragments of %d bytes]", + drv_beos.description, chunk_num, chunk_size); + drv_beos.description = desc; + + fmt.frame_rate = o->freq; + fmt.channel_count = o->outfmt & XMP_FORMAT_MONO ? 1 : 2; + fmt.format = o->resol > 8 ? B_AUDIO_SHORT : B_AUDIO_CHAR; + fmt.byte_order = B_HOST_IS_LENDIAN ? + B_MEDIA_LITTLE_ENDIAN : B_MEDIA_BIG_ENDIAN; + fmt.buffer_size = chunk_size * chunk_num; + + buffer_len = chunk_size * chunk_num; + buffer = (uint8 *)calloc(1, buffer_len); + buf_read_pos = 0; + buf_write_pos = 0; + paused = 1; + + player = new BSoundPlayer(&fmt, "xmp output", render_proc); + + return 0; +} + + +/* Build and write one tick (one PAL frame or 1/50 s in standard vblank + * timed mods) of audio data to the output device. + */ +static void bufdump(struct context_data *ctx, void *b, int i) +{ + int j = 0; + + /* block until we have enough free space in the buffer */ + while (buf_free() < i) + snooze(100000); + + while (i) { + if ((j = write_buffer((uint8 *)b, i)) > 0) { + i -= j; + b += j; + } else + break; + } + + if (paused) { + player->Start(); + player->SetHasData(true); + paused = 0; + } +} + +static void myshutdown(struct context_data *ctx) +{ + player->Stop(); + be_app->Lock(); + be_app->Quit(); +} + diff --git a/drivers/bsd.c b/drivers/bsd.c new file mode 100644 index 0000000..5d49f13 --- /dev/null +++ b/drivers/bsd.c @@ -0,0 +1,123 @@ +/* Extended Module Player + * Copyright (C) 1996-2012 Claudio Matsuoka and Hipolito Carraro Jr + * + * This file is part of the Extended Module Player and is distributed + * under the terms of the GNU General Public License. See doc/COPYING + * for more information. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "common.h" +#include "driver.h" +#include "mixer.h" + +static int audio_fd; + +static int init(struct context_data *); +static int setaudio(struct xmp_option *); +static void bufdump(struct context_data *, void *, int); +static void shutdown(struct context_data *); + +static void dummy() +{ +} + +static char *help[] = { + "gain=val", "Audio output gain (0 to 255)", + "buffer=val", "Audio buffer size (default is 32768)", + NULL +}; + +struct xmp_drv_info drv_bsd = { + "bsd", /* driver ID */ + "BSD PCM audio", /* driver description */ + help, /* help */ + init, /* init */ + shutdown, /* shutdown */ + dummy, /* starttimer */ + dummy, /* flush */ + bufdump, /* bufdump */ +}; + +static int setaudio(struct xmp_options *o) +{ + audio_info_t ainfo; + int gain = 128; + int bsize = 32 * 1024; + char *token, **parm; + + parm_init(); + chkparm1("gain", gain = strtoul(token, NULL, 0)); + chkparm1("buffer", bsize = strtoul(token, NULL, 0)); + parm_end(); + + if (gain < AUDIO_MIN_GAIN) + gain = AUDIO_MIN_GAIN; + if (gain > AUDIO_MAX_GAIN) + gain = AUDIO_MAX_GAIN; + + AUDIO_INITINFO(&ainfo); + + ainfo.play.sample_rate = o->freq; + ainfo.play.channels = o->outfmt & XMP_FORMAT_MONO ? 1 : 2; + ainfo.play.precision = o->resol; + ainfo.play.encoding = o->resol > 8 ? + AUDIO_ENCODING_SLINEAR : AUDIO_ENCODING_ULINEAR; + ainfo.play.gain = gain; + ainfo.play.buffer_size = bsize; + + if (ioctl(audio_fd, AUDIO_SETINFO, &ainfo) == -1) { + close(audio_fd); + return XMP_ERR_DINIT; + } + + drv_bsd.description = "BSD PCM audio"; + + return 0; +} + +static int init(struct context_data *ctx) +{ + if ((audio_fd = open("/dev/sound", O_WRONLY)) == -1) + return XMP_ERR_DINIT; + + if (setaudio(o) != 0) + return XMP_ERR_DINIT; + + return 0; +} + +/* Build and write one tick (one PAL frame or 1/50 s in standard vblank + * timed mods) of audio data to the output device. + */ +static void bufdump(struct context_data *ctx, void *b, int i) +{ + int j; + + while (i) { + if ((j = write(audio_fd, b, i)) > 0) { + i -= j; + (char *)b += j; + } else + break; + }; +} + +static void shutdown(struct context_data *ctx) +{ + close(audio_fd); +} diff --git a/drivers/debug.c b/drivers/debug.c new file mode 100644 index 0000000..3d29571 --- /dev/null +++ b/drivers/debug.c @@ -0,0 +1,57 @@ +/* Extended Module Player + * Copyright (C) 1996-2012 Claudio Matsuoka and Hipolito Carraro Jr + * + * This file is part of the Extended Module Player and is distributed + * under the terms of the GNU General Public License. See doc/COPYING + * for more information. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include "common.h" +#include "driver.h" + +static void starttimer(void); +static void stoptimer(void); +static void bufdump(void); +static int init(struct context_data *ctx); +static void shutdown(struct context_data *); + +struct xmp_drv_info drv_debug = { + "debug", /* driver ID */ + "Driver debugger", /* driver description */ + NULL, /* help */ + init, /* init */ + shutdown, /* shutdown */ + starttimer, /* settimer */ + stoptimer, /* stoptimer */ + bufdump, /* bufdump */ +}; + +static void starttimer() +{ + printf("** starttimer\n"); +} + +static void stoptimer() +{ + printf("** stoptimer\n"); +} + +static void bufdump() +{ +} + +static int init(struct context_data *ctx) +{ + return 0; +} + +static void shutdown(struct context_data *ctx) +{ + printf("** shutdown\n"); +} diff --git a/drivers/file.c b/drivers/file.c new file mode 100644 index 0000000..bd5ac01 --- /dev/null +++ b/drivers/file.c @@ -0,0 +1,127 @@ +/* Extended Module Player + * Copyright (C) 1996-2012 Claudio Matsuoka and Hipolito Carraro Jr + * + * This file is part of the Extended Module Player and is distributed + * under the terms of the GNU General Public License. See doc/COPYING + * for more information. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include "common.h" +#include "driver.h" +#include "convert.h" + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#define DATA(x) (((struct data *)drv_file.data)->x) + +struct data { + int fd; + int endian; +}; + +static int init(struct context_data *); +static void bufdump(struct context_data *, void *, int); +static void shutdown(struct context_data *); + +static void dummy() +{ +} + +static char *help[] = { + "big-endian", "Generate big-endian 16-bit samples", + "little-endian", "Generate little-endian 16-bit samples", + NULL +}; + +struct xmp_drv_info drv_file = { + "file", /* driver ID */ + "file", /* driver description */ + help, /* help */ + init, /* init */ + shutdown, /* shutdown */ + dummy, /* starttimer */ + dummy, /* stoptimer */ + bufdump, /* bufdump */ +}; + +static int init(struct context_data *ctx) +{ + struct xmp_options *o = &ctx->o; + char *buf; + int bsize; + char *token, **parm; + + drv_file.data = malloc(sizeof (struct data)); + if (drv_file.data == NULL) + return -1; + + DATA(endian) = 0; + parm_init(); + chkparm0("big-endian", DATA(endian) = 1); + chkparm0("little-endian", DATA(endian) = -1); + parm_end(); + + if (!o->outfile) + o->outfile = "xmp.out"; + + if (strcmp(o->outfile, "-")) { + DATA(fd) = open(o->outfile, O_WRONLY | O_CREAT | O_TRUNC + | O_BINARY, 0644); + if (DATA(fd) < 0) + return -1; + } else { + DATA(fd) = 1; + } + + if (strcmp(o->outfile, "-")) { + bsize = strlen(drv_file.description) + strlen(o->outfile) + 8; + buf = malloc(bsize); + snprintf(buf, bsize, "%s: %s", drv_file.description, + o->outfile); + drv_file.description = buf; + } else { + drv_file.description = strdup("Output to stdout"); + } + + return 0; +} + +static void bufdump(struct context_data *ctx, void *b, int i) +{ + struct xmp_options *o = &ctx->o; + int j; + + if ((o->big_endian && DATA(endian) == -1) || + (!o->big_endian && DATA(endian) == 1)) { + xmp_cvt_sex(i, b); + } + + while (i) { + if ((j = write(DATA(fd), b, i)) > 0) { + i -= j; + b = (char *)b + j; + } else + break; + } +} + +static void shutdown(struct context_data *ctx) +{ + if (DATA(fd) > 0) + close(DATA(fd)); + + free(drv_file.description); + free(drv_file.data); +} diff --git a/drivers/hpux.c b/drivers/hpux.c new file mode 100644 index 0000000..fb9164e --- /dev/null +++ b/drivers/hpux.c @@ -0,0 +1,164 @@ +/* Extended Module Player + * Copyright (C) 1996-2012 Claudio Matsuoka and Hipolito Carraro Jr + * + * This file is part of the Extended Module Player and is distributed + * under the terms of the GNU General Public License. See doc/COPYING + * for more information. + */ + +/* This code was tested on a 9000/710 running HP-UX 9.05 with 8 kHz, + * 16 bit mono output. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "driver.h" +#include "mixer.h" + +static int audio_fd; + +static int init(struct context_data *); +static int setaudio(struct xmp_options *); +static void bufdump(struct context_data *, void *, int); +static void shutdown(struct context_data *); + +static void dummy() +{ +} + +/* Standard sampling rates */ +static int srate[] = { + 44100, 32000, 22050, 16000, 11025, 8000, 0 +}; + +static char *help[] = { + "gain=val", "Audio output gain (0 to 255)", + "port={s|h|l}", "Audio port (s[peaker], h[eadphones], l[ineout])", + "buffer=val", "Audio buffer size", + NULL +}; + +struct xmp_drv_info drv_hpux = { + "hpux", /* driver ID */ + "HP-UX PCM audio", /* driver description */ + help, /* help */ + init, /* init */ + shutdown, /* shutdown */ + dummy, /* starttimer */ + dummy, /* flush */ + bufdump, /* bufdump */ +}; + +static int setaudio(struct xmp_options *o) +{ + int flags; + int gain = 128; + int bsize = 32 * 1024; + int port = AUDIO_OUT_SPEAKER; + struct audio_gains agains; + struct audio_describe adescribe; + char *token, **parm; + int i; + + parm_init(); + chkparm1("gain", gain = strtoul(token, NULL, 0)); + chkparm1("buffer", bsize = strtoul(token, NULL, 0)); + chkparm1("port", port = (int)*token) + parm_end(); + + switch (port) { + case 'h': + port = AUDIO_OUT_HEADPHONE; + break; + case 'l': + port = AUDIO_OUT_LINE; + break; + default: + port = AUDIO_OUT_SPEAKER; + } + + if ((flags = fcntl(audio_fd, F_GETFL, 0)) < 0) + return XMP_ERR_DINIT; + + flags |= O_NDELAY; + if ((flags = fcntl(audio_fd, F_SETFL, flags)) < 0) + return XMP_ERR_DINIT; + + if (ioctl(audio_fd, AUDIO_SET_DATA_FORMAT, AUDIO_FORMAT_LINEAR16BIT) == + -1) + return XMP_ERR_DINIT; + + if (ioctl(audio_fd, AUDIO_SET_CHANNELS, + o->outfmt & XMP_FORMAT_MONO ? 1 : 2) == -1) { + o->outfmt ^= XMP_FORMAT_MONO; + + if (ioctl(audio_fd, AUDIO_SET_CHANNELS, + o->outfmt & XMP_FORMAT_MONO ? 1 : 2) == -1) + return XMP_ERR_DINIT; + } + + ioctl(audio_fd, AUDIO_SET_OUTPUT, port); + + for (i = 0; ioctl(audio_fd, AUDIO_SET_SAMPLE_RATE, o->freq) == -1; i++) + if ((o->freq = srate[i]) == 0) + return XMP_ERR_DINIT; + + if (ioctl(audio_fd, AUDIO_DESCRIBE, &adescribe) == -1) + return XMP_ERR_DINIT; + + if (ioctl(audio_fd, AUDIO_GET_GAINS, &agains) == -1) + return XMP_ERR_DINIT; + + agains.transmit_gain = adescribe.min_transmit_gain + + (adescribe.max_transmit_gain - adescribe.min_transmit_gain) * + gain / 256; + + if (ioctl(audio_fd, AUDIO_SET_GAINS, &agains) == -1) + return XMP_ERR_DINIT; + + ioctl(audio_fd, AUDIO_SET_TXBUFSIZE, bsize); + + return 0; +} + +static int init(struct context_data *ctx) +{ + if ((audio_fd = open("/dev/audio", O_WRONLY)) == -1) + return XMP_ERR_DINIT; + + if (setaudio(ctl) != 0) + return XMP_ERR_DINIT; + + return 0; +} + +/* Build and write one tick (one PAL frame or 1/50 s in standard vblank + * timed mods) of audio data to the output device. + */ +static void bufdump(struct context_data *ctx, void *b, int i) +{ + int j; + + while (i) { + if ((j = write(audio_fd, b, i)) > 0) { + i -= j; + (char *)b += j; + } else + break; + } +} + +static void shutdown(struct context_data *ctx) +{ + close(audio_fd); +} diff --git a/drivers/netbsd.c b/drivers/netbsd.c new file mode 100644 index 0000000..3888e10 --- /dev/null +++ b/drivers/netbsd.c @@ -0,0 +1,168 @@ +/* Extended Module Player + * Copyright (C) 1996-2012 Claudio Matsuoka and Hipolito Carraro Jr + * + * This file is part of the Extended Module Player and is distributed + * under the terms of the GNU General Public License. See doc/COPYING + * for more information. + */ + +/* Based on bsd.c and solaris.c */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "common.h" +#include "driver.h" +#include "mixer.h" + +static int audio_fd; +static int audioctl_fd; + +static int init(struct context_data *); +static int setaudio(struct xmp_options *); +static void bufdump(struct context_data *, void *, int); +static void shutdown(struct context_data *); + +static void dummy() +{ +} + +static char *help[] = { + "gain=val", "Audio output gain (0 to 255)", +/* XXX "port={s|h|l}", "Audio port (s[peaker], h[eadphones], l[ineout])", XXX */ + "buffer=val", "Audio buffer size (default is 32768)", + NULL +}; + +struct xmp_drv_info drv_netbsd = { + "netbsd", /* driver ID */ + "NetBSD PCM audio", /* driver description */ + help, /* help */ + init, /* init */ + shutdown, /* shutdown */ + dummy, /* starttimer */ + dummy, /* flush */ + bufdump, /* bufdump */ +}; + +static int setaudio(struct xmp_options *o) +{ + audio_info_t ainfo; + int gain = 128; + int bsize = 32 * 1024; + char *token, **parm; + + /* try to open audioctldevice */ + if ((audioctl_fd = open("/dev/audioctl", O_RDWR)) < 0) { + fprintf(stderr, "couldn't open audioctldevice\n"); + close(audio_fd); + return -1; + } + + /* empty buffers before change config */ + ioctl(audio_fd, AUDIO_DRAIN, 0); /* drain everything out */ + ioctl(audio_fd, AUDIO_FLUSH); /* flush audio */ + ioctl(audioctl_fd, AUDIO_FLUSH); /* flush audioctl */ + + /* get audio parameters. */ + if (ioctl(audioctl_fd, AUDIO_GETINFO, &ainfo) < 0) { + fprintf(stderr, "AUDIO_GETINFO failed!\n"); + close(audio_fd); + close(audioctl_fd); + return -1; + } + + close(audioctl_fd); + + parm_init(); + chkparm1("gain", gain = strtoul(token, NULL, 0)); + chkparm1("buffer", bsize = strtoul(token, NULL, 0)); + /* chkparm1 ("port", port = (int)*token) */ + parm_end(); + +#if 0 + switch (port) { + case 'h': + port = AUDIO_HEADPHONE; + break; + case 'l': + port = AUDIO_LINE_OUT; + break; + case 's': + port = AUDIO_SPEAKER; + } +#endif + if (gain < AUDIO_MIN_GAIN) + gain = AUDIO_MIN_GAIN; + if (gain > AUDIO_MAX_GAIN) + gain = AUDIO_MAX_GAIN; + + AUDIO_INITINFO(&ainfo); + + ainfo.play.sample_rate = o->freq; + ainfo.play.channels = o->outfmt & XMP_FORMAT_MONO ? 1 : 2; + ainfo.play.precision = o->resol; + /* ainfo.play.precision = AUDIO_ENCODING_ULINEAR; */ + ainfo.play.encoding = o->resol > 8 ? + AUDIO_ENCODING_SLINEAR : AUDIO_ENCODING_ULINEAR; + ainfo.play.gain = gain; + /* ainfo.play.port = port; */ + ainfo.play.buffer_size = bsize; + + if (ioctl(audio_fd, AUDIO_SETINFO, &ainfo) == -1) { + close(audio_fd); + return XMP_ERR_DINIT; + } + + /* o->resol = 0; */ + /* o->freq = 8000; */ + /* o->outfmt |=XMP_FORMAT_MONO; */ + o->freq = 44000; + drv_netbsd.description = "NetBSD PCM audio"; + return 0; +} + +static int init(struct context_data *ctx) +{ + if ((audio_fd = open("/dev/sound", O_WRONLY)) == -1) + return XMP_ERR_DINIT; + + if (setaudio(&ctx->o) != 0) + return XMP_ERR_DINIT; + + return 0; +} + +/* Build and write one tick (one PAL frame or 1/50 s in standard vblank + * timed mods) of audio data to the output device. + */ +static void bufdump(struct context_data *ctx, void *b, int i) +{ + int j; + + while (i) { + if ((j = write(audio_fd, b, i)) > 0) { + i -= j; + (char *)b += j; + } else + break; + } +} + +static void shutdown(struct context_data *ctx) +{ + close(audio_fd); +} diff --git a/drivers/openbsd.c b/drivers/openbsd.c new file mode 100644 index 0000000..ab7ee88 --- /dev/null +++ b/drivers/openbsd.c @@ -0,0 +1,125 @@ +/* Extended Module Player + * Copyright (C) 1996-2012 Claudio Matsuoka and Hipolito Carraro Jr + * + * This file is part of the Extended Module Player and is distributed + * under the terms of the GNU General Public License. See doc/COPYING + * for more information. + */ + +/* This should work for OpenBSD */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "common.h" +#include "driver.h" +#include "mixer.h" + +static int audio_fd; + +static int init(struct context_data *); +static int setaudio(struct xmp_options *); +static void bufdump(struct context_data *, void *, int); +static void shutdown(struct context_data *); + +static void dummy() +{ +} + +static char *help[] = { + "gain=val", "Audio output gain (0 to 255)", + "buffer=val", "Audio buffer size (default is 32768)", + NULL +}; + +struct xmp_drv_info drv_openbsd = { + "openbsd", /* driver ID */ + "OpenBSD PCM audio", /* driver description */ + help, /* help */ + init, /* init */ + shutdown, /* shutdown */ + dummy, /* starttimer */ + dummy, /* flush */ + bufdump, /* bufdump */ +}; + +static int setaudio(struct xmp_options *o) +{ + audio_info_t ainfo; + int gain = 128; + int bsize = 32 * 1024; + char *token, **parm; + + parm_init(); + chkparm1("gain", gain = strtoul(token, NULL, 0)); + chkparm1("buffer", bsize = strtoul(token, NULL, 0)); + parm_end(); + + if (gain < AUDIO_MIN_GAIN) + gain = AUDIO_MIN_GAIN; + if (gain > AUDIO_MAX_GAIN) + gain = AUDIO_MAX_GAIN; + + AUDIO_INITINFO(&ainfo); + + ainfo.play.sample_rate = o->freq; + ainfo.play.channels = o->outfmt & XMP_FORMAT_MONO ? 1 : 2; + ainfo.play.precision = o->resol; + ainfo.play.encoding = o->resol > 8 ? + AUDIO_ENCODING_SLINEAR : AUDIO_ENCODING_ULINEAR; + ainfo.play.gain = gain; + ainfo.play.buffer_size = bsize; + + if (ioctl(audio_fd, AUDIO_SETINFO, &ainfo) == -1) { + close(audio_fd); + return XMP_ERR_DINIT; + } + + drv_openbsd.description = "OpenBSD PCM audio"; + + return 0; +} + +static int init(struct context_data *ctx) +{ + if ((audio_fd = open("/dev/sound", O_WRONLY)) == -1) + return XMP_ERR_DINIT; + + if (setaudio(&ctx->o) != 0) + return XMP_ERR_DINIT; + + return 0; +} + +/* Build and write one tick (one PAL frame or 1/50 s in standard vblank + * timed mods) of audio data to the output device. + */ +static void bufdump(struct context_data *ctx, void *b, int i) +{ + int j; + + while (i) { + if ((j = write(audio_fd, b, i)) > 0) { + i -= j; + b = (char *)b + j; + } else + break; + } +} + +static void shutdown(struct context_data *ctx) +{ + close(audio_fd); +} diff --git a/drivers/os2_dart.c b/drivers/os2_dart.c new file mode 100644 index 0000000..cf5bb39 --- /dev/null +++ b/drivers/os2_dart.c @@ -0,0 +1,261 @@ +//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\// +//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\// +/* + This should work for OS/2 Dart + + History: + 1.0 - By Kevin Langman + +*/ +//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\// +//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\// + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#define INCL_DOS +#include +#include +#include +#include + +#include "common.h" +#include "driver.h" +#include "mixer.h" + +#define BUFFERCOUNT 4 +#define BUF_MIN 8 +#define BUF_MAX 32 + +static int init(struct context_data *); +static int setaudio(struct xmp_options *); +static void bufdump(struct context_data *, void *, int); +static void shutdown(struct context_data *); + +static MCI_MIX_BUFFER MixBuffers[BUFFERCOUNT]; +static MCI_MIXSETUP_PARMS MixSetupParms; +static MCI_BUFFER_PARMS BufferParms; +static MCI_GENERIC_PARMS GenericParms; + +static ULONG DeviceID = 0; +static int bsize = 16; +static short next = 2; +static short ready = 1; + +static HMTX mutex; + +static void dummy() +{ +} + +static char *help[] = { + "sharing={Y,N}", "Device Sharing (default is N)", + "device=val", "OS/2 Audio Device (default is 0 auto-detect)", + "buffer=val", "Audio buffer size (default is 16)", + NULL +}; + +struct xmp_drv_info drv_os2dart = { + "dart", /* driver ID */ + "OS/2 Direct Audio Realtime", /* driver description */ + help, /* help */ + init, /* init */ + shutdown, /* shutdown */ + dummy, /* starttimer */ + dummy, /* flush */ + bufdump, /* bufdump */ +}; + +// Buffer update thread (created and called by DART) +static LONG APIENTRY OS2_Dart_UpdateBuffers + (ULONG ulStatus, PMCI_MIX_BUFFER pBuffer, ULONG ulFlags) { + + if ((ulFlags == MIX_WRITE_COMPLETE) || + ((ulFlags == (MIX_WRITE_COMPLETE | MIX_STREAM_ERROR)) && + (ulStatus == ERROR_DEVICE_UNDERRUN))) { + DosRequestMutexSem(mutex, SEM_INDEFINITE_WAIT); + ready++; + DosReleaseMutexSem(mutex); + } + + return (TRUE); +} + +static int setaudio(struct xmp_options *o) +{ + char sharing = 0; + int device = 0; + int flags; + int i; + MCI_AMP_OPEN_PARMS AmpOpenParms; + char *token, **parm; + + //printf( "In SetAudio...\n" ); + + parm_init(); + chkparm1("sharing", sharing = *token); + chkparm1("device", device = atoi(token)); + chkparm1("buffer", bsize = strtoul(token, NULL, 0)); + parm_end(); + + if ((bsize < BUF_MIN || bsize > BUF_MAX) && bsize != 0) { + bsize = 16 * 1024; + } else { + bsize *= 1024; + } + + //if( sharing < 1 || sharing > 0 ){ + // sharing = 0; + //} + + MixBuffers[0].pBuffer = NULL; /* marker */ + memset(&GenericParms, 0, sizeof(MCI_GENERIC_PARMS)); + + /* open AMP device */ + memset(&AmpOpenParms, 0, sizeof(MCI_AMP_OPEN_PARMS)); + AmpOpenParms.usDeviceID = 0; + + AmpOpenParms.pszDeviceType = + (PSZ) MAKEULONG(MCI_DEVTYPE_AUDIO_AMPMIX, (USHORT) device); + + flags = MCI_WAIT | MCI_OPEN_TYPE_ID; + if (sharing == 'Y' || sharing == 'y') { + flags = flags | MCI_OPEN_SHAREABLE; + } + + if (mciSendCommand(0, MCI_OPEN, flags, + (PVOID) & AmpOpenParms, 0) != MCIERR_SUCCESS) { + return -1; + } + + DeviceID = AmpOpenParms.usDeviceID; + + /* setup playback parameters */ + memset(&MixSetupParms, 0, sizeof(MCI_MIXSETUP_PARMS)); + + MixSetupParms.ulBitsPerSample = o->resol; + MixSetupParms.ulFormatTag = MCI_WAVE_FORMAT_PCM; + MixSetupParms.ulSamplesPerSec = o->freq; + MixSetupParms.ulChannels = o->outfmt & XMP_FORMAT_MONO ? 1 : 2; + MixSetupParms.ulFormatMode = MCI_PLAY; + MixSetupParms.ulDeviceType = MCI_DEVTYPE_WAVEFORM_AUDIO; + MixSetupParms.pmixEvent = OS2_Dart_UpdateBuffers; + + if (mciSendCommand(DeviceID, MCI_MIXSETUP, + MCI_WAIT | MCI_MIXSETUP_INIT, + (PVOID) & MixSetupParms, 0) != MCIERR_SUCCESS) { + + mciSendCommand(DeviceID, MCI_CLOSE, MCI_WAIT, + (PVOID) & GenericParms, 0); + return -1; + } + + /* take in account the DART suggested buffer size... */ + if (bsize == 0) { + bsize = MixSetupParms.ulBufferSize; + } + //printf( "Dart Buffer Size = %d\n", bsize ); + + BufferParms.ulNumBuffers = BUFFERCOUNT; + BufferParms.ulBufferSize = bsize; + BufferParms.pBufList = MixBuffers; + + if (mciSendCommand(DeviceID, MCI_BUFFER, + MCI_WAIT | MCI_ALLOCATE_MEMORY, + (PVOID) & BufferParms, 0) != MCIERR_SUCCESS) { + mciSendCommand(DeviceID, MCI_CLOSE, MCI_WAIT, + (PVOID) & GenericParms, 0); + return -1; + } + + for (i = 0; i < BUFFERCOUNT; i++) { + MixBuffers[i].ulBufferLength = bsize; + } + + //MixBuffers[0].ulBufferLength = bsize; + //MixBuffers[1].ulBufferLength = bsize; + //MixBuffers[2].ulBufferLength = bsize; + //MixBuffers[3].ulBufferLength = bsize; + + return 0; +} + +static int init(struct context_data *ctx) +{ + //printf( "In Init...\n" ); + + if (setaudio(ctl) != 0) + return XMP_ERR_DINIT; + + /* Start Playback */ + //printf("Starting Playback!!\n"); + memset(MixBuffers[0].pBuffer, /*32767 */ 0, bsize); + memset(MixBuffers[1].pBuffer, /*32767 */ 0, bsize); + MixSetupParms.pmixWrite(MixSetupParms.ulMixHandle, MixBuffers, 2); + + return 0; +} + +/* Build and write one tick (one PAL frame or 1/50 s in standard vblank + * timed mods) of audio data to the output device. + */ +static void bufdump(struct context_data *ctx, void *b, int i) +{ + static int index = 0; + + //printf( "In BufDump...\n" ); + + if (index + i > bsize) { + + //printf("Next = %d, ready = %d\n", next, ready); + + do { + DosRequestMutexSem(mutex, SEM_INDEFINITE_WAIT); + if (ready != 0) { + DosReleaseMutexSem(mutex); + break; + } + DosReleaseMutexSem(mutex); + DosSleep(20); + } while (TRUE); + + MixBuffers[next].ulBufferLength = index; + MixSetupParms.pmixWrite(MixSetupParms.ulMixHandle, + &(MixBuffers[next]), 1); + ready--; + next++; + index = 0; + if (next == BUFFERCOUNT) { + next = 0; + } + } + memcpy(&((char *)MixBuffers[next].pBuffer)[index], b, i); + index += i; + +} + +static void shutdown(struct context_data *ctx) +{ + //printf( "In ShutDown...\n" ); + + if (MixBuffers[0].pBuffer) { + mciSendCommand(DeviceID, MCI_BUFFER, + MCI_WAIT | MCI_DEALLOCATE_MEMORY, &BufferParms, + 0); + MixBuffers[0].pBuffer = NULL; + } + if (DeviceID) { + mciSendCommand(DeviceID, MCI_CLOSE, MCI_WAIT, + (PVOID) & GenericParms, 0); + DeviceID = 0; + } +} diff --git a/drivers/oss.c b/drivers/oss.c new file mode 100644 index 0000000..6082e65 --- /dev/null +++ b/drivers/oss.c @@ -0,0 +1,217 @@ +/* Extended Module Player + * Copyright (C) 1996-2012 Claudio Matsuoka and Hipolito Carraro Jr + * + * This file is part of the Extended Module Player and is distributed + * under the terms of the GNU General Public License. See doc/COPYING + * for more information. + */ + +/* + * devfs /dev/sound/dsp support by Dirk Jagdmann + * resume/onpause by Test Rat + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(HAVE_SYS_SOUNDCARD_H) +#include +#elif defined(HAVE_MACHINE_SOUNDCARD_H) +#include +#endif + +#include "driver.h" + +static int audio_fd; + +static void from_fmt(struct xmp_options *, int); +static int to_fmt(struct xmp_options *); +static void setaudio(struct xmp_options *); +static int init(struct context_data *); +static void shutdown(struct context_data *); +static void bufdump(struct context_data *, void *, int); +static void resume(void); +static void onpause(void); + +static char *help[] = { + "frag=num,size", "Set the number and size of fragments", + "dev=", "Audio device to use (default /dev/dsp)", + "nosync", "Don't flush OSS buffers between modules", + NULL +}; + +struct xmp_drv_info drv_oss = { + "oss", /* driver ID */ + "OSS PCM audio", /* driver description */ + help, /* help */ + init, /* init */ + shutdown, /* shutdown */ + resume, /* starttimer */ + onpause, /* stoptimer */ + bufdump, /* bufdump */ +}; + +static int fragnum, fragsize; +static int do_sync = 1; + +static int to_fmt(struct xmp_options *o) +{ + int fmt; + + if (!o->resol) + return AFMT_MU_LAW; + + if (o->resol == 8) + fmt = AFMT_U8 | AFMT_S8; + else { + fmt = o->big_endian ? AFMT_S16_BE | AFMT_U16_BE : + AFMT_S16_LE | AFMT_U16_LE; + } + + if (o->outfmt & XMP_FMT_UNS) + fmt &= AFMT_U8 | AFMT_U16_LE | AFMT_U16_BE; + else + fmt &= AFMT_S8 | AFMT_S16_LE | AFMT_S16_BE; + + return fmt; +} + +static void from_fmt(struct xmp_options *o, int outfmt) +{ + if (outfmt & AFMT_MU_LAW) { + o->resol = 0; + return; + } + + if (outfmt & (AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE | AFMT_U16_BE)) + o->resol = 16; + else + o->resol = 8; + + if (outfmt & (AFMT_U8 | AFMT_U16_LE | AFMT_U16_BE)) + o->outfmt |= XMP_FMT_UNS; + else + o->outfmt &= ~XMP_FMT_UNS; +} + +static void setaudio(struct xmp_options *o) +{ + static int fragset = 0; + int frag = 0; + int fmt; + + frag = (fragnum << 16) + fragsize; + + fmt = to_fmt(o); + ioctl(audio_fd, SNDCTL_DSP_SETFMT, &fmt); + from_fmt(o, fmt); + + fmt = !(o->outfmt & XMP_FORMAT_MONO); + ioctl(audio_fd, SNDCTL_DSP_STEREO, &fmt); + if (fmt) + o->outfmt &= ~XMP_FORMAT_MONO; + else + o->outfmt |= XMP_FORMAT_MONO; + + ioctl(audio_fd, SNDCTL_DSP_SPEED, &o->freq); + + /* Set the fragments only once */ + if (!fragset) { + if (fragnum && fragsize) + ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag); + fragset++; + } +} + +static int init(struct context_data *ctx) +{ + char *dev_audio[] = { "/dev/dsp", "/dev/sound/dsp" }; + struct xmp_options *o = &ctx->o; + audio_buf_info info; + static char buf[80]; + char *token, **parm; + int i; + + fragnum = 16; /* default number of fragments */ + i = 1024; /* default size of fragment */ + + parm_init(); + chkparm2("frag", "%d,%d", &fragnum, &i); + chkparm1("dev", dev_audio[0] = token); + chkparm0("nosync", do_sync = 0); + parm_end(); + + for (fragsize = 0; i >>= 1; fragsize++) ; + if (fragsize < 4) + fragsize = 4; + + for (i = 0; i < sizeof(dev_audio) / sizeof(dev_audio[0]); i++) + if ((audio_fd = open(dev_audio[i], O_WRONLY)) >= 0) + break; + if (audio_fd < 0) + return XMP_ERR_DINIT; + + setaudio(o); + + if (ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info) == 0) { + snprintf(buf, 80, "%s [%d fragments of %d bytes]", + drv_oss.description, info.fragstotal, + info.fragsize); + drv_oss.description = buf; + } + + return 0; +} + +/* Build and write one tick (one PAL frame or 1/50 s in standard vblank + * timed mods) of audio data to the output device. + */ +static void bufdump(struct context_data *ctx, void *b, int i) +{ + int j; + + while (i) { + if ((j = write(audio_fd, b, i)) > 0) { + i -= j; + b = (char *)b + j; + } else + break; + }; +} + +static void shutdown(struct context_data *ctx) +{ + ioctl(audio_fd, SNDCTL_DSP_RESET, NULL); + close(audio_fd); +} + +static void resume() +{ +#ifdef SNDCTL_DSP_SETTRIGGER + int trig = PCM_ENABLE_OUTPUT; + ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &trig); +#endif +} + +static void onpause() +{ +#ifdef SNDCTL_DSP_SETTRIGGER + int trig = 0; + ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &trig); +#else + ioctl(audio_fd, SNDCTL_DSP_RESET, NULL); +#endif + + if (do_sync) + ioctl(audio_fd, SNDCTL_DSP_SYNC, NULL); +} diff --git a/drivers/osx.c b/drivers/osx.c new file mode 100644 index 0000000..4dba4fa --- /dev/null +++ b/drivers/osx.c @@ -0,0 +1,279 @@ +/* Extended Module Player + * Copyright (C) 1996-2012 Claudio Matsuoka and Hipolito Carraro Jr + * CoreAudio helpers (C) 2000 Timothy J. Wood + * + * This file is part of the Extended Module Player and is distributed + * under the terms of the GNU General Public License. See doc/COPYING + * for more information. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +#include "common.h" +#include "driver.h" +#include "mixer.h" + + +static int init (struct context_data *ctx); +static void bufdump (struct context_data *, void *, int); +static void shutdown (struct context_data *); + +static void dummy () { } + +struct xmp_drv_info drv_osx = { + "osx", /* driver ID */ + "OSX CoreAudio", /* driver description */ + NULL, /* help */ + init, /* init */ + shutdown, /* shutdown */ + dummy, /* starttimer */ + dummy, /* flush */ + bufdump, /* bufdump */ +}; + +static AudioUnit au; + + +/* + * CoreAudio helpers from mplayer/libao + * The player fills a ring buffer, OSX retrieves data from the buffer + */ + +static int paused; +static uint8 *buffer; +static int buffer_len; +static int buf_write_pos; +static int buf_read_pos; +static int num_chunks; +static int chunk_size; +static int packet_size; + + +/* return minimum number of free bytes in buffer, value may change between + * two immediately following calls, and the real number of free bytes + * might actually be larger! */ +static int buf_free() +{ + int free = buf_read_pos - buf_write_pos - chunk_size; + if (free < 0) + free += buffer_len; + return free; +} + +/* return minimum number of buffered bytes, value may change between + * two immediately following calls, and the real number of buffered bytes + * might actually be larger! */ +static int buf_used() +{ + int used = buf_write_pos - buf_read_pos; + if (used < 0) + used += buffer_len; + return used; +} + +/* add data to ringbuffer */ +static int write_buffer(unsigned char *data, int len) +{ + int first_len = buffer_len - buf_write_pos; + int free = buf_free(); + + if (len > free) + len = free; + if (first_len > len) + first_len = len; + + /* till end of buffer */ + memcpy(buffer + buf_write_pos, data, first_len); + if (len > first_len) { /* we have to wrap around */ + /* remaining part from beginning of buffer */ + memcpy(buffer, data + first_len, len - first_len); + } + buf_write_pos = (buf_write_pos + len) % buffer_len; + + return len; +} + +/* remove data from ringbuffer */ +static int read_buffer(unsigned char *data, int len) +{ + int first_len = buffer_len - buf_read_pos; + int buffered = buf_used(); + + if (len > buffered) + len = buffered; + if (first_len > len) + first_len = len; + + /* till end of buffer */ + memcpy(data, buffer + buf_read_pos, first_len); + if (len > first_len) { /* we have to wrap around */ + /* remaining part from beginning of buffer */ + memcpy(data + first_len, buffer, len - first_len); + } + buf_read_pos = (buf_read_pos + len) % buffer_len; + + return len; +} + +OSStatus render_proc(void *inRefCon, + AudioUnitRenderActionFlags *inActionFlags, + const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, + UInt32 inNumFrames, AudioBufferList *ioData) +{ + int amt = buf_used(); + int req = inNumFrames * packet_size; + + if (amt > req) + amt = req; + + read_buffer((unsigned char *)ioData->mBuffers[0].mData, amt); + ioData->mBuffers[0].mDataByteSize = amt; + + return noErr; +} + +/* + * end of CoreAudio helpers + */ + + +static int init(struct context_data *ctx) +{ + struct xmp_options *o = &ctx->o; + AudioStreamBasicDescription ad; + Component comp; + ComponentDescription cd; + AURenderCallbackStruct rc; + //char *token, **parm; + OSStatus err; + UInt32 size, max_frames; + + //parm_init(); + //parm_end(); + + ad.mSampleRate = o->freq; + ad.mFormatID = kAudioFormatLinearPCM; + ad.mFormatFlags = kAudioFormatFlagIsPacked | + kAudioFormatFlagIsSignedInteger; + if (o->big_endian) + ad.mFormatFlags |= kAudioFormatFlagIsBigEndian; + else + ad.mFormatFlags &= ~kAudioFormatFlagIsBigEndian; + + ad.mChannelsPerFrame = o->outfmt & XMP_FORMAT_MONO ? 1 : 2; + ad.mBitsPerChannel = o->resol; + + ad.mBytesPerFrame = o->resol / 8 * ad.mChannelsPerFrame; + ad.mBytesPerPacket = ad.mBytesPerFrame; + ad.mFramesPerPacket = 1; + + packet_size = ad.mFramesPerPacket * ad.mChannelsPerFrame * + (ad.mBitsPerChannel / 8); + + cd.componentType = kAudioUnitType_Output; + cd.componentSubType = kAudioUnitSubType_DefaultOutput; + cd.componentManufacturer = kAudioUnitManufacturer_Apple; + cd.componentFlags = 0; + cd.componentFlagsMask = 0; + + if ((comp = FindNextComponent(NULL, &cd)) == NULL) { + fprintf(stderr, "error: FindNextComponent\n"); + return XMP_ERR_DINIT; + } + + if ((err = OpenAComponent(comp, &au))) { + fprintf(stderr, "error: OpenAComponent (%d)\n", (int)err); + return XMP_ERR_DINIT; + } + + if ((err = AudioUnitInitialize(au))) { + fprintf(stderr, "error: AudioUnitInitialize (%d)\n", (int)err); + return XMP_ERR_DINIT; + } + + if ((err = AudioUnitSetProperty(au, kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, 0, &ad, sizeof(ad)))) { + fprintf(stderr, "error: AudioUnitSetProperty: StreamFormat (%d)\n", (int)err); + fprintf(stderr, "mSampleRate = %lf\n", ad.mSampleRate); + fprintf(stderr, "mFormatID = 0x%x\n", (unsigned)ad.mFormatID); + fprintf(stderr, "mFormatFlags = 0x%x\n", (unsigned)ad.mFormatFlags); + fprintf(stderr, "mChannelsPerFrame = %d\n", (int)ad.mChannelsPerFrame); + fprintf(stderr, "mBitsPerChannel = %d\n", (int)ad.mBitsPerChannel); + fprintf(stderr, "mBytesPerFrame = %d\n", (int)ad.mBytesPerFrame); + fprintf(stderr, "mBytesPerPacket = %d\n", (int)ad.mBytesPerPacket); + fprintf(stderr, "mFramesPerPacket = %d\n", (int)ad.mFramesPerPacket); + + return XMP_ERR_DINIT; + } + + size = sizeof(UInt32); + if ((err = AudioUnitGetProperty(au, kAudioDevicePropertyBufferSize, + kAudioUnitScope_Input, 0, &max_frames, &size))) { + fprintf(stderr, "error: AudioUnitGetProperty: BufferSize (%d)\n", (int)err); + return XMP_ERR_DINIT; + } + + chunk_size = max_frames; + num_chunks = (o->freq * ad.mBytesPerFrame + chunk_size - 1) / + chunk_size; + buffer_len = (num_chunks + 1) * chunk_size; + buffer = calloc(num_chunks + 1, chunk_size); + + rc.inputProc = render_proc; + rc.inputProcRefCon = 0; + + buf_read_pos = 0; + buf_write_pos = 0; + paused = 1; + + if ((err = AudioUnitSetProperty(au, kAudioUnitProperty_SetRenderCallback, + kAudioUnitScope_Input, 0, &rc, sizeof(rc)))) { + fprintf(stderr, "error: AudioUnitSetProperty: SetRenderCallback (%d)\n", (int)err); + return XMP_ERR_DINIT; + } + + return 0; +} + + +/* Build and write one tick (one PAL frame or 1/50 s in standard vblank + * timed mods) of audio data to the output device. + */ +static void bufdump(struct context_data *ctx, void *b, int i) +{ + int j = 0; + + /* block until we have enough free space in the buffer */ + while (buf_free() < i) + usleep(100000); + + while (i) { + if ((j = write_buffer(b, i)) > 0) { + i -= j; + b += j; + } else + break; + } + + if (paused) { + AudioOutputUnitStart(au); + paused = 0; + } +} + + +static void shutdown(struct context_data *ctx) +{ + AudioOutputUnitStop(au); + AudioUnitUninitialize(au); + CloseComponent(au); + free(buffer); +} diff --git a/drivers/pulseaudio.c b/drivers/pulseaudio.c new file mode 100644 index 0000000..33f1ef5 --- /dev/null +++ b/drivers/pulseaudio.c @@ -0,0 +1,101 @@ +/* Extended Module Player + * Copyright (C) 1996-2012 Claudio Matsuoka and Hipolito Carraro Jr + * + * This file is part of the Extended Module Player and is distributed + * under the terms of the GNU General Public License. See doc/COPYING + * for more information. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "common.h" +#include "driver.h" +#include "mixer.h" + +static pa_simple *s; + +static int init(struct context_data *); +static void bufdump(struct context_data *, void *, int); +static void myshutdown(struct context_data *); +static void flush(); + +static void dummy() +{ +} + +struct xmp_drv_info drv_pulseaudio = { + "pulseaudio", /* driver ID */ + "PulseAudio", /* driver description */ + NULL, /* help */ + init, /* init */ + myshutdown, /* shutdown */ + dummy, /* starttimer */ + flush, /* flush */ + bufdump, /* bufdump */ +}; + +static int init(struct context_data *ctx) +{ + struct xmp_options *o = &ctx->o; + pa_sample_spec ss; + int error; + + ss.format = PA_SAMPLE_S16NE; + ss.channels = o->outfmt & XMP_FORMAT_MONO ? 1 : 2; + ss.rate = o->freq; + + s = pa_simple_new(NULL, /* Use the default server */ + "xmp", /* Our application's name */ + PA_STREAM_PLAYBACK, + NULL, /* Use the default device */ + "Music", /* Description of our stream */ + &ss, /* Our sample format */ + NULL, /* Use default channel map */ + NULL, /* Use default buffering attributes */ + &error); /* Ignore error code */ + + if (s == 0) { + fprintf(stderr, "pulseaudio error: %s\n", pa_strerror(error)); + return XMP_ERR_DINIT; + } + + return 0; +} + +static void flush() +{ + int error; + + if (pa_simple_drain(s, &error) < 0) { + fprintf(stderr, "pulseaudio error: %s\n", pa_strerror(error)); + } +} + +static void bufdump(struct context_data *ctx, void *b, int i) +{ + int j, error; + + do { + if ((j = pa_simple_write(s, b, i, &error)) > 0) { + i -= j; + b += j; + } else + break; + } while (i); + + if (j < 0) { + fprintf(stderr, "pulseaudio error: %s\n", pa_strerror(error)); + } +} + +static void myshutdown(struct context_data *ctx) +{ + if (s) + pa_simple_free(s); +} diff --git a/drivers/qnx.c b/drivers/qnx.c new file mode 100644 index 0000000..8224472 --- /dev/null +++ b/drivers/qnx.c @@ -0,0 +1,130 @@ +/* Extended Module Player + * Copyright (C) 1996-2012 Claudio Matsuoka and Hipolito Carraro Jr + * + * This file is part of the Extended Module Player and is distributed + * under the terms of the GNU General Public License. See doc/COPYING + * for more information. + */ + +/* + * Based on the QNX4 port of nspmod by Mike Gorchak + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include "common.h" +#include "driver.h" +#include "mixer.h" + +static int fd_audio; + +static int init(struct context_data *); +static void bufdump(struct context_data *, void *, int); +static void myshutdown(struct context_data *); +static void mysync(); + +static void dummy() +{ +} + +static char *help[] = { + "dev=", "Audio device name (default is /dev/dsp)", + "buffer=val", "Audio buffer size (default is 32768)", + NULL +}; + +struct xmp_drv_info drv_qnx = { + "QNX", /* driver ID */ + "QNX PCM audio", /* driver description */ + NULL, /* help */ + init, /* init */ + myshutdown, /* shutdown */ + dummy, /* starttimer */ + dummy, /* flush */ + bufdump, /* bufdump */ +}; + +static int init(struct context_data *ctx) +{ + struct xmp_options *o = &ctx->o; + int rc, rate, bits, stereo, bsize; + char *dev; + char *token, **parm; + + parm_init(); + chkparm1("dev", dev = token); + chkparm1("buffer", bsize = strtoul(token, NULL, 0)); + parm_end(); + + rate = o->freq; + bits = o->resol; + stereo = 1; + bufsize = 32 * 1024; + + fd_audio = open(dev, O_WRONLY); + if (fd_audio < 0) { + fprintf(stderr, "can't open audio device\n"); + return XMP_ERR_DINIT; + } + + if (o->outfmt & XMP_FORMAT_MONO) + stereo = 0; + + if (ioctl(fd_audio, SOUND_PCM_WRITE_BITS, &bits) < 0) { + perror("can't set resolution"); + goto error; + } + + if (ioctl(fd, SNDCTL_DSP_STEREO, &stereo) < 0) { + perror("can't set channels"); + goto error; + } + + if (ioctl(fd, SNDCTL_DSP_SPEED, &rate) < 0) { + perror("can't set rate"); + goto error; + } + + if (ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &buf.size) < 0) { + perror("can't set rate"); + goto error; + } + + if (ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &bufsize) < 0) { + perror("can't set buffer"); + goto error; + } + + return 0; + + error: + close(fd_audio); + return XMP_ERR_DINIT; +} + +static void bufdump(struct context_data *ctx, void *b, int i) +{ + int j; + + do { + if ((j = write(fd_audio, b, i)) > 0) { + i -= j; + b += j; + } else + break; + } while (i); +} + +static void myshutdown(struct context_data *ctx) +{ + close(fd_audio); +} + +static void mysync() +{ + ioctl(fd, SNDCTL_DSP_SYNC, NULL); +} diff --git a/drivers/sgi.c b/drivers/sgi.c new file mode 100644 index 0000000..5c20026 --- /dev/null +++ b/drivers/sgi.c @@ -0,0 +1,221 @@ +/* Extended Module Player + * Copyright (C) 1996-2012 Claudio Matsuoka and Hipolito Carraro Jr + * + * This file is part of the Extended Module Player and is distributed + * under the terms of the GNU General Public License. See doc/COPYING + * for more information. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "driver.h" +#include "mixer.h" + +static ALport audio_port; + +/* Hack to get 16 bit sound working - 19990706 bdowning */ +static int al_sample_16; + +static int init(struct context_data *); +static int setaudio(struct xmp_options *); +static void bufdump(struct context_data *, void *, int); +static void shutdown(struct context_data *); + +static void dummy() +{ +} + +/* + * audio port sample rates (these are the only ones supported by the library) + */ + +static int srate[] = { + 48000, + 44100, + 32000, + 22050, + 16000, + 11025, + 8000, + 0 +}; + +static char *help[] = { + "buffer=val", "Audio buffer size", + NULL +}; + +struct xmp_drv_info drv_sgi = { + "sgi", /* driver ID */ + "SGI PCM audio", /* driver description */ + help, /* help */ + init, /* init */ + shutdown, /* shutdown */ + dummy, /* starttimer */ + dummy, /* flush */ + bufdump, /* bufdump */ +}; + +static int setaudio(struct xmp_options *o) +{ + int bsize = 32 * 1024; + ALconfig config; + long pvbuffer[2]; + char *token, **parm; + int i; + + parm_init(); + chkparm1("buffer", bsize = strtoul(token, NULL, 0)); + parm_end(); + + if ((config = ALnewconfig()) == 0) + return XMP_ERR_DINIT; + + /* + * Set sampling rate + */ + + pvbuffer[0] = AL_OUTPUT_RATE; + +#if 0 /* DOESN'T WORK */ + for (i = 0; srate[i]; i++) { + if (srate[i] <= o->freq) + pvbuffer[1] = o->freq = srate[i]; + } +#endif /* DOESN'T WORK */ + + /* + * This was flawed as far as I can tell - it just progressively lowered + * the sample rate to the lowest possible! + * + * o->freq = 44100 + * + * i = 0 / if (48000 <= 44100) + * i = 1 / if (44100 <= 44100) + * then pvbuffer[1] = o->freq = 44100 + * i = 2 / if (32000 <= 44100) + * then pvbuffer[1] = o->freq = 32000 + * i = 3 / if (22050 <= 32000) + * then pvbuffer[1] = o->freq = 22050 + * etc... + * + * Below is my attempt to write a new one. It picks the next highest + * rate available up to the maximum. This seems a lot more reasonable. + * + * - 19990706 bdowning + */ + + for (i = 0; srate[i]; i++) ; /* find the end of the array */ + + while (i-- > 0) { + if (srate[i] >= o->freq) { + pvbuffer[1] = o->freq = srate[i]; + break; + } + } + + if (i == 0) + pvbuffer[1] = o->freq = srate[0]; /* 48 kHz. Wow! */ + + if (ALsetparams(AL_DEFAULT_DEVICE, pvbuffer, 2) < 0) + return XMP_ERR_DINIT; + + /* + * Set sample format to signed integer + */ + + if (ALsetsampfmt(config, AL_SAMPFMT_TWOSCOMP) < 0) + return XMP_ERR_DINIT; + + /* + * Set sample width; 24 bit samples are not currently supported by xmp + */ + + if (o->resol > 8) { + if (ALsetwidth(config, AL_SAMPLE_16) < 0) { + if (ALsetwidth(config, AL_SAMPLE_8) < 0) + return XMP_ERR_DINIT; + o->resol = 8; + } else + al_sample_16 = 1; + } else { + if (ALsetwidth(config, AL_SAMPLE_8) < 0) { + if (ALsetwidth(config, AL_SAMPLE_16) < 0) + return XMP_ERR_DINIT; + o->resol = 16; + } else + al_sample_16 = 0; + } + + /* + * Set number of channels; 4 channel output is not currently supported + */ + + if (o->outfmt & XMP_FORMAT_MONO) { + if (ALsetchannels(config, AL_MONO) < 0) { + if (ALsetchannels(config, AL_STEREO) < 0) + return XMP_ERR_DINIT; + o->outfmt &= ~XMP_FORMAT_MONO; + } + } else { + if (ALsetchannels(config, AL_STEREO) < 0) { + if (ALsetchannels(config, AL_MONO) < 0) + return XMP_ERR_DINIT; + o->outfmt |= XMP_FORMAT_MONO; + } + } + + /* + * Set buffer size + */ + + if (ALsetqueuesize(config, bsize) < 0) + return XMP_ERR_DINIT; + + /* + * Open the audio port + */ + + if ((audio_port = ALopenport("xmp", "w", config)) == 0) + return XMP_ERR_DINIT; + + return 0; +} + +static int init(struct context_data *ctx) +{ + if (setaudio(&ctx->o) != 0) + return XMP_ERR_DINIT; + + return 0; +} + +/* Build and write one tick (one PAL frame or 1/50 s in standard vblank + * timed mods) of audio data to the output device. + * + * Apparently ALwritesamps requires the number of samples instead of + * the number of bytes, which is what I assume i is. This was a + * trial-and-error fix, but it appears to work. - 19990706 bdowning + */ +static void bufdump(struct context_data *ctx, void *b, int i) +{ + if (al_sample_16) + i /= 2; + + ALwritesamps(audio_port, b, i); +} + +static void shutdown(struct context_data *ctx) +{ + ALcloseport(audio_port); +} diff --git a/drivers/smix.c b/drivers/smix.c new file mode 100644 index 0000000..f371970 --- /dev/null +++ b/drivers/smix.c @@ -0,0 +1,42 @@ +/* Extended Module Player + * Copyright (C) 1996-2012 Claudio Matsuoka and Hipolito Carraro Jr + * + * This file is part of the Extended Module Player and is distributed + * under the terms of the GNU General Public License. See doc/COPYING + * for more information. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "common.h" +#include "driver.h" +#include "mixer.h" + +static int init(struct context_data *); +static void shutdown(struct context_data *); + +static void dummy() +{ +} + +struct xmp_drv_info drv_smix = { + "smix", /* driver ID */ + "nil softmixer", /* driver description */ + NULL, /* help */ + init, /* init */ + shutdown, /* shutdown */ + dummy, /* starttimer */ + dummy, /* stoptimer */ + dummy, /* bufdump */ + NULL +}; + +static int init(struct context_data *ctx) +{ +} + +static void shutdown(struct context_data *ctx) +{ +} diff --git a/drivers/sndio.c b/drivers/sndio.c new file mode 100644 index 0000000..5121995 --- /dev/null +++ b/drivers/sndio.c @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2009 Thomas Pfaff + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include + +#include "driver.h" +#include "mixer.h" +#include "common.h" + +static struct sio_hdl *hdl; + +static int init (struct context_data *); +static void bufdump (struct context_data *, void *, int); +static void shutdown (struct context_data *); +static void dummy (void); + +struct xmp_drv_info drv_sndio = { + "sndio", /* driver ID */ + "OpenBSD sndio", /* driver description */ + NULL, /* help */ + init, /* init */ + shutdown, /* shutdown */ + dummy, /* starttimer */ + dummy, /* stoptimer */ + bufdump, /* bufdump */ +}; + +static void +dummy (void) +{ +} + +static int +init (struct context_data *ctx) +{ + struct sio_par par, askpar; + struct xmp_options *opt = &ctx->o; + + hdl = sio_open (NULL, SIO_PLAY, 0); + if (hdl == NULL) { + fprintf (stderr, "%s: failed to open audio device\n", + __func__); + return XMP_ERR_DINIT; + } + + sio_initpar (&par); + par.pchan = opt->outfmt & XMP_FORMAT_MONO ? 1 : 2; + par.rate = opt->freq; + par.bits = opt->resol; + par.sig = opt->resol > 8 ? 1 : 0; + par.le = SIO_LE_NATIVE; + par.appbufsz = par.rate / 4; + + askpar = par; + if (!sio_setpar (hdl, &par) || !sio_getpar (hdl, &par)) { + fprintf (stderr, "%s: failed to set parameters\n", __func__); + goto error; + } + + if ((par.bits == 16 && par.le != askpar.le) || + par.bits != askpar.bits || + par.sig != askpar.sig || + par.pchan != askpar.pchan || + ((par.rate * 1000 < askpar.rate * 995) || + (par.rate * 1000 > askpar.rate * 1005))) { + fprintf (stderr, "%s: parameters not supported\n", __func__); + goto error; + } + + if (!sio_start (hdl)) { + fprintf (stderr, "%s: failed to start audio device\n", + __func__); + goto error; + } + return 0; + +error: + sio_close (hdl); + return XMP_ERR_DINIT; +} + +static void +bufdump (struct context_data *ctx, void *b, int len) +{ + if (b != NULL) + sio_write (hdl, buf, len); +} + +static void +shutdown (struct context_data *ctx) +{ + sio_close (hdl); + hdl = NULL; +} diff --git a/drivers/solaris.c b/drivers/solaris.c new file mode 100644 index 0000000..2dcef30 --- /dev/null +++ b/drivers/solaris.c @@ -0,0 +1,206 @@ +/* Extended Module Player + * Copyright (C) 1996-2012 Claudio Matsuoka and Hipolito Carraro Jr + * + * This file is part of the Extended Module Player and is distributed + * under the terms of the GNU General Public License. See doc/COPYING + * for more information. + */ + +/* CS4231 code tested on Sparc 20 and Ultra 1 running Solaris 2.5.1 + * with mono/stereo, 16 bit, 22.05 kHz and 44.1 kHz using the internal + * speaker and headphones. + * + * AMD 7930 code tested on Axil 240 running Solaris 2.5.1 and an Axil + * 220 running Linux 2.0.33. + */ + +/* Fixed and improved by Keith Hargrove + * Wed, 30 Jun 1999 14:23:52 -0700 (PDT) + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#if defined(HAVE_SYS_AUDIOIO_H) +#include +#elif defined(HAVE_SYS_AUDIO_IO_H) +#include +#elif defined(HAVE_SUN_AUDIOIO_H) +#include +#endif +#include + +/* This is for Linux on Sparc */ +#if defined(HAVE_SBUS_AUDIO_AUDIO_H) +#include +#endif + +#include "common.h" +#include "driver.h" +#include "mixer.h" + +static int audio_fd; +static int audioctl_fd; + +static int init(struct context_data *); +static int setaudio(struct xmp_options *); +static void bufdump(struct context_data *, void *, int); +static void shutdown(struct context_data *); + +static void dummy() +{ +} + +static char *help[] = { + "gain=val", "Audio output gain (0 to 255)", + "port={s|h|l}", "Audio port (s[peaker], h[eadphones], l[ineout])", + "buffer=val", "Audio buffer size (default is 32768)", + NULL +}; + +struct xmp_drv_info drv_solaris = { + "solaris", /* driver ID */ + "Solaris PCM audio", /* driver description */ + help, /* help */ + init, /* init */ + shutdown, /* shutdown */ + dummy, /* starttimer */ + dummy, /* flush */ + bufdump, /* bufdump */ +}; + +static int setaudio(struct xmp_options *o) +{ + audio_info_t ainfo, ainfo2; + int gain; + int bsize = 32 * 1024; + int port; + char *token, **parm; + AUDIO_INITINFO(&ainfo); + + /* try to open audioctl device */ + if ((audioctl_fd = open("/dev/audioctl", O_RDWR)) < 0) { + fprintf(stderr, "couldn't open audioctl device\n"); + close(audio_fd); + return -1; + } + + /* sleep(2); -- Really needed? */ + + /* empty buffers before change config */ + ioctl(audio_fd, AUDIO_DRAIN, 0); /* drain everything out */ + ioctl(audio_fd, I_FLUSH, FLUSHRW); /* flush everything */ + ioctl(audioctl_fd, I_FLUSH, FLUSHRW); /* flush everything */ + + /* get audio parameters. */ + if (ioctl(audioctl_fd, AUDIO_GETINFO, &ainfo) < 0) { + fprintf(stderr, "AUDIO_GETINFO failed!\n"); + close(audio_fd); + close(audioctl_fd); + return -1; + } + + close(audioctl_fd); + + /* KH: Sun Dbri doesn't support 8bits linear. I dont muck with the gain + * or the port setting. I hate when a program does that. There is + * nothing more frustrating then having a program change your volume + * and change from external speakers to the tiny one + */ + + gain = ainfo.play.gain; + port = ainfo.play.port; + + parm_init(); + chkparm1("gain", gain = strtoul(token, NULL, 0)); + chkparm1("buffer", bsize = strtoul(token, NULL, 0)); + chkparm1("port", port = (int)*token) + parm_end(); + + switch (port) { + case 'h': + port = AUDIO_HEADPHONE; + break; + case 'l': + port = AUDIO_LINE_OUT; + break; + case 's': + port = AUDIO_SPEAKER; + } + + if (gain < AUDIO_MIN_GAIN) + gain = AUDIO_MIN_GAIN; + if (gain > AUDIO_MAX_GAIN) + gain = AUDIO_MAX_GAIN; + + AUDIO_INITINFO(&ainfo); /* For CS4231 */ + AUDIO_INITINFO(&ainfo2); /* For AMD 7930 if needed */ + + ainfo.play.sample_rate = o->freq; + ainfo.play.channels = o->outfmt & XMP_FORMAT_MONO ? 1 : 2; + ainfo.play.precision = o->resol; + ainfo.play.encoding = AUDIO_ENCODING_LINEAR; + ainfo2.play.gain = ainfo.play.gain = gain; + ainfo2.play.port = ainfo.play.port = port; + ainfo2.play.buffer_size = ainfo.play.buffer_size = bsize; + + if (ioctl(audio_fd, AUDIO_SETINFO, &ainfo) == -1) { + /* CS4231 initialization Failed, perhaps we have an AMD 7930 */ + if (ioctl(audio_fd, AUDIO_SETINFO, &ainfo2) == -1) { + close(audio_fd); + return XMP_ERR_DINIT; + } + + o->resol = 0; + o->freq = 8000; + o->outfmt |= XMP_FORMAT_MONO; + drv_solaris.description = "Solaris AMD7930 PCM audio"; + } else { + drv_solaris.description = "Solaris CS4231 PCM audio"; + } + + return 0; +} + +static int init(struct context_data *ctx) +{ + struct xmp_options *o = &ctx->o; + + if ((audio_fd = open("/dev/audio", O_WRONLY)) == -1) + return XMP_ERR_DINIT; + + if (setaudio(o) != 0) + return XMP_ERR_DINIT; + + return 0; +} + +/* Build and write one tick (one PAL frame or 1/50 s in standard vblank + * timed mods) of audio data to the output device. + */ +static void bufdump(struct context_data *ctx, void *b, int i) +{ + int j; + + while (i) { + if ((j = write(audio_fd, b, i)) > 0) { + i -= j; + b = (char *)b + j; + } else + break; + } +} + +static void shutdown(struct context_data *ctx) +{ + close(audio_fd); +} diff --git a/drivers/wav.c b/drivers/wav.c new file mode 100644 index 0000000..15430d1 --- /dev/null +++ b/drivers/wav.c @@ -0,0 +1,183 @@ +/* Extended Module Player + * Copyright (C) 1996-2012 Claudio Matsuoka and Hipolito Carraro Jr + * + * This file is part of the Extended Module Player and is distributed + * under the terms of the GNU General Public License. See doc/COPYING + * for more information. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include "common.h" +#include "driver.h" +#include "convert.h" + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#define DATA(x) (((struct data *)drv_wav.data)->x) + +struct data { + int fd; + uint32 size; +}; + +static int init(struct context_data *); +static void bufdump(struct context_data *, void *, int); +static void shutdown(struct context_data *); + +static void dummy() +{ +} + +struct xmp_drv_info drv_wav = { + "wav", /* driver ID */ + "WAV writer", /* driver description */ + NULL, /* help */ + init, /* init */ + shutdown, /* shutdown */ + dummy, /* starttimer */ + dummy, /* stoptimer */ + bufdump, /* bufdump */ +}; + +static void writeval_16l(int fd, uint16 v) +{ + uint8 x; + + x = v & 0xff; + write(DATA(fd), &x, 1); + + x = v >> 8; + write(DATA(fd), &x, 1); +} + +static void writeval_32l(int fd, uint32 v) +{ + uint8 x; + + x = v & 0xff; + write(DATA(fd), &x, 1); + + x = (v >> 8) & 0xff; + write(DATA(fd), &x, 1); + + x = (v >> 16) & 0xff; + write(DATA(fd), &x, 1); + + x = (v >> 24) & 0xff; + write(DATA(fd), &x, 1); +} + +static int init(struct context_data *ctx) +{ + char *buf; + uint32 len = 0; + uint16 chan; + uint32 sampling_rate, bytes_per_second; + uint16 bytes_per_frame, bits_per_sample; + char *f, filename[PATH_MAX]; + struct xmp_options *o = &ctx->o; + + drv_wav.data = malloc(sizeof (struct data)); + if (drv_wav.data == NULL) + return -1; + + if (!o->outfile) { + if (global_filename) { + if ((f = strrchr(global_filename, '/')) != NULL) + strncpy(filename, f + 1, PATH_MAX); + else + strncpy(filename, global_filename, PATH_MAX); + } else { + strcpy(filename, "xmp"); + } + + strncat(filename, ".wav", PATH_MAX); + + o->outfile = filename; + } + + if (strcmp(o->outfile, "-")) { + DATA(fd) = open(o->outfile, O_WRONLY | O_CREAT | O_TRUNC + | O_BINARY, 0644); + if (DATA(fd) < 0) + return -1; + } else { + DATA(fd) = 1; + } + + if (strcmp(o->outfile, "-")) { + int len = strlen(drv_wav.description) + strlen(o->outfile) + 8; + if ((buf = malloc(len)) == NULL) + return -1; + snprintf(buf, len, "%s: %s", drv_wav.description, o->outfile); + drv_wav.description = buf; + } else { + drv_wav.description = strdup("WAV writer: stdout"); + len = -1; + } + + write(DATA(fd), "RIFF", 4); + writeval_32l(DATA(fd), len); + write(DATA(fd), "WAVE", 4); + + chan = o->outfmt & XMP_FORMAT_MONO ? 1 : 2; + sampling_rate = o->freq; + bits_per_sample = o->resol; + if (bits_per_sample == 8) + o->outfmt |= XMP_FMT_UNS; + bytes_per_frame = chan * bits_per_sample / 8; + bytes_per_second = sampling_rate * bytes_per_frame; + + write(DATA(fd), "fmt ", 4); + writeval_32l(DATA(fd), 16); + writeval_16l(DATA(fd), 1); + writeval_16l(DATA(fd), chan); + writeval_32l(DATA(fd), sampling_rate); + writeval_32l(DATA(fd), bytes_per_second); + writeval_16l(DATA(fd), bytes_per_frame); + writeval_16l(DATA(fd), bits_per_sample); + + write(DATA(fd), "data", 4); + writeval_32l(DATA(fd), len); + + DATA(size) = 0; + + return 0; +} + +static void bufdump(struct context_data *ctx, void *b, int i) +{ + struct xmp_options *o = &ctx->o; + + if (o->big_endian) + xmp_cvt_sex(i, b); + write(DATA(fd), b, i); + DATA(size) += i; +} + +static void shutdown(struct context_data *ctx) +{ + lseek(DATA(fd), 40, SEEK_SET); + writeval_32l(DATA(fd), DATA(size)); + + lseek(DATA(fd), 4, SEEK_SET); + writeval_32l(DATA(fd), DATA(size) + 40); + + if (DATA(fd) > 0) + close(DATA(fd)); + + free(drv_wav.description); + free(drv_wav.data); +} diff --git a/drivers/win32.c b/drivers/win32.c new file mode 100644 index 0000000..2d5bb11 --- /dev/null +++ b/drivers/win32.c @@ -0,0 +1,177 @@ +/* Extended Module Player + * Copyright (C) 1996-2012 Claudio Matsuoka and Hipolito Carraro Jr + * + * This file is part of the Extended Module Player and is distributed + * under the terms of the GNU General Public License. See doc/COPYING + * for more information. + */ + +/* + * Based on Bjornar Henden's driver for Mikmod + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "common.h" +#include "driver.h" +#include "mixer.h" + +#define MAXBUFFERS 32 /* max number of buffers */ +#define BUFFERSIZE 120 /* buffer size in ms */ + +static HWAVEOUT hwaveout; +static WAVEHDR header[MAXBUFFERS]; +static LPSTR buffer[MAXBUFFERS]; /* pointers to buffers */ +static WORD freebuffer; /* */ +static WORD nextbuffer; /* next buffer to be mixed */ +static int num_buffers; + +static int init(struct context_data *); +static void bufdump(struct context_data *, void *, int); +static void deinit(struct context_data *); + +static void dummy() +{ +} + +static char *help[] = { + "buffers=val", "Number of buffers (default 10)", + NULL +}; + +struct xmp_drv_info drv_win32 = { + "win32", /* driver ID */ + "Windows WinMM driver", /* driver description */ + help, /* help */ + init, /* init */ + deinit, /* shutdown */ + dummy, /* starttimer */ + dummy, /* flush */ + bufdump, /* bufdump */ +}; + +static void show_error(int res) +{ + char *msg; + + switch (res) { + case MMSYSERR_ALLOCATED: + msg = "Device is already open"; + break; + case MMSYSERR_BADDEVICEID: + msg = "Device is out of range"; + break; + case MMSYSERR_NODRIVER: + msg = "No audio driver in this system"; + break; + case MMSYSERR_NOMEM: + msg = "Unable to allocate sound memory"; + break; + case WAVERR_BADFORMAT: + msg = "Audio format not supported"; + break; + case WAVERR_SYNC: + msg = "The device is synchronous"; + break; + default: + msg = "Unknown media error"; + } + + fprintf(stderr, "Error: %s", msg); +} + +static void CALLBACK wave_callback(HWAVEOUT hwo, UINT uMsg, DWORD dwInstance, + DWORD dwParam1, DWORD dwParam2) +{ + if (uMsg == WOM_DONE) { + freebuffer++; + freebuffer %= num_buffers; + } +} + +static int init(struct context_data *ctx) +{ + struct xmp_options *o = &ctx->o; + MMRESULT res; + WAVEFORMATEX wfe; + int i; + char *token, **parm; + + num_buffers = 10; + + parm_init(); + chkparm1("buffers", num_buffers = strtoul(token, NULL, 0)); + parm_end(); + + if (num_buffers > MAXBUFFERS) + num_buffers = MAXBUFFERS; + + if (!waveOutGetNumDevs()) + return XMP_ERR_DINIT; + + wfe.wFormatTag = WAVE_FORMAT_PCM; + wfe.wBitsPerSample = o->resol; + wfe.nChannels = o->flags & XMP_FORMAT_MONO ? 1 : 2; + wfe.nSamplesPerSec = o->freq; + wfe.nAvgBytesPerSec = wfe.nSamplesPerSec * wfe.nChannels * + wfe.wBitsPerSample / 8; + wfe.nBlockAlign = wfe.nChannels * wfe.wBitsPerSample / 8; + + res = waveOutOpen(&hwaveout, WAVE_MAPPER, &wfe, (DWORD) wave_callback, + 0, CALLBACK_FUNCTION); + + if (res != MMSYSERR_NOERROR) { + show_error(res); + return XMP_ERR_DINIT; + } + + waveOutReset(hwaveout); + + for (i = 0; i < num_buffers; i++) { + buffer[i] = malloc(OUT_MAXLEN); + header[i].lpData = buffer[i]; + + if (!buffer[i] || res != MMSYSERR_NOERROR) { + show_error(res); + return XMP_ERR_DINIT; + } + } + + freebuffer = nextbuffer = 0; + + return 0; +} + +static void bufdump(struct context_data *ctx, void *b, int len) +{ + memcpy(buffer[nextbuffer], b, len); + + while ((nextbuffer + 1) % num_buffers == freebuffer) + Sleep(10); + + header[nextbuffer].dwBufferLength = len; + waveOutPrepareHeader(hwaveout, &header[nextbuffer], sizeof(WAVEHDR)); + waveOutWrite(hwaveout, &header[nextbuffer], sizeof(WAVEHDR)); + + nextbuffer++; + nextbuffer %= num_buffers; +} + +static void deinit(struct context_data *ctx) +{ + int i; + + if (hwaveout) { + for (i = 0; i < num_buffers; i++) { + if (header[i].dwFlags & WHDR_PREPARED) + waveOutUnprepareHeader(hwaveout, &header[i], + sizeof(WAVEHDR)); + free(buffer[i]); + } + while (waveOutClose(hwaveout) == WAVERR_STILLPLAYING) + Sleep(10); + hwaveout = NULL; + } +} diff --git a/getopt.c b/getopt.c new file mode 100644 index 0000000..ad4e1d7 --- /dev/null +++ b/getopt.c @@ -0,0 +1,1006 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu + before changing it! + + Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97 + Free Software Foundation, Inc. + + This file is part of the GNU C Library. Its master source is NOT part of + the C library, however. The master source lives in /gd/gnu/lib. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* This tells Alpha OSF/1 not to define a getopt prototype in . + Ditto for AIX 3.2 and . */ +#ifndef _NO_PROTO +#define _NO_PROTO +#endif + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef HAVE_GETOPT_H + +#if !defined (__STDC__) || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +#ifndef const +#define const +#endif +#endif + +#include +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2 +#include +#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +#define ELIDE_CODE +#endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +#include +#include +#endif /* GNU C library. */ + +#ifdef VMS +#include +#if HAVE_STRING_H - 0 +#include +#endif +#endif + +#if defined (WIN32) && !defined (__CYGWIN32__) +/* It's not Unix, really. See? Capital letters. */ +#include +#define getpid() GetCurrentProcessId() +#endif + +#ifndef _ +/* This is for other GNU distributions with internationalized messages. + When compiling libc, the _ macro is predefined. */ +#ifdef HAVE_LIBINTL_H +# include +# define _(msgid) gettext (msgid) +#else +# define _(msgid) (msgid) +#endif +#endif + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = NULL; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* 1003.2 says this must be 1 before any call. */ +int optind = 1; + +/* Formerly, initialization of getopt depended on optind==0, which + causes problems with re-calling getopt as programs generally don't + know that. */ + +int __getopt_initialized = 0; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return -1 with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +/* Value of POSIXLY_CORRECT environment variable. */ +static char *posixly_correct; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +#include +#define my_index strchr +#else + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +char *getenv (); + +static char * +my_index (str, chr) + const char *str; + int chr; +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +/* If using GCC, we can safely declare strlen this way. + If not using GCC, it is ok not to declare it. */ +#ifdef __GNUC__ +/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. + That was relevant to code that was here before. */ +#if !defined (__STDC__) || !__STDC__ +/* gcc with -traditional declares the built-in strlen to return int, + and has done so at least since version 2.4.5. -- rms. */ +extern int strlen (const char *); +#endif /* not __STDC__ */ +#endif /* __GNUC__ */ + +#endif /* not __GNU_LIBRARY__ */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +#ifdef _LIBC +/* Bash 2.0 gives us an environment variable containing flags + indicating ARGV elements that should not be considered arguments. */ + +static const char *nonoption_flags; +static int nonoption_flags_len; + +static int original_argc; +static char *const *original_argv; + +/* Make sure the environment variable bash 2.0 puts in the environment + is valid for the getopt call we must make sure that the ARGV passed + to getopt is that one passed to the process. */ +static void store_args (int argc, char *const *argv) __attribute__ ((unused)); +static void +store_args (int argc, char *const *argv) +{ + /* XXX This is no good solution. We should rather copy the args so + that we can compare them later. But we must not use malloc(3). */ + original_argc = argc; + original_argv = argv; +} +text_set_element (__libc_subinit, store_args); +#endif + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +#if defined (__STDC__) && __STDC__ +static void exchange (char **); +#endif + +static void +exchange (argv) + char **argv; +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Initialize the internal data when the first call is made. */ + +#if defined (__STDC__) && __STDC__ +static const char *_getopt_initialize (int, char *const *, const char *); +#endif +static const char * +_getopt_initialize (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + first_nonopt = last_nonopt = optind = 1; + + nextchar = NULL; + + posixly_correct = getenv ("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + +#ifdef _LIBC + if (posixly_correct == NULL + && argc == original_argc && argv == original_argv) + { + /* Bash 2.0 puts a special variable in the environment for each + command it runs, specifying which ARGV elements are the results of + file name wildcard expansion and therefore should not be + considered as options. */ + char var[100]; + sprintf (var, "_%d_GNU_nonoption_argv_flags_", getpid ()); + nonoption_flags = getenv (var); + if (nonoption_flags == NULL) + nonoption_flags_len = 0; + else + nonoption_flags_len = strlen (nonoption_flags); + } + else + nonoption_flags_len = 0; +#endif + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns -1. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct option *longopts; + int *longind; + int long_only; +{ + optarg = NULL; + + if (!__getopt_initialized || optind == 0) + { + optstring = _getopt_initialize (argc, argv, optstring); + optind = 1; /* Don't scan ARGV[0], the program name. */ + __getopt_initialized = 1; + } + + /* Test whether ARGV[optind] points to a non-option argument. + Either it does not have option syntax, or there is an environment flag + from the shell indicating it is not an option. The later information + is only used when the used in the GNU libc. */ +#ifdef _LIBC +#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ + || (optind < nonoption_flags_len \ + && nonoption_flags[optind] == '1')) +#else +#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') +#endif + + if (nextchar == NULL || *nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been + moved back by the user (who may also have changed the arguments). */ + if (last_nonopt > optind) + last_nonopt = optind; + if (first_nonopt > optind) + first_nonopt = optind; + + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc && NONOPTION_P) + optind++; + last_nonopt = optind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return -1; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if (NONOPTION_P) + { + if (ordering == REQUIRE_ORDER) + return -1; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[optind][1] == '-' + || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = -1; + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) + == (unsigned int) strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, _("%s: option `%s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + optopt = 0; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + _("%s: option `--%s' doesn't allow an argument\n"), + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + _("%s: option `%c%s' doesn't allow an argument\n"), + argv[0], argv[optind - 1][0], pfound->name); + } + + nextchar += strlen (nextchar); + + optopt = pfound->val; + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + optopt = pfound->val; + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' + || my_index (optstring, *nextchar) == NULL) + { + if (opterr) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, _("%s: unrecognized option `--%s'\n"), + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + optopt = 0; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (opterr) + { + if (posixly_correct) + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: illegal option -- %c\n"), + argv[0], c); + else + fprintf (stderr, _("%s: invalid option -- %c\n"), + argv[0], c); + } + optopt = c; + return '?'; + } + /* Convenience. Treat POSIX -W foo same as long option --foo */ + if (temp[0] == 'W' && temp[1] == ';') + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = 0; + int option_index; + + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + return c; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + + /* optarg is now the argument, see if it's in the + table of longopts. */ + + for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + if (pfound != NULL) + { + option_index = indfound; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + fprintf (stderr, _("\ +%s: option `-W %s' doesn't allow an argument\n"), + argv[0], pfound->name); + + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + nextchar = NULL; + return 'W'; /* Let the application handle it. */ + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, + _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == -1) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ + +#endif /* HAVE_GETOPT_H */ diff --git a/getopt1.c b/getopt1.c new file mode 100644 index 0000000..d22579f --- /dev/null +++ b/getopt1.c @@ -0,0 +1,193 @@ +/* getopt_long and getopt_long_only entry points for GNU getopt. + Copyright (C) 1987,88,89,90,91,92,93,94,96,97 Free Software Foundation, Inc. + + This file is part of the GNU C Library. Its master source is NOT part of + the C library, however. The master source lives in /gd/gnu/lib. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef HAVE_GETOPT_LONG + +#include "getopt.h" + +#if !defined (__STDC__) || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +#ifndef const +#define const +#endif +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2 +#include +#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +#define ELIDE_CODE +#endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +#include +#endif + +#ifndef NULL +#define NULL 0 +#endif + +int +getopt_long (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 0); +} + +/* Like getopt_long, but '-' as well as '--' can indicate a long option. + If an option that starts with '-' (not '--') doesn't match a long option, + but does match a short option, it is parsed as a short option + instead. */ + +int +getopt_long_only (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 1); +} + + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +#include + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = + { + {"add", 1, 0, 0}, + {"append", 0, 0, 0}, + {"delete", 1, 0, 0}, + {"verbose", 0, 0, 0}, + {"create", 0, 0, 0}, + {"file", 1, 0, 0}, + {0, 0, 0, 0} + }; + + c = getopt_long (argc, argv, "abc:d:0123456789", + long_options, &option_index); + if (c == -1) + break; + + switch (c) + { + case 0: + printf ("option %s", long_options[option_index].name); + if (optarg) + printf (" with arg %s", optarg); + printf ("\n"); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case 'd': + printf ("option d with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ + +#endif /* HAVE_GETOPT_LONG */ diff --git a/info.c b/info.c new file mode 100644 index 0000000..935585f --- /dev/null +++ b/info.c @@ -0,0 +1,63 @@ +#include +#include + +void info_mod(struct xmp_module_info *mi) +{ + int i; + + printf("Module name : %s\n", mi->mod->name); + printf("Module type : %s\n", mi->mod->type); + printf("Module length : %d patterns\n", mi->mod->len); + printf("Stored patterns: %d\n", mi->mod->pat); + printf("Instruments : %d\n", mi->mod->ins); + printf("Samples : %d\n", mi->mod->smp); + printf("Channels : %d [ ", mi->mod->chn); + + for (i = 0; i < mi->mod->chn; i++) { + printf("%x ", mi->mod->xxc[i].pan >> 4); + } + printf("]\n"); + + printf("Estimated time : %dmin%ds\n", (mi->total_time + 500) / 60000, + ((mi->total_time + 500) / 1000) % 60); +} + + +void info_frame(struct xmp_module_info *mi, int reset) +{ + static int ord = -1, tpo = -1, bpm = -1; + static int max_channels = -1; + int time; + + if (reset) { + ord = -1; + max_channels = -1; + } + + if (mi->virt_used > max_channels) + max_channels = mi->virt_used; + + if (mi->frame != 0) + return; + + time = mi->current_time / 100; + + if (mi->order != ord || mi->bpm != bpm || mi->tempo != tpo) { + printf("\rTempo[%02X] BPM[%02X] Pos[%02X/%02X] " + "Pat[%02X/%02X] Row[ / ] Chn[ / ] 0:00:00.0", + mi->tempo, mi->bpm, + mi->order, mi->mod->len - 1, + mi->pattern, mi->mod->pat - 1); + ord = mi->order; + bpm = mi->bpm; + tpo = mi->tempo; + } + + printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" + "%02X/%02X] Chn[%02X/%02X] %3d:%02d:%02d.%d", + mi->row, mi->num_rows - 1, mi->virt_used, max_channels, + (int)(time / (60 * 600)), (int)((time / 600) % 60), + (int)((time / 10) % 60), (int)(time % 10)); + + fflush(stdout); +} diff --git a/main-old.c b/main-old.c new file mode 100644 index 0000000..b528a30 --- /dev/null +++ b/main-old.c @@ -0,0 +1,637 @@ +/* Extended Module Player + * Copyright (C) 1996-2012 Claudio Matsuoka and Hipolito Carraro Jr + * + * This file is part of the Extended Module Player and is distributed + * under the terms of the GNU General Public License. See doc/COPYING + * for more information. + */ + +/* + * Fri, 05 Jun 1998 16:46:13 -0700 "Mark R. Boyns" + * I needed to use xmp as a filter and was able to use /dev/fd/0 as an + * input source but ran into problems with xmp trying to read interactive + * commands. So, I added a few lines of code to disable command reading + * with the --nocmd option. + */ + +/* + * Wed, 10 Jun 1998 15:42:25 -0500 Douglas Carmichael + * Support for real-time priority in FreeBSD. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#ifdef __CYGWIN__ +#include +#endif +#ifdef HAVE_SIGNAL_H +#include +#endif +#include + +#ifdef __AROS__ +#define __AMIGA__ +#endif + +#ifdef __AMIGA__ +#undef HAVE_TERMIOS_H +#define __USE_INLINE__ +#include +#endif + +#ifdef HAVE_TERMIOS_H +#include +#endif + +#ifdef HAVE_SYS_RTPRIO_H +#include +#include +#endif + +#ifdef __EMX__ +#define INCL_BASE +#include +#endif + +#ifdef WIN32 +#include +#include "conio.h" +#endif + +#include "xmp.h" + +extern int optind; + +/* Options not belonging to libxmp */ +int probeonly = 0; +int randomize = 0; +int loadonly = 0; +int nocmd = 0; +int showtime = 0; +#ifdef HAVE_SYS_RTPRIO_H +int rt = 0; +#endif + +static struct xmp_options *opt; + +static int verbosity; +#ifdef HAVE_TERMIOS_H +static struct termios term; +#endif +static int background = 0; +#ifdef SIGTSTP +static int stopped = 0; +#endif +static int refresh_status = 0; +static int max_nch = 0; +static double rows, tot_nch; +int skip = 0; + +#ifdef HAVE_SIGNAL_H +static int sigusr = 0; +#endif + +void get_options (int, char **, struct xmp_options *, xmp_context); +void init_drivers (void); + +static xmp_context ctx; +static int paused; + + +static int set_tty() +{ +#ifdef HAVE_TERMIOS_H + struct termios t; + + if (background) + return -1; + + if (tcgetattr (0, &term) < 0) + return -1; + + t = term; + t.c_lflag &= ~(ECHO | ICANON | TOSTOP); + t.c_cc[VMIN] = t.c_cc[VTIME] = 0; + + if (tcsetattr (0, TCSAFLUSH, &t) < 0) + return -1; +#endif + + return 0; +} + + +static int reset_tty() +{ +#ifdef HAVE_TERMIOS_H + if (background) + return -1; + + if (tcsetattr(0, TCSAFLUSH, &term) < 0) { + fprintf(stderr, "can't reset terminal!\n"); + return -1; + } +#endif + + return 0; +} + + +#ifdef HAVE_SIGNAL_H + +#ifdef SIGTSTP +static void sigtstp_handler(int n) +{ + if (!stopped) { + if (showtime) + fprintf(stderr, "\n"); + else if (verbosity) + fprintf(stderr, "] - STOPPED\n"); + xmp_timer_stop(ctx); + stopped = 1; + } + + signal (SIGTSTP, SIG_DFL); + kill (getpid (), SIGTSTP); +} + + +static void sigcont_handler(int n) +{ +#ifndef __AMIGA__ + background = (tcgetpgrp(0) == getppid()); +#endif + + if (background) + reset_tty(); + else + set_tty(); + + if (stopped) + xmp_timer_restart(ctx); + + stopped = 0; + refresh_status = 1; + + /* Unlike BSD systems, signals under Linux are reset to their + * default behavior when raised. + */ + signal(SIGCONT, sigcont_handler); + signal(SIGTSTP, sigtstp_handler); +} +#endif + +static void sigusr_handler(int n) +{ + signal(sigusr = n, sigusr_handler); +} + + +static void cleanup(int s) +{ + signal(SIGTERM, SIG_DFL); + signal(SIGINT, SIG_DFL); +#ifdef SIGQUIT + signal(SIGQUIT, SIG_DFL); +#endif + signal(SIGFPE, SIG_DFL); + signal(SIGSEGV, SIG_DFL); + + fprintf(stderr, "\n*** Interrupted: signal %d caught\n", s); + xmp_stop_module(ctx); + xmp_close_audio(ctx); + + reset_tty (); + + exit (-2); +} + +#endif /* HAVE_SIGNAL_H */ + +#ifdef __CYGWIN__ +/* + * from daniel åkerud + * date Tue, Jul 28, 2009 at 9:59 AM + * + * Under Cygwin, the read() in process_echoback blocks because VTIME = 0 + * is not handled correctly. To fix this you can either: + * + * 1. Enable "tty emulation" in Cygwin using an environment variable. + * http://www.mail-archive.com/cygwin@cygwin.com/msg99417.html + * For me this is _very_ slow and I can see the characters as they are + * typed out when running xmp. I have not investigated why this is + * happening, but there is of course a reason why this mode is not + * enabled by default. + * + * 2. Do a select() before read()ing if the platform is Cygwin. + * This makes Cygwin builds work out of the box with no fiddling around, + * but does impose a neglectible cpu overhead (for Cygwin builds only). + */ +static int stdin_ready_for_reading() +{ + fd_set fds; + struct timeval tv; + int ret; + + tv.tv_sec = 0; + tv.tv_usec = 0; + + FD_ZERO(&fds); + FD_SET(STDIN_FILENO, &fds); + + ret = select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv); + + if (ret > 0 && FD_ISSET(STDIN_FILENO, &fds)) + return 1; + + return 0; +} +#endif + +void read_keyboard() +{ + unsigned char cmd; + int k; + + /* Interactive commands */ + + if (nocmd || background) + return; + +#if defined HAVE_TERMIOS_H && !defined WIN32 +#ifdef __CYGWIN__ + k = 0; + if (stdin_ready_for_reading()) +#endif + k = read(0, &cmd, 1); +#elif defined WIN32 + k = cmd = kbhit() ? getch() : 0; +#elif defined __AMIGA__ + /* Amiga CLI */ + { + BPTR in = Input(); + k = cmd = 0; + if (WaitForChar(in, 1)) { + char c; + Read(in, &c, 1); + cmd = k = c; + } + } +#else + k = cmd = 0; +#endif + + if (k > 0) { + switch (cmd) { + case 'q': /* quit */ + skip = -2; + xmp_mod_stop(ctx); + paused = 0; + break; + case 'f': /* jump to next order */ + xmp_ord_next(ctx); + paused = 0; + break; + case 'b': /* jump to previous order */ + xmp_ord_prev(ctx); + paused = 0; + break; + case 'n': /* skip to next module */ + skip = 1; + xmp_mod_stop(ctx); + paused = 0; + break; + case 'p': /* skip to previous module */ + skip = -1; + xmp_mod_stop(ctx); + paused = 0; + break; + case 'l': + if (xmp_test_flag(ctx, XMP_CTL_LOOP)) + xmp_reset_flag(ctx, XMP_CTL_LOOP); + else + xmp_set_flag(ctx, XMP_CTL_LOOP); + break; + case ' ': /* paused module */ + paused ^= 1; + + if (verbosity) { + fprintf (stderr, "%s", paused ? + "] - PAUSED\b\b\b\b\b\b\b\b\b\b" : + "] \b\b\b\b\b\b\b\b\b\b"); + } + + if (paused) + xmp_timer_stop(ctx); + else + xmp_timer_restart(ctx); + + break; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + xmp_channel_mute(ctx, cmd - '1', 1, -1); + break; + case '0': + xmp_channel_mute(ctx, 9, 1, -1); + break; + case '!': + xmp_channel_mute(ctx, 0, 8, 0); + break; + } + } +} + + +static void shuffle (int argc, char **argv) +{ + int i, j; + char *x; + + for (i = 1; i < argc; i++) { + j = 1 + rand () % (argc - 1); + x = argv[i]; + argv[i] = argv[j]; + argv[j] = x; + } +} + + +int main(int argc, char **argv) +{ + int i, t, lf_flag, first, num_mod; + time_t t0, t1, t2, t3; +#ifndef WIN32 + struct timeval tv; + struct timezone tz; +#endif +#ifdef HAVE_SYS_RTPRIO_H + struct rtprio rtp; +#endif + int getprevious = 0, skipprev = 0; +#ifdef __EMX__ + long rc; +#endif + struct xmp_module_info mi; + + init_drivers(); + + ctx = xmp_create_context(); + + xmp_init(ctx, argc, argv); + opt = xmp_get_options(ctx); + + //opt->verbosity = 1; + get_options(argc, argv, opt, ctx); + //verbosity = opt->verbosity; + + if (!(probeonly || argv[optind])) { + fprintf (stderr, "%s: no modules to play\n" + "Use `%s --help' for more information.\n", argv[0], argv[0]); + exit (-1); + } + +#ifndef WIN32 + gettimeofday(&tv, &tz); + srand(tv.tv_usec); +#else + srand(GetTickCount()); +#endif + + if (randomize) + shuffle(argc - optind + 1, &argv[optind - 1]); + + if (opt->outfile && (!opt->drv_id || strcmp(opt->drv_id, "wav"))) + opt->drv_id = "file"; + + global_filename = argv[optind]; + +#ifdef HAVE_TERMIOS_H + if ((background = (tcgetpgrp (0) == getppid ()))) { + //verb = opt->verbosity; + //opt->verbosity = 0; + i = xmp_open_audio(ctx); + //xmp_verbosity_level(ctx, opt->verbosity = verb); + } else +#endif + { + i = xmp_open_audio(ctx); + } + + if (i < 0) { + fprintf (stderr, "%s: ", argv[0]); + switch (i) { + case XMP_ERR_DINIT: + fprintf (stderr, "can't initialize driver\n"); + return -1; + case XMP_ERR_NODRV: + fprintf (stderr, "driver does not exist\n"); + return -2; + case XMP_ERR_DSPEC: + fprintf (stderr, "driver not specified\n"); + return -3; + default: + fprintf (stderr, "unknown error\n"); + return -128; + } + } + + fprintf(stderr, "Extended Module Player " VERSION "\n" + "Copyright (C) 1996-2012 Claudio Matsuoka and Hipolito Carraro Jr\n"); + + if (1) { + int srate, res, chn, itpt; + + xmp_get_driver_cfg(ctx, &srate, &res, &chn, &itpt); + fprintf(stderr, "Using %s\n", (char*)xmp_get_driver_description(ctx)); + if (srate) { + fprintf(stderr, "Mixer set to %dbit, %d Hz, %s%s\n", res, srate, + itpt ? "interpolated " : "", chn > 1 ? "stereo" : "mono"); + } + } + + if (probeonly) + exit(0); + + /* + * Support for real-time priority by Douglas Carmichael + */ +#ifdef HAVE_SYS_RTPRIO_H + if (rt) { + rtp.type = RTP_PRIO_REALTIME; + rtp.prio = 0; + if (rtprio (RTP_SET, 0, &rtp) == -1) { + fprintf (stderr, "%s: Can't get realtime priority.\n", argv[0]); + exit(1); + } + } +#endif + +#ifdef __EMX__ + rc = DosSetPriority (PRTYS_THREAD, PRTYC_TIMECRITICAL, PRTYD_MINIMUM, 0); + if (rc != 0) { + printf ("Error setting Priority. Priority defaulting to the System Default.\n" ); + } +#endif + +#ifdef HAVE_SIGNAL_H + signal(SIGTERM, cleanup); + signal(SIGINT, cleanup); + signal(SIGFPE, cleanup); + signal(SIGSEGV, cleanup); +#ifdef SIGQUIT + signal(SIGQUIT, cleanup); +#endif +#ifdef SIGTSTP + signal(SIGTSTP, sigtstp_handler); +#endif +#ifdef SIGCONT + signal(SIGCONT, sigcont_handler); +#endif +#ifdef SIGUSR1 + signal(SIGUSR1, sigusr_handler); + signal(SIGUSR2, sigusr_handler); +#endif +#endif + + set_tty (); + + paused = 0; + time (&t0); + + num_mod = lf_flag = 0; + for (first = optind; optind < argc; optind++) { + if (getprevious) { + optind -= 2; + if (optind < first) { + optind += 2; + } + } + + if (!background) { + if (lf_flag) + fprintf (stderr, "\n"); + lf_flag = fprintf (stderr, "Loading %s... (%d of %d)\n", + argv[optind], optind - first + 1, argc - first); + } + + if (background) { + //verb = xmp_verbosity_level(ctx, 0); + t = xmp_load_module(ctx, argv[optind]); + //xmp_verbosity_level(ctx, verb); + } else { + t = xmp_load_module(ctx, argv[optind]); + } + + if (t < 0) { + switch (t) { + case -1: + fprintf (stderr, "%s: %s: unrecognized file format\n", + argv[0], argv[optind]); + getprevious = skipprev; + continue; + case -2: + fprintf (stderr, "%s: %s: possibly corrupted file\n", + argv[0], argv[optind]); + getprevious = skipprev; + continue; + case -3: { + char line[1024]; + snprintf(line, 1024, "%s: %s", *argv, argv[optind]); + perror(line); + getprevious = skipprev; + continue; } + } + } + + rows = tot_nch = max_nch = getprevious = skipprev = 0; + num_mod++; + + //xmp_get_module_info(ctx, &mi); + + if (loadonly) + goto skip_play; + + /* Play the module */ + time(&t2); + xmp_player_start(ctx); + for (;;) { + read_keyboard(); + + if (paused) { + usleep(100000); + } else { + if (xmp_player_frame(ctx) != 0) + break; + xmp_play_buffer(ctx); + xmp_player_get_info(ctx, &mi); + printf("%2d %2d %2d\r", mi.order, mi.pattern, mi.row); + fflush(stdout); + } + } + xmp_player_end(ctx); + time(&t3); + t = difftime(t3, t2); + + xmp_release_module(ctx); + + if (showtime) { + fprintf(stderr, "\r \r"); + } + if (!background) { + fprintf (stderr, +"\rElapsed time : %dmin%02ds %s \n", + t / 60, t % 60, skip ? "(SKIPPED)" : " "); + +#if 0 + fprintf (stderr, "Channels used : %d/%d", max_nch, mi.chn); + if (max_nch) + fprintf (stderr, ", avg %.2f (%.1f%%)\n", + tot_nch / rows, 100.0 * tot_nch / rows / mi.chn); + else + fprintf (stderr, "\n"); +#endif + } + +skip_play: + + if (skip == -1) { + optind -= optind > first ? 2 : 1; + skipprev = 1; + } + + if (skip == -2) + break; + + skip = 0; + } + + time (&t1); + + if (!loadonly && !background && num_mod > 1) { + t = difftime (t1, t0); + fprintf (stderr, "\n\t%d modules played, total time %dh%02dmin%02ds\n", + num_mod, t / 3600, (t % 3600) / 60, t % 60); + } + + xmp_close_audio(ctx); + xmp_deinit(ctx); + xmp_free_context(ctx); + reset_tty(); + + return 0; +} diff --git a/main.c b/main.c new file mode 100644 index 0000000..440c974 --- /dev/null +++ b/main.c @@ -0,0 +1,141 @@ +/* A simple frontend for xmp */ + +#define HAVE_TERMIOS_H + +#include +#include +#include +#include +#include +#include +//#include "sound.h" +#include "common.h" + +extern int optind; + +static void cleanup() +{ + signal(SIGTERM, SIG_DFL); + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGFPE, SIG_DFL); + signal(SIGSEGV, SIG_DFL); + + sound_deinit(); + reset_tty(); + printf("\n"); + exit(EXIT_FAILURE); +} + + +static void shuffle(int argc, char **argv) +{ + int i, j; + char *x; + + for (i = 1; i < argc; i++) { + j = 1 + rand() % (argc - 1); + x = argv[i]; + argv[i] = argv[j]; + argv[j] = x; + } +} + +int main(int argc, char **argv) +{ + xmp_context ctx; + struct xmp_module_info mi; + struct options options; + int i; + int silent = 0; + int first; +#ifndef WIN32 + struct timeval tv; + struct timezone tz; + + gettimeofday(&tv, &tz); + srand(tv.tv_usec); +#else + srand(GetTickCount()); +#endif + + memset(&options, 0, sizeof (struct options)); + get_options(argc, argv, &options); + + if (options.random) { + shuffle(argc - optind + 1, &argv[optind - 1]); + } + + if (!silent && sound_init(44100, 2) < 0) { + fprintf(stderr, "%s: can't initialize sound\n", argv[0]); + exit(EXIT_FAILURE); + } + + signal(SIGTERM, cleanup); + signal(SIGINT, cleanup); + signal(SIGFPE, cleanup); + signal(SIGSEGV, cleanup); + signal(SIGQUIT, cleanup); + + set_tty(); + + ctx = xmp_create_context(); + + for (first = optind; optind < argc; optind++) { + printf("\nLoading %s... (%d of %d)\n", + argv[optind], optind - first + 1, argc - first); + + if (xmp_load_module(ctx, argv[optind]) < 0) { + fprintf(stderr, "%s: error loading %s\n", argv[0], + argv[i]); + continue; + } + + if (xmp_player_start(ctx, options.start, 44100, 0) == 0) { + int new_mod = 1; + + /* Mute channels */ + + for (i = 0; i < XMP_MAX_CHANNELS; i++) { + xmp_channel_mute(ctx, i, options.mute[i]); + } + + /* Show module data */ + + xmp_player_get_info(ctx, &mi); + + info_mod(&mi); + + /* Play module */ + + while (xmp_player_frame(ctx) == 0) { + + xmp_player_get_info(ctx, &mi); + if (mi.loop_count > 0) + break; + + info_frame(&mi, new_mod); + if (!silent) { + sound_play(mi.buffer, mi.buffer_size); + } + + new_mod = 0; + options.start = 0; + + } + xmp_player_end(ctx); + } + + xmp_release_module(ctx); + printf("\n"); + } + xmp_free_context(ctx); + + reset_tty(); + + if (!silent) { + sound_deinit(); + } + + exit(EXIT_SUCCESS); +} diff --git a/options.c b/options.c new file mode 100644 index 0000000..ab14880 --- /dev/null +++ b/options.c @@ -0,0 +1,325 @@ +/* Extended Module Player + * Copyright (C) 1996-2012 Claudio Matsuoka and Hipolito Carraro Jr + * + * This file is part of the Extended Module Player and is distributed + * under the terms of the GNU General Public License. See doc/COPYING + * for more information. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" + +extern char *optarg; +static int o, i; +static char *token; + +extern int probeonly; +extern int loadonly; +extern int randomize; +extern int nocmd; +#ifdef HAVE_SYS_RTPRIO_H +extern int rt; +#endif + +#define OPT_LOADONLY 0x103 +#define OPT_FX9BUG 0x105 +#define OPT_PROBEONLY 0x106 +#define OPT_STDOUT 0x109 +#define OPT_STEREO 0x10a +#define OPT_NOCMD 0x10b +#define OPT_REALTIME 0x10c +#define OPT_FIXLOOP 0x10d +#define OPT_CRUNCH 0x10e +#define OPT_NOFILTER 0x10f +#define OPT_VBLANK 0x110 +#define OPT_SHOWTIME 0x111 +#define OPT_DUMP 0x112 + +static void exclude_formats(char *list) +{ + char *tok; + + tok = strtok(list, ", "); + while (tok) { + xmp_enable_format(tok, 0); + tok = strtok(NULL, ", "); + } +} + +static void list_wrap(char *s, int l, int r, int v) +{ + int i; + static int c = 0; + static int m = 0; + char *t; + + if (s == NULL) { + for (i = 0; i < l; i++) + printf(" "); + c = l; + m = r; + return; + } else if (c > l) { + c++; + printf(v ? "," : " "); + } + + t = strtok(s, " "); + + while (t) { + if ((c + strlen(t) + 1) > m) { + c = l; + printf("\n"); + for (i = 0; i < l; i++) { + printf(" "); + } + } else if (c > l) { + printf(" "); + } + c += strlen(t) + 1; + printf("%s", t); + t = strtok(NULL, " "); + } +} + +static void usage(char *s) +{ + struct xmp_fmt_info *f, *fmt; + struct xmp_drv_info *d, *drv; + char **hlp, buf[80]; + int i; + + printf("Usage: %s [options] [modules]\n", s); + +#if 0 + printf("\nRegistered module loaders:\n"); + xmp_get_fmt_info(&fmt); + list_wrap(NULL, 3, 78, 1); + + for (i = 0, f = fmt; f; i++, f = f->next) { + snprintf(buf, 80, "%s (%s)", f->id, f->tracker); + list_wrap(buf, 3, 0, 1); + } + + snprintf(buf, 80, "[%d registered loaders]", i); + list_wrap(buf, 3, 0, 0); + printf("\n"); + + printf("\nAvailable drivers:\n"); + + xmp_get_drv_info(&drv); + list_wrap(NULL, 3, 78, 1); + for (d = drv; d; d = d->next) { + snprintf(buf, 80, "%s (%s)", d->id, d->description); + list_wrap(buf, 3, 0, 1); + } + + printf("\n"); + + for (d = drv; d; d = d->next) { + if (d->help) + printf("\n%s options:\n", d->description); + for (hlp = d->help; hlp && *hlp; hlp += 2) + printf(" -D%-20.20s %s\n", hlp[0], hlp[1]); + } +#endif + + printf("\nPlayer control options:\n" + " -D parameter[=val] Pass configuration parameter to the output driver\n" + " -d --driver name Force output to the specified device\n" + " --fix-sample-loops Use sample loop start /2 in MOD/UNIC/NP3\n" + " --offset-bug-emulation Emulate Protracker 2.x bug in effect 9\n" + " -l --loop Enable module looping\n" + " -M --mute ch-list Mute the specified channels\n" + " --nocmd Disable interactive commands\n" + " --norc Don't read configuration files\n" + " -R --random Random order playing\n" +#ifdef HAVE_SYS_RTPRIO_H + " --realtime Run in real-time priority\n" +#endif + " -S --solo ch-list Set channels to solo mode\n" + " -s --start num Start from the specified order\n" + " -T --tempo num Initial tempo (default 6)\n" + " -t --time num Maximum playing time in seconds\n" + " --vblank Force vblank timing in Amiga modules (no CIA)\n" + "\nPlayer sound options:\n" + " -8 --8bit Convert 16 bit samples to 8 bit\n" + " -m --mono Mono output\n" + " -P --pan pan Percentual pan amplitude\n" + " -r --reverse Reverse left/right stereo channels\n" + " --stereo Stereo output\n" + "\nSoftware mixer options:\n" + " -a --amplify {0|1|2|3} Amplification factor: 0=Normal, 1=x2, 2=x4, 3=x8\n" + " -b --bits {8|16} Software mixer resolution (8 or 16 bits)\n" + " -c --stdout Mix the module to stdout\n" + " -F --click-filter Apply low pass filter to reduce clicks\n" + " -f --frequency rate Sampling rate in hertz (default 44100)\n" + " -n --nearest Use nearest neighbor interpolation\n" + " -o --output-file name Mix the module to file ('-' for stdout)\n" + " -u --unsigned Set the mixer to use unsigned samples\n" + "\nEnvironment options:\n" + " -I --instrument-path Set pathname to external samples\n" + "\nInformation options:\n" + " -h --help Print a summary of the command line options\n" + " --load-only Load module and exit\n" + " --probe-only Probe audio device and exit\n" + " -q --quiet Quiet mode (verbosity level = 0)\n" + " --show-time Display elapsed and remaining time\n" + " -V --version Print version information\n" + " -v --verbose Verbose mode (incremental)\n"); +} + +void get_options(int argc, char **argv, struct options *options) +{ + int optidx = 0; +#define OPTIONS "a:b:cD:d:f:hI:lM:mo:qRS:s:T:t:uVv" + static struct option lopt[] = { + {"amplify", 1, 0, 'a'}, + {"bits", 1, 0, 'b'}, + {"driver", 1, 0, 'd'}, + {"frequency", 1, 0, 'f'}, + {"offset-bug-emulation", 0, 0, OPT_FX9BUG}, + {"help", 0, 0, 'h'}, + {"instrument-path", 1, 0, 'I'}, + {"load-only", 0, 0, OPT_LOADONLY}, + {"loop", 0, 0, 'l'}, + {"mono", 0, 0, 'm'}, + {"mute", 1, 0, 'M'}, + {"nocmd", 0, 0, OPT_NOCMD}, + {"output-file", 1, 0, 'o'}, + {"pan", 1, 0, 'P'}, + {"quiet", 0, 0, 'q'}, + {"random", 0, 0, 'R'}, + {"solo", 1, 0, 'S'}, + {"start", 1, 0, 's'}, + {"stdout", 0, 0, 'c'}, + {"tempo", 1, 0, 'T'}, + {"time", 1, 0, 't'}, + {"unsigned", 0, 0, 'u'}, + {"version", 0, 0, 'V'}, + {"verbose", 0, 0, 'v'}, + {NULL, 0, 0, 0} + }; + + i = 0; + while ((o = getopt_long(argc, argv, OPTIONS, lopt, &optidx)) != -1) { + switch (o) { + case 'a': + options->amplify = atoi(optarg); + break; + case 'b': + if (atoi(optarg) == 8) { + options->format |= XMP_FORMAT_8BIT; + } + break; + case 'c': + options->out_file = "-"; + break; +#if 0 + case 'D': + xmp_set_driver_parameter(opt, optarg); + break; + case 'd': + options->drv_id = optarg; + break; + case OPT_FX9BUG: + options->quirk |= XMP_QRK_FX9BUG; + break; +#endif + case 'f': + options->freq = strtoul(optarg, NULL, 0); + break; + case 'I': + options->ins_path = optarg; + break; + case 'l': + options->loop = 1; + break; + case OPT_LOADONLY: + options->load_only = 1; + break; + case 'm': + options->format |= XMP_FORMAT_MONO; + break; + case 'o': + options->out_file = optarg; + if (strlen(optarg) >= 4 && + !strcasecmp(optarg + strlen(optarg) - 4, ".wav")) { + //options->drv_id = "wav"; + } + break; + case 'P': + options->mix = strtoul(optarg, NULL, 0); + if (options->mix < 0) + options->mix = 0; + if (options->mix > 100) + options->mix = 100; + break; + case 'q': + //options->verbosity = 0; + break; + case 'R': + options->random = 1; + break; + case 'M': + case 'S': + if (o == 'S') { + memset(options->mute, 1, XMP_MAX_CHANNELS); + } + token = strtok(optarg, ","); + while (token) { + int a, b; + char buf[40]; + memset(buf, 0, 40); + if (strchr(token, '-')) { + b = strcspn(token, "-"); + strncpy(buf, token, b); + a = atoi(buf); + strncpy(buf, token + b + 1, + strlen(token) - b - 1); + b = atoi(buf); + } else { + a = b = atoi(token); + } + for (; b >= a; b--) { + if (b < XMP_MAX_CHANNELS) + options->mute[b] = (o == 'M'); + } + token = strtok(NULL, ","); + } + break; + case 's': + options->start = strtoul(optarg, NULL, 0); + break; + case 't': + options->time = strtoul(optarg, NULL, 0); + break; + case 'u': + options->format |= XMP_FORMAT_UNSIGNED; + break; + case 'V': + puts("Extended Module Player " VERSION); + exit(0); + case 'v': + //options->verbosity++; + break; + case 'h': + usage(argv[0]); + default: + exit(-1); + } + } + + /* Set limits */ + if (options->freq < 1000) + options->freq = 1000; /* Min. rate 1 kHz */ + if (options->freq > 48000) + options->freq = 48000; /* Max. rate 48 kHz */ +} diff --git a/sound.h b/sound.h new file mode 100644 index 0000000..f38cdb5 --- /dev/null +++ b/sound.h @@ -0,0 +1,11 @@ + +struct sound_driver { + char *id; + char *description; + char **help; + int (*init) (); + void (*deinit) (); + void (*pause) (); + void (*resume) (); + struct list_head *next; +}; diff --git a/sound_alsa.c b/sound_alsa.c new file mode 100644 index 0000000..61f3f99 --- /dev/null +++ b/sound_alsa.c @@ -0,0 +1,70 @@ + +#include +#include +#include "sound.h" + +static snd_pcm_t *pcm_handle; + + +int sound_init(int sampling_rate, int channels) +{ + snd_pcm_hw_params_t *hwparams; + int ret; + unsigned int chan, rate; + unsigned int btime = 250000; /* 250ms */ + unsigned int ptime = 50000; /* 50ms */ + char *card_name = "default"; + + if ((ret = snd_pcm_open(&pcm_handle, card_name, + SND_PCM_STREAM_PLAYBACK, 0)) < 0) { + fprintf(stderr, "Unable to initialize ALSA pcm device: %s\n", + snd_strerror(ret)); + return -1; + } + + chan = channels; + rate = sampling_rate; + + snd_pcm_hw_params_alloca(&hwparams); + snd_pcm_hw_params_any(pcm_handle, hwparams); + snd_pcm_hw_params_set_access(pcm_handle, hwparams, + SND_PCM_ACCESS_RW_INTERLEAVED); + snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16); + snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &rate, 0); + snd_pcm_hw_params_set_channels_near(pcm_handle, hwparams, &chan); + snd_pcm_hw_params_set_buffer_time_near(pcm_handle, hwparams, &btime, 0); + snd_pcm_hw_params_set_period_time_near(pcm_handle, hwparams, &ptime, 0); + snd_pcm_nonblock(pcm_handle, 0); + + if ((ret = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) { + fprintf(stderr, "Unable to set ALSA output parameters: %s\n", + snd_strerror(ret)); + return -1; + } + + if ((ret = snd_pcm_prepare(pcm_handle)) < 0) { + fprintf(stderr, "Unable to prepare ALSA: %s\n", + snd_strerror(ret)); + return -1; + } + + return 0; +} + +void sound_play(void *b, int i) +{ + int frames; + + frames = snd_pcm_bytes_to_frames(pcm_handle, i); + if (snd_pcm_writei(pcm_handle, b, frames) < 0) { + snd_pcm_prepare(pcm_handle); + } +} + +void sound_deinit() +{ + /* snd_pcm_drain(pcm_handle); */ + snd_pcm_close(pcm_handle); +} + + diff --git a/terminal.c b/terminal.c new file mode 100644 index 0000000..40a46ef --- /dev/null +++ b/terminal.c @@ -0,0 +1,41 @@ +#include +#include +#include +#include "common.h" + +static int background = 0; +static struct termios term; + +int set_tty() +{ + struct termios t; + + if (background) + return -1; + + if (tcgetattr(0, &term) < 0) + return -1; + + t = term; + t.c_lflag &= ~(ECHO | ICANON | TOSTOP); + t.c_cc[VMIN] = t.c_cc[VTIME] = 0; + + if (tcsetattr(0, TCSAFLUSH, &t) < 0) + return -1; + + return 0; +} + +int reset_tty() +{ + if (background) + return -1; + + if (tcsetattr(0, TCSAFLUSH, &term) < 0) { + fprintf(stderr, "can't reset terminal!\n"); + return -1; + } + + return 0; +} +