Merge pull request #5 from rnlf/master

Soundblaster 16 Driver
This commit is contained in:
rxi
2017-01-27 21:01:04 +00:00
committed by GitHub
14 changed files with 849 additions and 3 deletions

View File

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

View File

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

View File

@@ -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);
@@ -55,6 +58,7 @@ int main(int argc, char **argv) {
/* Init everything */
atexit(deinit);
soundblaster_init(mixer_getNextBlock);
vga_init();
palette_init();
keyboard_init();

97
src/mixer.c Normal file
View File

@@ -0,0 +1,97 @@
/**
* Copyright (c) 2017 Florian Kesseler
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the MIT license. See LICENSE for details.
*/
#include <string.h>
#include <dos.h>
#include <stdbool.h>
#include "mixer.h"
#include "soundblaster.h"
// Configure me!
#define MIXER_MAX_SOURCES 8
typedef struct {
int offset;
source_t const *source;
} mixed_sound_t;
static mixed_sound_t sources[MIXER_MAX_SOURCES];
static int activeSources = 0;
static int16_t data[SOUNDBLASTER_SAMPLES_PER_BUFFER] = {0};
static bool canmix = true;
int16_t const * mixer_getNextBlock(void) {
canmix = true;
return data;
}
void mixer_play(source_t const *source) {
if(activeSources == MIXER_MAX_SOURCES) {
// TODO Replace older source with new one instead?
return;
}
for(int i = 0; i < activeSources; ++i) {
if(sources[i].source == source) {
sources[i].offset = 0;
return;
}
}
sources[activeSources].offset = 0;
sources[activeSources].source = source;
++activeSources;
}
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 * sizeof(int16_t));
for(int i = 0; i < activeSources; ++i) {
mixed_sound_t *snd = sources + i;
int len = snd->source->sampleCount - snd->offset;
int16_t const* sourceBuf = snd->source->samples + snd->offset;
if(len > SOUNDBLASTER_SAMPLES_PER_BUFFER) {
len = SOUNDBLASTER_SAMPLES_PER_BUFFER;
}
for(int offset = 0; offset < len; ++offset) {
data[offset] = mix(data[offset], sourceBuf[offset]);
}
snd->offset += len;
}
for(int i = activeSources-1; i >= 0; --i) {
mixed_sound_t *snd = sources + i;
if(snd->offset == snd->source->sampleCount) {
*snd = sources[activeSources-1];
--activeSources;
}
}
canmix = false;
}

18
src/mixer.h Normal file
View File

@@ -0,0 +1,18 @@
/**
* Copyright (c) 2017 Florian Kesseler
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the MIT license. See LICENSE for details.
*/
#ifndef MIXER_H
#define MIXER_H
#include <stdint.h>
#include "source.h"
int16_t const* mixer_getNextBlock(void);
void mixer_play(source_t const *source);
void mixer_mix(void);
#endif

View File

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

30
src/modules/l_sound.c Normal file
View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2017 Florian Kesseler
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the MIT license. See LICENSE for details.
*/
#include "mixer.h"
#include "luaobj.h"
int l_sound_mix(lua_State *L) {
mixer_mix();
return 0;
}
int l_source_new(lua_State *L);
int luaopen_sound(lua_State *L) {
luaL_Reg reg[] = {
{ "mix", l_sound_mix },
{ "newSource", l_source_new },
{ 0, 0 }
};
luaL_newlib(L, reg);
return 1;
}

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

@@ -0,0 +1,52 @@
/**
* Copyright (c) 2017 Florian Kesseler
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the MIT license. See LICENSE for details.
*/
#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;
}

408
src/soundblaster.c Normal file
View File

