You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
234 lines
5.4 KiB
234 lines
5.4 KiB
/* 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 <R.Adamson@fitz.cam.ac.uk> |
|
* 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 <string.h> |
|
#include <stdlib.h> |
|
#include <stdio.h> |
|
#include <sys/time.h> |
|
#include <sys/types.h> |
|
#include <sys/stat.h> |
|
#include <fcntl.h> |
|
#include <unistd.h> |
|
#include <sys/ioctl.h> |
|
#include <sys/asoundlib.h> |
|
|
|
#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 <name>", "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(); |
|
}
|
|
|