Subversion Repositories eduke32

Compare Revisions

Ignore whitespace Rev 8754 → Rev 8755

/source/audiolib/include/driver_sf2.h
1,4 → 1,7
#pragma once
#ifndef driver_sf2_h__
#define driver_sf2_h__
 
#include "compat.h"
#include "midifuncs.h"
 
6,18 → 9,18
 
enum
{
SF2_Warning = -2,
SF2_Error = -1,
SF2_Ok = 0,
SF2_Error = -1,
SF2_Ok = 0,
SF2_BankError = 1,
};
 
int SF2_GetError(void);
const char *SF2_ErrorString(int ErrorNumber);
int SF2Drv_GetError(void);
const char *SF2Drv_ErrorString(int ErrorNumber);
 
int SF2_MIDI_Init(midifuncs *);
void SF2_MIDI_Shutdown(void);
int SF2_MIDI_StartPlayback(void);
void SF2_MIDI_HaltPlayback(void);
void SF2_MIDI_SetTempo(int tempo, int division);
void SF2_MIDI_Service(void);
int SF2Drv_MIDI_Init(midifuncs *);
void SF2Drv_MIDI_Shutdown(void);
int SF2Drv_MIDI_StartPlayback(void);
void SF2Drv_MIDI_HaltPlayback(void);
void SF2Drv_MIDI_SetTempo(int tempo, int division);
void SF2Drv_MIDI_Service(void);
#endif // driver_sf2_h__
/source/audiolib/src/driver_sf2.cpp
1,111 → 1,133
//-------------------------------------------------------------------------
/*
Copyright (C) 2020 EDuke32 developers and contributors
 
This file is part of EDuke32.
 
EDuke32 is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
See the GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
//-------------------------------------------------------------------------
 
#include "driver_sf2.h"
#include "compat.h"
 
#include "_multivc.h"
#include "common.h"
#include "midi.h"
#include "_multivc.h"
#include "vfs.h"
 
#define TSF_IMPLEMENTATION
#define TSF_NO_STDIO
#define TSF_MALLOC Bmalloc
#define TSF_REALLOC Brealloc
#define TSF_FREE Bfree
#define TSF_MALLOC Xmalloc
#define TSF_REALLOC Xrealloc
#define TSF_FREE Xfree
#define TSF_MEMCPY Bmemcpy
#define TSF_MEMSET Bmemset
 
#include "tsf.h"
 
char SF2_BankFile[BMAX_PATH];
static int SF2_Volume = MIDI_MaxVolume;
static tsf *synth;
static tsf *sf2_synth;
char SF2_BankFile[BMAX_PATH];
static int SF2_Volume = MIDI_MaxVolume;
static int ErrorCode = SF2_Ok;
 
static inline int SF2_SetError(int const status) { return (ErrorCode = status); }
 
int SF2Drv_GetError(void) { return ErrorCode; }
 
static void SF2_NoteOff(int channel, int key, int velocity)
{
UNREFERENCED_PARAMETER(velocity);
tsf_channel_note_off(synth, channel, key);
tsf_channel_note_off(sf2_synth, channel, key);
}
 
static void SF2_NoteOn(int channel, int key, int velocity)
{
tsf_channel_note_on(synth, channel, key, velocity * (1.f/127.f));
}
static void SF2_NoteOn(int channel, int key, int velocity) { tsf_channel_note_on(sf2_synth, channel, key, velocity * (1.f / 127.f)); }
static void SF2_ControlChange(int channel, int number, int value) { tsf_channel_midi_control(sf2_synth, channel, number, value); }
static void SF2_ProgramChange(int channel, int program) { tsf_channel_set_presetnumber(sf2_synth, channel, program, channel == 9); }
static void SF2_SetPitchBend(int channel, int lsb, int msb) { tsf_channel_set_pitchwheel(sf2_synth, channel, (msb << 7) | lsb); }
static void SF2_SetVolume(int volume) { SF2_Volume = clamp(volume, 0, MIDI_MaxVolume); }
 
static void SF2_ControlChange(int channel, int number, int value)
const char *SF2Drv_ErrorString(int ErrorNumber)
{
tsf_channel_midi_control(synth, channel, number, value);
switch (ErrorNumber)
{
case SF2_Error: return SF2Drv_ErrorString(ErrorCode);
case SF2_Ok: return "SF2 ok.";
case SF2_BankError: return "SF2 bank error.";
default: return "Unknown SF2 error.";
}
}
 
static void SF2_ProgramChange(int channel, int program)
static int sf2_stream_read(void *handle, void *ptr, unsigned int size) { return kread(*(buildvfs_kfd *)handle, ptr, size); };
static int sf2_stream_skip(void *handle, unsigned int size) { return !klseek(*(buildvfs_kfd *)handle, size, SEEK_CUR); };
 
static int SF2_Load(char const *const filename)
{
tsf_channel_set_presetnumber(synth, channel, program, channel == 9);
}
buildvfs_kfd sf2_kfd = kopen4loadfrommod(filename, 0);
tsf_stream sf2_stream = { &sf2_kfd, &sf2_stream_read, &sf2_stream_skip };
 
static void SF2_SetPitchBend(int channel, int lsb, int msb)
{
tsf_channel_set_pitchwheel(synth, channel, (msb << 7) | lsb);
if (sf2_kfd != buildvfs_kfd_invalid)
{
tsf_close(sf2_synth);
sf2_synth = tsf_load(&sf2_stream);
kclose(sf2_kfd);
 
if (sf2_synth)
{
MV_Printf(": loaded \"%s\"", filename);
return SF2_Ok;
}
}
 
MV_Printf(": error loading \"%s\"!", filename);
return SF2_Error;
}
 
static void SF2_SetVolume(int volume)
int SF2Drv_MIDI_Init(midifuncs* const funcs)
{
SF2_Volume = clamp(volume, 0, MIDI_MaxVolume);
}
SF2Drv_MIDI_Shutdown();
 
static int ErrorCode = SF2_Ok;
auto filename = SF2_BankFile;
 
#define SF2_SetErrorCode(status) ErrorCode = (status);
 
int SF2_GetError(void) { return ErrorCode; }
 
const char* SF2_ErrorString(int ErrorNumber)
{
const char *ErrorString;
switch( ErrorNumber )
if (!filename[0])
{
case SF2_Warning :
case SF2_Error :
ErrorString = SF2_ErrorString( ErrorCode );
break;
fnlist_t fnl = FNLIST_INITIALIZER;
 
case SF2_Ok :
ErrorString = "SF2 ok.";
break;
// default to the first .sf2 we find if cvar mus_sf2_bank is unset
fnlist_getnames(&fnl, g_modDir, "*.sf2", 0, 0);
 
case SF2_BankError:
ErrorString = "SF2 bank error.";
break;
default:
ErrorString = "Unknown SF2 error.";
break;
}
return ErrorString;
}
if (!fnl.findfiles)
fnlist_getnames(&fnl, "/", "*.sf2", 0, 0);
 
static int sf2_stream_read(buildvfs_kfd *handle, void *ptr, unsigned int size)
{
return kread(*handle, ptr, size);
};
if (fnl.findfiles)
filename = Xstrdup(fnl.findfiles->name);
 
static int sf2_stream_skip(buildvfs_kfd *handle, unsigned int size)
{
return !klseek(*handle, size, SEEK_CUR);
};
fnlist_clearnames(&fnl);
 
int SF2_MIDI_Init(midifuncs * const funcs)
{
SF2_MIDI_Shutdown();
if (!filename[0])
{
MV_Printf(": no .sf2 data found!\n");
return SF2_SetError(SF2_BankError);
}
}
 
auto sf2_handle = kopen4load(SF2_BankFile, 0);
if (sf2_handle == buildvfs_kfd_invalid)
return SF2_BankError;
int const loaded = SF2_Load(filename);
 
struct tsf_stream sf2_stream = { &sf2_handle, (int(*)(void*,void*,unsigned int)) & sf2_stream_read, (int(*)(void*,unsigned int)) & sf2_stream_skip };
if (filename != SF2_BankFile)
Xfree(filename);
 
synth = tsf_load(&sf2_stream);
if (!synth)
return SF2_BankError;
kclose(sf2_handle);
if (loaded != SF2_Ok || !sf2_synth)
return SF2_SetError(SF2_BankError);
 
Bmemset(funcs, 0, sizeof(midifuncs));
 
123,45 → 145,45
return SF2_Ok;
}
 
void SF2_MIDI_HaltPlayback(void) { MV_UnhookMusicRoutine(); }
void SF2Drv_MIDI_HaltPlayback(void) { MV_UnhookMusicRoutine(); }
 
void SF2_MIDI_Shutdown(void)
void SF2Drv_MIDI_Shutdown(void)
{
SF2_MIDI_HaltPlayback();
SF2Drv_MIDI_HaltPlayback();
 
if (synth != nullptr)
tsf_close(synth);
synth = nullptr;
tsf_close(sf2_synth);
sf2_synth = nullptr;
ErrorCode = SF2_Ok;
}
 
int SF2_MIDI_StartPlayback(void)
int SF2Drv_MIDI_StartPlayback(void)
{
SF2_MIDI_HaltPlayback();
SF2Drv_MIDI_HaltPlayback();
 
tsf_set_output(synth, MV_Channels == 1 ? TSF_MONO : TSF_STEREO_INTERLEAVED, MV_MixRate, 0);
tsf_reset(synth);
tsf_set_output(sf2_synth, MV_Channels == 1 ? TSF_MONO : TSF_STEREO_INTERLEAVED, MV_MixRate, 0);
tsf_channel_set_bank_preset(sf2_synth, 9, 128, 0);
tsf_reset(sf2_synth);
 
for (int channel = 0; channel < 16; channel++)
{
SF2_ProgramChange(channel, 0);
}
MV_HookMusicRoutine(SF2_MIDI_Service);
 
MV_HookMusicRoutine(SF2Drv_MIDI_Service);
 
return MIDI_Ok;
}
 
void SF2_MIDI_SetTempo(int const tempo, int const division)
void SF2Drv_MIDI_SetTempo(int const tempo, int const division)
{
MV_MIDIRenderTempo = tempo * division / 60;
MV_MIDIRenderTimer = 0;
}
 
void SF2_MIDI_Service(void)
void SF2Drv_MIDI_Service(void)
{
int16_t * buffer16 = (int16_t *)MV_MusicBuffer;
int32_t const samples = MV_MIXBUFFERSIZE;
float buf[samples * 2];
int16_t * buffer16 = (int16_t *)MV_MusicBuffer;
static float fbuf[MV_MIXBUFFERSIZE * 2];
 
for (int32_t i = 0; i < samples;)
for (int i = 0; i < MV_MIXBUFFERSIZE;)
{
while (MV_MIDIRenderTimer >= MV_MixRate)
{
169,14 → 191,20
MIDI_ServiceRoutine();
MV_MIDIRenderTimer -= MV_MixRate;
}
int32_t samplesrender = MV_MIDIRenderTempo > 0 ? (MV_MixRate - MV_MIDIRenderTimer + MV_MIDIRenderTempo - 1) / MV_MIDIRenderTempo : INT32_MAX;
samplesrender = min(samplesrender, samples - i);
tsf_render_float(synth, buf, samplesrender);
for (int32_t j = 0; j < samplesrender * MV_Channels; j++)
{
*buffer16++ = clamp(int32_t(buf[j]*SF2_Volume*(32768.f/MIDI_MaxVolume)), INT16_MIN, INT16_MAX);
}
if (MV_MIDIRenderTempo >= 0) MV_MIDIRenderTimer += MV_MIDIRenderTempo * samplesrender;
i += samplesrender;
 
int samples = MV_MIDIRenderTempo > 0 ? (MV_MixRate - MV_MIDIRenderTimer + MV_MIDIRenderTempo - 1) / MV_MIDIRenderTempo : MV_MIXBUFFERSIZE;
samples = min(samples, MV_MIXBUFFERSIZE - i);
tsf_render_float(sf2_synth, fbuf, samples);
 
int const nsamples = samples * MV_Channels;
float const fvolume = SF2_Volume * (32768.f / MIDI_MaxVolume);
 
for (int j = 0; j < nsamples; j++)
*buffer16++ = clamp(Blrintf(fbuf[j] * fvolume), INT16_MIN, INT16_MAX);
 
if (MV_MIDIRenderTempo >= 0)
MV_MIDIRenderTimer += MV_MIDIRenderTempo * samples;
 
i += samples;
}
}
/source/audiolib/src/drivers.cpp
152,20 → 152,20
// TinySoundFont
{
"SoundFont2 synthesizer",
SF2_GetError,
SF2_ErrorString,
SF2Drv_GetError,
SF2Drv_ErrorString,
 
UNSUPPORTED_PCM,
 
EMIDI_GeneralMIDI,
SF2_MIDI_Init,
SF2_MIDI_Shutdown,
SF2_MIDI_StartPlayback,
SF2_MIDI_HaltPlayback,
SF2_MIDI_SetTempo,
SF2Drv_MIDI_Init,
SF2Drv_MIDI_Shutdown,
SF2Drv_MIDI_StartPlayback,
SF2Drv_MIDI_HaltPlayback,
SF2Drv_MIDI_SetTempo,
nullptr,
nullptr,
SF2_MIDI_Service,
SF2Drv_MIDI_Service,
},
};
 
/source/duke3d/src/menus.cpp
1241,7 → 1241,7
#ifdef _WIN32
"Windows MME",
#endif
"SF2 synth",
"SoundFont 2",
};
static int32_t MEOSV_SOUND_MIDIDRIVER[] = {
ASS_OPL3,