diff --git a/src/main.c b/src/main.c index 918b370..b4efd53 100644 --- a/src/main.c +++ b/src/main.c @@ -20,12 +20,15 @@ #include "image.h" #include "palette.h" #include "package.h" +#include "soundblaster.h" +#include "mixer.h" static lua_State *L; static void deinit(void) { /* Deinit and clear up everything. Called at exit */ + soundblaster_deinit(); vga_deinit(); keyboard_deinit(); lua_close(L); @@ -59,6 +62,7 @@ int main(int argc, char **argv) { palette_init(); keyboard_init(); mouse_init(); + soundblaster_init(mixer_getNextBlock); /* Init lua */ L = luaL_newstate(); diff --git a/src/mixer.c b/src/mixer.c new file mode 100644 index 0000000..41acef1 --- /dev/null +++ b/src/mixer.c @@ -0,0 +1,90 @@ +#include +#include +#include +#include "mixer.h" +#include "soundblaster.h" + +#define MIXER_MAX_SOUNDS 8 + +typedef struct { + int offset; + sound_t const *sound; +} mixed_sound_t; + +static mixed_sound_t sounds[MIXER_MAX_SOUNDS]; +static int activeSounds = 0; +static int16_t data[SOUNDBLASTER_SAMPLES_PER_BUFFER] = {0}; +static bool canmix = true; + +void mixer_init(void) { +} + +int16_t const * mixer_getNextBlock(void) { + canmix = true; + return data; +} + + +void mixer_play(sound_t const *sound) { + if(activeSounds == MIXER_MAX_SOUNDS) { + // TODO Replace older sound with new one instead? + return; + } + + for(int i = 0; i < activeSounds; ++i) { + if(sounds[i].sound == sound) { + sounds[i].offset = 0; + return; + } + } + + sounds[activeSounds].offset = 0; + sounds[activeSounds].sound = sound; + ++activeSounds; +} + +static inline int16_t mix(int32_t a, int32_t b) { + int32_t res = a + b; + if(res > INT16_MAX) + res = INT16_MAX; + if(res < INT16_MIN) + res = INT16_MIN; + return res; +} + + +void mixer_mix(void) { + if(!canmix) + return; + + memset(data, 0, SOUNDBLASTER_SAMPLES_PER_BUFFER); + + for(int i = 0; i < activeSounds; ++i) { + mixed_sound_t *snd = sounds + i; + int len = snd->sound->sampleCount; + int16_t const* soundBuf = snd->sound->samples; + + if(len > SOUNDBLASTER_SAMPLES_PER_BUFFER) { + len = SOUNDBLASTER_SAMPLES_PER_BUFFER; + } + + for(int offset = 0; offset < len; ++offset) { + data[offset] = mix(data[offset], soundBuf[offset]); + } + + snd->offset += len; + + + } + + + for(int i = activeSounds-1; i >= 0; --i) { + mixed_sound_t *snd = sounds + i; + if(snd->offset == snd->sound->sampleCount) { + *snd = sounds[activeSounds-1]; + --activeSounds; + } + } + + canmix = false; +} diff --git a/src/mixer.h b/src/mixer.h new file mode 100644 index 0000000..75ecb9d --- /dev/null +++ b/src/mixer.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include "sound.h" + + +void mixer_init(void); +void mixer_deinit(void); +int16_t const* mixer_getNextBlock(void); +void mixer_play(sound_t const *sound); +void mixer_mix(void); diff --git a/src/sound.c b/src/sound.c new file mode 100644 index 0000000..0bb7e4a --- /dev/null +++ b/src/sound.c @@ -0,0 +1,15 @@ +#include +#include "sound.h" +#include "lib/dmt/dmt.h" + +int sound_init(sound_t *self, int samples) { + self->sampleCount = samples; + self->samples = (int16_t*)dmt_malloc(samples * sizeof(int16_t)); + return 0; +} + + +void sound_deinit(sound_t *self) { + dmt_free(self->samples); +} + diff --git a/src/sound.h b/src/sound.h new file mode 100644 index 0000000..99f4670 --- /dev/null +++ b/src/sound.h @@ -0,0 +1,14 @@ +#ifndef SOUND_H +#define SOUND_H + + +typedef struct { + int sampleCount; + int16_t *samples; +} sound_t; + +int sound_init(sound_t *self, int samples); +void sound_deinit(sound_t *self); + + +#endif diff --git a/src/soundblaster.c b/src/soundblaster.c index 285b1ff..c2ac979 100644 --- a/src/soundblaster.c +++ b/src/soundblaster.c @@ -44,6 +44,8 @@ static uint16_t dmaChannel; #define BLASTER_PROGRAM_STEREO 0x20 #define BLASTER_PROGRAM_SIGNED 0x10 #define BLASTER_SPEAKER_ON_CMD 0xD1 +#define BLASTER_SPEAKER_OFF_CMD 0xD3 +#define BLASTER_EXIT_AUTO_DMA 0xD9 // PIC @@ -78,8 +80,10 @@ static const struct { { 0xC8, 0xCA, 0xD4, 0xD6, 0xD8, 0x89 }, { 0xCC, 0xCE, 0xD4, 0xD6, 0xD8, 0x8A } }; +int stopDma = 0; // ISR data +int isrInstalled = 0; static _go32_dpmi_seginfo oldBlasterHandler, newBlasterHandler; int writePage = 0; @@ -87,14 +91,13 @@ static soundblaster_getSampleProc getSamples; static void writeDSP(uint8_t value) { while((inportb(baseAddress + BLASTER_WRITE_PORT) & - BLASTER_WRITE_BUFFER_STATUS_UNAVAIL) != 0) - ; + BLASTER_WRITE_BUFFER_STATUS_UNAVAIL) != 0) {} outportb(baseAddress + BLASTER_WRITE_PORT, value); } -static int resetBlaster() { +static int resetBlaster(void) { for(int j = 0; j < 1000; ++j) { outportb(baseAddress + BLASTER_RESET_PORT, 1); delay(1); @@ -112,19 +115,24 @@ static int resetBlaster() { } -static void soundblasterISR() { - outportb(baseAddress + BLASTER_MIXER_OUT_PORT, - BLASTER_MIXER_INTERRUPT_STATUS); - uint8_t status = inportb(baseAddress + BLASTER_MIXER_IN_PORT); +static void soundblasterISR(void) { + if(stopDma == 1) { + writeDSP(BLASTER_EXIT_AUTO_DMA); + stopDma = 2; + } else { + outportb(baseAddress + BLASTER_MIXER_OUT_PORT, + BLASTER_MIXER_INTERRUPT_STATUS); + uint8_t status = inportb(baseAddress + BLASTER_MIXER_IN_PORT); - if(status & BLASTER_16BIT_INTERRUPT) { - uint8_t* dst = (uint8_t*)(sampleBuffer) - + writePage * SAMPLE_BUFFER_SIZE / 2; + if(status & BLASTER_16BIT_INTERRUPT) { + uint8_t* dst = (uint8_t*)(sampleBuffer) + + writePage * SAMPLE_BUFFER_SIZE / 2; - memcpy(dst, getSamples(), SAMPLE_BUFFER_SIZE / 2); + memcpy(dst, getSamples(), SAMPLE_BUFFER_SIZE / 2); - writePage = 1 - writePage; - inportb(baseAddress + BLASTER_INTERRUPT_ACKNOWLEDGE_16BIT); + writePage = 1 - writePage; + inportb(baseAddress + BLASTER_INTERRUPT_ACKNOWLEDGE_16BIT); + } } if(irq >= 8) { @@ -134,7 +142,7 @@ static void soundblasterISR() { } -static void setBlasterISR() { +static void setBlasterISR(void) { // Map IRQ to interrupt number on the CPU uint16_t interruptVector = irq + irq + (irq < 8) ? PIC_IRQ07_MAP @@ -156,6 +164,8 @@ static void setBlasterISR() { uint8_t irqmask = inportb(PIC2_DATA); outportb(PIC2_DATA, irqmask & ~(1<<(irq-8))); } + + isrInstalled = 1; } @@ -175,7 +185,7 @@ static int parseBlasterSettings(void) { } -static int allocSampleBuffer() { +static int allocSampleBuffer(void) { static int maxRetries = 10; int selectors[maxRetries]; int current; @@ -211,11 +221,16 @@ static int allocSampleBuffer() { } -static void turnSpeakerOn() { +static void turnSpeakerOn(void) { writeDSP(BLASTER_SPEAKER_ON_CMD); } +static void turnSpeakerOff(void) { + writeDSP(BLASTER_SPEAKER_OFF_CMD); +} + + static void dmaSetupTransfer(int channel, uint8_t direction, bool autoReload, @@ -252,7 +267,7 @@ static void dmaSetupTransfer(int channel, } -static void startDMAOutput() { +static void startDMAOutput(void) { uint32_t offset = (uint32_t)sampleBuffer; uint32_t samples = SAMPLE_BUFFER_SIZE / sizeof(int16_t); @@ -302,6 +317,33 @@ int soundblaster_init(soundblaster_getSampleProc getsamplesproc) { } -void soundblaster_deinit(void) { - +static void deallocSampleBuffer(void) { + __dpmi_free_dos_memory(sampleBufferSelector); +} + + +static void resetBlasterISR(void) { + if(isrInstalled == 1) { + uint16_t interruptVector = irq + irq + (irq < 8) + ? PIC_IRQ07_MAP + : PIC_IRQ8F_MAP; + + _go32_dpmi_set_protected_mode_interrupt_vector(interruptVector, &oldBlasterHandler); + isrInstalled = 0; + } +} + + +static void stopDMAOutput(void) { + stopDma = 1; + while(stopDma == 1) {} +} + + +void soundblaster_deinit(void) { + turnSpeakerOff(); + stopDMAOutput(); + resetBlaster(); + resetBlasterISR(); + deallocSampleBuffer(); }