Basic output working

This commit is contained in:
rnlf
2017-01-22 19:55:59 +01:00
parent d14cf3ac74
commit b954b0ff2d
17 changed files with 274 additions and 5579 deletions

View File

@@ -104,7 +104,7 @@ function love.run()
love.graphics.clear() love.graphics.clear()
if love.draw then love.draw() end if love.draw then love.draw() end
love.graphics.present() love.graphics.present()
love.mixer.mix() love.sound.mix()
end end
end end

File diff suppressed because it is too large Load Diff

View File

@@ -22,10 +22,10 @@ typedef struct {
/* Each mask should consist of its unique bit and the unique bit of all its /* Each mask should consist of its unique bit and the unique bit of all its
* super classes */ * super classes */
#define LUAOBJ_TYPE_IMAGE (1 << 0) #define LUAOBJ_TYPE_IMAGE (1 << 0)
#define LUAOBJ_TYPE_QUAD (1 << 1) #define LUAOBJ_TYPE_QUAD (1 << 1)
#define LUAOBJ_TYPE_FONT (1 << 2) #define LUAOBJ_TYPE_FONT (1 << 2)
#define LUAOBJ_TYPE_SOUND (1 << 3) #define LUAOBJ_TYPE_SOURCE (1 << 3)
int luaobj_newclass(lua_State *L, const char *name, const char *extends, int luaobj_newclass(lua_State *L, const char *name, const char *extends,

View File

@@ -58,11 +58,11 @@ int main(int argc, char **argv) {
/* Init everything */ /* Init everything */
atexit(deinit); atexit(deinit);
vga_init(); soundblaster_init(mixer_getNextBlock);
//vga_init();
palette_init(); palette_init();
keyboard_init(); keyboard_init();
mouse_init(); mouse_init();
soundblaster_init(mixer_getNextBlock);
/* Init lua */ /* Init lua */
L = luaL_newstate(); L = luaL_newstate();

View File

@@ -4,45 +4,50 @@
#include "mixer.h" #include "mixer.h"
#include "soundblaster.h" #include "soundblaster.h"
#define MIXER_MAX_SOUNDS 8 #define MIXER_MAX_SOURCES 8
typedef struct { typedef struct {
int offset; int offset;
sound_t const *sound; source_t const *source;
} mixed_sound_t; } mixed_sound_t;
static mixed_sound_t sounds[MIXER_MAX_SOUNDS];
static int activeSounds = 0; static mixed_sound_t sources[MIXER_MAX_SOURCES];
static int activeSources = 0;
static int16_t data[SOUNDBLASTER_SAMPLES_PER_BUFFER] = {0}; static int16_t data[SOUNDBLASTER_SAMPLES_PER_BUFFER] = {0};
static bool canmix = true; static bool canmix = true;
void mixer_init(void) { void mixer_init(void) {
} }
int16_t const * mixer_getNextBlock(void) { int16_t const * mixer_getNextBlock(void) {
canmix = true; canmix = true;
return data; return data;
} }
void mixer_play(sound_t const *sound) { void mixer_play(source_t const *source) {
if(activeSounds == MIXER_MAX_SOUNDS) { if(activeSources == MIXER_MAX_SOURCES) {
// TODO Replace older sound with new one instead? // TODO Replace older source with new one instead?
return; return;
} }
for(int i = 0; i < activeSounds; ++i) { for(int i = 0; i < activeSources; ++i) {
if(sounds[i].sound == sound) { if(sources[i].source == source) {
sounds[i].offset = 0; sources[i].offset = 0;
return; return;
} }
} }
sounds[activeSounds].offset = 0; sources[activeSources].offset = 0;
sounds[activeSounds].sound = sound; sources[activeSources].source = source;
++activeSounds; ++activeSources;
} }
static inline int16_t mix(int32_t a, int32_t b) { static inline int16_t mix(int32_t a, int32_t b) {
int32_t res = a + b; int32_t res = a + b;
if(res > INT16_MAX) if(res > INT16_MAX)
@@ -59,30 +64,28 @@ void mixer_mix(void) {
memset(data, 0, SOUNDBLASTER_SAMPLES_PER_BUFFER); memset(data, 0, SOUNDBLASTER_SAMPLES_PER_BUFFER);
for(int i = 0; i < activeSounds; ++i) { for(int i = 0; i < activeSources; ++i) {
mixed_sound_t *snd = sounds + i; mixed_sound_t *snd = sources + i;
int len = snd->sound->sampleCount; int len = snd->source->sampleCount;
int16_t const* soundBuf = snd->sound->samples; int16_t const* sourceBuf = snd->source->samples;
if(len > SOUNDBLASTER_SAMPLES_PER_BUFFER) { if(len > SOUNDBLASTER_SAMPLES_PER_BUFFER) {
len = SOUNDBLASTER_SAMPLES_PER_BUFFER; len = SOUNDBLASTER_SAMPLES_PER_BUFFER;
} }
for(int offset = 0; offset < len; ++offset) { for(int offset = 0; offset < len; ++offset) {
data[offset] = mix(data[offset], soundBuf[offset]); data[offset] = mix(data[offset], sourceBuf[offset]);
} }
snd->offset += len; snd->offset += len;
} }
for(int i = activeSounds-1; i >= 0; --i) { for(int i = activeSources-1; i >= 0; --i) {
mixed_sound_t *snd = sounds + i; mixed_sound_t *snd = sources + i;
if(snd->offset == snd->sound->sampleCount) { if(snd->offset == snd->source->sampleCount) {
*snd = sounds[activeSounds-1]; *snd = sources[activeSources-1];
--activeSounds; --activeSources;
} }
} }

View File

@@ -1,11 +1,11 @@
#pragma once #pragma once
#include <stdint.h> #include <stdint.h>
#include "sound.h" #include "source.h"
void mixer_init(void); void mixer_init(void);
void mixer_deinit(void); void mixer_deinit(void);
int16_t const* mixer_getNextBlock(void); int16_t const* mixer_getNextBlock(void);
void mixer_play(sound_t const *sound); void mixer_play(source_t const *source);
void mixer_mix(void); void mixer_mix(void);

View File

@@ -26,8 +26,8 @@ int luaopen_graphics(lua_State *L);
int luaopen_timer(lua_State *L); int luaopen_timer(lua_State *L);
int luaopen_keyboard(lua_State *L); int luaopen_keyboard(lua_State *L);
int luaopen_mouse(lua_State *L); int luaopen_mouse(lua_State *L);
int luaopen_mixer(lua_State *L);
int luaopen_sound(lua_State *L); int luaopen_sound(lua_State *L);
int luaopen_source(lua_State *L);
int luaopen_love(lua_State *L) { int luaopen_love(lua_State *L) {
int i; int i;
@@ -38,6 +38,7 @@ int luaopen_love(lua_State *L) {
luaopen_image, luaopen_image,
luaopen_quad, luaopen_quad,
luaopen_font, luaopen_font,
luaopen_source,
NULL, NULL,
}; };
for (i = 0; classes[i]; i++) { for (i = 0; classes[i]; i++) {
@@ -61,8 +62,7 @@ int luaopen_love(lua_State *L) {
{ "timer", luaopen_timer }, { "timer", luaopen_timer },
{ "keyboard", luaopen_keyboard }, { "keyboard", luaopen_keyboard },
{ "mouse", luaopen_mouse }, { "mouse", luaopen_mouse },
{ "mixer", luaopen_mixer }, { "sound", luaopen_sound },
{ "sound", luaopen_mixer },
{ 0 }, { 0 },
}; };
for (i = 0; mods[i].name; i++) { for (i = 0; mods[i].name; i++) {

View File

@@ -1,21 +0,0 @@
#include "mixer.h"
#include "luaobj.h"
int l_mixer_mix(lua_State *L) {
mixer_mix();
return 0;
}
int luaopen_mixer(lua_State *L) {
luaL_Reg reg[] = {
{ "mix", l_mixer_mix },
{ 0, 0 }
};
luaL_newlib(L, reg);
return 1;
}

View File

@@ -1,47 +1,23 @@
#include "luaobj.h"
#include "sound.h"
#include "mixer.h" #include "mixer.h"
#include "luaobj.h"
#define CLASS_TYPE LUAOBJ_TYPE_SOUND int l_sound_mix(lua_State *L) {
#define CLASS_NAME "Sound" mixer_mix();
int l_sound_new(lua_State *L) {
const char *filename = luaL_checkstring(L, 1);
sound_t *self = luaobj_newudata(L, sizeof(*self));
luaobj_setclass(L, CLASS_TYPE, CLASS_NAME);
const char *err = sound_init(self, filename);
if (err) luaL_error(L, err);
return 1;
}
int l_sound_gc(lua_State *L) {
sound_t *self = luaobj_checkudata(L, 1, CLASS_TYPE);
sound_deinit(self);
return 0;
}
int l_sound_play(lua_State *L) {
sound_t *self = luaobj_checkudata(L, 1, CLASS_TYPE);
mixer_play(self);
return 0; return 0;
} }
int l_source_new(lua_State *L);
int luaopen_sound(lua_State *L) { int luaopen_sound(lua_State *L) {
luaL_Reg reg[] = { luaL_Reg reg[] = {
{ "new", l_sound_new }, { "mix", l_sound_mix },
{ "__gc", l_sound_gc }, { "newSource", l_source_new },
{ "play", l_sound_play },
{ 0, 0 } { 0, 0 }
}; };
luaobj_newclass(L, CLASS_NAME, NULL, l_sound_new, reg); luaL_newlib(L, reg);
return 1; return 1;
} }

47
src/modules/l_source.c Normal file
View File

@@ -0,0 +1,47 @@
#include "luaobj.h"
#include "source.h"
#include "mixer.h"
#define CLASS_TYPE LUAOBJ_TYPE_SOURCE
#define CLASS_NAME "Source"
int l_source_new(lua_State *L) {
const char *filename = luaL_checkstring(L, 1);
source_t *self = luaobj_newudata(L, sizeof(*self));
luaobj_setclass(L, CLASS_TYPE, CLASS_NAME);
const char *err = source_init(self, filename);
if (err) luaL_error(L, err);
return 1;
}
int l_source_gc(lua_State *L) {
source_t *self = luaobj_checkudata(L, 1, CLASS_TYPE);
source_deinit(self);
return 0;
}
int l_source_play(lua_State *L) {
source_t *self = luaobj_checkudata(L, 1, CLASS_TYPE);
mixer_play(self);
return 0;
}
int luaopen_source(lua_State *L) {
luaL_Reg reg[] = {
{ "new", l_source_new },
{ "__gc", l_source_gc },
{ "play", l_source_play },
{ 0, 0 }
};
luaobj_newclass(L, CLASS_NAME, NULL, l_source_new, reg);
return 1;
}

View File

@@ -1,51 +0,0 @@
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include "sound.h"
#include "lib/dmt/dmt.h"
#define STB_VORBIS_HEADER_ONLY 1
#include "lib/stb/stb_vorbis.c"
int sound_initSilence(sound_t *self, int samples) {
self->sampleCount = samples;
self->samples = (int16_t*)dmt_malloc(samples * sizeof(int16_t));
return 0;
}
char const* sound_init(sound_t *self, char const* filename) {
short *buffer;
int channels;
int samplingRate;
char const* err = NULL;
int len = stb_vorbis_decode_filename(filename, &channels, &samplingRate, &buffer);
if(len == -1) {
return "could not decode Vorbis file";
}
if(channels != 1) {
err = "only single channel audio files supported";
goto fail;
}
if(samplingRate != 22050) {
err = "only 22050Hz audio files suported";
goto fail;
}
int bufSize = len * sizeof(int16_t);
self->samples = (int16_t*)dmt_malloc(bufSize);
memcpy(self->samples, buffer, bufSize);
fail:
free(buffer);
return err;
}
void sound_deinit(sound_t *self) {
dmt_free(self->samples);
}

View File

@@ -1,19 +0,0 @@
#ifndef SOUND_H
#define SOUND_H
#define SOUND_ERROR_DECODE 1
#define SOUND_ERROR_CHANNEL_COUNT 2
#define SOUND_ERROR_SAMPLING_RATE 3
typedef struct {
int sampleCount;
int16_t *samples;
} sound_t;
int sound_initSilence(sound_t *self, int samples);
char const* sound_init(sound_t *self, char const* filename);
void sound_deinit(sound_t *self);
#endif

View File

@@ -16,6 +16,7 @@ static int sampleBufferSelector;
static uint16_t baseAddress; static uint16_t baseAddress;
static uint16_t irq; static uint16_t irq;
static uint16_t dmaChannel; static uint16_t dmaChannel;
int isrcount = 0;
#define SAMPLE_BUFFER_SIZE (SOUNDBLASTER_SAMPLES_PER_BUFFER * sizeof(uint16_t) * 2) #define SAMPLE_BUFFER_SIZE (SOUNDBLASTER_SAMPLES_PER_BUFFER * sizeof(uint16_t) * 2)
@@ -89,6 +90,7 @@ int writePage = 0;
static soundblaster_getSampleProc getSamples; static soundblaster_getSampleProc getSamples;
static void writeDSP(uint8_t value) { static void writeDSP(uint8_t value) {
while((inportb(baseAddress + BLASTER_WRITE_PORT) & while((inportb(baseAddress + BLASTER_WRITE_PORT) &
BLASTER_WRITE_BUFFER_STATUS_UNAVAIL) != 0) {} BLASTER_WRITE_BUFFER_STATUS_UNAVAIL) != 0) {}
@@ -97,34 +99,41 @@ static void writeDSP(uint8_t value) {
} }
static uint8_t readDSP() {
uint8_t status;
while(((status = inportb(baseAddress + BLASTER_READ_BUFFER_STATUS_PORT))
& BLASTER_READ_BUFFER_STATUS_AVAIL) == 0) {
}
return inportb(baseAddress + BLASTER_READ_PORT);
}
static int resetBlaster(void) { static int resetBlaster(void) {
for(int j = 0; j < 1000; ++j) { for(int j = 0; j < 1000; ++j) {
outportb(baseAddress + BLASTER_RESET_PORT, 1); outportb(baseAddress + BLASTER_RESET_PORT, 1);
delay(1); delay(1);
outportb(baseAddress + BLASTER_RESET_PORT, 0); outportb(baseAddress + BLASTER_RESET_PORT, 0);
uint8_t status;
while(((status = inportb(baseAddress + BLASTER_READ_BUFFER_STATUS_PORT)) if(readDSP() == BLASTER_READY_BYTE) {
& BLASTER_READ_BUFFER_STATUS_AVAIL) == 0)
;
if(inportb(baseAddress + BLASTER_READ_PORT) == BLASTER_READY_BYTE)
return 0; return 0;
}
} }
return SOUNDBLASTER_RESET_ERROR; return SOUNDBLASTER_RESET_ERROR;
} }
static void soundblasterISR(void) { static void soundblasterISR(void) {
if(stopDma == 1) { outportb(baseAddress + BLASTER_MIXER_OUT_PORT,
writeDSP(BLASTER_EXIT_AUTO_DMA); BLASTER_MIXER_INTERRUPT_STATUS);
stopDma = 2; uint8_t status = inportb(baseAddress + BLASTER_MIXER_IN_PORT);
} 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) { isrcount++;
if(status & BLASTER_16BIT_INTERRUPT) {
if(stopDma == 1) {
writeDSP(BLASTER_EXIT_AUTO_DMA);
stopDma = 2;
} else {
uint8_t* dst = (uint8_t*)(sampleBuffer) uint8_t* dst = (uint8_t*)(sampleBuffer)
+ writePage * SAMPLE_BUFFER_SIZE / 2; + writePage * SAMPLE_BUFFER_SIZE / 2;
@@ -175,12 +184,19 @@ static int parseBlasterSettings(void) {
return SOUNDBLASTER_ENV_NOT_SET; return SOUNDBLASTER_ENV_NOT_SET;
} }
int res = sscanf(blasterEnv, "A%hu I%hu D%hu", &baseAddress, &irq, &dmaChannel); int res = sscanf(blasterEnv, "A%hx I%hu", &baseAddress, &irq);
if(res < 2) {
if(res < 3) {
return SOUNDBLASTER_ENV_INVALID; return SOUNDBLASTER_ENV_INVALID;
} }
// "H" field may be preceeded by any number of other fields, so let's just search it
char const *dmaLoc = strchr(blasterEnv, 'H');
if(dmaLoc == NULL) {
return SOUNDBLASTER_ENV_INVALID;
}
dmaChannel = atoi(dmaLoc+1);
return 0; return 0;
} }
@@ -241,13 +257,17 @@ static void dmaSetupTransfer(int channel,
uint8_t modeByte = direction uint8_t modeByte = direction
| mode | mode
| (uint8_t)autoReload << 4 | ((uint8_t)autoReload << 4)
| (uint8_t)down << 5 | ((uint8_t)down << 5)
| (channel & 0x03); | (channel & 0x03);
uint8_t maskEnable = (channel & 0x03) | 0x04; uint8_t maskEnable = (channel & 0x03) | 0x04;
uint32_t offset = startAddress; uint32_t offset = startAddress;
// Special handling of 16 bit DMA channels:
// The DMA controller needs offset and count to be half the actual value and
// internally doubles it again
if(channel > 3) { if(channel > 3) {
offset >>= 1; offset >>= 1;
count >>= 1; count >>= 1;
@@ -268,7 +288,7 @@ static void dmaSetupTransfer(int channel,
static void startDMAOutput(void) { static void startDMAOutput(void) {
uint32_t offset = (uint32_t)sampleBuffer; uint32_t offset = ((uint32_t)sampleBuffer) - __djgpp_conventional_base;
uint32_t samples = SAMPLE_BUFFER_SIZE / sizeof(int16_t); uint32_t samples = SAMPLE_BUFFER_SIZE / sizeof(int16_t);
@@ -295,24 +315,28 @@ int soundblaster_init(soundblaster_getSampleProc getsamplesproc) {
int err = parseBlasterSettings(); int err = parseBlasterSettings();
if(err != 0) { if(err != 0) {
fprintf(stderr, "BLASTER environment variable not set or invalid\n");
return err; return err;
} }
err = resetBlaster(); err = resetBlaster();
if(err != 0) { if(err != 0) {
fprintf(stderr, "Could not reset Soundblaster\n");
return err; return err;
} }
err = allocSampleBuffer(); err = allocSampleBuffer();
if(err != 0) { if(err != 0) {
fprintf(stderr, "Could not allocate sample buffer in conventional memory\n");
return err; return err;
} }
getSamples = getsamplesproc;
setBlasterISR(); setBlasterISR();
turnSpeakerOn(); turnSpeakerOn();
startDMAOutput(); startDMAOutput();
getSamples = getsamplesproc;
return 0; return 0;
} }

24
src/source.c Normal file
View File

@@ -0,0 +1,24 @@
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include "source.h"
#include "lib/dmt/dmt.h"
#include "wavefile.h"
int source_initSilence(source_t *self, int samples) {
self->sampleCount = samples;
self->samples = (int16_t*)dmt_malloc(samples * sizeof(int16_t));
return 0;
}
char const* source_init(source_t *self, char const* filename) {
return wavefile_load(self, filename);
}
void source_deinit(source_t *self) {
dmt_free(self->samples);
}

16
src/source.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef SOURCE_H
#define SOURCE_H
#include <stdint.h>
typedef struct {
int sampleCount;
int16_t *samples;
} source_t;
int source_initSilence(source_t *self, int samples);
char const* source_init(source_t *self, char const* filename);
void source_deinit(source_t *self);
#endif

90
src/wavefile.c Normal file
View File

@@ -0,0 +1,90 @@
#include <string.h>
#include "filesystem.h"
#include "wavefile.h"
#include "source.h"
#include "lib/dmt/dmt.h"
typedef struct {
uint32_t chunkId;
uint32_t chunkSize;
uint32_t format;
uint32_t subchunk1Id;
uint32_t subchunk1Size;
uint16_t audioFormat;
uint16_t numChannels;
uint32_t sampleRate;
uint32_t byteRate;
uint16_t blockAlign;
uint16_t bitsPerSample;
uint32_t subchunk2Id;
uint32_t subchunk2Size;
} waveheader_t;
#define RIFF_CHUNK_ID 0x46464952
#define WAVE_FORMAT 0x45564157
#define FORMAT_CHUNK_ID 0x20746d66
#define DATA_CHUNK_ID 0x61746164
#define FORMAT_CHUNK_SIZE 16
#define AUDIO_FORMAT_PCM 1
char const* wavefile_load(source_t *source, char const *filename) {
int fileSize;
char const *err = 0;
uint8_t *waveData = filesystem_read(filename, &fileSize);
waveheader_t const* header = (waveheader_t const*)waveData;
if(header->chunkId != RIFF_CHUNK_ID
|| header->format != WAVE_FORMAT
|| header->subchunk1Id != FORMAT_CHUNK_ID
|| header->subchunk2Id != DATA_CHUNK_ID) {
err = "Invalid WAV header";
goto fail;
}
if(header->subchunk1Size != FORMAT_CHUNK_SIZE
|| header->audioFormat != AUDIO_FORMAT_PCM) {
err = "Invalid Audio Format (only PCM supported)";
goto fail;
}
if(header->numChannels != 1) {
err = "Invalid Audio Format (only 1 channel supported)";
goto fail;
}
if(header->sampleRate != 22050) {
err = "Invalid Audio Format (only 22050Hz supported)";
goto fail;
}
if(header->bitsPerSample != 16) {
err = "Invalid Audio Format (only 16 Bit supported)";
goto fail;
}
if(header->byteRate != header->sampleRate * header->numChannels * header->bitsPerSample / 8) {
err = "Byte rate doesn't match header info";
goto fail;
}
if(header->blockAlign != header->numChannels * header->bitsPerSample / 8) {
err = "Block alignment doesn't match header info";
goto fail;
}
if(fileSize < sizeof(waveheader_t) + header->subchunk2Size) {
err = "Short read while reading samples";
goto fail;
}
source->sampleCount = header->subchunk2Size / 2;
source->samples = (int16_t*)dmt_malloc(header->subchunk2Size);
memcpy(source->samples, ((uint8_t*)waveData) + sizeof(waveheader_t), header->subchunk2Size);
fail:
filesystem_free(waveData);
return err;
}

5
src/wavefile.h Normal file
View File

@@ -0,0 +1,5 @@
#pragma once
#include "source.h"
char const* wavefile_load(source_t *source, char const *filename);