/ source / audiolib / src / driver_sf2.cpp
driver_sf2.cpp
  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  
 23  #include "driver_sf2.h"
 24  
 25  #include "_multivc.h"
 26  #include "common.h"
 27  #include "midi.h"
 28  
 29  int SF2_EffectSampleBlockSize = 16;
 30  
 31  #define TSF_IMPLEMENTATION
 32  #define TSF_NO_STDIO
 33  #define TSF_RENDER_EFFECTSAMPLEBLOCK SF2_EffectSampleBlockSize
 34  #define TSF_MALLOC  Xmalloc
 35  #define TSF_REALLOC Xrealloc
 36  #define TSF_FREE    Xfree
 37  #define TSF_MEMCPY Bmemcpy
 38  #define TSF_MEMSET Bmemset
 39  
 40  #include "tsf.h"
 41  
 42  static tsf *sf2_synth;
 43  char        SF2_BankFile[BMAX_PATH];
 44  static int  SF2_Volume = MIDI_MaxVolume;
 45  static int  ErrorCode  = SF2_Ok;
 46  
 47  static inline int SF2_SetError(int const status) { return (ErrorCode = status); }
 48  
 49  int SF2Drv_GetError(void) { return ErrorCode; }
 50  
 51  static void SF2_NoteOff(int channel, int key, int velocity)
 52  {
 53      UNREFERENCED_PARAMETER(velocity);
 54      tsf_channel_note_off(sf2_synth, channel, key);
 55  }
 56  
 57  static void SF2_NoteOn(int channel, int key, int velocity)        { tsf_channel_note_on(sf2_synth, channel, key, velocity * (1.f / 127.f)); }
 58  static void SF2_ControlChange(int channel, int number, int value) { tsf_channel_midi_control(sf2_synth, channel, number, value); }
 59  static void SF2_ProgramChange(int channel, int program)           { tsf_channel_set_presetnumber(sf2_synth, channel, program, channel == 9); }
 60  static void SF2_SetPitchBend(int channel, int lsb, int msb)       { tsf_channel_set_pitchwheel(sf2_synth, channel, (msb << 7) | lsb); }
 61  static void SF2_SetVolume(int volume)                             { SF2_Volume = clamp(volume, 0, MIDI_MaxVolume); }
 62  
 63  const char *SF2Drv_ErrorString(int ErrorNumber)
 64  {
 65      switch (ErrorNumber)
 66      {
 67          case SF2_Error:     return SF2Drv_ErrorString(ErrorCode);
 68          case SF2_Ok:        return "SF2 ok.";
 69          case SF2_BankError: return "SF2 bank error.";
 70          default:            return "Unknown SF2 error.";
 71      }
 72  }
 73  
 74  static int sf2_stream_read(void *handle, void *ptr, unsigned int size) { return kread(*(buildvfs_kfd *)handle, ptr, size); };
 75  static int sf2_stream_skip(void *handle, unsigned int size)            { return !klseek(*(buildvfs_kfd *)handle, size, SEEK_CUR); };
 76  
 77  static int SF2_LoadBank(char const *const filename)
 78  {
 79      buildvfs_kfd sf2_kfd    = kopen4loadfrommod(filename, 0);
 80      tsf_stream   sf2_stream = { &sf2_kfd, &sf2_stream_read, &sf2_stream_skip };
 81  
 82      if (sf2_kfd != buildvfs_kfd_invalid)
 83      {
 84          tsf_close(sf2_synth);
 85          sf2_synth = tsf_load(&sf2_stream);
 86          kclose(sf2_kfd);
 87  
 88          if (sf2_synth)
 89          {
 90              VLOG_F(LOG_ASS, "Loaded \"%s\"", filename);
 91              return SF2_Ok;
 92          }
 93      }
 94  
 95      LOG_F(ERROR, "Unable to load \"%s\"!", filename);
 96      return SF2_Error;
 97  }
 98  
 99  int SF2Drv_MIDI_Init(midifuncs* const funcs)