@@ -0,0 +1,408 @@
/**
* Copyright (c) 2017 Florian Kesseler
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the MIT license. See LICENSE for details.
*/
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <stdio.h>
#include <dos.h>
#include <dpmi.h>
#include <go32.h>
#include <sys/nearptr.h>
#include "soundblaster.h"
#define BYTE(val, byte) (((val) >> ((byte) * 8)) & 0xFF)
#define SOUNDBLASTER_SAMPLES_PER_BUFFER 2048
#define SAMPLE_BUFFER_SIZE (SOUNDBLASTER_SAMPLES_PER_BUFFER * sizeof(uint16_t) * 2)
#define SAMPLE_RATE 22050
// SB16
#define BLASTER_RESET_PORT 0x6
#define BLASTER_READ_PORT 0xA
#define BLASTER_WRITE_PORT 0xC
#define BLASTER_READ_BUFFER_STATUS_PORT 0xE
#define BLASTER_INTERRUPT_ACKNOWLEDGE_8BIT 0xE
#define BLASTER_INTERRUPT_ACKNOWLEDGE_16BIT 0xF
#define BLASTER_MIXER_OUT_PORT 0x4
#define BLASTER_MIXER_IN_PORT 0x5
#define BLASTER_MIXER_INTERRUPT_STATUS 0x82
#define BLASTER_16BIT_INTERRUPT 0x02
#define BLASTER_READ_BUFFER_STATUS_AVAIL 0x80
#define BLASTER_WRITE_BUFFER_STATUS_UNAVAIL 0x80
#define BLASTER_READY_BYTE 0xAA
#define BLASTER_SET_OUTPUT_SAMPLING_RATE 0x41
#define BLASTER_PROGRAM_16BIT_IO_CMD 0xB0
#define BLASTER_PROGRAM_FLAG_FIFO 0x02
#define BLASTER_PROGRAM_FLAG_AUTO_INIT 0x04
#define BLASTER_PROGRAM_FLAG_INPUT 0x08
#define BLASTER_PROGRAM_8BIT_IO_CMD 0xC0
#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
#define PIC1_COMMAND 0x20
#define PIC2_COMMAND 0xA0
#define PIC1_DATA 0x21
#define PIC2_DATA 0xA1
#define PIC_EOI 0x20
#define PIC_IRQ07_MAP 0x08
#define PIC_IRQ8F_MAP 0x70
// DMA
#define DMA_DIRECTION_READ_FROM_MEMORY 0x08
#define DMA_TRANSFER_MODE_BLOCK 0x80
static const struct {
uint8_t startAddressRegister;
uint8_t countRegister;
uint8_t singleChannelMaskRegister;
uint8_t modeRegister;
uint8_t flipFlopResetRegister;
uint8_t pageRegister;
} dmaRegisters[] = {
{ 0x00, 0x01, 0x0A, 0x0B, 0x0C, 0x87 },
{ 0x02, 0x03, 0x0A, 0x0B, 0x0C, 0x83 },
{ 0x04, 0x05, 0x0A, 0x0B, 0x0C, 0x81 },
{ 0x06, 0x07, 0x0A, 0x0B, 0x0C, 0x82 },
{ 0xC0, 0xC2, 0xD4, 0xD6, 0xD8, 0x8F },
{ 0xC4, 0xC6, 0xD4, 0xD6, 0xD8, 0x8B },
{ 0xC8, 0xCA, 0xD4, 0xD6, 0xD8, 0x89 },
{ 0xCC, 0xCE, 0xD4, 0xD6, 0xD8, 0x8A }
};
static volatile int stopDma = 0;
static uint16_t *sampleBuffer;
static int sampleBufferSelector;
static uint16_t baseAddress;
static uint16_t irq;
static uint16_t dmaChannel;
static bool isrInstalled = false;
static int writePage = 0;
static bool blasterInitialized = false;
static _go32_dpmi_seginfo oldBlasterHandler, newBlasterHandler;
static soundblaster_getSampleProc getSamples;
static void writeDSP(uint8_t value) {
while((inportb(baseAddress + BLASTER_WRITE_PORT) &
BLASTER_WRITE_BUFFER_STATUS_UNAVAIL) != 0) {}
outportb(baseAddress + BLASTER_WRITE_PORT, 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 inline void delay3us() {
uint64_t waited = 0;
uclock_t lastTime = uclock();
while(waited < (3*UCLOCKS_PER_SEC) / 1000000) {
uclock_t nowTime = uclock();
// Just ignore timer wraps. In the worst case we get a slightly
// longer delay, but who cares?
if(nowTime > lastTime) {
waited += nowTime - lastTime;
}
lastTime = nowTime;
}
}
static int resetBlaster(void) {
for(int j = 0; j < 1000; ++j) {
outportb(baseAddress + BLASTER_RESET_PORT, 1);
delay3us();
outportb(baseAddress + BLASTER_RESET_PORT, 0);
if(readDSP() == BLASTER_READY_BYTE) {
return 0;
}
}
return SOUNDBLASTER_RESET_ERROR;
}
static void soundblasterISR(void) {
outportb(baseAddress + BLASTER_MIXER_OUT_PORT,
BLASTER_MIXER_INTERRUPT_STATUS);
uint8_t status = inportb(baseAddress + BLASTER_MIXER_IN_PORT);
if(status & BLASTER_16BIT_INTERRUPT) {
if(stopDma == 1) {
writeDSP(BLASTER_EXIT_AUTO_DMA);
stopDma = 2;
} else {
uint8_t* dst = (uint8_t*)(sampleBuffer)
+ writePage * SAMPLE_BUFFER_SIZE / 2;
memcpy(dst, getSamples(), SAMPLE_BUFFER_SIZE / 2);
writePage = 1 - writePage;
inportb(baseAddress + BLASTER_INTERRUPT_ACKNOWLEDGE_16BIT);
}
}
if(irq >= 8) {
outportb(PIC2_COMMAND, PIC_EOI);
}
outportb(PIC1_COMMAND, PIC_EOI);
}
static void setBlasterISR(void) {
// Map IRQ to interrupt number on the CPU
uint16_t interruptVector = irq + irq + (irq < 8)
? PIC_IRQ07_MAP
: PIC_IRQ8F_MAP;
_go32_dpmi_get_protected_mode_interrupt_vector(interruptVector,
&oldBlasterHandler);
newBlasterHandler.pm_offset = (int)soundblasterISR;
newBlasterHandler.pm_selector = _go32_my_cs();
_go32_dpmi_chain_protected_mode_interrupt_vector(interruptVector, &newBlasterHandler);
// PIC: unmask SB IRQ
if(irq < 8) {
uint8_t irqmask = inportb(PIC1_DATA);
outportb(PIC1_DATA, irqmask & ~(1<<irq));
} else {
uint8_t irqmask = inportb(PIC2_DATA);
outportb(PIC2_DATA, irqmask & ~(1<<(irq-8)));
}
isrInstalled = true;
}
static int parseBlasterSettings(void) {
char const* blasterEnv = getenv("BLASTER");
if(blasterEnv == 0) {
return SOUNDBLASTER_ENV_NOT_SET;
}
int res = sscanf(blasterEnv, "A%hx I%hu", &baseAddress, &irq);
if(res < 2) {
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;
}
static int allocSampleBuffer(void) {
static int maxRetries = 10;
int selectors[maxRetries];
int current;
for(current = 0; current < maxRetries; ++current) {
int segment = __dpmi_allocate_dos_memory((SAMPLE_BUFFER_SIZE+15)>>4, &selectors[current]);
if(segment == -1) {
break;
}
uint32_t bufferPhys = __djgpp_conventional_base + segment * 16;
// The DMA buffer must not cross a 64k boundary
if(bufferPhys % 0x10000 < 0x10000 - SAMPLE_BUFFER_SIZE) {
sampleBuffer = (uint16_t*)bufferPhys;
memset(sampleBuffer, 0, SAMPLE_BUFFER_SIZE);
sampleBufferSelector = selectors[current];
--current;
break;
}
}
// Free misaligned buffers
for(; current > 0; --current) {
__dpmi_free_dos_memory(selectors[current]);
}
if(sampleBuffer == NULL) {
return SOUNDBLASTER_ALLOC_ERROR;
}
return 0;
}
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,
bool down,
uint8_t mode,
uint32_t startAddress,
uint32_t count) {
uint8_t modeByte = direction
| mode
| ((uint8_t)autoReload << 4)
| ((uint8_t)down << 5)
| (channel & 0x03);
uint8_t maskEnable = (channel & 0x03) | 0x04;
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) {
offset >>= 1;
count >>= 1;
}
uint8_t page = BYTE(startAddress, 2);
outportb(dmaRegisters[channel].singleChannelMaskRegister, maskEnable);
outportb(dmaRegisters[channel].flipFlopResetRegister, 0x00);
outportb(dmaRegisters[channel].modeRegister, modeByte);
outportb(dmaRegisters[channel].startAddressRegister, BYTE(offset, 0));
outportb(dmaRegisters[channel].startAddressRegister, BYTE(offset, 1));
outportb(dmaRegisters[channel].countRegister, BYTE(count-1, 0));
outportb(dmaRegisters[channel].countRegister, BYTE(count-1, 1));
outportb(dmaRegisters[channel].pageRegister, page);
outportb(dmaRegisters[channel].singleChannelMaskRegister, maskEnable & 0x03);
}
static void startDMAOutput(void) {
uint32_t offset = ((uint32_t)sampleBuffer) - __djgpp_conventional_base;
uint32_t samples = SAMPLE_BUFFER_SIZE / sizeof(int16_t);
dmaSetupTransfer(dmaChannel, DMA_DIRECTION_READ_FROM_MEMORY, true, false,
DMA_TRANSFER_MODE_BLOCK, offset, SAMPLE_BUFFER_SIZE);
// SB16 setup
writeDSP(BLASTER_SET_OUTPUT_SAMPLING_RATE);
writeDSP(BYTE(SAMPLE_RATE, 1));
writeDSP(BYTE(SAMPLE_RATE, 0));
writeDSP(BLASTER_PROGRAM_16BIT_IO_CMD
| BLASTER_PROGRAM_FLAG_AUTO_INIT
| BLASTER_PROGRAM_FLAG_FIFO);
writeDSP(BLASTER_PROGRAM_SIGNED);
writeDSP(BYTE(samples/2-1, 0));
writeDSP(BYTE(samples/2-1, 1));
}
int soundblaster_init(soundblaster_getSampleProc getsamplesproc) {
if(!__djgpp_nearptr_enable()) {
return SOUNDBLASTER_DOS_ERROR;
}
int err = parseBlasterSettings();
if(err != 0) {
fprintf(stderr, "BLASTER environment variable not set or invalid\n");
return err;
}
err = resetBlaster();
if(err != 0) {
fprintf(stderr, "Could not reset Soundblaster\n");
return err;
}
err = allocSampleBuffer();
if(err != 0) {
fprintf(stderr, "Could not allocate sample buffer in conventional memory\n");
return err;
}
getSamples = getsamplesproc;
setBlasterISR();
turnSpeakerOn();
startDMAOutput();
blasterInitialized = true;
return 0;
}
static void deallocSampleBuffer(void) {
__dpmi_free_dos_memory(sampleBufferSelector);
}
static void resetBlasterISR(void) {
if(isrInstalled) {
uint16_t interruptVector = irq + irq + (irq < 8)
? PIC_IRQ07_MAP
: PIC_IRQ8F_MAP;
_go32_dpmi_set_protected_mode_interrupt_vector(interruptVector, &oldBlasterHandler);
isrInstalled = false;
}
}
static void stopDMAOutput(void) {
stopDma = 1;
while(stopDma == 1) {}
}
void soundblaster_deinit(void) {
if(blasterInitialized) {
turnSpeakerOff();
stopDMAOutput();
resetBlaster();
resetBlasterISR();
deallocSampleBuffer();
blasterInitialized = false;
}
}
int soundblaster_getSampleRate(void) {
return SAMPLE_RATE;
}
int soundblaster_getSampleBufferSize(void) {
return SOUNDBLASTER_SAMPLES_PER_BUFFER;
}

29
src/soundblaster.h Normal file
View File

@@ -0,0 +1,29 @@
/**
* Copyright (c) 2017 Florian Kesseler
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the MIT license. See LICENSE for details.
*/
#ifndef SOUNDBLASTER_H
#define SOUNDBLASTER_H
#include <stdint.h>
// Error codes
#define SOUNDBLASTER_ENV_NOT_SET 1
#define SOUNDBLASTER_ENV_INVALID 2
#define SOUNDBLASTER_DOS_ERROR 3
#define SOUNDBLASTER_RESET_ERROR 4
#define SOUNDBLASTER_ALLOC_ERROR 5
#define SOUNDBLASTER_SAMPLES_PER_BUFFER 2048
typedef int16_t const* (*soundblaster_getSampleProc)(void);
int soundblaster_init(soundblaster_getSampleProc sampleproc);
void soundblaster_deinit(void);
int soundblaster_getSampleRate(void);
int soundblaster_getSampleBufferSize(void);
#endif

60
src/source.c Normal file
View File

@@ -0,0 +1,60 @@
/**
* Copyright (c) 2017 Florian Kesseler
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the MIT license. See LICENSE for details.
*/
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include "source.h"
#include "filesystem.h"
#include "lib/dmt/dmt.h"
#include "wav.h"
char const* source_init(source_t *self, char const* filename) {
int fileSize;
void *waveData = filesystem_read(filename, &fileSize);
char const *err = NULL;
wav_t wav;
int res = wav_read(&wav, waveData, fileSize);
if(res != WAV_ESUCCESS) {
err = wav_strerror(res);
goto fail;
}
if(wav.bitdepth != 16) {
err = "Invalid Audio Format (only 16 Bit supported)";
goto fail;
}
if(wav.samplerate != 22050) {
err = "Invalid Audio Format (only 22050Hz supported)";
goto fail;
}
if(wav.channels != 1) {
err = "Invalid Audio Format (only mono supported)";
goto fail;
}
self->sampleCount = wav.length;
self->data = waveData;
self->samples = (int16_t*)wav.data;
return NULL;
fail:
filesystem_free(waveData);
return err;
}
void source_deinit(source_t *self) {
filesystem_free(self->data);
}

22
src/source.h Normal file
View File

@@ -0,0 +1,22 @@
/**
* Copyright (c) 2017 Florian Kesseler
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the MIT license. See LICENSE for details.
*/
#ifndef SOURCE_H
#define SOURCE_H
#include <stdint.h>
typedef struct {
int sampleCount;
int16_t *samples;
void *data;
} source_t;
char const* source_init(source_t *self, char const* filename);
void source_deinit(source_t *self);
#endif

84
src/wav.c Normal file
View File

@@ -0,0 +1,84 @@
/**
* Copyright (c) 2015 rxi
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the MIT license. See LICENSE for details.
*/
#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include "wav.h"
typedef unsigned short Uint16;
typedef unsigned int Uint32;
static const char *findSubChunk(
const char *data, size_t len, const char *id, size_t *size
) {
/* TODO : Error handling on malformed wav file */
size_t idLen = strlen(id);
const char *p = data + 12;
next:
*size = *((Uint32*) (p + 4));
if (memcmp(p, id, idLen)) {
p += 8 + *size;
if (p > data + len) return NULL;
goto next;
}
return p + 8;
}
int wav_read(wav_t *w, const void *data, size_t len) {
int bitdepth, channels, samplerate, format;
size_t sz;
const char *p = data;
memset(w, 0, sizeof(*w));
/* Check header */
if (memcmp(p, "RIFF", 4) || memcmp(p + 8, "WAVE", 4)) {
return WAV_EBADHEADER;
}
/* Find fmt subchunk */
p = findSubChunk(data, len, "fmt", &sz);
if (!p) return WAV_ENOFMT;
/* Load fmt info */
format = *((Uint16*) (p));
channels = *((Uint16*) (p + 2));
samplerate = *((Uint32*) (p + 4));
bitdepth = *((Uint16*) (p + 14));
if (format != 1) {
return WAV_ENOSUPPORT;
}
if (channels == 0 || samplerate == 0 || bitdepth == 0) {
return WAV_EBADFMT;
}
/* Find data subchunk */
p = findSubChunk(data, len, "data", &sz);
if (!p) return WAV_ENODATA;
/* Init wav_t struct */
w->data = p;
w->samplerate = samplerate;
w->channels = channels;
w->length = (sz / (bitdepth / 8)) / channels;
w->bitdepth = bitdepth;
/* Done! */
return WAV_ESUCCESS;
}
const char *wav_strerror(int err) {
switch (err) {
case WAV_ESUCCESS : return "success";
case WAV_EFAILURE : return "failure";
case WAV_EBADHEADER : return "bad header data";
case WAV_EBADFMT : return "bad fmt data";
case WAV_ENOFMT : return "missing 'fmt' subchunk";
case WAV_ENODATA : return "missing 'data' subchunk";
case WAV_ENOSUPPORT : return "unsupported format; "
"expected uncompressed PCM";
}
return "unknown error";
}

36
src/wav.h Normal file
View File

@@ -0,0 +1,36 @@
/**
* Copyright (c) 2015 rxi
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the MIT license. See LICENSE for details.
*/
#ifndef WAV_H
#define WAV_H
#include <stdlib.h>
#include <stdint.h>
typedef struct {
const void *data;
int bitdepth;
int samplerate;
int channels;
size_t length;
} wav_t;
enum {
WAV_ESUCCESS = 0,
WAV_EFAILURE = -1,
WAV_EBADHEADER = -2,
WAV_EBADFMT = -3,
WAV_ENOFMT = -4,
WAV_ENODATA = -5,
WAV_ENOSUPPORT = -6
};
int wav_read(wav_t *w, const void *data, size_t len);
const char *wav_strerror(int err);
#endif