Subversion Repositories eduke32

Rev

Rev 8752 | Rev 8769 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
8755 terminx 1
//-------------------------------------------------------------------------
2
/*
3
Copyright (C) 2020 EDuke32 developers and contributors
4
 
5
This file is part of EDuke32.
6
 
7
EDuke32 is free software; you can redistribute it and/or
8
modify it under the terms of the GNU General Public License version 2
9
as published by the Free Software Foundation.
10
 
11
This program is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
 
15
See the GNU General Public License for more details.
16
 
17
You should have received a copy of the GNU General Public License
18
along with this program; if not, write to the Free Software
19
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20
*/
21
//-------------------------------------------------------------------------
22
 
8752 terminx 23
#include "driver_sf2.h"
8755 terminx 24
 
25
#include "_multivc.h"
26
#include "common.h"
8752 terminx 27
#include "midi.h"
28
 
29
#define TSF_IMPLEMENTATION
30
#define TSF_NO_STDIO
8755 terminx 31
#define TSF_MALLOC  Xmalloc
32
#define TSF_REALLOC Xrealloc
33
#define TSF_FREE    Xfree
8752 terminx 34
#define TSF_MEMCPY Bmemcpy
35
#define TSF_MEMSET Bmemset
8755 terminx 36
 
8752 terminx 37
#include "tsf.h"
38
 
8755 terminx 39
static tsf *sf2_synth;
40
char        SF2_BankFile[BMAX_PATH];
41
static int  SF2_Volume = MIDI_MaxVolume;
42
static int  ErrorCode  = SF2_Ok;
8752 terminx 43
 
8755 terminx 44
static inline int SF2_SetError(int const status) { return (ErrorCode = status); }
45
 
46
int SF2Drv_GetError(void) { return ErrorCode; }
47
 
8752 terminx 48
static void SF2_NoteOff(int channel, int key, int velocity)
49
{
50
    UNREFERENCED_PARAMETER(velocity);
8755 terminx 51
    tsf_channel_note_off(sf2_synth, channel, key);
8752 terminx 52
}
53
 
8755 terminx 54
static void SF2_NoteOn(int channel, int key, int velocity)        { tsf_channel_note_on(sf2_synth, channel, key, velocity * (1.f / 127.f)); }
55
static void SF2_ControlChange(int channel, int number, int value) { tsf_channel_midi_control(sf2_synth, channel, number, value); }
56
static void SF2_ProgramChange(int channel, int program)           { tsf_channel_set_presetnumber(sf2_synth, channel, program, channel == 9); }
57
static void SF2_SetPitchBend(int channel, int lsb, int msb)       { tsf_channel_set_pitchwheel(sf2_synth, channel, (msb << 7) | lsb); }
58
static void SF2_SetVolume(int volume)                             { SF2_Volume = clamp(volume, 0, MIDI_MaxVolume); }
8752 terminx 59
 
8755 terminx 60
const char *SF2Drv_ErrorString(int ErrorNumber)
8752 terminx 61
{
8755 terminx 62
    switch (ErrorNumber)
63
    {
64
        case SF2_Error:     return SF2Drv_ErrorString(ErrorCode);
65
        case SF2_Ok:        return "SF2 ok.";
66
        case SF2_BankError: return "SF2 bank error.";
67
        default:            return "Unknown SF2 error.";
68
    }
8752 terminx 69
}
70
 
8755 terminx 71
static int sf2_stream_read(void *handle, void *ptr, unsigned int size) { return kread(*(buildvfs_kfd *)handle, ptr, size); };
72
static int sf2_stream_skip(void *handle, unsigned int size)            { return !klseek(*(buildvfs_kfd *)handle, size, SEEK_CUR); };
73
 
74
static int SF2_Load(char const *const filename)
8752 terminx 75
{
8755 terminx 76
    buildvfs_kfd sf2_kfd    = kopen4loadfrommod(filename, 0);
77
    tsf_stream   sf2_stream = { &sf2_kfd, &sf2_stream_read, &sf2_stream_skip };
8752 terminx 78
 
8755 terminx 79
    if (sf2_kfd != buildvfs_kfd_invalid)
80
    {
81
        tsf_close(sf2_synth);
82
        sf2_synth = tsf_load(&sf2_stream);
83
        kclose(sf2_kfd);
84
 
85
        if (sf2_synth)
86
        {
87
            MV_Printf(": loaded \"%s\"", filename);
88
            return SF2_Ok;
89
        }
90
    }
91
 
92
    MV_Printf(": error loading \"%s\"!", filename);
93
    return SF2_Error;
8752 terminx 94
}
95
 
