This is soundsb.c in view mode; [Download] [Up]
/*
* soundsb.c - Implementation of the Sound Blaster sound device.
*
* Written by
* Ettore Perazzoli (ettore@comm2000.it)
*
* This file is part of VICE, the Versatile Commodore Emulator.
* See README for copyright notice.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA.
*
*/
/* Warning: this could be optimized a lot. */
#include "vice.h"
#include <stdio.h>
#include <allegro.h> /* Must come after <stdio.h>. */
#include <go32.h>
#include <dpmi.h>
#include <dos.h>
#include <sys/farptr.h>
#include "sound.h"
#include "utils.h"
#include "vicesb.h"
/* ------------------------------------------------------------------------- */
/* Circular buffer for streaming. */
#define MAX_AUDIO_BUFFER_SIZE 0x20000
static BYTE audio_buffer[MAX_AUDIO_BUFFER_SIZE];
/* Size of the audio buffer (bytes). */
static unsigned int audio_buffer_size;
/* Number of bytes pending in the streaming buffer. */
static volatile unsigned int num_bytes_in_buffer;
/* Pointer to the first sample waiting to be played in the buffer. */
static volatile unsigned int first_sample;
/* Pointer to the place where the next streamed sample must to be put. */
static unsigned int next_sample;
/* Fragment size (bytes). */
static unsigned int fragment_size;
/* Flag: have we already detected the soundcard? */
static int detect_done;
/* Flag: are we running in 16bit mode? */
static int is_16bit;
/* Last sample played by the SB interrupt. */
static WORD last_played_sample;
/* ------------------------------------------------------------------------- */
static void interrupt_function(unsigned long buf)
{
if (num_bytes_in_buffer < fragment_size) {
int i;
/* Underflowing: repeat the last sample. */
_farsetsel(_dos_ds);
if (is_16bit) {
for (i = 0; i < fragment_size / 2; i++) {
_farnspokew(buf, last_played_sample);
buf += 2;
}
} else {
for (i = 0; i < fragment_size; i++) {
_farnspokeb(buf, (BYTE) last_played_sample);
buf++;
}
}
} else {
/* Play one fragment. */
movedata(_my_ds(), (unsigned long) (audio_buffer + first_sample),
_dos_ds, buf, fragment_size);
if (is_16bit)
last_played_sample = *((WORD *)(audio_buffer + first_sample +
fragment_size) - 1);
else
last_played_sample = *((BYTE *)(audio_buffer + first_sample +
fragment_size) - 1);
num_bytes_in_buffer -= fragment_size;
first_sample += fragment_size;
if (first_sample >= audio_buffer_size)
first_sample = 0;
}
}
static void interrupt_function_end(void) {}
static void lock_mem(void)
{
_go32_dpmi_lock_code((void *) interrupt_function,
((unsigned long) interrupt_function_end
- (unsigned long) interrupt_function));
_go32_dpmi_lock_data((void *) audio_buffer, MAX_AUDIO_BUFFER_SIZE);
_go32_dpmi_lock_data((void *) &audio_buffer_size, sizeof(audio_buffer_size));
_go32_dpmi_lock_data((void *) &num_bytes_in_buffer, sizeof(num_bytes_in_buffer));
_go32_dpmi_lock_data((void *) &first_sample, sizeof(first_sample));
_go32_dpmi_lock_data((void *) &next_sample, sizeof(next_sample));
_go32_dpmi_lock_data((void *) &fragment_size, sizeof(fragment_size));
_go32_dpmi_lock_data((void *) &detect_done, sizeof(detect_done));
_go32_dpmi_lock_data((void *) &is_16bit, sizeof(is_16bit));
}
static int sb_init(warn_t *w, char *param, int *speed,
int *fragsize, int *fragnr, double bufsize)
{
int tmp_fragsize = *fragsize;
if (!detect_done) {
if (!vicesb_detect(&is_16bit))
return -1;
detect_done = 1;
printf("SB Detected; using %s bits\n", is_16bit ? "16" : "8");
}
if (is_16bit)
tmp_fragsize *= 2;
/* XXX: We assume we are given a power of 2 as the `fragsize'. */
if (!vicesb_init(speed, &tmp_fragsize, interrupt_function))
return -1;
vicesb_set_volume(255);
#if 0 /* (not sure this is a good thing) */
/* If the fragment size is larger than requested, reduce the number of
fragments accordingly. */
if (tmp_fragsize > *fragsize) {
int new_fragnr = (int) ((double) *fragnr * ((double) *fragsize
/ (double) *tmp_fragsize));
if (new_fragnr > 0) {
printf("%s(): Adjusting fragnr from %d to %d\n",
*fragnr, new_fragnr);
*fragnr = new_fragnr;
}
}
#endif
if (is_16bit)
*fragsize = tmp_fragsize / 2;
else
*fragsize = tmp_fragsize;
fragment_size = tmp_fragsize;
audio_buffer_size = *fragnr * fragment_size;
printf("%s(): fragment_size = %d, *fragnr = %d, audio_buffer_size = %d\n",
__FUNCTION__, fragment_size, *fragnr, audio_buffer_size);
/* FIXME: Check for audio buffer size. */
num_bytes_in_buffer = 0;
first_sample = 0;
next_sample = 0;
last_played_sample = is_16bit ? 0x0 : 0x80;
return 0;
}
static int sb_write(warn_t *w, SWORD *pbuf, int nr)
{
int total;
if (audio_buffer_size == 0)
return -1;
if (nr == 0)
return 0;
if (is_16bit)
total = nr * 2;
else
total = nr;
/* XXX: We only allow writing full fragments here. */
while (total >= fragment_size) {
/* Block until there is space in the buffer. The interrupt routine
will decrement `num_bytes_in_buffer' as soon as a new fragment is
played. */
while (num_bytes_in_buffer == audio_buffer_size)
;
/* Disable interrupts to make sure we don't interfere with the
playback routine in ugly ways. */
asm volatile ("cli");
/* Write one fragment. */
if (!is_16bit) {
BYTE *p = audio_buffer + next_sample;
int i;
for (i = 0; i < fragment_size; i++)
*(p++) = (*(pbuf++) >> 8) + 0x80;
} else {
SWORD *p = (SWORD *) (audio_buffer + next_sample);
int i;
for (i = 0; i < fragment_size / 2; i++)
*(p++) = *(pbuf++);
}
num_bytes_in_buffer += fragment_size;
/* Done writing the fragment. Enable interrupts. */
asm volatile("sti");
next_sample += fragment_size;
if (next_sample >= audio_buffer_size)
next_sample = 0;
total -= fragment_size;
}
if (total != 0)
printf("%s: Argh, we have a problem! total = %d\n",
__FUNCTION__, total);
return 0;
}
static int sb_bufferstatus(warn_t *s, int first)
{
int ret;
if (first)
return 0;
ret = num_bytes_in_buffer;
if (is_16bit)
ret /= 2;
return ret;
}
static void sb_close(warn_t *w)
{
vicesb_close();
audio_buffer_size = 0;
num_bytes_in_buffer = 0;
first_sample = 0;
next_sample = 0;
fragment_size = 0;
}
#if 0
static int sb_suspend(warn_t *w)
{
return 0;
}
static int sb_resume(warn_t *w)
{
return 0;
}
#endif
static sound_device_t sb_device =
{
"sb",
sb_init,
sb_write,
NULL,
NULL,
sb_bufferstatus,
sb_close,
NULL,
NULL
};
int sound_init_sb_device(void)
{
printf("Initializing SB sound device.\n");
return sound_register_device(&sb_device);
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.