ftp.nice.ch/pub/next/tools/emulators/vice.0.15.0.NeXT.sd.tgz#/vice-0.15.0/src/sounddrv/soundsb.c

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.