patch-2.4.0-test7 linux/drivers/sound/emu10k1/cardwo.c
Next file: linux/drivers/sound/emu10k1/cardwo.h
Previous file: linux/drivers/sound/emu10k1/cardwi.h
Back to the patch index
Back to the overall index
- Lines: 987
- Date:
Mon Aug 14 08:32:48 2000
- Orig file:
v2.4.0-test6/linux/drivers/sound/emu10k1/cardwo.c
- Orig date:
Wed Apr 26 16:34:08 2000
diff -u --recursive --new-file v2.4.0-test6/linux/drivers/sound/emu10k1/cardwo.c linux/drivers/sound/emu10k1/cardwo.c
@@ -1,4 +1,3 @@
-
/*
**********************************************************************
* cardwo.c - PCM output HAL for emu10k1 driver
@@ -30,47 +29,17 @@
**********************************************************************
*/
+#include <linux/poll.h>
#include "hwaccess.h"
+#include "8010.h"
+#include "voicemgr.h"
#include "cardwo.h"
#include "audio.h"
-/* Volume calcs */
-
-static int set_volume_instance(struct emu10k1_waveout *card_waveout, struct wave_out *wave_out, struct voice_param *left)
+static u32 samplerate_to_linearpitch(u32 samplingrate)
{
- /* only applicable for playback */
- u32 volL, volR, vol = 0;
-
- volL = (wave_out->localvol & 0xffff);
- volR = ((wave_out->localvol >> 16) & 0xffff);
-
- if (wave_out->globalvolFactor) {
- volL = ((u32) (((u16) card_waveout->globalvol & 0xffff) * (u16) volL)) / 0xffff;
- volR = ((u32) (((u16) (card_waveout->globalvol >> 16) & 0xffff) * ((u16) volR))) / 0xffff;
- }
-
- /* BIG ASSUMPTION HERE THAT DEFAULT WAVE PAN/AUX IS 0xff/0xff */
- /* New volume and pan */
-
- if (volL == volR) {
- vol = volL;
- left->send_c = 0xff;
- left->send_b = 0xff;
- } else {
- if (volL > volR) {
- vol = volL;
- left->send_c = 0xff;
- left->send_b = (char) ((volR * 255) / vol);
- } else {
- vol = volR;
- left->send_b = 0xff;
- left->send_c = (char) ((volL * 255) / vol);
- }
- }
-
- left->initial_attn = 0xff & sumVolumeToAttenuation(vol * 2);
-
- return vol;
+ samplingrate = (samplingrate << 8) / 375;
+ return (samplingrate >> 1) + (samplingrate & 1);
}
static void query_format(struct wave_format *wave_fmt)
@@ -78,593 +47,404 @@
if ((wave_fmt->channels != 1) && (wave_fmt->channels != 2))
wave_fmt->channels = 2;
- if (wave_fmt->samplingrate >= 0x2EE00)
- wave_fmt->samplingrate = 0x2EE00;
+ if (wave_fmt->samplingrate >= 0x2ee00)
+ wave_fmt->samplingrate = 0x2ee00;
if ((wave_fmt->bitsperchannel != 8) && (wave_fmt->bitsperchannel != 16))
wave_fmt->bitsperchannel = 16;
+ wave_fmt->bytesperchannel = wave_fmt->bitsperchannel >> 3;
+ wave_fmt->bytespersample = wave_fmt->channels * wave_fmt->bytesperchannel;
+ wave_fmt->bytespersec = wave_fmt->bytespersample * wave_fmt->samplingrate;
+
return;
}
-static int alloc_xferbuffer(struct emu10k1_card *card, struct wave_out *wave_out, u32 * size, void ***buffer)
+static int alloc_buffer(struct emu10k1_card *card, struct waveout_buffer *buffer)
{
- u32 numpages, reqsize, pageindex, pagecount;
- struct wave_xferbuf *wavexferbuf = wave_out->wavexferbuf;
+ u32 pageindex, pagecount;
unsigned long busaddx;
int i;
- reqsize = *size;
- numpages = reqsize / PAGE_SIZE;
-
- /* If size is not a multiple of PAGE_SIZE then we need to round up */
- if (reqsize % PAGE_SIZE)
- numpages += 1;
-
- DPD(2, "requested pages is: %d\n", numpages);
-
- wavexferbuf->numpages = numpages;
-
- /* Only for playback, request for emu address space */
- /* Support non page-aligned buffer, don't need interpolation page */
+ DPD(2, "requested pages is: %d\n", buffer->pages);
- if ((wave_out->emupageindex = emu10k1_addxmgr_alloc(numpages * PAGE_SIZE, card)) < 0)
- return CTSTATUS_ERROR;
-
- if ((wave_out->pagetable = (void **) kmalloc(sizeof(void *) * numpages, GFP_KERNEL)) == NULL)
- return CTSTATUS_ERROR;
+ if ((buffer->emupageindex = emu10k1_addxmgr_alloc(buffer->pages * PAGE_SIZE, card)) < 0)
+ return -1;
/* Fill in virtual memory table */
- for (pagecount = 0; pagecount < numpages; pagecount++) {
- if ((wave_out->pagetable[pagecount] = (void *) __get_free_page(GFP_KERNEL)) == NULL) {
- wavexferbuf->numpages = pagecount;
- return CTSTATUS_ERROR;
+ for (pagecount = 0; pagecount < buffer->pages; pagecount++) {
+ if ((buffer->addr[pagecount] = pci_alloc_consistent(card->pci_dev, PAGE_SIZE, &buffer->dma_handle[pagecount])) == NULL) {
+ buffer->pages = pagecount;
+ return -1;
}
- DPD(2, "Virtual Addx: %p\n", wave_out->pagetable[pagecount]);
+ DPD(2, "Virtual Addx: %p\n", buffer->addr[pagecount]);
for (i = 0; i < PAGE_SIZE / EMUPAGESIZE; i++) {
- busaddx = virt_to_bus((u8 *) wave_out->pagetable[pagecount] + i * EMUPAGESIZE);
+ busaddx = buffer->dma_handle[pagecount] + i * EMUPAGESIZE;
DPD(3, "Bus Addx: %lx\n", busaddx);
- pageindex = wave_out->emupageindex + pagecount * PAGE_SIZE / EMUPAGESIZE + i;
+ pageindex = buffer->emupageindex + pagecount * PAGE_SIZE / EMUPAGESIZE + i;
- ((u32 *) card->virtualpagetable->virtaddx)[pageindex] = ((u32) busaddx * 2) | pageindex;
+ ((u32 *) card->virtualpagetable.addr)[pageindex] = (busaddx * 2) | pageindex;
}
}
- *buffer = wave_out->pagetable;
-
- return CTSTATUS_SUCCESS;
-}
-
-static int get_xferbuffer(struct emu10k1_card *card, struct wave_out *wave_out, u32 * size)
-{
- struct wave_xferbuf *wavexferbuf = wave_out->wavexferbuf;
- void **buffer;
-
- wavexferbuf->xferpos = 0;
- wavexferbuf->silence_xferpos = 0;
- wavexferbuf->stopposition = 0;
- wavexferbuf->is_stereo = (wave_out->wave_fmt.channels == 2) ? 1 : 0;
- wavexferbuf->is_16bit = (wave_out->wave_fmt.bitsperchannel == 16) ? 1 : 0;
- wavexferbuf->bytespersample = (wavexferbuf->is_stereo + 1) * (wavexferbuf->is_16bit + 1);
-
- if (alloc_xferbuffer(card, wave_out, size, &buffer) != CTSTATUS_SUCCESS)
- return CTSTATUS_ERROR;
-
- /* xferbufsize contains actual transfer buffer size */
- wavexferbuf->xferbufsize = *size;
- wavexferbuf->xferbuffer = buffer;
-
- return CTSTATUS_SUCCESS;
+ return 0;
}
-static void dealloc_xferbuffer(struct emu10k1_card *card, struct wave_out *wave_out)
+static void free_buffer(struct emu10k1_card *card, struct waveout_buffer *buffer)
{
u32 pagecount, pageindex;
int i;
- if (wave_out->pagetable != NULL) {
- for (pagecount = 0; pagecount < wave_out->wavexferbuf->numpages; pagecount++) {
- free_page((unsigned long) wave_out->pagetable[pagecount]);
-
- for (i = 0; i < PAGE_SIZE / EMUPAGESIZE; i++) {
- pageindex = wave_out->emupageindex + pagecount * PAGE_SIZE / EMUPAGESIZE + i;
- ((u32 *) card->virtualpagetable->virtaddx)[pageindex] = (card->silentpage->busaddx * 2) | pageindex;
- }
+ if (buffer->emupageindex < 0)
+ return;
+
+ for (pagecount = 0; pagecount < buffer->pages; pagecount++) {
+ pci_free_consistent(card->pci_dev, PAGE_SIZE, buffer->addr[pagecount], buffer->dma_handle[pagecount]);
+
+ for (i = 0; i < PAGE_SIZE / EMUPAGESIZE; i++) {
+ pageindex = buffer->emupageindex + pagecount * PAGE_SIZE / EMUPAGESIZE + i;
+ ((u32 *) card->virtualpagetable.addr)[pageindex] = (card->silentpage.dma_handle * 2) | pageindex;
}
- kfree(wave_out->pagetable);
}
- emu10k1_addxmgr_free(card, wave_out->emupageindex);
+ emu10k1_addxmgr_free(card, buffer->emupageindex);
+ buffer->emupageindex = -1;
return;
}
-static int get_voice(struct emu10k1_card *card, struct wave_out *wave_out, int device)
+static int get_voice(struct emu10k1_card *card, struct woinst *woinst)
{
- struct emu10k1_waveout *card_waveout = card->waveout;
- struct wave_xferbuf *wavexferbuf = wave_out->wavexferbuf;
- struct voice_allocdesc voice_allocdesc;
- struct voice_param *left, *right;
- u32 size;
-
+ struct emu_voice *voice = &woinst->voice;
/* Allocate voices here, if no voices available, return error.
* Init voice_allocdesc first.*/
- voice_allocdesc.usage = VOICEMGR_USAGE_PLAYBACK;
-
- voice_allocdesc.flags = 0;
-
- if (device == 1)
- voice_allocdesc.flags |= VOICEMGR_FLAGS_FXRT2;
-
- if (wave_out->wave_fmt.channels == 1)
- voice_allocdesc.flags |= VOICEMGR_FLAGS_MONO;
+ voice->usage = VOICE_USAGE_PLAYBACK;
- if (wave_out->wave_fmt.bitsperchannel == 16)
- voice_allocdesc.flags |= VOICEMGR_FLAGS_16BIT;
+ voice->flags = 0;
- if ((wave_out->voice = emu10k1_voice_alloc(&card->voicemgr, &voice_allocdesc)) == NULL)
- return CTSTATUS_ERROR;
+ if (woinst->format.channels == 2)
+ voice->flags |= VOICE_FLAGS_STEREO;
- /* voice initialization */
+ if (woinst->format.bitsperchannel == 16)
+ voice->flags |= VOICE_FLAGS_16BIT;
- left = &wave_out->voice->params;
+ if (emu10k1_voice_alloc(card, voice) < 0)
+ return -1;
/* Calculate pitch */
- left->initial_pitch = (u16) (srToPitch(wave_out->wave_fmt.samplingrate) >> 8);
+ voice->initial_pitch = (u16) (srToPitch(woinst->format.samplingrate) >> 8);
+ voice->pitch_target = samplerate_to_linearpitch(woinst->format.samplingrate);
- DPD(2, "Initial pitch --> %x\n", left->initial_pitch);
+ DPD(2, "Initial pitch --> 0x%x\n", voice->initial_pitch);
- /* Easy way out.. gotta calculate value */
- left->pitch_target = 0;
- left->volume_target = 0;
- left->FC_target = 0;
+ voice->startloop = (woinst->buffer.emupageindex << 12) / woinst->format.bytespersample;
+ voice->endloop = voice->startloop + woinst->buffer.size / woinst->format.bytespersample;
+ voice->start = voice->startloop;
+
+ if (voice->flags & VOICE_FLAGS_STEREO) {
+ voice->params[0].send_a = card->waveout.send_a[1];
+ voice->params[0].send_b = card->waveout.send_b[1];
+ voice->params[0].send_c = card->waveout.send_c[1];
+ voice->params[0].send_d = card->waveout.send_d[1];
- left->byampl_env_sustain = 0x7f;
- left->byampl_env_decay = 0x7f;
+ if (woinst->device)
+ voice->params[0].send_routing = 0xd23c;
+ else
+ voice->params[0].send_routing = card->waveout.send_routing[1];
- if (wave_out->globalreverbFactor) {
- u8 t = (card_waveout->globalreverb & 0xff) + (wave_out->localreverb & 0xff);
+ voice->params[0].volume_target = 0xffff;
+ voice->params[0].initial_fc = 0xff;
+ voice->params[0].initial_attn = 0x00;
+ voice->params[0].byampl_env_sustain = 0x7f;
+ voice->params[0].byampl_env_decay = 0x7f;
+
+ voice->params[1].send_a = card->waveout.send_a[2];
+ voice->params[1].send_b = card->waveout.send_b[2];
+ voice->params[1].send_c = card->waveout.send_c[2];
+ voice->params[1].send_d = card->waveout.send_d[2];
- left->send_a = (t > 255) ? 255 : t;
- } else {
- left->send_a = 0;
- }
-
- if (wave_out->globalchorusFactor) {
- u8 t = (card_waveout->globalchorus & 0xff) + (wave_out->localchorus & 0xff);
+ if (woinst->device)
+ voice->params[1].send_routing = 0xd23c;
+ else
+ voice->params[1].send_routing = card->waveout.send_routing[2];
- left->send_d = (t > 255) ? 255 : t;
+ voice->params[1].volume_target = 0xffff;
+ voice->params[1].initial_fc = 0xff;
+ voice->params[1].initial_attn = 0x00;
+ voice->params[1].byampl_env_sustain = 0x7f;
+ voice->params[1].byampl_env_decay = 0x7f;
} else {
- left->send_d = 0;
- }
-
- set_volume_instance(card_waveout, wave_out, left);
-
- left->pan_target = left->send_c;
- left->aux_target = left->send_b;
-
- size = wavexferbuf->xferbufsize / wavexferbuf->bytespersample;
- left->start = 2 * (wave_out->emupageindex << 11) / wavexferbuf->bytespersample;
- left->end = left->start + size;
- left->startloop = left->start;
- left->endloop = left->end;
-
- if (wave_out->voice->linked_voice) {
- DPF(2, "is stereo\n");
- right = &wave_out->voice->linked_voice->params;
-
- right->initial_pitch = left->initial_pitch;
-
- /* Easy way out.. gotta calculate value */
- right->pitch_target = 0;
- right->volume_target = 0;
- right->FC_target = 0;
-
- right->byampl_env_sustain = 0x7f;
- right->byampl_env_decay = 0x7f;
-
- right->send_d = left->send_d;
- right->send_a = left->send_a;
-
- /* Left output of right channel is always zero */
- right->send_c = 0;
-
- /* Update right channel aux */
- right->pan_target = 0;
- right->send_b = left->send_b;
- right->aux_target = right->send_b;
-
- /* Zero out right output of left channel */
- left->send_b = 0;
- left->aux_target = 0;
+ voice->params[0].send_a = card->waveout.send_a[0];
+ voice->params[0].send_b = card->waveout.send_b[0];
+ voice->params[0].send_c = card->waveout.send_c[0];
+ voice->params[0].send_d = card->waveout.send_d[0];
- /* Update right channel attenuation */
- right->initial_attn = left->initial_attn;
-
- right->start = left->start;
- right->end = left->end;
- right->startloop = left->startloop;
- right->endloop = left->endloop;
+ if (woinst->device)
+ voice->params[0].send_routing = 0xd23c;
+ else
+ voice->params[0].send_routing = card->waveout.send_routing[0];
+ voice->params[0].volume_target = 0xffff;
+ voice->params[0].initial_fc = 0xff;
+ voice->params[0].initial_attn = 0x00;
+ voice->params[0].byampl_env_sustain = 0x7f;
+ voice->params[0].byampl_env_decay = 0x7f;
}
- DPD(2, "voice: start=%x, end=%x, startloop=%x, endloop=%x\n", left->start, left->end, left->startloop, left->endloop);
+ DPD(2, "voice: startloop=0x%x, endloop=0x%x\n", voice->startloop, voice->endloop);
+
+ emu10k1_voice_playback_setup(voice);
- return CTSTATUS_SUCCESS;
+ return 0;
}
int emu10k1_waveout_open(struct emu10k1_wavedevice *wave_dev)
{
struct emu10k1_card *card = wave_dev->card;
struct woinst *woinst = wave_dev->woinst;
- struct wave_out *wave_out;
- u32 bytespersec, delay;
- u32 buffsize;
+ u32 delay;
DPF(2, "emu10k1_waveout_open()\n");
- if ((wave_out = (struct wave_out *) kmalloc(sizeof(struct wave_out), GFP_KERNEL)) == NULL) {
- ERROR();
- emu10k1_waveout_close(wave_dev);
- return CTSTATUS_ERROR;
- }
-
- woinst->wave_out = wave_out;
-
- /* Init channel object */
- wave_out->state = CARDWAVE_STATE_STOPPED;
- wave_out->wave_fmt = woinst->wave_fmt;
- wave_out->voice = NULL;
- wave_out->emupageindex = -1;
- wave_out->wavexferbuf = NULL;
- wave_out->pagetable = NULL;
- wave_out->timer = NULL;
-
- /* Assign default local volume */
- /* FIXME: Should we be maxing the initial values like this? */
- wave_out->localvol = 0xffffffff;
- wave_out->localreverb = 0xffffffff;
- wave_out->localchorus = 0xffffffff;
- wave_out->globalvolFactor = 0xffff;
- wave_out->globalreverbFactor = 0xffff;
- wave_out->globalchorusFactor = 0xffff;
-
- wave_out->setpos = 0;
- wave_out->position = 0;
-
- wave_out->fill_silence = 0;
-
- if ((wave_out->wavexferbuf = (struct wave_xferbuf *) kmalloc(sizeof(struct wave_xferbuf), GFP_KERNEL)) == NULL) {
+ if (alloc_buffer(card, &woinst->buffer) < 0) {
ERROR();
emu10k1_waveout_close(wave_dev);
- return CTSTATUS_ERROR;
+ return -1;
}
- buffsize = woinst->fragment_size * woinst->numfrags;
+ woinst->buffer.fill_silence = 0;
+ woinst->buffer.silence_bytes = 0;
+ woinst->buffer.silence_pos = 0;
+ woinst->buffer.hw_pos = 0;
+ woinst->buffer.bytestocopy = woinst->buffer.size;
- if (get_xferbuffer(card, wave_out, &buffsize) != CTSTATUS_SUCCESS) {
+ if (get_voice(card, woinst) < 0) {
ERROR();
emu10k1_waveout_close(wave_dev);
- return CTSTATUS_ERROR;
+ return -1;
}
- woinst->fragment_size = buffsize / woinst->numfrags;
- wave_out->callbacksize = woinst->fragment_size;
+ delay = (48000 * woinst->buffer.fragment_size) / woinst->format.bytespersec;
- if (get_voice(card, wave_out, woinst->device) != CTSTATUS_SUCCESS) {
- ERROR();
- emu10k1_waveout_close(wave_dev);
- return CTSTATUS_ERROR;
- }
+ emu10k1_timer_install(card, &woinst->timer, delay / 2);
- bytespersec = wave_out->wave_fmt.channels * (wave_out->wave_fmt.bitsperchannel >> 3) * (wave_out->wave_fmt.samplingrate);
- delay = (48000 * wave_out->callbacksize) / bytespersec;
+ woinst->state = WAVE_STATE_OPEN;
- if ((wave_out->timer = emu10k1_timer_install(card, emu10k1_waveout_bh, (unsigned long) wave_dev, delay / 2)) == NULL) {
- ERROR();
- emu10k1_waveout_close(wave_dev);
- return CTSTATUS_ERROR;
- }
-
- return CTSTATUS_SUCCESS;
+ return 0;
}
void emu10k1_waveout_close(struct emu10k1_wavedevice *wave_dev)
{
struct emu10k1_card *card = wave_dev->card;
- struct wave_out *wave_out = wave_dev->woinst->wave_out;
+ struct woinst *woinst = wave_dev->woinst;
DPF(2, "emu10k1_waveout_close()\n");
- if (wave_out->state != CARDWAVE_STATE_STOPPED)
- emu10k1_waveout_stop(wave_dev);
-
- if (wave_out->timer != NULL)
- emu10k1_timer_uninstall(card, wave_out->timer);
+ emu10k1_waveout_stop(wave_dev);
- if (wave_out->voice != NULL)
- emu10k1_voice_free(&card->voicemgr, wave_out->voice);
+ emu10k1_timer_uninstall(card, &woinst->timer);
- if (wave_out->emupageindex >= 0)
- dealloc_xferbuffer(card, wave_out);
+ emu10k1_voice_free(&woinst->voice);
- if (wave_out->wavexferbuf != NULL)
- kfree(wave_out->wavexferbuf);
+ free_buffer(card, &woinst->buffer);
- kfree(wave_out);
- wave_dev->woinst->wave_out = NULL;
+ woinst->state = WAVE_STATE_CLOSED;
return;
}
-int emu10k1_waveout_start(struct emu10k1_wavedevice *wave_dev)
+void emu10k1_waveout_start(struct emu10k1_wavedevice *wave_dev)
{
struct emu10k1_card *card = wave_dev->card;
- struct wave_out *wave_out = wave_dev->woinst->wave_out;
- u32 start, startPosition;
+ struct woinst *woinst = wave_dev->woinst;
DPF(2, "emu10k1_waveout_start()\n");
-
- /* If already started, return success */
- if (wave_out->state == CARDWAVE_STATE_STARTED)
- return CTSTATUS_SUCCESS;
-
- if (wave_out->state == CARDWAVE_STATE_STOPPED && wave_out->setpos)
- startPosition = wave_out->position / (wave_out->wavexferbuf->bytespersample);
- else
- startPosition = wave_out->wavexferbuf->stopposition;
-
- start = wave_out->voice->params.start;
- wave_out->voice->params.start += startPosition;
-
- DPD(2, "CA is %x\n", wave_out->voice->params.start);
-
- emu10k1_voice_playback_setup(wave_out->voice);
-
- wave_out->voice->params.start = start;
-
/* Actual start */
- emu10k1_voice_start(wave_out->voice);
- wave_out->state = CARDWAVE_STATE_STARTED;
- wave_out->setpos = 0;
+ emu10k1_voice_start(&woinst->voice, woinst->total_played);
+
+ emu10k1_timer_enable(card, &woinst->timer);
- emu10k1_timer_enable(card, wave_out->timer);
+ woinst->state |= WAVE_STATE_STARTED;
- return CTSTATUS_SUCCESS;
+ return;
}
-int emu10k1_waveout_setformat(struct emu10k1_wavedevice *wave_dev)
+int emu10k1_waveout_setformat(struct emu10k1_wavedevice *wave_dev, struct wave_format *format)
{
struct emu10k1_card *card = wave_dev->card;
struct woinst *woinst = wave_dev->woinst;
- struct wave_out *wave_out = woinst->wave_out;
- u32 bytespersec, delay;
+ u32 delay;
DPF(2, "emu10k1_waveout_setformat()\n");
- query_format(&woinst->wave_fmt);
-
- if (wave_out == NULL)
- return CTSTATUS_SUCCESS;
-
- if (wave_out->state == CARDWAVE_STATE_STARTED) {
- woinst->wave_fmt = wave_out->wave_fmt;
- return CTSTATUS_SUCCESS;
- }
+ if (woinst->state & WAVE_STATE_STARTED)
+ return -1;
- if ((wave_out->wave_fmt.samplingrate != woinst->wave_fmt.samplingrate)
- || (wave_out->wave_fmt.bitsperchannel != woinst->wave_fmt.bitsperchannel)
- || (wave_out->wave_fmt.channels != woinst->wave_fmt.channels)) {
- struct wave_xferbuf *wavexferbuf = wave_out->wavexferbuf;
+ query_format(format);
- emu10k1_timer_uninstall(card, wave_out->timer);
+ if (woinst->format.samplingrate != format->samplingrate ||
+ woinst->format.channels != format->channels ||
+ woinst->format.bitsperchannel != format->bitsperchannel) {
- emu10k1_voice_free(&card->voicemgr, wave_out->voice);
+ woinst->format = *format;
- wave_out->wave_fmt = woinst->wave_fmt;
- wave_out->timer = NULL;
+ if (woinst->state == WAVE_STATE_CLOSED)
+ return 0;
- wavexferbuf->xferpos = 0;
- wavexferbuf->silence_xferpos = 0;
- wavexferbuf->stopposition = 0;
- wavexferbuf->is_stereo = (wave_out->wave_fmt.channels == 2) ? 1 : 0;
- wavexferbuf->is_16bit = (wave_out->wave_fmt.bitsperchannel == 16) ? 1 : 0;
- wavexferbuf->bytespersample = (wavexferbuf->is_stereo + 1) * (wavexferbuf->is_16bit + 1);
+ emu10k1_timer_uninstall(card, &woinst->timer);
+ emu10k1_voice_free(&woinst->voice);
- if (get_voice(card, wave_out, woinst->device) != CTSTATUS_SUCCESS) {
+ if (get_voice(card, woinst) < 0) {
ERROR();
emu10k1_waveout_close(wave_dev);
- return CTSTATUS_ERROR;
+ return -1;
}
- bytespersec = wave_out->wave_fmt.channels * (wave_out->wave_fmt.bitsperchannel >> 3) * (wave_out->wave_fmt.samplingrate);
- delay = (48000 * wave_out->callbacksize) / bytespersec;
+ delay = (48000 * woinst->buffer.fragment_size) / woinst->format.bytespersec;
- if ((wave_out->timer = emu10k1_timer_install(card, emu10k1_waveout_bh, (unsigned long) wave_dev, delay / 2)) == NULL) {
- ERROR();
- emu10k1_waveout_close(wave_dev);
- return CTSTATUS_ERROR;
- }
+ emu10k1_timer_install(card, &woinst->timer, delay / 2);
}
- return CTSTATUS_SUCCESS;
+ return 0;
}
void emu10k1_waveout_stop(struct emu10k1_wavedevice *wave_dev)
{
struct emu10k1_card *card = wave_dev->card;
- struct wave_out *wave_out = wave_dev->woinst->wave_out;
- struct wave_xferbuf *wavexferbuf = wave_out->wavexferbuf;
- u32 samples = 32;
- u32 position;
+ struct woinst *woinst = wave_dev->woinst;
DPF(2, "emu10k1_waveout_stop()\n");
- if (wave_out->state == CARDWAVE_STATE_STOPPED)
+ if (!(woinst->state & WAVE_STATE_STARTED))
return;
- emu10k1_timer_disable(card, wave_out->timer);
+ emu10k1_timer_disable(card, &woinst->timer);
/* Stop actual voice */
- emu10k1_voice_stop(wave_out->voice);
-
- /* Save the stop position */
- emu10k1_voice_getcontrol(wave_out->voice, CCCA_CURRADDR, &wavexferbuf->stopposition);
-
- wavexferbuf->stopposition -= wave_out->voice->params.start;
-
- /* Refer to voicemgr.c, CA is not started at zero. We need to take this into account. */
- position = wavexferbuf->stopposition * wavexferbuf->bytespersample;
-
- if (!wavexferbuf->is_16bit)
- samples <<= 1;
+ emu10k1_voice_stop(&woinst->voice);
- if (wavexferbuf->is_stereo)
- samples <<= 1;
+ emu10k1_waveout_update(woinst);
- samples -= 4;
-
- if (position >= samples * (wavexferbuf->is_16bit + 1))
- position -= samples * (wavexferbuf->is_16bit + 1);
- else
- position += wavexferbuf->xferbufsize - samples * (wavexferbuf->is_16bit + 1);
-
- wavexferbuf->stopposition = position / wavexferbuf->bytespersample;
-
- DPD(2, "position is %x\n", wavexferbuf->stopposition);
-
- wave_out->state = CARDWAVE_STATE_STOPPED;
- wave_out->setpos = 0;
- wave_out->position = 0;
+ woinst->state &= ~WAVE_STATE_STARTED;
return;
}
-void emu10k1_waveout_getxfersize(struct wave_out *wave_out, u32 * size, u32 * pending, u32 * curpos)
+void emu10k1_waveout_getxfersize(struct woinst *woinst, u32 * size)
{
- struct wave_xferbuf *wavexferbuf = wave_out->wavexferbuf;
-
- /* Get position of current address, this is in no. of bytes in play buffer */
- emu10k1_waveout_getcontrol(wave_out, WAVECURPOS, curpos);
+ struct waveout_buffer *buffer = &woinst->buffer;
+ int pending;
- if ((*curpos > wavexferbuf->silence_xferpos)
- || ((*curpos == wavexferbuf->silence_xferpos)
- && (wave_out->state == CARDWAVE_STATE_STARTED))
- || ((*curpos == wavexferbuf->silence_xferpos) && (wavexferbuf->silence_xferpos != 0)
- && (wave_out->state == CARDWAVE_STATE_STOPPED))) {
- *size = *curpos - wavexferbuf->silence_xferpos;
- *pending = wavexferbuf->xferbufsize - *size;
- } else {
- *pending = wavexferbuf->silence_xferpos - *curpos;
- *size = wavexferbuf->xferbufsize - *pending;
+ if (woinst->mmapped) {
+ *size = buffer->bytestocopy;
+ return;
}
- if (wavexferbuf->silence_xferpos != wavexferbuf->xferpos) {
- if (*pending < wave_out->callbacksize) {
- wave_out->fill_silence = 2;
- *pending = 0;
- *size = wavexferbuf->xferbufsize;
- wavexferbuf->xferpos = *curpos;
- } else {
- if (wave_out->fill_silence == 2) {
- *pending = 0;
- *size = wavexferbuf->xferbufsize;
- wavexferbuf->xferpos = *curpos;
- } else {
- *pending -= wave_out->callbacksize;
- *size += wave_out->callbacksize;
- }
- }
+ pending = buffer->size - buffer->bytestocopy;
+
+ buffer->fill_silence = (pending < (signed) buffer->fragment_size) ? 1 : 0;
+
+ if (pending > (signed) buffer->silence_bytes) {
+ *size = buffer->bytestocopy + buffer->silence_bytes;
} else {
- if (*pending < wave_out->callbacksize)
- wave_out->fill_silence = 1;
- else
- wave_out->fill_silence = 0;
+ *size = buffer->size;
+ buffer->silence_bytes = pending;
+ if (pending < 0) {
+ buffer->silence_pos = buffer->hw_pos;
+ buffer->silence_bytes = 0;
+ buffer->bytestocopy = buffer->size;
+ DPF(1, "buffer underrun\n");
+ }
}
return;
}
-static void copy_block(u32 dst, u8 * src, u32 len, void **pt)
+static void copy_block(void **dst, u32 str, u8 *src, u32 len)
{
int i, j, k;
- i = dst / PAGE_SIZE;
- j = dst % PAGE_SIZE;
- k = (len > PAGE_SIZE - j) ? PAGE_SIZE - j : len;
- copy_from_user(pt[i] + j, src, k);
- len -= k;
- while (len >= PAGE_SIZE) {
- copy_from_user(pt[++i], src + k, PAGE_SIZE);
- k += PAGE_SIZE;
- len -= PAGE_SIZE;
- }
- copy_from_user(pt[++i], src + k, len);
+ i = str / PAGE_SIZE;
+ j = str % PAGE_SIZE;
+
+ if (len > PAGE_SIZE - j) {
+ k = PAGE_SIZE - j;
+ copy_from_user(dst[i] + j, src, k);
+ len -= k;
+ while (len > PAGE_SIZE) {
+ copy_from_user(dst[++i], src + k, PAGE_SIZE);
+ k += PAGE_SIZE;
+ len -= PAGE_SIZE;
+ }
+ copy_from_user(dst[++i], src + k, len);
+
+ } else
+ copy_from_user(dst[i] + j, src, len);
return;
}
-static void fill_block(u32 dst, u8 val, u32 len, void **pt)
+static void fill_block(void **dst, u32 str, u8 src, u32 len)
{
int i, j, k;
- i = dst / PAGE_SIZE;
- j = dst % PAGE_SIZE;
- k = (len > PAGE_SIZE - j) ? PAGE_SIZE - j : len;
- memset(pt[i] + j, val, k);
- len -= k;
- while (len >= PAGE_SIZE) {
- memset(pt[++i], val, PAGE_SIZE);
- len -= PAGE_SIZE;
- }
- memset(pt[++i], val, len);
+ i = str / PAGE_SIZE;
+ j = str % PAGE_SIZE;
+
+ if (len > PAGE_SIZE - j) {
+ k = PAGE_SIZE - j;
+ memset(dst[i] + j, src, k);
+ len -= k;
+ while (len > PAGE_SIZE) {
+ memset(dst[++i], src, PAGE_SIZE);
+ len -= PAGE_SIZE;
+ }
+ memset(dst[++i], src, len);
+
+ } else
+ memset(dst[i] + j, src, len);
return;
}
-void emu10k1_waveout_xferdata(struct woinst *woinst, u8 * data, u32 * size)
+void emu10k1_waveout_xferdata(struct woinst *woinst, u8 *data, u32 *size)
{
- struct wave_out *wave_out = woinst->wave_out;
- struct wave_xferbuf *wavexferbuf = wave_out->wavexferbuf;
+ struct waveout_buffer *buffer = &woinst->buffer;
u32 sizetocopy, sizetocopy_now, start;
unsigned long flags;
- sizetocopy = min(wavexferbuf->xferbufsize, *size);
+ sizetocopy = min(buffer->size, *size);
*size = sizetocopy;
if (!sizetocopy)
return;
spin_lock_irqsave(&woinst->lock, flags);
+ start = (buffer->size + buffer->silence_pos - buffer->silence_bytes) % buffer->size;
- sizetocopy_now = wavexferbuf->xferbufsize - wavexferbuf->xferpos;
+ if(sizetocopy > buffer->silence_bytes) {
+ buffer->silence_pos += sizetocopy - buffer->silence_bytes;
+ buffer->bytestocopy -= sizetocopy - buffer->silence_bytes;
+ buffer->silence_bytes = 0;
+ } else
+ buffer->silence_bytes -= sizetocopy;
- start = wavexferbuf->xferpos;
+ sizetocopy_now = buffer->size - start;
+
+ spin_unlock_irqrestore(&woinst->lock, flags);
if (sizetocopy > sizetocopy_now) {
sizetocopy -= sizetocopy_now;
- wavexferbuf->xferpos = sizetocopy;
- wavexferbuf->silence_xferpos = wavexferbuf->xferpos;
- spin_unlock_irqrestore(&woinst->lock, flags);
-
- copy_block(start, data, sizetocopy_now, wavexferbuf->xferbuffer);
- copy_block(0, data + sizetocopy_now, sizetocopy, wavexferbuf->xferbuffer);
+ copy_block(buffer->addr, start, data, sizetocopy_now);
+ copy_block(buffer->addr, 0, data + sizetocopy_now, sizetocopy);
} else {
- if (sizetocopy == sizetocopy_now)
- wavexferbuf->xferpos = 0;
- else
- wavexferbuf->xferpos += sizetocopy;
-
- wavexferbuf->silence_xferpos = wavexferbuf->xferpos;
- spin_unlock_irqrestore(&woinst->lock, flags);
-
- copy_block(start, data, sizetocopy, wavexferbuf->xferbuffer);
+ copy_block(buffer->addr, start, data, sizetocopy);
}
return;
@@ -672,84 +452,63 @@
void emu10k1_waveout_fillsilence(struct woinst *woinst)
{
- struct wave_out *wave_out = woinst->wave_out;
- struct wave_xferbuf *wavexferbuf = wave_out->wavexferbuf;
+ struct waveout_buffer *buffer = &woinst->buffer;
u16 filldata;
u32 sizetocopy, sizetocopy_now, start;
unsigned long flags;
- sizetocopy = wave_out->callbacksize;
+ sizetocopy = woinst->buffer.fragment_size;
- if (wave_out->wave_fmt.bitsperchannel == 8)
- filldata = 0x8080;
- else
+ if (woinst->format.bitsperchannel == 16)
filldata = 0x0000;
+ else
+ filldata = 0x8080;
spin_lock_irqsave(&woinst->lock, flags);
+ buffer->silence_bytes += sizetocopy;
+ buffer->bytestocopy -= sizetocopy;
+ buffer->silence_pos %= buffer->size;
+ start = buffer->silence_pos;
+ buffer->silence_pos += sizetocopy;
+ sizetocopy_now = buffer->size - start;
- sizetocopy_now = wavexferbuf->xferbufsize - wavexferbuf->silence_xferpos;
- start = wavexferbuf->silence_xferpos;
+ spin_unlock_irqrestore(&woinst->lock, flags);
if (sizetocopy > sizetocopy_now) {
sizetocopy -= sizetocopy_now;
- wavexferbuf->silence_xferpos = sizetocopy;
- spin_unlock_irqrestore(&woinst->lock, flags);
- fill_block(start, filldata, sizetocopy_now, wavexferbuf->xferbuffer);
- fill_block(0, filldata, sizetocopy, wavexferbuf->xferbuffer);
+ fill_block(buffer->addr, start, filldata, sizetocopy_now);
+ fill_block(buffer->addr, 0, filldata, sizetocopy);
} else {
- if (sizetocopy == sizetocopy_now)
- wavexferbuf->silence_xferpos = 0;
- else
- wavexferbuf->silence_xferpos += sizetocopy;
-
- spin_unlock_irqrestore(&woinst->lock, flags);
-
- fill_block(start, filldata, sizetocopy, wavexferbuf->xferbuffer);
+ fill_block(buffer->addr, start, filldata, sizetocopy);
}
return;
}
-/* get the specified control value of the wave device. */
-
-int emu10k1_waveout_getcontrol(struct wave_out *wave_out, u32 ctrl_id, u32 * value)
+void emu10k1_waveout_update(struct woinst *woinst)
{
- switch (ctrl_id) {
- case WAVECURPOS:
- /* There is no actual start yet */
- if (wave_out->state == CARDWAVE_STATE_STOPPED) {
- if (wave_out->setpos)
- *value = wave_out->position;
- else
- *value = wave_out->wavexferbuf->stopposition * wave_out->wavexferbuf->bytespersample;
- } else {
- emu10k1_voice_getcontrol(wave_out->voice, CCCA_CURRADDR, value);
-
- *value -= wave_out->voice->params.start;
+ u32 hw_pos;
+ u32 diff;
- /* Get number of bytes in play buffer per channel.
- * If 8 bit mode is enabled, this needs to be changed. */
- {
- u32 samples = 64 * (wave_out->wavexferbuf->is_stereo + 1);
-
- *value *= wave_out->wavexferbuf->bytespersample;
-
- /* Refer to voicemgr.c, CA is not started at zero.
- * We need to take this into account. */
-
- samples -= 4 * (wave_out->wavexferbuf->is_16bit + 1);
+ /* There is no actual start yet */
+ if (!(woinst->state & WAVE_STATE_STARTED)) {
+ hw_pos = woinst->buffer.hw_pos;
+ } else {
+ /* hw_pos in sample units */
+ hw_pos = sblive_readptr(woinst->voice.card, CCCA_CURRADDR, woinst->voice.num);
- if (*value >= samples)
- *value -= samples;
- else
- *value += wave_out->wavexferbuf->xferbufsize - samples;
- }
- }
+ if(hw_pos < woinst->voice.start)
+ hw_pos += woinst->buffer.size / woinst->format.bytespersample - woinst->voice.start;
+ else
+ hw_pos -= woinst->voice.start;
- break;
- default:
- return CTSTATUS_ERROR;
+ hw_pos *= woinst->format.bytespersample;
}
- return CTSTATUS_SUCCESS;
+ diff = (woinst->buffer.size + hw_pos - woinst->buffer.hw_pos) % woinst->buffer.size;
+ woinst->total_played += diff;
+ woinst->buffer.bytestocopy += diff;
+ woinst->buffer.hw_pos = hw_pos;
+
+ return;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)