mirror of
https://github.com/QB64-Phoenix-Edition/QB64pe.git
synced 2024-09-07 15:20:18 +00:00
348 lines
10 KiB
C
348 lines
10 KiB
C
#ifndef DEPENDENCY_AUDIO_DECODE
|
|
// Stubs:
|
|
//(none required)
|
|
#else
|
|
|
|
# define DEPENDENCY_AUDIO_DECODE_OGG
|
|
# define DEPENDENCY_AUDIO_DECODE_MP3
|
|
# define DEPENDENCY_AUDIO_DECODE_WAV
|
|
|
|
# ifdef QB64_BACKSLASH_FILESYSTEM
|
|
# ifdef DEPENDENCY_AUDIO_DECODE_MP3
|
|
# include "mp3_mini\\src.c"
|
|
# endif
|
|
# ifdef DEPENDENCY_AUDIO_DECODE_WAV
|
|
# include "wav\\src.c"
|
|
# endif
|
|
# ifdef DEPENDENCY_AUDIO_DECODE_OGG
|
|
# include "ogg\\src.c"
|
|
# endif
|
|
# else
|
|
# ifdef DEPENDENCY_AUDIO_DECODE_MP3
|
|
# include "mp3_mini/src.c"
|
|
# endif
|
|
# ifdef DEPENDENCY_AUDIO_DECODE_WAV
|
|
# include "wav/src.c"
|
|
# endif
|
|
# ifdef DEPENDENCY_AUDIO_DECODE_OGG
|
|
# include "ogg/src.c"
|
|
# endif
|
|
# endif
|
|
# include <string.h>
|
|
// forward refs:
|
|
void sub__sndvol(int32 handle, float volume);
|
|
void sub__sndclose(int32 handle);
|
|
|
|
int32 func__sndopen(qbs *filename, qbs *requirements, int32 passed) {
|
|
sndsetup();
|
|
if (new_error)
|
|
return 0;
|
|
|
|
static qbs *s1 = NULL;
|
|
if (!s1)
|
|
s1 = qbs_new(0, 0);
|
|
qbs_set(s1, qbs_add(filename, qbs_new_txt_len("\0", 1))); // s1=filename+CHR$(0)
|
|
|
|
// load file
|
|
if (s1->len == 1)
|
|
return 0; // return invalid handle if null length string
|
|
static int32 fh, result;
|
|
static int64 lof;
|
|
fh = gfs_open(s1, 1, 0, 0);
|
|
if (fh < 0)
|
|
return 0;
|
|
lof = gfs_lof(fh);
|
|
static uint8 *content;
|
|
content = (uint8 *)malloc(lof);
|
|
if (!content) {
|
|
gfs_close(fh);
|
|
return 0;
|
|
}
|
|
result = gfs_read(fh, -1, content, lof);
|
|
gfs_close(fh);
|
|
if (result < 0) {
|
|
free(content);
|
|
return 0;
|
|
}
|
|
|
|
// identify file format
|
|
static snd_sequence_struct *seq;
|
|
|
|
// OGG?
|
|
# ifdef DEPENDENCY_AUDIO_DECODE_OGG
|
|
if (lof >= 3) {
|
|
if (content[0] == 79) {
|
|
if (content[1] == 103) {
|
|
if (content[2] == 103) { //"Ogg"
|
|
seq = snd_decode_ogg(content, lof);
|
|
goto got_seq;
|
|
}
|
|
}
|
|
}
|
|
} // 3
|
|
# endif
|
|
|
|
// WAV?
|
|
# ifdef DEPENDENCY_AUDIO_DECODE_WAV
|
|
if (lof >= 12) {
|
|
if ((*(uint32 *)&content[8]) == 0x45564157) { // WAVE
|
|
seq = snd_decode_wav(content, lof);
|
|
goto got_seq;
|
|
} // WAVE
|
|
}
|
|
# endif
|
|
|
|
// assume mp3!
|
|
// MP3?
|
|
# ifdef DEPENDENCY_AUDIO_DECODE_MP3
|
|
seq = snd_decode_mp3(content, lof);
|
|
# endif
|
|
|
|
got_seq:
|
|
free(content);
|
|
if (seq == NULL)
|
|
return 0;
|
|
|
|
// convert sequence (includes sample rate conversion etc etc)
|
|
|
|
// just perform sample_rate fix for now...
|
|
|
|
// 1. 8->16bit conversion and/or edian conversion
|
|
static int32 incorrect_format;
|
|
incorrect_format = 0;
|
|
if (seq->bits_per_sample != 16)
|
|
incorrect_format = 1;
|
|
if (seq->is_unsigned)
|
|
incorrect_format = 1;
|
|
// todo... if (seq->endian==???)
|
|
|
|
// this section does not fix the frequency, only the bits per sample
|
|
// and signed-ness of the data
|
|
if (incorrect_format) {
|
|
static int32 bps;
|
|
bps = seq->bits_per_sample / 8;
|
|
static int32 samples;
|
|
samples = seq->data_size / bps;
|
|
static uint8 *new_data;
|
|
if (bps != 2) {
|
|
new_data = (uint8 *)malloc(samples * 2);
|
|
} else {
|
|
new_data = (uint8 *)seq->data;
|
|
}
|
|
static int32 i, v;
|
|
for (i = 0; i < samples; i++) {
|
|
// read original value
|
|
v = 0;
|
|
if (bps == 1) {
|
|
if (seq->is_unsigned) {
|
|
v = *(uint8 *)(seq->data + i * 1);
|
|
v = (v - 128) * 256;
|
|
} else {
|
|
v = *(int8 *)(seq->data + i * 1);
|
|
v = v * 128;
|
|
}
|
|
}
|
|
if (bps == 2) {
|
|
if (seq->is_unsigned) {
|
|
v = *(uint16 *)(seq->data + i * 2);
|
|
v = v - 32768;
|
|
} else {
|
|
v = *(int16 *)(seq->data + i * 2);
|
|
}
|
|
}
|
|
// place new value into array
|
|
((int16 *)new_data)[i] = v;
|
|
} // i
|
|
if (bps != 2) {
|
|
free(seq->data);
|
|
seq->data = (uint16 *)new_data;
|
|
seq->data_size = samples * 2;
|
|
}
|
|
// update seq info
|
|
seq->bits_per_sample = 16;
|
|
seq->is_unsigned = 0;
|
|
} // incorrect format
|
|
|
|
// 2. samplerate conversion
|
|
if (seq->sample_rate != snd_frequency) { // need to resample seq->data
|
|
// create new resampler
|
|
SpeexResamplerState *state;
|
|
state = speex_resampler_init(seq->channels, seq->sample_rate, snd_frequency, SPEEX_RESAMPLER_QUALITY_MIN, NULL);
|
|
if (!state) { // NULL means failure
|
|
free(seq->data);
|
|
return 0;
|
|
}
|
|
|
|
// allocate new memory for output
|
|
int32 out_samples_max = ((double)seq->data_size / seq->channels / 2) * ((((double)snd_frequency) / ((double)seq->sample_rate)) + 0.1) +
|
|
100; // 10%+100 extra samples as a buffer-zone
|
|
int16 *resampled = (int16 *)malloc(out_samples_max * seq->channels * sizeof(int16));
|
|
if (!resampled) {
|
|
free(seq->data);
|
|
return 0;
|
|
}
|
|
|
|
// establish data sizes
|
|
// in_len will be set by the resampler to number of samples processed
|
|
spx_uint32_t in_len =
|
|
seq->data_size / seq->channels / 2; // divide by 2 because 2byte samples, divide by #channels because function wants it per-channel
|
|
// out_len will be set to the number of samples written
|
|
spx_uint32_t out_len = out_samples_max * seq->channels * sizeof(int16);
|
|
|
|
// resample!
|
|
if (speex_resampler_process_interleaved_int(state, (spx_int16_t *)seq->data, &in_len, (spx_int16_t *)resampled, &out_len) != RESAMPLER_ERR_SUCCESS) {
|
|
// Error
|
|
free(resampled);
|
|
free(seq->data);
|
|
speex_resampler_destroy(state);
|
|
return 0;
|
|
}
|
|
|
|
// destroy the resampler anyway
|
|
speex_resampler_destroy(state);
|
|
|
|
// establish real size of new data and update seq
|
|
free(seq->data); // That was the old data
|
|
seq->data_size = out_len * seq->channels * 2; // remember out_len is perchannel, and each sample is 2 bytes
|
|
seq->data = (uint16_t *)realloc(resampled, seq->data_size); // we overestimated the array size before, so make it the correct size now
|
|
if (!seq->data) { // realloc could fail
|
|
free(resampled);
|
|
return 0;
|
|
}
|
|
seq->sample_rate = snd_frequency;
|
|
}
|
|
|
|
// Unpack stereo data into separate left/right buffers
|
|
if (seq->channels == 1) {
|
|
seq->channels = 1;
|
|
seq->data_left = seq->data;
|
|
seq->data_left_size = seq->data_size;
|
|
seq->data_right = NULL;
|
|
seq->data_right_size = 0;
|
|
} else if (seq->channels == 2) {
|
|
seq->data_left_size = seq->data_right_size = seq->data_size / 2;
|
|
seq->data_left = (uint16_t *)malloc(seq->data_size / 2);
|
|
if (!seq->data_left) {
|
|
free(seq->data);
|
|
return 0;
|
|
}
|
|
seq->data_right = (uint16_t *)malloc(seq->data_size / 2);
|
|
if (!seq->data_right) {
|
|
free(seq->data_left);
|
|
free(seq->data);
|
|
return 0;
|
|
}
|
|
for (int sample = 0; sample < seq->data_size / 4; sample++) {
|
|
seq->data_left[sample] = seq->data[sample * 2];
|
|
seq->data_right[sample] = seq->data[sample * 2 + 1];
|
|
}
|
|
free(seq->data);
|
|
seq->data = NULL;
|
|
} else {
|
|
free(seq->data);
|
|
return 0;
|
|
}
|
|
|
|
// attach sequence to handle (& inc. refs)
|
|
// create snd handle
|
|
static int32 handle;
|
|
handle = list_add(snd_handles);
|
|
static snd_struct *snd;
|
|
snd = (snd_struct *)list_get(snd_handles, handle);
|
|
|
|
snd->internal = 0;
|
|
snd->type = 2;
|
|
snd->seq = seq;
|
|
snd->volume = 1.0;
|
|
|
|
if (seq->channels == 1) {
|
|
snd->bal_left_x = snd->bal_left_y = snd->bal_left_z = 0;
|
|
} else if (seq->channels == 2) {
|
|
snd->bal_left_x = -0.01;
|
|
snd->bal_left_y = snd->bal_left_z = 0;
|
|
snd->bal_right_x = 0.01;
|
|
snd->bal_right_y = snd->bal_right_z = 0;
|
|
}
|
|
snd->bal_update = 1;
|
|
sndupdate(snd);
|
|
return handle;
|
|
}
|
|
|
|
mem_block func__memsound(int32 i, int32 targetChannel) {
|
|
|
|
static mem_block b;
|
|
|
|
if (new_error)
|
|
goto error;
|
|
if (i <= 0)
|
|
goto error;
|
|
|
|
sndsetup();
|
|
|
|
static snd_struct *sn;
|
|
sn = (snd_struct *)list_get(snd_handles, i);
|
|
if (!sn) {
|
|
goto error;
|
|
}
|
|
if (!snd_allow_internal) {
|
|
if (sn->internal) {
|
|
goto error;
|
|
}
|
|
}
|
|
if (targetChannel < 1 || targetChannel > sn->seq->channels)
|
|
goto error;
|
|
|
|
if (sn->lock_id) {
|
|
b.lock_offset = (ptrszint)sn->lock_offset;
|
|
b.lock_id = sn->lock_id; // get existing tag
|
|
} else {
|
|
new_mem_lock();
|
|
mem_lock_tmp->type = 5; // sound
|
|
b.lock_offset = (ptrszint)mem_lock_tmp;
|
|
b.lock_id = mem_lock_id;
|
|
sn->lock_offset = (void *)mem_lock_tmp;
|
|
sn->lock_id = mem_lock_id; // create tag
|
|
}
|
|
|
|
if (targetChannel == 1) {
|
|
b.offset = (ptrszint)sn->seq->data_left;
|
|
b.size = sn->seq->data_left_size;
|
|
}
|
|
|
|
if (targetChannel == 2) {
|
|
b.offset = (ptrszint)sn->seq->data_right;
|
|
b.size = sn->seq->data_right_size;
|
|
}
|
|
|
|
b.type = 0; // sn->bytes_per_pixel+128+1024+2048;//integer+unsigned+pixeltype
|
|
b.elementsize = sn->seq->bits_per_sample / 8;
|
|
b.sound = i;
|
|
|
|
return b;
|
|
error:
|
|
b.offset = 0;
|
|
b.size = 0;
|
|
b.lock_offset = (ptrszint)mem_lock_base;
|
|
b.lock_id = 1073741821; // set invalid lock
|
|
b.type = 0;
|
|
b.elementsize = 0;
|
|
b.sound = 0;
|
|
return b;
|
|
}
|
|
|
|
void sub__sndplayfile(qbs *filename, int32 sync, double volume, int32 passed) {
|
|
if (new_error)
|
|
return;
|
|
sndsetup();
|
|
int32 handle;
|
|
handle = func__sndopen(filename, NULL, 0);
|
|
if (!handle)
|
|
return;
|
|
if (passed & 2) {
|
|
sub__sndvol(handle, volume);
|
|
}
|
|
sub__sndplay(handle);
|
|
sub__sndclose(handle);
|
|
}
|
|
|
|
#endif
|