OpenTTD Source  1.11.0-beta2
fluidsynth.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of OpenTTD.
3  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4  * OpenTTD 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.
5  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6  */
7 
10 #include "../stdafx.h"
11 #include "../openttd.h"
12 #include "../sound_type.h"
13 #include "../debug.h"
14 #include "fluidsynth.h"
15 #include "midifile.hpp"
16 #include <fluidsynth.h>
17 #include "../mixer.h"
18 #include <mutex>
19 
20 static struct {
21  fluid_settings_t* settings;
22  fluid_synth_t* synth;
23  fluid_player_t* player;
24  std::mutex synth_mutex;
25 } _midi;
26 
29 
31 static const char *default_sf[] = {
32  /* Debian/Ubuntu/OpenSUSE preferred */
33  "/usr/share/sounds/sf2/FluidR3_GM.sf2",
34 
35  /* RedHat/Fedora/Arch preferred */
36  "/usr/share/soundfonts/FluidR3_GM.sf2",
37 
38  /* Debian/Ubuntu/OpenSUSE alternatives */
39  "/usr/share/sounds/sf2/TimGM6mb.sf2",
40  "/usr/share/sounds/sf2/FluidR3_GS.sf2",
41 
42  nullptr
43 };
44 
45 static void RenderMusicStream(int16 *buffer, size_t samples)
46 {
47  std::unique_lock<std::mutex> lock{ _midi.synth_mutex, std::try_to_lock };
48 
49  if (!lock.owns_lock() || !_midi.synth || !_midi.player) return;
50  fluid_synth_write_s16(_midi.synth, samples, buffer, 0, 2, buffer, 1, 2);
51 }
52 
53 const char *MusicDriver_FluidSynth::Start(const StringList &param)
54 {
55  std::lock_guard<std::mutex> lock{ _midi.synth_mutex };
56 
57  const char *sfont_name = GetDriverParam(param, "soundfont");
58  int sfont_id;
59 
60  DEBUG(driver, 1, "Fluidsynth: sf %s", sfont_name);
61 
62  /* Create the settings. */
63  _midi.settings = new_fluid_settings();
64  if (!_midi.settings) return "Could not create midi settings";
65  /* Don't try to lock sample data in memory, OTTD usually does not run with privileges allowing that */
66  fluid_settings_setint(_midi.settings, "synth.lock-memory", 0);
67 
68  /* Install the music render routine and set up the samplerate */
69  uint32 samplerate = MxSetMusicSource(RenderMusicStream);
70  fluid_settings_setnum(_midi.settings, "synth.sample-rate", samplerate);
71  DEBUG(driver, 1, "Fluidsynth: samplerate %.0f", (float)samplerate);
72 
73  /* Create the synthesizer. */
74  _midi.synth = new_fluid_synth(_midi.settings);
75  if (!_midi.synth) return "Could not open synth";
76 
77  /* Load a SoundFont and reset presets (so that new instruments
78  * get used from the SoundFont) */
79  if (!sfont_name) {
80  int i;
81  sfont_id = FLUID_FAILED;
82  for (i = 0; default_sf[i]; i++) {
83  if (!fluid_is_soundfont(default_sf[i])) continue;
84  sfont_id = fluid_synth_sfload(_midi.synth, default_sf[i], 1);
85  if (sfont_id != FLUID_FAILED) break;
86  }
87  if (sfont_id == FLUID_FAILED) return "Could not open any sound font";
88  } else {
89  sfont_id = fluid_synth_sfload(_midi.synth, sfont_name, 1);
90  if (sfont_id == FLUID_FAILED) return "Could not open sound font";
91  }
92 
93  _midi.player = nullptr;
94 
95  return nullptr;
96 }
97 
99 {
100  MxSetMusicSource(nullptr);
101 
102  std::lock_guard<std::mutex> lock{ _midi.synth_mutex };
103 
104  if (_midi.player != nullptr) delete_fluid_player(_midi.player);
105  _midi.player = nullptr;
106 
107  if (_midi.synth != nullptr) delete_fluid_synth(_midi.synth);
108  _midi.synth = nullptr;
109 
110  if (_midi.settings != nullptr) delete_fluid_settings(_midi.settings);
111  _midi.settings = nullptr;
112 }
113 
115 {
116  std::string filename = MidiFile::GetSMFFile(song);
117 
118  this->StopSong();
119 
120  if (filename.empty()) {
121  return;
122  }
123 
124  std::lock_guard<std::mutex> lock{ _midi.synth_mutex };
125 
126  _midi.player = new_fluid_player(_midi.synth);
127  if (!_midi.player) {
128  DEBUG(driver, 0, "Could not create midi player");
129  return;
130  }
131 
132  if (fluid_player_add(_midi.player, filename.c_str()) != FLUID_OK) {
133  DEBUG(driver, 0, "Could not open music file");
134  delete_fluid_player(_midi.player);
135  _midi.player = nullptr;
136  return;
137  }
138  if (fluid_player_play(_midi.player) != FLUID_OK) {
139  DEBUG(driver, 0, "Could not start midi player");
140  delete_fluid_player(_midi.player);
141  _midi.player = nullptr;
142  return;
143  }
144 }
145 
147 {
148  std::lock_guard<std::mutex> lock{ _midi.synth_mutex };
149 
150  if (!_midi.player) return;
151 
152  fluid_player_stop(_midi.player);
153  if (fluid_player_join(_midi.player) != FLUID_OK) {
154  DEBUG(driver, 0, "Could not join player");
155  }
156  delete_fluid_player(_midi.player);
157  fluid_synth_system_reset(_midi.synth);
158  fluid_synth_all_sounds_off(_midi.synth, -1);
159  _midi.player = nullptr;
160 }
161 
163 {
164  std::lock_guard<std::mutex> lock{ _midi.synth_mutex };
165  if (!_midi.player) return false;
166 
167  return fluid_player_get_status(_midi.player) == FLUID_PLAYER_PLAYING;
168 }
169 
171 {
172  std::lock_guard<std::mutex> lock{ _midi.synth_mutex };
173  if (_midi.settings == nullptr) return;
174 
175  /* Allowed range of synth.gain is 0.0 to 10.0 */
176  /* fluidsynth's default gain is 0.2, so use this as "full
177  * volume". Set gain using OpenTTD's volume, as a number between 0
178  * and 0.2. */
179  double gain = (1.0 * vol) / (128.0 * 5.0);
180  if (fluid_settings_setnum(_midi.settings, "synth.gain", gain) != FLUID_OK) {
181  DEBUG(driver, 0, "Could not set volume");
182  }
183 }
MusicDriver_FluidSynth::IsSongPlaying
bool IsSongPlaying() override
Are we currently playing a song?
Definition: fluidsynth.cpp:162
MusicDriver_FluidSynth::SetVolume
void SetVolume(byte vol) override
Set the volume, if possible.
Definition: fluidsynth.cpp:170
MusicDriver_FluidSynth::Start
const char * Start(const StringList &param) override
Start this driver.
Definition: fluidsynth.cpp:53
fluidsynth.h
synth_mutex
std::mutex synth_mutex
Guard mutex for synth access.
Definition: fluidsynth.cpp:24
lock
std::mutex lock
synchronization for playback status fields
Definition: win32_m.cpp:34
synth
fluid_synth_t * synth
FluidSynth synthesizer handle.
Definition: fluidsynth.cpp:22
MusicDriver_FluidSynth::StopSong
void StopSong() override
Stop playing the current song.
Definition: fluidsynth.cpp:146
_midi
static struct @25 _midi
Metadata about the midi we're playing.
iFMusicDriver_FluidSynth
static FMusicDriver_FluidSynth iFMusicDriver_FluidSynth
Factory for the FluidSynth driver.
Definition: fluidsynth.cpp:28
MusicDriver_FluidSynth::PlaySong
void PlaySong(const MusicSongInfo &song) override
Play a particular song.
Definition: fluidsynth.cpp:114
GetDriverParam
const char * GetDriverParam(const StringList &parm, const char *name)
Get a string parameter the list of parameters.
Definition: driver.cpp:39
default_sf
static const char * default_sf[]
List of sound fonts to try by default.
Definition: fluidsynth.cpp:31
player
fluid_player_t * player
FluidSynth MIDI player handle.
Definition: fluidsynth.cpp:23
DEBUG
#define DEBUG(name, level,...)
Output a line of debugging information.
Definition: debug.h:35
MusicSongInfo
Metadata about a music track.
Definition: base_media_base.h:291
StringList
std::vector< std::string > StringList
Type for a list of strings.
Definition: string_type.h:58
FMusicDriver_FluidSynth
Factory for the fluidsynth driver.
Definition: fluidsynth.h:33
settings
fluid_settings_t * settings
FluidSynth settings handle.
Definition: fluidsynth.cpp:21
MidiFile::GetSMFFile
static std::string GetSMFFile(const MusicSongInfo &song)
Get the name of a Standard MIDI File for a given song.
Definition: midifile.cpp:1047
MxSetMusicSource
uint32 MxSetMusicSource(MxStreamCallback music_callback)
Set source of PCM music.
Definition: mixer.cpp:228
MusicDriver_FluidSynth::Stop
void Stop() override
Stop this driver.
Definition: fluidsynth.cpp:98