198 lines
5.5 KiB
C
198 lines
5.5 KiB
C
|
#include <kinc/error.h>
|
||
|
#include <kinc/io/filereader.h>
|
||
|
|
||
|
#define STB_VORBIS_HEADER_ONLY
|
||
|
#include <kinc/libs/stb_vorbis.c>
|
||
|
|
||
|
#include <hl.h>
|
||
|
|
||
|
struct WaveData {
|
||
|
uint16_t audioFormat;
|
||
|
uint16_t numChannels;
|
||
|
uint32_t sampleRate;
|
||
|
uint32_t bytesPerSecond;
|
||
|
uint16_t bitsPerSample;
|
||
|
uint32_t dataSize;
|
||
|
uint8_t *data;
|
||
|
};
|
||
|
|
||
|
static void checkFOURCC(uint8_t **data, const char *fourcc, const char *filename) {
|
||
|
for (int i = 0; i < 4; ++i) {
|
||
|
kinc_affirm_message(*(*data) == fourcc[i], "Corrupt wav file: %s", filename);
|
||
|
++(*data);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void readFOURCC(uint8_t **data, char *fourcc) {
|
||
|
for (int i = 0; i < 4; ++i) {
|
||
|
fourcc[i] = *(*data);
|
||
|
++(*data);
|
||
|
}
|
||
|
fourcc[4] = 0;
|
||
|
}
|
||
|
|
||
|
static void readChunk(uint8_t **data, struct WaveData *wave) {
|
||
|
char fourcc[5];
|
||
|
readFOURCC(data, fourcc);
|
||
|
uint32_t chunksize = kinc_read_u32le(*data);
|
||
|
*data += 4;
|
||
|
if (strcmp(fourcc, "fmt ") == 0) {
|
||
|
wave->audioFormat = kinc_read_u16le(*data + 0);
|
||
|
wave->numChannels = kinc_read_u16le(*data + 2);
|
||
|
wave->sampleRate = kinc_read_u32le(*data + 4);
|
||
|
wave->bytesPerSecond = kinc_read_u32le(*data + 8);
|
||
|
wave->bitsPerSample = kinc_read_u16le(*data + 14);
|
||
|
*data += chunksize;
|
||
|
}
|
||
|
else if (strcmp(fourcc, "data") == 0) {
|
||
|
wave->dataSize = chunksize;
|
||
|
wave->data = (uint8_t *)malloc(chunksize * sizeof(uint8_t));
|
||
|
memcpy(wave->data, *data, chunksize);
|
||
|
*data += chunksize;
|
||
|
}
|
||
|
else {
|
||
|
*data += chunksize;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int16_t convert8to16(uint8_t sample) {
|
||
|
return (sample - 128) << 8;
|
||
|
}
|
||
|
|
||
|
static void splitStereo8(uint8_t *data, int size, int16_t *left, int16_t *right) {
|
||
|
for (int i = 0; i < size; ++i) {
|
||
|
left[i] = convert8to16(data[i * 2 + 0]);
|
||
|
right[i] = convert8to16(data[i * 2 + 1]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void splitStereo16(int16_t *data, int size, int16_t *left, int16_t *right) {
|
||
|
for (int i = 0; i < size; ++i) {
|
||
|
left[i] = data[i * 2 + 0];
|
||
|
right[i] = data[i * 2 + 1];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void splitMono8(uint8_t *data, int size, int16_t *left, int16_t *right) {
|
||
|
for (int i = 0; i < size; ++i) {
|
||
|
left[i] = convert8to16(data[i]);
|
||
|
right[i] = convert8to16(data[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void splitMono16(int16_t *data, int size, int16_t *left, int16_t *right) {
|
||
|
for (int i = 0; i < size; ++i) {
|
||
|
left[i] = data[i];
|
||
|
right[i] = data[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
vbyte *hl_kinc_sound_init_wav(vbyte *filename, vbyte *outSize, int *outSampleRate, double *outLength) {
|
||
|
struct WaveData wave = {0};
|
||
|
{
|
||
|
kinc_file_reader_t reader;
|
||
|
bool opened = kinc_file_reader_open(&reader, (char *)filename, KINC_FILE_TYPE_ASSET);
|
||
|
kinc_affirm(opened);
|
||
|
uint8_t *filedata = (uint8_t *)malloc(kinc_file_reader_size(&reader));
|
||
|
kinc_file_reader_read(&reader, filedata, kinc_file_reader_size(&reader));
|
||
|
kinc_file_reader_close(&reader);
|
||
|
uint8_t *data = filedata;
|
||
|
|
||
|
checkFOURCC(&data, "RIFF", (char *)filename);
|
||
|
uint32_t filesize = kinc_read_u32le(data);
|
||
|
data += 4;
|
||
|
checkFOURCC(&data, "WAVE", (char *)filename);
|
||
|
while (data + 8 - filedata < (intptr_t)filesize) {
|
||
|
readChunk(&data, &wave);
|
||
|
}
|
||
|
|
||
|
free(filedata);
|
||
|
}
|
||
|
|
||
|
float length = (wave.dataSize / (wave.bitsPerSample / 8) / wave.numChannels) / (float)wave.sampleRate;
|
||
|
|
||
|
int16_t *left;
|
||
|
int16_t *right;
|
||
|
|
||
|
if (wave.numChannels == 1) {
|
||
|
if (wave.bitsPerSample == 8) {
|
||
|
left = (int16_t *)malloc(wave.dataSize * sizeof(int16_t));
|
||
|
right = (int16_t *)malloc(wave.dataSize * sizeof(int16_t));
|
||
|
splitMono8(wave.data, wave.dataSize, left, right);
|
||
|
}
|
||
|
else if (wave.bitsPerSample == 16) {
|
||
|
wave.dataSize /= 2;
|
||
|
left = (int16_t *)malloc(wave.dataSize * sizeof(int16_t));
|
||
|
right = (int16_t *)malloc(wave.dataSize * sizeof(int16_t));
|
||
|
splitMono16((int16_t *)wave.data, wave.dataSize, left, right);
|
||
|
}
|
||
|
else {
|
||
|
kinc_affirm(false);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
// Left and right channel are in s16 audio stream, alternating.
|
||
|
if (wave.bitsPerSample == 8) {
|
||
|
wave.dataSize /= 2;
|
||
|
left = (int16_t *)malloc(wave.dataSize * sizeof(int16_t));
|
||
|
right = (int16_t *)malloc(wave.dataSize * sizeof(int16_t));
|
||
|
splitStereo8(wave.data, wave.dataSize, left, right);
|
||
|
}
|
||
|
else if (wave.bitsPerSample == 16) {
|
||
|
wave.dataSize /= 4;
|
||
|
left = (int16_t *)malloc(wave.dataSize * sizeof(int16_t));
|
||
|
right = (int16_t *)malloc(wave.dataSize * sizeof(int16_t));
|
||
|
splitStereo16((int16_t *)wave.data, wave.dataSize, left, right);
|
||
|
}
|
||
|
else {
|
||
|
kinc_affirm(false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
free(wave.data);
|
||
|
|
||
|
float *uncompressedData = (float *)malloc(wave.dataSize * 2 * sizeof(float));
|
||
|
*((unsigned int *)outSize) = wave.dataSize * 2; // Return array size to Kha
|
||
|
for (uint32_t i = 0; i < wave.dataSize; i += 1) {
|
||
|
uncompressedData[i * 2 + 0] = (float)(left[i] / 32767.0);
|
||
|
uncompressedData[i * 2 + 1] = (float)(right[i] / 32767.0);
|
||
|
}
|
||
|
*outSampleRate = wave.sampleRate;
|
||
|
*outLength = (double)length;
|
||
|
|
||
|
free(left);
|
||
|
free(right);
|
||
|
|
||
|
return (vbyte *)uncompressedData;
|
||
|
}
|
||
|
|
||
|
vbyte *hl_kinc_sound_init_vorbis(vbyte *data, int length) {
|
||
|
return (vbyte *)stb_vorbis_open_memory(data, length, NULL, NULL);
|
||
|
}
|
||
|
|
||
|
bool hl_kinc_sound_next_vorbis_samples(vbyte *vorbis, vbyte *samples, int length, bool loop, bool atend) {
|
||
|
int read = stb_vorbis_get_samples_float_interleaved((stb_vorbis *)vorbis, 2, (float *)samples, length);
|
||
|
if (read < length / 2) {
|
||
|
if (loop) {
|
||
|
stb_vorbis_seek_start((stb_vorbis *)vorbis);
|
||
|
}
|
||
|
else {
|
||
|
atend = true;
|
||
|
}
|
||
|
for (int i = read * 2; i < length; ++i) {
|
||
|
samples[i] = 0;
|
||
|
}
|
||
|
}
|
||
|
return atend;
|
||
|
}
|
||
|
|
||
|
float hl_kinc_sound_vorbis_get_length(vbyte *vorbis) {
|
||
|
if (vorbis == NULL) return 0;
|
||
|
return stb_vorbis_stream_length_in_seconds((stb_vorbis *)vorbis);
|
||
|
}
|
||
|
|
||
|
float hl_kinc_sound_vorbis_get_position(vbyte *vorbis) {
|
||
|
if (vorbis == NULL) return 0;
|
||
|
return stb_vorbis_get_sample_offset((stb_vorbis *)vorbis) / (float)stb_vorbis_stream_length_in_samples((stb_vorbis *)vorbis);
|
||
|
}
|