mirror of
https://github.com/QB64-Phoenix-Edition/QB64pe.git
synced 2024-09-07 15:20:18 +00:00
Merge pull request #325 from a740g/main
Update TSF and add SF3 support and enhance $MIDISOUNDFONT behavior
This commit is contained in:
commit
f0bc9773de
3 changed files with 160 additions and 65 deletions
|
@ -13,15 +13,10 @@
|
||||||
// Soundfont (awe32rom.h) from dos-like
|
// Soundfont (awe32rom.h) from dos-like
|
||||||
// https://github.com/mattiasgustavsson/dos-like (MIT)
|
// https://github.com/mattiasgustavsson/dos-like (MIT)
|
||||||
//
|
//
|
||||||
// Copyright (c) 2022 Samuel Gomes
|
|
||||||
// https://github.com/a740g
|
|
||||||
//
|
|
||||||
//-----------------------------------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------------------------------
|
|
||||||
// HEADER FILES
|
|
||||||
//-----------------------------------------------------------------------------------------------------
|
|
||||||
#include "libqb-common.h"
|
#include "libqb-common.h"
|
||||||
|
|
||||||
#include "audio.h"
|
#include "audio.h"
|
||||||
#include "filepath.h"
|
#include "filepath.h"
|
||||||
|
|
||||||
|
@ -29,40 +24,43 @@
|
||||||
|
|
||||||
#include "../miniaudio.h"
|
#include "../miniaudio.h"
|
||||||
|
|
||||||
|
#define STB_VORBIS_HEADER_ONLY
|
||||||
|
#include "stb_vorbis.c"
|
||||||
#define TSF_IMPLEMENTATION
|
#define TSF_IMPLEMENTATION
|
||||||
#include "tinysoundfont/tsf.h"
|
#include "tinysoundfont/tsf.h"
|
||||||
#define TML_IMPLEMENTATION
|
#define TML_IMPLEMENTATION
|
||||||
#include "tinysoundfont/tml.h"
|
#include "tinysoundfont/tml.h"
|
||||||
|
#undef STB_VORBIS_HEADER_ONLY
|
||||||
|
|
||||||
#include "vtables.h"
|
#include "vtables.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
// These symbols reference a soundfont compiled into the program
|
// These symbols reference a soundfont compiled into the program
|
||||||
//
|
//
|
||||||
// We provide a macro to expand to the correct symbol name
|
// We provide a macro to expand to the correct symbol name
|
||||||
|
|
||||||
#if defined(QB64_WINDOWS) && defined(QB64_32)
|
#if defined(QB64_WINDOWS) && defined(QB64_32)
|
||||||
// On 32-bit Windows, we use objcopy, and the symbols do not have an
|
// On 32-bit Windows, we use objcopy, and the symbols do not have an
|
||||||
// underscore prefix
|
// underscore prefix
|
||||||
extern char binary_soundfont_sf2_start[];
|
extern char binary_soundfont_sf2_start[];
|
||||||
extern char binary_soundfont_sf2_end[];
|
extern char binary_soundfont_sf2_end[];
|
||||||
|
|
||||||
# define SOUNDFONT_BIN binary_soundfont_sf2_start
|
# define SOUNDFONT_BIN binary_soundfont_sf2_start
|
||||||
# define SOUNDFONT_SIZE (binary_soundfont_sf2_end - binary_soundfont_sf2_start)
|
# define SOUNDFONT_SIZE (binary_soundfont_sf2_end - binary_soundfont_sf2_start)
|
||||||
|
|
||||||
#elif defined(QB64_WINDOWS) || defined(QB64_LINUX)
|
#elif defined(QB64_WINDOWS) || defined(QB64_LINUX)
|
||||||
// On Linux and 64-bit Windows, we use objcopy, and the symbols do have an
|
// On Linux and 64-bit Windows, we use objcopy, and the symbols do have an
|
||||||
// underscore prefix.
|
// underscore prefix.
|
||||||
extern char _binary_soundfont_sf2_start[];
|
extern char _binary_soundfont_sf2_start[];
|
||||||
extern char _binary_soundfont_sf2_end[];
|
extern char _binary_soundfont_sf2_end[];
|
||||||
|
|
||||||
# define SOUNDFONT_BIN _binary_soundfont_sf2_start
|
# define SOUNDFONT_BIN _binary_soundfont_sf2_start
|
||||||
# define SOUNDFONT_SIZE (_binary_soundfont_sf2_end - _binary_soundfont_sf2_start)
|
# define SOUNDFONT_SIZE (_binary_soundfont_sf2_end - _binary_soundfont_sf2_start)
|
||||||
|
|
||||||
#else
|
#else
|
||||||
// On Mac OS we use xxd, which gives an array and size
|
// On Mac OS we use xxd, which gives an array and size
|
||||||
extern unsigned char soundfont_sf2[];
|
extern unsigned char soundfont_sf2[];
|
||||||
extern unsigned int soundfont_sf2_len;
|
extern unsigned int soundfont_sf2_len;
|
||||||
|
|
||||||
# define SOUNDFONT_BIN soundfont_sf2
|
# define SOUNDFONT_BIN soundfont_sf2
|
||||||
# define SOUNDFONT_SIZE soundfont_sf2_len
|
# define SOUNDFONT_SIZE soundfont_sf2_len
|
||||||
|
@ -70,8 +68,6 @@ extern "C" {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
struct ma_tsf {
|
struct ma_tsf {
|
||||||
// This part is for miniaudio
|
// This part is for miniaudio
|
||||||
ma_data_source_base ds; /* The decoder can be used independently as a data source. */
|
ma_data_source_base ds; /* The decoder can be used independently as a data source. */
|
||||||
|
@ -274,8 +270,14 @@ static ma_result ma_tsf_ds_get_length(ma_data_source *pDataSource, ma_uint64 *pL
|
||||||
return ma_tsf_get_length_in_pcm_frames((ma_tsf *)pDataSource, pLength);
|
return ma_tsf_get_length_in_pcm_frames((ma_tsf *)pDataSource, pLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ma_data_source_vtable ma_data_source_vtable_tsf = {ma_tsf_ds_read, ma_tsf_ds_seek, ma_tsf_ds_get_data_format, ma_tsf_ds_get_cursor,
|
// clang-format off
|
||||||
ma_tsf_ds_get_length};
|
static ma_data_source_vtable ma_data_source_vtable_tsf = {
|
||||||
|
ma_tsf_ds_read, ma_tsf_ds_seek,
|
||||||
|
ma_tsf_ds_get_data_format,
|
||||||
|
ma_tsf_ds_get_cursor,
|
||||||
|
ma_tsf_ds_get_length
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
static int ma_tsf_of_callback__read(void *pUserData, unsigned char *pBufferOut, int bytesToRead) {
|
static int ma_tsf_of_callback__read(void *pUserData, unsigned char *pBufferOut, int bytesToRead) {
|
||||||
ma_tsf *pTsf = (ma_tsf *)pUserData;
|
ma_tsf *pTsf = (ma_tsf *)pUserData;
|
||||||
|
@ -357,13 +359,12 @@ static ma_result ma_tsf_init_internal(const ma_decoding_backend_config *pConfig,
|
||||||
return MA_SUCCESS;
|
return MA_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
ma_result ma_tsf_load_memory(ma_tsf *pTsf)
|
ma_result ma_tsf_load_memory(ma_tsf *pTsf) {
|
||||||
{
|
|
||||||
// Attempt to load a SoundFont from memory
|
// Attempt to load a SoundFont from memory
|
||||||
pTsf->tinySoundFont = tsf_load_memory(SOUNDFONT_BIN, SOUNDFONT_SIZE);
|
pTsf->tinySoundFont = tsf_load_memory(SOUNDFONT_BIN, SOUNDFONT_SIZE);
|
||||||
|
|
||||||
// Return failue if loading from memory also failed. This should not happen though
|
// Return failue if loading from memory also failed. This should not happen though
|
||||||
return pTsf->tinySoundFont? MA_SUCCESS: MA_OUT_OF_MEMORY;
|
return pTsf->tinySoundFont ? MA_SUCCESS : MA_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ma_result ma_tsf_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void *pReadSeekTellUserData,
|
static ma_result ma_tsf_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void *pReadSeekTellUserData,
|
||||||
|
@ -566,6 +567,7 @@ static void ma_decoding_backend_uninit__tsf(void *pUserData, ma_data_source *pBa
|
||||||
ma_free(pTsf, pAllocationCallbacks);
|
ma_free(pTsf, pAllocationCallbacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
ma_decoding_backend_vtable ma_vtable_midi = {
|
ma_decoding_backend_vtable ma_vtable_midi = {
|
||||||
ma_decoding_backend_init__tsf,
|
ma_decoding_backend_init__tsf,
|
||||||
ma_decoding_backend_init_file__tsf,
|
ma_decoding_backend_init_file__tsf,
|
||||||
|
@ -573,4 +575,4 @@ ma_decoding_backend_vtable ma_vtable_midi = {
|
||||||
NULL, /* onInitMemory() */
|
NULL, /* onInitMemory() */
|
||||||
ma_decoding_backend_uninit__tsf
|
ma_decoding_backend_uninit__tsf
|
||||||
};
|
};
|
||||||
//-----------------------------------------------------------------------------------------------------
|
// clang-format on
|
||||||
|
|
|
@ -121,7 +121,7 @@ enum TSFOutputMode
|
||||||
// Two channels with all samples for the left channel first then right
|
// Two channels with all samples for the left channel first then right
|
||||||
TSF_STEREO_UNWEAVED,
|
TSF_STEREO_UNWEAVED,
|
||||||
// A single channel (stereo instruments are mixed into center)
|
// A single channel (stereo instruments are mixed into center)
|
||||||
TSF_MONO,
|
TSF_MONO
|
||||||
};
|
};
|
||||||
|
|
||||||
// Thread safety:
|
// Thread safety:
|
||||||
|
@ -861,27 +861,108 @@ static int tsf_load_presets(tsf* res, struct tsf_hydra *hydra, unsigned int font
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tsf_load_samples(float** fontSamples, unsigned int* fontSampleCount, struct tsf_riffchunk *chunkSmpl, struct tsf_stream* stream)
|
#ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H
|
||||||
|
static int tsf_decode_samples_ogg(tsf_u8* smplBuffer, tsf_u32 smplLength, float** outSamples, unsigned int* outSampleCount, struct tsf_hydra *hydra)
|
||||||
{
|
{
|
||||||
// Read sample data into float format buffer.
|
float *res = TSF_NULL;
|
||||||
float* out; unsigned int samplesLeft, samplesToRead, samplesToConvert;
|
unsigned int resNum = 0, resMax = 0, resInitial = (smplLength > 0x100000 ? (smplLength & ~0xFFFFF) : 65536);
|
||||||
samplesLeft = *fontSampleCount = chunkSmpl->size / sizeof(short);
|
int i;
|
||||||
out = *fontSamples = (float*)TSF_MALLOC(samplesLeft * sizeof(float));
|
for (i = 0; i < hydra->shdrNum; i++)
|
||||||
if (!out) return 0;
|
|
||||||
for (; samplesLeft; samplesLeft -= samplesToRead)
|
|
||||||
{
|
{
|
||||||
short sampleBuffer[1024], *in = sampleBuffer;;
|
stb_vorbis *v;
|
||||||
samplesToRead = (samplesLeft > 1024 ? 1024 : samplesLeft);
|
struct tsf_hydra_shdr *shdr = &hydra->shdrs[i];
|
||||||
stream->read(stream->data, sampleBuffer, samplesToRead * sizeof(short));
|
const tsf_u8 *pSmpl = smplBuffer + shdr->start, *pSmplEnd = smplBuffer + shdr->end;
|
||||||
|
if (pSmplEnd <= pSmpl) continue;
|
||||||
|
|
||||||
// Convert from signed 16-bit to float.
|
// Use whatever stb_vorbis API that is available (either pull or push)
|
||||||
for (samplesToConvert = samplesToRead; samplesToConvert > 0; --samplesToConvert)
|
#if !defined(STB_VORBIS_NO_PULLDATA_API) && !defined(STB_VORBIS_NO_FROMMEMORY)
|
||||||
// If we ever need to compile for big-endian platforms, we'll need to byte-swap here.
|
v = stb_vorbis_open_memory(pSmpl, (int)(pSmplEnd - pSmpl), TSF_NULL, TSF_NULL);
|
||||||
*out++ = (float)(*in++ / 32767.0);
|
#else
|
||||||
|
{ int use, err; v = stb_vorbis_open_pushdata(pSmpl, (int)(pSmplEnd - pSmpl), &use, &err, TSF_NULL); pSmpl += use; }
|
||||||
|
#endif
|
||||||
|
if (v == TSF_NULL) { TSF_FREE(res); return 0; }
|
||||||
|
|
||||||
|
// Fix up sample indices in shdr (end index is set after decoding)
|
||||||
|
shdr->start = resNum;
|
||||||
|
shdr->startLoop += resNum;
|
||||||
|
shdr->endLoop += resNum;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
float** outputs; int n_samples;
|
||||||
|
|
||||||
|
// Decode one frame of vorbis samples with whatever stb_vorbis API that is available
|
||||||
|
#if !defined(STB_VORBIS_NO_PULLDATA_API) && !defined(STB_VORBIS_NO_FROMMEMORY)
|
||||||
|
n_samples = stb_vorbis_get_frame_float(v, TSF_NULL, &outputs);
|
||||||
|
if (!n_samples) break;
|
||||||
|
#else
|
||||||
|
if (pSmpl >= pSmplEnd) break;
|
||||||
|
{ int use = stb_vorbis_decode_frame_pushdata(v, pSmpl, (int)(pSmplEnd - pSmpl), TSF_NULL, &outputs, &n_samples); pSmpl += use; }
|
||||||
|
if (!n_samples) continue;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Expand our output buffer if necessary then copy over the decoded frame samples
|
||||||
|
resNum += n_samples;
|
||||||
|
if (resNum > resMax)
|
||||||
|
{
|
||||||
|
do { resMax += (resMax ? (resMax < 1048576 ? resMax : 1048576) : resInitial); } while (resNum > resMax);
|
||||||
|
res = (float*)TSF_REALLOC(res, resMax * sizeof(float));
|
||||||
|
if (!res) { stb_vorbis_close(v); return 0; }
|
||||||
}
|
}
|
||||||
|
TSF_MEMCPY(res + resNum - n_samples, outputs[0], n_samples * sizeof(float));
|
||||||
|
}
|
||||||
|
shdr->end = resNum;
|
||||||
|
stb_vorbis_close(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim the sample buffer down then return success (unless out of memory)
|
||||||
|
res = (float*)TSF_REALLOC(res, resNum * sizeof(float));
|
||||||
|
*outSamples = res;
|
||||||
|
*outSampleCount = resNum;
|
||||||
|
return (res ? 1 : 0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int tsf_decode_samples(tsf_u8* smplBuffer, tsf_u32 smplLength, float** outSamples, unsigned int* outSampleCount, struct tsf_hydra *hydra)
|
||||||
|
{
|
||||||
|
float *out; const short *in;
|
||||||
|
|
||||||
|
#ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H
|
||||||
|
if (TSF_FourCCEquals(smplBuffer, "OggS"))
|
||||||
|
return tsf_decode_samples_ogg(smplBuffer, smplLength, outSamples, outSampleCount, hydra);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Inline convert the samples from short to float (buffer was allocated big enough in tsf_load_samples)
|
||||||
|
*outSamples = (float*)smplBuffer;
|
||||||
|
*outSampleCount = smplLength / sizeof(short);
|
||||||
|
for (in = (short*)smplBuffer + *outSampleCount, out = *outSamples + *outSampleCount; in != (short*)smplBuffer;)
|
||||||
|
*(--out) = (float)(*(--in) / 32767.0);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int tsf_load_samples(tsf_u8** smplBuffer, tsf_u32 smplLength, struct tsf_stream* stream)
|
||||||
|
{
|
||||||
|
#ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H
|
||||||
|
// With OGG Vorbis support scan for a specific 4 byte sample header first
|
||||||
|
if (smplLength >= sizeof(tsf_fourcc))
|
||||||
|
{
|
||||||
|
// If the format is not OGG the buffer is made large enough to hold the decoded float samples
|
||||||
|
tsf_fourcc format;
|
||||||
|
stream->read(stream->data, &format, sizeof(tsf_fourcc));
|
||||||
|
if (TSF_FourCCEquals(format, "OggS"))
|
||||||
|
*smplBuffer = (tsf_u8*)TSF_MALLOC(smplLength);
|
||||||
|
else
|
||||||
|
*smplBuffer = (tsf_u8*)TSF_MALLOC(smplLength / sizeof(short) * sizeof(float));
|
||||||
|
if (!*smplBuffer) return 0;
|
||||||
|
memcpy(*smplBuffer, &format, sizeof(tsf_fourcc));
|
||||||
|
return stream->read(stream->data, (char*)*smplBuffer + sizeof(tsf_fourcc), smplLength - sizeof(tsf_fourcc));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Allocate enough to hold the decoded float samples (see tsf_decode_samples)
|
||||||
|
*smplBuffer = (tsf_u8*)TSF_MALLOC(smplLength / sizeof(short) * sizeof(float));
|
||||||
|
return (*smplBuffer ? stream->read(stream->data, *smplBuffer, smplLength) : 0);
|
||||||
|
}
|
||||||
|
|
||||||
static void tsf_voice_envelope_nextsegment(struct tsf_voice_envelope* e, short active_segment, float outSampleRate)
|
static void tsf_voice_envelope_nextsegment(struct tsf_voice_envelope* e, short active_segment, float outSampleRate)
|
||||||
{
|
{
|
||||||
switch (active_segment)
|
switch (active_segment)
|
||||||
|
@ -1239,8 +1320,8 @@ TSFDEF tsf* tsf_load(struct tsf_stream* stream)
|
||||||
struct tsf_riffchunk chunkHead;
|
struct tsf_riffchunk chunkHead;
|
||||||
struct tsf_riffchunk chunkList;
|
struct tsf_riffchunk chunkList;
|
||||||
struct tsf_hydra hydra;
|
struct tsf_hydra hydra;
|
||||||
float* fontSamples = TSF_NULL;
|
tsf_u8* smplBuffer = TSF_NULL;
|
||||||
unsigned int fontSampleCount = 0;
|
unsigned int smplLength = 0;
|
||||||
|
|
||||||
if (!tsf_riffchunk_read(TSF_NULL, &chunkHead, stream) || !TSF_FourCCEquals(chunkHead.id, "sfbk"))
|
if (!tsf_riffchunk_read(TSF_NULL, &chunkHead, stream) || !TSF_FourCCEquals(chunkHead.id, "sfbk"))
|
||||||
{
|
{
|
||||||
|
@ -1282,9 +1363,10 @@ TSFDEF tsf* tsf_load(struct tsf_stream* stream)
|
||||||
{
|
{
|
||||||
while (tsf_riffchunk_read(&chunkList, &chunk, stream))
|
while (tsf_riffchunk_read(&chunkList, &chunk, stream))
|
||||||
{
|
{
|
||||||
if (TSF_FourCCEquals(chunk.id, "smpl") && !fontSamples && chunk.size >= sizeof(short))
|
if (TSF_FourCCEquals(chunk.id, "smpl") && !smplBuffer && chunk.size >= sizeof(short))
|
||||||
{
|
{
|
||||||
if (!tsf_load_samples(&fontSamples, &fontSampleCount, &chunk, stream)) goto out_of_memory;
|
smplLength = chunk.size;
|
||||||
|
if (!tsf_load_samples(&smplBuffer, smplLength, stream)) goto out_of_memory;
|
||||||
}
|
}
|
||||||
else stream->skip(stream->data, chunk.size);
|
else stream->skip(stream->data, chunk.size);
|
||||||
}
|
}
|
||||||
|
@ -1295,18 +1377,19 @@ TSFDEF tsf* tsf_load(struct tsf_stream* stream)
|
||||||
{
|
{
|
||||||
//if (e) *e = TSF_INVALID_INCOMPLETE;
|
//if (e) *e = TSF_INVALID_INCOMPLETE;
|
||||||
}
|
}
|
||||||
else if (fontSamples == TSF_NULL)
|
else if (smplBuffer == TSF_NULL)
|
||||||
{
|
{
|
||||||
//if (e) *e = TSF_INVALID_NOSAMPLEDATA;
|
//if (e) *e = TSF_INVALID_NOSAMPLEDATA;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
float* fontSamples; unsigned int fontSampleCount;
|
||||||
|
if (!tsf_decode_samples(smplBuffer, smplLength, &fontSamples, &fontSampleCount, &hydra)) goto out_of_memory;
|
||||||
|
if (fontSamples == (float*)smplBuffer) smplBuffer = TSF_NULL; // Was converted inline, don't free below
|
||||||
res = (tsf*)TSF_MALLOC(sizeof(tsf));
|
res = (tsf*)TSF_MALLOC(sizeof(tsf));
|
||||||
if (!res) goto out_of_memory;
|
if (res) TSF_MEMSET(res, 0, sizeof(tsf));
|
||||||
TSF_MEMSET(res, 0, sizeof(tsf));
|
if (!res || !tsf_load_presets(res, &hydra, fontSampleCount)) { TSF_FREE(fontSamples); goto out_of_memory; }
|
||||||
if (!tsf_load_presets(res, &hydra, fontSampleCount)) goto out_of_memory;
|
|
||||||
res->fontSamples = fontSamples;
|
res->fontSamples = fontSamples;
|
||||||
fontSamples = TSF_NULL; //don't free below
|
|
||||||
res->outSampleRate = 44100.0f;
|
res->outSampleRate = 44100.0f;
|
||||||
}
|
}
|
||||||
if (0)
|
if (0)
|
||||||
|
@ -1319,7 +1402,7 @@ TSFDEF tsf* tsf_load(struct tsf_stream* stream)
|
||||||
TSF_FREE(hydra.phdrs); TSF_FREE(hydra.pbags); TSF_FREE(hydra.pmods);
|
TSF_FREE(hydra.phdrs); TSF_FREE(hydra.pbags); TSF_FREE(hydra.pmods);
|
||||||
TSF_FREE(hydra.pgens); TSF_FREE(hydra.insts); TSF_FREE(hydra.ibags);
|
TSF_FREE(hydra.pgens); TSF_FREE(hydra.insts); TSF_FREE(hydra.ibags);
|
||||||
TSF_FREE(hydra.imods); TSF_FREE(hydra.igens); TSF_FREE(hydra.shdrs);
|
TSF_FREE(hydra.imods); TSF_FREE(hydra.igens); TSF_FREE(hydra.shdrs);
|
||||||
TSF_FREE(fontSamples);
|
TSF_FREE(smplBuffer);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1610,7 +1693,7 @@ static struct tsf_channel* tsf_channel_init(tsf* f, int channel)
|
||||||
if (!f->channels)
|
if (!f->channels)
|
||||||
{
|
{
|
||||||
f->channels = (struct tsf_channels*)TSF_MALLOC(sizeof(struct tsf_channels) + sizeof(struct tsf_channel) * channel);
|
f->channels = (struct tsf_channels*)TSF_MALLOC(sizeof(struct tsf_channels) + sizeof(struct tsf_channel) * channel);
|
||||||
if (!f->channels) return NULL;
|
if (!f->channels) return TSF_NULL;
|
||||||
f->channels->setupVoice = &tsf_channel_setup_voice;
|
f->channels->setupVoice = &tsf_channel_setup_voice;
|
||||||
f->channels->channelNum = 0;
|
f->channels->channelNum = 0;
|
||||||
f->channels->activeChannel = 0;
|
f->channels->activeChannel = 0;
|
||||||
|
@ -1618,7 +1701,7 @@ static struct tsf_channel* tsf_channel_init(tsf* f, int channel)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
struct tsf_channels *newChannels = (struct tsf_channels*)TSF_REALLOC(f->channels, sizeof(struct tsf_channels) + sizeof(struct tsf_channel) * channel);
|
struct tsf_channels *newChannels = (struct tsf_channels*)TSF_REALLOC(f->channels, sizeof(struct tsf_channels) + sizeof(struct tsf_channel) * channel);
|
||||||
if (!newChannels) return NULL;
|
if (!newChannels) return TSF_NULL;
|
||||||
f->channels = newChannels;
|
f->channels = newChannels;
|
||||||
}
|
}
|
||||||
i = f->channels->channelNum;
|
i = f->channels->channelNum;
|
||||||
|
|
|
@ -3493,12 +3493,22 @@ DO
|
||||||
' Verify there are no extra characters after end quote
|
' Verify there are no extra characters after end quote
|
||||||
IF INSTR(MidiSoundFont$, CHR$(34)) <> LEN(MidiSoundFont$) THEN a$ = "Unexpected characters after the quoted file name": GOTO errmes
|
IF INSTR(MidiSoundFont$, CHR$(34)) <> LEN(MidiSoundFont$) THEN a$ = "Unexpected characters after the quoted file name": GOTO errmes
|
||||||
|
|
||||||
|
' Strip the trailing quote
|
||||||
MidiSoundFont$ = MID$(MidiSoundFont$, 1, LEN(MidiSoundFont$) - 1)
|
MidiSoundFont$ = MID$(MidiSoundFont$, 1, LEN(MidiSoundFont$) - 1)
|
||||||
|
|
||||||
|
IF NOT _FILEEXISTS(MidiSoundFont$) THEN
|
||||||
|
' Just try to concatenate the path with the source or include path and check if we are able to find the file
|
||||||
|
IF inclevel > 0 AND _FILEEXISTS(getfilepath(incname(inclevel)) + MidiSoundFont$) THEN
|
||||||
|
MidiSoundFont$ = getfilepath(incname(inclevel)) + MidiSoundFont$
|
||||||
|
ELSEIF _FILEEXISTS(FixDirectoryName(idepath$) + MidiSoundFont$) THEN
|
||||||
|
MidiSoundFont$ = FixDirectoryName(idepath$) + MidiSoundFont$
|
||||||
|
END IF
|
||||||
|
|
||||||
IF NOT _FILEEXISTS(MidiSoundFont$) THEN
|
IF NOT _FILEEXISTS(MidiSoundFont$) THEN
|
||||||
a$ = "Soundfont file " + AddQuotes$(MidiSoundFont$) + " could not be found!"
|
a$ = "Soundfont file " + AddQuotes$(MidiSoundFont$) + " could not be found!"
|
||||||
GOTO errmes
|
GOTO errmes
|
||||||
END IF
|
END IF
|
||||||
|
END IF
|
||||||
ELSE
|
ELSE
|
||||||
' Constant values, only one for now
|
' Constant values, only one for now
|
||||||
SELECT CASE UCASE$(MidiSoundFont$)
|
SELECT CASE UCASE$(MidiSoundFont$)
|
||||||
|
|
Loading…
Reference in a new issue