100  {
101      SF2Drv_MIDI_Shutdown();
102  
103      auto filename = SF2_BankFile;
104  
105      if (!filename[0])
106      {
107          fnlist_t fnl = FNLIST_INITIALIZER;
108  
109          // default to the first .sf2 we find if cvar mus_sf2_bank is unset
110          fnlist_getnames(&fnl, g_modDir, "*.sf2", 0, 0);
111  
112          if (!fnl.findfiles)
113              fnlist_getnames(&fnl, "/", "*.sf2", 0, 0);
114  
115          if (fnl.findfiles)
116              filename = Xstrdup(fnl.findfiles->name);
117  
118          fnlist_clearnames(&fnl);
119  
120          if (!filename[0])
121          {
122              LOG_F(WARNING, "No .sf2 data found!");
123              return SF2_SetError(SF2_BankError);
124          }
125      }
126  
127      int const loaded = SF2_LoadBank(filename);
128  
129      if (filename != SF2_BankFile)
130          Xfree(filename);
131  
132      if (loaded != SF2_Ok || !sf2_synth)
133          return SF2_SetError(SF2_BankError);
134  
135      Bmemset(funcs, 0, sizeof(midifuncs));
136  
137      funcs->NoteOff           = SF2_NoteOff;
138      funcs->NoteOn            = SF2_NoteOn;
139      funcs->PolyAftertouch    = nullptr;
140      funcs->ControlChange     = SF2_ControlChange;
141      funcs->ProgramChange     = SF2_ProgramChange;
142      funcs->ChannelAftertouch = nullptr;
143      funcs->PitchBend         = SF2_SetPitchBend;
144      funcs->SetVolume         = SF2_SetVolume;
145  
146      SF2_Volume = MIDI_MaxVolume;
147  
148      return SF2_Ok;
149  }
150  
151  void SF2Drv_MIDI_HaltPlayback(void) { MV_UnhookMusicRoutine(); }
152  
153  void SF2Drv_MIDI_Shutdown(void)
154  {
155      SF2Drv_MIDI_HaltPlayback();
156  
157      tsf_close(sf2_synth);
158      sf2_synth = nullptr;
159      ErrorCode = SF2_Ok;
160  }
161  
162  int SF2Drv_MIDI_StartPlayback(void)
163  {
164      SF2Drv_MIDI_HaltPlayback();
165  
166      tsf_set_output(sf2_synth, MV_Channels == 1 ? TSF_MONO : TSF_STEREO_INTERLEAVED, TSF_INTERP_CUBIC, MV_MixRate, 0);
167      tsf_channel_set_bank_preset(sf2_synth, 9, 128, 0);
168      tsf_reset(sf2_synth);
169  
170      for (int channel = 0; channel < 16; channel++)
171          SF2_ProgramChange(channel, 0);
172  
173      MV_HookMusicRoutine(SF2Drv_MIDI_Service);
174  
175      return MIDI_Ok;
176  }
177  
178  void SF2Drv_MIDI_SetTempo(int const tempo, int const division)
179  {
180      MV_MIDIRenderTempo = tempo * division / 60;
181      MV_MIDIRenderTimer = 0;
182  }
183  
184  void SF2Drv_MIDI_Service(void)
185  {
186      int16_t *    buffer16 = (int16_t *)MV_MusicBuffer;
187      static float fbuf[MV_MIXBUFFERSIZE * 2];
188      float const  fvolume = SF2_Volume * (32768.f / MIDI_MaxVolume);
189  
190      for (int i = 0; i < MV_MIXBUFFERSIZE;)
191      {
192          while (MV_MIDIRenderTimer >= MV_MixRate)
193          {
194              if (MV_MIDIRenderTempo >= 0)
195                  MIDI_ServiceRoutine();
196              MV_MIDIRenderTimer -= MV_MixRate;
197          }
198  
199          int samples = MV_MIDIRenderTempo > 0 ? (MV_MixRate - MV_MIDIRenderTimer + MV_MIDIRenderTempo - 1) / MV_MIDIRenderTempo : MV_MIXBUFFERSIZE;
200          samples     = min(samples, MV_MIXBUFFERSIZE - i);
201          tsf_render_float(sf2_synth, fbuf, samples);
202  
203          int const nsamples = samples * MV_Channels;
204  
205          for (int j = 0; j < nsamples; j++)
206              *buffer16++ = clamp((fbuf[j] * fvolume), INT16_MIN, INT16_MAX);
207  
208          if (MV_MIDIRenderTempo >= 0)
209              MV_MIDIRenderTimer += MV_MIDIRenderTempo * samples;
210  
211          i += samples;
212      }
213  }