1 /*
2 MusicIO.cpp
3
4 Copyright 2009-2011, Alan Calvert
5 Copyright 2014-2020, Will Godfrey & others
6
7 This file is part of yoshimi, which is free software: you can
8 redistribute it and/or modify it under the terms of the GNU General
9 Public License as published by the Free Software Foundation, either
10 version 2 of the License, or (at your option) any later version.
11
12 yoshimi is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with yoshimi. If not, see <http://www.gnu.org/licenses/>.
19
20 */
21
22 /*
23 * Uncomment the following define to emulate aftertouch
24 * To get the impression of channel aftertouch we change the
25 * event of the specified controller number.
26 * Change the value to suit your circumstances.
27 */
28 //#define AFTERTOUCH_EMULATE 94
29
30 #include "Misc/Config.h"
31 #include "Misc/SynthEngine.h"
32 #include "Misc/FormatFuncs.h"
33 #include "MusicIO/MusicIO.h"
34
35 using func::asString;
36
37
MusicIO(SynthEngine * _synth,BeatTracker * _beatTracker)38 MusicIO::MusicIO(SynthEngine *_synth, BeatTracker *_beatTracker) :
39 interleaved(NULL),
40 beatTracker(_beatTracker),
41 synth(_synth)
42 {
43 memset(zynLeft, 0, sizeof(float *) * (NUM_MIDI_PARTS + 1));
44 memset(zynRight, 0, sizeof(float *) * (NUM_MIDI_PARTS + 1));
45 LV2_engine = synth->getIsLV2Plugin();
46 }
47
48
~MusicIO()49 MusicIO::~MusicIO()
50 {
51 for (int npart = 0; npart < (NUM_MIDI_PARTS + 1); ++npart)
52 {
53 if (zynLeft[npart])
54 {
55 fftwf_free(zynLeft[npart]);
56 zynLeft[npart] = NULL;
57 }
58 if (zynRight[npart])
59 {
60 fftwf_free(zynRight[npart]);
61 zynRight[npart] = NULL;
62 }
63 }
64 }
65
66
setMidi(unsigned char par0,unsigned char par1,unsigned char par2,bool in_place)67 void MusicIO::setMidi(unsigned char par0, unsigned char par1, unsigned char par2, bool in_place)
68 {
69 if (synth->audioOut.load() != _SYS_::mute::Idle)
70 return; // nobody listening!
71
72 bool inSync = LV2_engine || (synth->getRuntime().audioEngine == jack_audio && synth->getRuntime().midiEngine == jack_midi);
73
74 CommandBlock putData;
75
76 unsigned int event = par0 & 0xf0;
77 unsigned char channel = par0 & 0xf;
78
79
80 #ifdef AFTERTOUCH_EMULATE
81
82 if (event == 0xb0 && par1 == AFTERTOUCH_EMULATE)
83 {
84 par0 = 0xd0 | channel; // change to chanel aftertouch
85 par1 = par2; // shift parameter across
86 synth->mididecode.midiProcess(par0, par1, par2, in_place, inSync);
87 return;
88 }
89 #endif
90
91 /*
92 * This below is a much simpler (faster) way
93 * to do note-on and note-off
94 * Tested on ALSA JACK and LV2 all combinations!
95 */
96
97 if (event == 0x80 || event == 0x90)
98 {
99 if (par2 < 1) // zero volume note on.
100 event = 0x80;
101
102 #ifdef REPORT_NOTES_ON_OFF
103 if (event == 0x80) // note test
104 ++synth->getRuntime().noteOffSent;
105 else
106 ++synth->getRuntime().noteOnSent;
107 #endif
108
109 if (inSync)
110 {
111 if (event == 0x80)
112 synth->NoteOff(channel, par1);
113 else
114 synth->NoteOn(channel, par1, par2);
115 }
116 else
117 {
118 putData.data.value = float(par2);
119 putData.data.type = 8;
120 putData.data.control = (event == 0x80);
121 putData.data.part = TOPLEVEL::section::midiIn;
122 putData.data.kit = channel;
123 putData.data.engine = par1;
124 synth->midilearn.writeMidi(&putData, false);
125 }
126 return;
127 }
128 synth->mididecode.midiProcess(par0, par1, par2, in_place, inSync);
129 }
130
131
prepBuffers(void)132 bool MusicIO::prepBuffers(void)
133 {
134 int buffersize = getBuffersize();
135 if (buffersize > 0)
136 {
137 for (int part = 0; part < (NUM_MIDI_PARTS + 1); part++)
138 {
139 if (!(zynLeft[part] = (float*) fftwf_malloc(buffersize * sizeof(float))))
140 goto bail_out;
141 if (!(zynRight[part] = (float*) fftwf_malloc(buffersize * sizeof(float))))
142 goto bail_out;
143 memset(zynLeft[part], 0, buffersize * sizeof(float));
144 memset(zynRight[part], 0, buffersize * sizeof(float));
145
146 }
147 return true;
148 }
149
150 bail_out:
151 synth->getRuntime().Log("Failed to allocate audio buffers, size " + asString(buffersize));
152 for (int part = 0; part < (NUM_MIDI_PARTS + 1); part++)
153 {
154 if (zynLeft[part])
155 {
156 fftwf_free(zynLeft[part]);
157 zynLeft[part] = NULL;
158 }
159 if (zynRight[part])
160 {
161 fftwf_free(zynRight[part]);
162 zynRight[part] = NULL;
163 }
164 }
165 if (interleaved)
166 {
167 delete[] interleaved;
168 interleaved = NULL;
169 }
170 return false;
171 }
172
BeatTracker()173 BeatTracker::BeatTracker() :
174 songVsMonotonicBeatDiff(0)
175 {
176 }
177
~BeatTracker()178 BeatTracker::~BeatTracker()
179 {
180 }
181
adjustMonotonicRounding(BeatTracker::BeatValues * beats)182 void BeatTracker::adjustMonotonicRounding(BeatTracker::BeatValues *beats)
183 {
184 // Try to compensate for rounding errors in monotonic beat. If the
185 // difference is small enough from the song beat, then we assume we have not
186 // repositioned the transport and we derive an exact value of the monotonic
187 // beat from the song beat, instead of adding BPM on every cycle, which
188 // accumulates a lot of error over time.
189 if (fabsf(beats->songBeat + songVsMonotonicBeatDiff - beats->monotonicBeat) < 0.1f)
190 beats->monotonicBeat = beats->songBeat + songVsMonotonicBeatDiff;
191 else
192 songVsMonotonicBeatDiff = beats->monotonicBeat - beats->songBeat;
193 }
194
MultithreadedBeatTracker()195 MultithreadedBeatTracker::MultithreadedBeatTracker()
196 {
197 timespec time;
198 clock_gettime(CLOCK_MONOTONIC, &time);
199
200 uint64_t clock = time.tv_sec * 1000000 + time.tv_nsec / 1000;
201
202 lastTimeUs = clock;
203 lastSongBeat = 0;
204 lastMonotonicBeat = 0;
205 timeUs = clock;
206 songBeat = 0;
207 monotonicBeat = 0;
208 bpm = 120;
209 pthread_mutex_init(&mutex, NULL);
210 }
211
~MultithreadedBeatTracker()212 MultithreadedBeatTracker::~MultithreadedBeatTracker()
213 {
214 pthread_mutex_destroy(&mutex);
215 }
216
setBeatValues(BeatTracker::BeatValues beats)217 BeatTracker::BeatValues MultithreadedBeatTracker::setBeatValues(BeatTracker::BeatValues beats)
218 {
219 timespec time;
220 clock_gettime(CLOCK_MONOTONIC, &time);
221
222 uint64_t clock = time.tv_sec * 1000000 + time.tv_nsec / 1000;
223
224 adjustMonotonicRounding(&beats);
225
226 //--------------------------------
227 pthread_mutex_lock(&mutex);
228
229 lastTimeUs = timeUs;
230
231 if (beats.songBeat >= LFO_BPM_LCM) {
232 beats.songBeat -= LFO_BPM_LCM;
233 lastSongBeat = songBeat - LFO_BPM_LCM;
234 } else
235 lastSongBeat = songBeat;
236
237 if (beats.monotonicBeat >= LFO_BPM_LCM) {
238 beats.monotonicBeat -= LFO_BPM_LCM;
239 lastMonotonicBeat = monotonicBeat - LFO_BPM_LCM;
240 } else
241 lastMonotonicBeat = monotonicBeat;
242
243 timeUs = clock;
244 songBeat = beats.songBeat;
245 monotonicBeat = beats.monotonicBeat;
246 bpm = beats.bpm;
247
248 pthread_mutex_unlock(&mutex);
249 //--------------------------------
250
251 return beats;
252 }
253
getBeatValues()254 BeatTracker::BeatValues MultithreadedBeatTracker::getBeatValues()
255 {
256 timespec t;
257 clock_gettime(CLOCK_MONOTONIC, &t);
258
259 uint64_t clock = t.tv_sec * 1000000 + t.tv_nsec / 1000;
260
261 BeatTracker::BeatValues ret;
262
263 pthread_mutex_lock(&mutex);
264 uint64_t lastTime = lastTimeUs;
265 float lastSongBeatTmp = lastSongBeat;
266 float lastMonotonicBeatTmp = lastMonotonicBeat;
267 uint64_t time = timeUs;
268 float songBeatTmp = songBeat;
269 float monotonicBeatTmp = monotonicBeat;
270 ret.bpm = bpm;
271 pthread_mutex_unlock(&mutex);
272
273 if (time == lastTime) {
274 if (clock - time > 1000000) {
275 // If no MIDI clock messages have arrived for over a second, revert
276 // to a static 120 BPM. This is just a fallback to prevent
277 // oscillators from stalling completely.
278 ret.songBeat = songBeatTmp + (float)(clock - time) / 1000000.0f * 120.0f / 60.0f;
279 ret.monotonicBeat = monotonicBeatTmp + (float)(clock - time) / 1000000.0f * 120.0f / 60.0f;
280 }
281 } else {
282 // Based on beat and clock from MIDI thread, interpolate and find the
283 // beat for audio thread.
284 float ratio = (float)(clock - lastTime) / (time - lastTime);
285 ret.songBeat = ratio * (songBeatTmp - lastSongBeatTmp) + lastSongBeatTmp;
286 ret.monotonicBeat = ratio * (monotonicBeatTmp - lastMonotonicBeatTmp) + lastMonotonicBeatTmp;
287 }
288
289 return ret;
290 }
291
SinglethreadedBeatTracker()292 SinglethreadedBeatTracker::SinglethreadedBeatTracker()
293 {
294 beats.songBeat = 0;
295 beats.monotonicBeat = 0;
296 beats.bpm = 120;
297 }
298
setBeatValues(BeatTracker::BeatValues beats)299 BeatTracker::BeatValues SinglethreadedBeatTracker::setBeatValues(BeatTracker::BeatValues beats)
300 {
301 if (beats.songBeat >= LFO_BPM_LCM)
302 beats.songBeat -= LFO_BPM_LCM;
303 if (beats.monotonicBeat >= LFO_BPM_LCM)
304 beats.monotonicBeat -= LFO_BPM_LCM;
305
306 adjustMonotonicRounding(&beats);
307
308 this->beats = beats;
309
310 return beats;
311 }
312
getBeatValues()313 BeatTracker::BeatValues SinglethreadedBeatTracker::getBeatValues()
314 {
315 return beats;
316 }
317