8755 terminx 96
int SF2Drv_MIDI_Init(midifuncs* const funcs)
8752 terminx 97
{
8755 terminx 98
    SF2Drv_MIDI_Shutdown();
8752 terminx 99
 
8755 terminx 100
    auto filename = SF2_BankFile;
8752 terminx 101
 
8755 terminx 102
    if (!filename[0])
8752 terminx 103
    {
8755 terminx 104
        fnlist_t fnl = FNLIST_INITIALIZER;
8752 terminx 105
 
8755 terminx 106
        // default to the first .sf2 we find if cvar mus_sf2_bank is unset
107
        fnlist_getnames(&fnl, g_modDir, "*.sf2", 0, 0);
8752 terminx 108
 
8755 terminx 109
        if (!fnl.findfiles)
110
            fnlist_getnames(&fnl, "/", "*.sf2", 0, 0);
8752 terminx 111
 
8755 terminx 112
        if (fnl.findfiles)
113
            filename = Xstrdup(fnl.findfiles->name);
8752 terminx 114
 
8755 terminx 115
        fnlist_clearnames(&fnl);
8752 terminx 116
 
8755 terminx 117
        if (!filename[0])
118
        {
119
            MV_Printf(": no .sf2 data found!\n");
120
            return SF2_SetError(SF2_BankError);
121
        }
122
    }
8752 terminx 123
 
8755 terminx 124
    int const loaded = SF2_Load(filename);
8752 terminx 125
 
8755 terminx 126
    if (filename != SF2_BankFile)
127
        Xfree(filename);
8752 terminx 128
 
8755 terminx 129
    if (loaded != SF2_Ok || !sf2_synth)
130
        return SF2_SetError(SF2_BankError);
8752 terminx 131
 
132
    Bmemset(funcs, 0, sizeof(midifuncs));
133
 
134
    funcs->NoteOff           = SF2_NoteOff;
135
    funcs->NoteOn            = SF2_NoteOn;
136
    funcs->PolyAftertouch    = nullptr;
137
    funcs->ControlChange     = SF2_ControlChange;
138
    funcs->ProgramChange     = SF2_ProgramChange;
139
    funcs->ChannelAftertouch = nullptr;
140
    funcs->PitchBend         = SF2_SetPitchBend;
141
    funcs->SetVolume         = SF2_SetVolume;
142
 
143
    SF2_Volume = MIDI_MaxVolume;
144
 
145
    return SF2_Ok;
146
}
147
 
8755 terminx 148
void SF2Drv_MIDI_HaltPlayback(void) { MV_UnhookMusicRoutine(); }
8752 terminx 149
 
8755 terminx 150
void SF2Drv_MIDI_Shutdown(void)
8752 terminx 151
{
8755 terminx 152
    SF2Drv_MIDI_HaltPlayback();
8752 terminx 153
 
8755 terminx 154
    tsf_close(sf2_synth);
155
    sf2_synth = nullptr;
156
    ErrorCode = SF2_Ok;
8752 terminx 157
}
158
 
8755 terminx 159
int SF2Drv_MIDI_StartPlayback(void)
8752 terminx 160
{
8755 terminx 161
    SF2Drv_MIDI_HaltPlayback();
8752 terminx 162
 
8755 terminx 163
    tsf_set_output(sf2_synth, MV_Channels == 1 ? TSF_MONO : TSF_STEREO_INTERLEAVED, MV_MixRate, 0);
164
    tsf_channel_set_bank_preset(sf2_synth, 9, 128, 0);
165
    tsf_reset(sf2_synth);
166
 
8752 terminx 167
    for (int channel = 0; channel < 16; channel++)
168
        SF2_ProgramChange(channel, 0);
169
 
8755 terminx 170
    MV_HookMusicRoutine(SF2Drv_MIDI_Service);
171
 
8752 terminx 172
    return MIDI_Ok;
173
}
174
 
8755 terminx 175
void SF2Drv_MIDI_SetTempo(int const tempo, int const division)
8752 terminx 176
{
177
    MV_MIDIRenderTempo = tempo * division / 60;
178
    MV_MIDIRenderTimer = 0;
179
}
180
 
8755 terminx 181
void SF2Drv_MIDI_Service(void)
8752 terminx 182
{
8755 terminx 183
    int16_t *    buffer16 = (int16_t *)MV_MusicBuffer;
184
    static float fbuf[MV_MIXBUFFERSIZE * 2];
8752 terminx 185
 
8755 terminx 186
    for (int i = 0; i < MV_MIXBUFFERSIZE;)
8752 terminx 187
    {
188
        while (MV_MIDIRenderTimer >= MV_MixRate)
189
        {
190
            if (MV_MIDIRenderTempo >= 0)
191
                MIDI_ServiceRoutine();
192
            MV_MIDIRenderTimer -= MV_MixRate;
193
        }
8755 terminx 194
 
195
        int samples = MV_MIDIRenderTempo > 0 ? (MV_MixRate - MV_MIDIRenderTimer + MV_MIDIRenderTempo - 1) / MV_MIDIRenderTempo : MV_MIXBUFFERSIZE;
196
        samples     = min(samples, MV_MIXBUFFERSIZE - i);
197
        tsf_render_float(sf2_synth, fbuf, samples);
198
 
199
        int const   nsamples = samples * MV_Channels;
200
        float const fvolume  = SF2_Volume * (32768.f / MIDI_MaxVolume);
201
 
202
        for (int j = 0; j < nsamples; j++)
203
            *buffer16++ = clamp(Blrintf(fbuf[j] * fvolume), INT16_MIN, INT16_MAX);
204
 
205
        if (MV_MIDIRenderTempo >= 0)
206
            MV_MIDIRenderTimer += MV_MIDIRenderTempo * samples;
207
 
208
        i += samples;
8752 terminx 209
    }
210
}