Chris@49
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@0
|
2
|
Chris@0
|
3 /*
|
Chris@52
|
4 Sonic Visualiser
|
Chris@52
|
5 An audio file viewer and annotation editor.
|
Chris@52
|
6 Centre for Digital Music, Queen Mary, University of London.
|
Chris@0
|
7
|
Chris@52
|
8 This program is free software; you can redistribute it and/or
|
Chris@52
|
9 modify it under the terms of the GNU General Public License as
|
Chris@52
|
10 published by the Free Software Foundation; either version 2 of the
|
Chris@52
|
11 License, or (at your option) any later version. See the file
|
Chris@52
|
12 COPYING included with this distribution for more information.
|
Chris@0
|
13 */
|
Chris@0
|
14
|
Chris@0
|
15 /*
|
Chris@0
|
16 Based on trivial_sampler from the DSSI distribution
|
Chris@0
|
17 (by Chris Cannam, public domain).
|
Chris@0
|
18 */
|
Chris@0
|
19
|
Chris@0
|
20 #include "SamplePlayer.h"
|
Chris@573
|
21 #include "system/System.h"
|
Chris@0
|
22
|
Chris@646
|
23 #include "../api/dssi.h"
|
Chris@646
|
24
|
Chris@0
|
25 #include <cmath>
|
Chris@405
|
26 #include <cstdlib>
|
Chris@0
|
27
|
Chris@0
|
28 #include <QMutexLocker>
|
Chris@0
|
29 #include <QDir>
|
Chris@0
|
30 #include <QFileInfo>
|
Chris@0
|
31
|
Chris@1359
|
32 #ifdef Q_OS_WIN
|
Chris@1359
|
33 #include <windows.h>
|
Chris@1359
|
34 #define ENABLE_SNDFILE_WINDOWS_PROTOTYPES 1
|
Chris@1359
|
35 #endif
|
Chris@1359
|
36
|
Chris@0
|
37 #include <sndfile.h>
|
Chris@0
|
38 #include <samplerate.h>
|
Chris@0
|
39 #include <iostream>
|
Chris@0
|
40
|
Chris@601
|
41 //#define DEBUG_SAMPLE_PLAYER 1
|
Chris@601
|
42
|
Chris@0
|
43 const char *const
|
Chris@0
|
44 SamplePlayer::portNames[PortCount] =
|
Chris@0
|
45 {
|
Chris@0
|
46 "Output",
|
Chris@0
|
47 "Tuned (on/off)",
|
Chris@0
|
48 "Base Pitch (MIDI)",
|
Chris@143
|
49 "Tuning of A (Hz)",
|
Chris@0
|
50 "Sustain (on/off)",
|
Chris@0
|
51 "Release time (s)"
|
Chris@0
|
52 };
|
Chris@0
|
53
|
Chris@0
|
54 const LADSPA_PortDescriptor
|
Chris@0
|
55 SamplePlayer::ports[PortCount] =
|
Chris@0
|
56 {
|
Chris@0
|
57 LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO,
|
Chris@0
|
58 LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL,
|
Chris@0
|
59 LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL,
|
Chris@0
|
60 LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL,
|
Chris@0
|
61 LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL
|
Chris@0
|
62 };
|
Chris@0
|
63
|
Chris@0
|
64 const LADSPA_PortRangeHint
|
Chris@0
|
65 SamplePlayer::hints[PortCount] =
|
Chris@0
|
66 {
|
Chris@0
|
67 { 0, 0, 0 },
|
Chris@0
|
68 { LADSPA_HINT_DEFAULT_MAXIMUM | LADSPA_HINT_INTEGER |
|
Chris@0
|
69 LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 0, 1 },
|
Chris@0
|
70 { LADSPA_HINT_DEFAULT_MIDDLE | LADSPA_HINT_INTEGER |
|
Chris@0
|
71 LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 0, 120 },
|
Chris@143
|
72 { LADSPA_HINT_DEFAULT_440 | LADSPA_HINT_LOGARITHMIC |
|
Chris@144
|
73 LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 400, 499 },
|
Chris@0
|
74 { LADSPA_HINT_DEFAULT_MINIMUM | LADSPA_HINT_INTEGER |
|
Chris@0
|
75 LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 0, 1 },
|
Chris@0
|
76 { LADSPA_HINT_DEFAULT_MINIMUM | LADSPA_HINT_LOGARITHMIC |
|
Chris@1039
|
77 LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 0.001f, 2.0f }
|
Chris@0
|
78 };
|
Chris@0
|
79
|
Chris@0
|
80 const LADSPA_Properties
|
Chris@0
|
81 SamplePlayer::properties = LADSPA_PROPERTY_HARD_RT_CAPABLE;
|
Chris@0
|
82
|
Chris@0
|
83 const LADSPA_Descriptor
|
Chris@0
|
84 SamplePlayer::ladspaDescriptor =
|
Chris@0
|
85 {
|
Chris@0
|
86 0, // "Unique" ID
|
Chris@0
|
87 "sample_player", // Label
|
Chris@0
|
88 properties,
|
Chris@0
|
89 "Library Sample Player", // Name
|
Chris@0
|
90 "Chris Cannam", // Maker
|
Chris@0
|
91 "GPL", // Copyright
|
Chris@0
|
92 PortCount,
|
Chris@0
|
93 ports,
|
Chris@0
|
94 portNames,
|
Chris@0
|
95 hints,
|
Chris@1582
|
96 nullptr, // Implementation data
|
Chris@0
|
97 instantiate,
|
Chris@0
|
98 connectPort,
|
Chris@0
|
99 activate,
|
Chris@0
|
100 run,
|
Chris@1582
|
101 nullptr, // Run adding
|
Chris@1582
|
102 nullptr, // Set run adding gain
|
Chris@0
|
103 deactivate,
|
Chris@0
|
104 cleanup
|
Chris@0
|
105 };
|
Chris@0
|
106
|
Chris@0
|
107 const DSSI_Descriptor
|
Chris@0
|
108 SamplePlayer::dssiDescriptor =
|
Chris@0
|
109 {
|
Chris@0
|
110 2, // DSSI API version
|
Chris@0
|
111 &ladspaDescriptor,
|
Chris@75
|
112 configure,
|
Chris@0
|
113 getProgram,
|
Chris@0
|
114 selectProgram,
|
Chris@0
|
115 getMidiController,
|
Chris@0
|
116 runSynth,
|
Chris@1582
|
117 nullptr, // Run synth adding
|
Chris@1582
|
118 nullptr, // Run multiple synths
|
Chris@1582
|
119 nullptr, // Run multiple synths adding
|
Chris@0
|
120 receiveHostDescriptor
|
Chris@0
|
121 };
|
Chris@0
|
122
|
Chris@0
|
123 const DSSI_Host_Descriptor *
|
Chris@1582
|
124 SamplePlayer::hostDescriptor = nullptr;
|
Chris@0
|
125
|
Chris@0
|
126
|
Chris@0
|
127 const DSSI_Descriptor *
|
Chris@0
|
128 SamplePlayer::getDescriptor(unsigned long index)
|
Chris@0
|
129 {
|
Chris@0
|
130 if (index == 0) return &dssiDescriptor;
|
Chris@1582
|
131 return nullptr;
|
Chris@0
|
132 }
|
Chris@0
|
133
|
Chris@0
|
134 SamplePlayer::SamplePlayer(int sampleRate) :
|
Chris@1582
|
135 m_output(nullptr),
|
Chris@1582
|
136 m_retune(nullptr),
|
Chris@1582
|
137 m_basePitch(nullptr),
|
Chris@1582
|
138 m_concertA(nullptr),
|
Chris@1582
|
139 m_sustain(nullptr),
|
Chris@1582
|
140 m_release(nullptr),
|
Chris@1582
|
141 m_sampleData(nullptr),
|
Chris@0
|
142 m_sampleCount(0),
|
Chris@0
|
143 m_sampleRate(sampleRate),
|
Chris@0
|
144 m_sampleNo(0),
|
Chris@83
|
145 m_sampleDir("samples"),
|
Chris@0
|
146 m_sampleSearchComplete(false),
|
Chris@0
|
147 m_pendingProgramChange(-1)
|
Chris@0
|
148 {
|
Chris@0
|
149 }
|
Chris@0
|
150
|
Chris@0
|
151 SamplePlayer::~SamplePlayer()
|
Chris@0
|
152 {
|
Chris@0
|
153 if (m_sampleData) free(m_sampleData);
|
Chris@0
|
154 }
|
Chris@0
|
155
|
Chris@0
|
156 LADSPA_Handle
|
Chris@0
|
157 SamplePlayer::instantiate(const LADSPA_Descriptor *, unsigned long rate)
|
Chris@0
|
158 {
|
Chris@0
|
159 if (!hostDescriptor || !hostDescriptor->request_non_rt_thread) {
|
Chris@1429
|
160 SVDEBUG << "SamplePlayer::instantiate: Host does not provide request_non_rt_thread, not instantiating" << endl;
|
Chris@1582
|
161 return nullptr;
|
Chris@0
|
162 }
|
Chris@0
|
163
|
Chris@1039
|
164 SamplePlayer *player = new SamplePlayer(int(rate));
|
Chris@1429
|
165 // std::cerr << "Instantiated sample player " << std::endl;
|
Chris@0
|
166
|
Chris@0
|
167 if (hostDescriptor->request_non_rt_thread(player, workThreadCallback)) {
|
Chris@1429
|
168 SVDEBUG << "SamplePlayer::instantiate: Host rejected request_non_rt_thread call, not instantiating" << endl;
|
Chris@1429
|
169 delete player;
|
Chris@1582
|
170 return nullptr;
|
Chris@0
|
171 }
|
Chris@0
|
172
|
Chris@0
|
173 return player;
|
Chris@0
|
174 }
|
Chris@0
|
175
|
Chris@0
|
176 void
|
Chris@0
|
177 SamplePlayer::connectPort(LADSPA_Handle handle,
|
Chris@1429
|
178 unsigned long port, LADSPA_Data *location)
|
Chris@0
|
179 {
|
Chris@0
|
180 SamplePlayer *player = (SamplePlayer *)handle;
|
Chris@0
|
181
|
Chris@0
|
182 float **ports[PortCount] = {
|
Chris@1429
|
183 &player->m_output,
|
Chris@1429
|
184 &player->m_retune,
|
Chris@1429
|
185 &player->m_basePitch,
|
Chris@143
|
186 &player->m_concertA,
|
Chris@1429
|
187 &player->m_sustain,
|
Chris@1429
|
188 &player->m_release
|
Chris@0
|
189 };
|
Chris@0
|
190
|
Chris@0
|
191 *ports[port] = (float *)location;
|
Chris@0
|
192 }
|
Chris@0
|
193
|
Chris@0
|
194 void
|
Chris@0
|
195 SamplePlayer::activate(LADSPA_Handle handle)
|
Chris@0
|
196 {
|
Chris@0
|
197 SamplePlayer *player = (SamplePlayer *)handle;
|
Chris@0
|
198 QMutexLocker locker(&player->m_mutex);
|
Chris@0
|
199
|
Chris@0
|
200 player->m_sampleNo = 0;
|
Chris@0
|
201
|
Chris@0
|
202 for (size_t i = 0; i < Polyphony; ++i) {
|
Chris@1429
|
203 player->m_ons[i] = -1;
|
Chris@1429
|
204 player->m_offs[i] = -1;
|
Chris@1429
|
205 player->m_velocities[i] = 0;
|
Chris@0
|
206 }
|
Chris@0
|
207 }
|
Chris@0
|
208
|
Chris@0
|
209 void
|
Chris@0
|
210 SamplePlayer::run(LADSPA_Handle handle, unsigned long samples)
|
Chris@0
|
211 {
|
Chris@1582
|
212 runSynth(handle, samples, nullptr, 0);
|
Chris@0
|
213 }
|
Chris@0
|
214
|
Chris@0
|
215 void
|
Chris@0
|
216 SamplePlayer::deactivate(LADSPA_Handle handle)
|
Chris@0
|
217 {
|
Chris@0
|
218 activate(handle); // both functions just reset the plugin
|
Chris@0
|
219 }
|
Chris@0
|
220
|
Chris@0
|
221 void
|
Chris@0
|
222 SamplePlayer::cleanup(LADSPA_Handle handle)
|
Chris@0
|
223 {
|
Chris@0
|
224 delete (SamplePlayer *)handle;
|
Chris@0
|
225 }
|
Chris@0
|
226
|
Chris@75
|
227 char *
|
Chris@75
|
228 SamplePlayer::configure(LADSPA_Handle handle, const char *key, const char *value)
|
Chris@75
|
229 {
|
Chris@83
|
230 if (key && !strcmp(key, "sampledir")) {
|
Chris@75
|
231
|
Chris@75
|
232 SamplePlayer *player = (SamplePlayer *)handle;
|
Chris@75
|
233
|
Chris@1429
|
234 QMutexLocker locker(&player->m_mutex);
|
Chris@75
|
235
|
Chris@83
|
236 if (QFileInfo(value).exists() &&
|
Chris@83
|
237 QFileInfo(value).isDir()) {
|
Chris@81
|
238
|
Chris@83
|
239 player->m_sampleDir = value;
|
Chris@81
|
240
|
Chris@83
|
241 if (player->m_sampleSearchComplete) {
|
Chris@83
|
242 player->m_sampleSearchComplete = false;
|
Chris@83
|
243 player->searchSamples();
|
Chris@83
|
244 }
|
Chris@75
|
245
|
Chris@1582
|
246 return nullptr;
|
Chris@83
|
247
|
Chris@83
|
248 } else {
|
Chris@197
|
249 char *buffer = (char *)malloc(strlen(value) + 80);
|
Chris@197
|
250 sprintf(buffer, "Sample directory \"%s\" does not exist, leaving unchanged", value);
|
Chris@197
|
251 return buffer;
|
Chris@75
|
252 }
|
Chris@75
|
253 }
|
Chris@75
|
254
|
Chris@75
|
255 return strdup("Unknown configure key");
|
Chris@75
|
256 }
|
Chris@75
|
257
|
Chris@0
|
258 const DSSI_Program_Descriptor *
|
Chris@0
|
259 SamplePlayer::getProgram(LADSPA_Handle handle, unsigned long program)
|
Chris@0
|
260 {
|
Chris@0
|
261 SamplePlayer *player = (SamplePlayer *)handle;
|
Chris@0
|
262
|
Chris@0
|
263 if (!player->m_sampleSearchComplete) {
|
Chris@1429
|
264 QMutexLocker locker(&player->m_mutex);
|
Chris@1429
|
265 if (!player->m_sampleSearchComplete) {
|
Chris@1429
|
266 player->searchSamples();
|
Chris@1429
|
267 }
|
Chris@0
|
268 }
|
Chris@1582
|
269 if (program >= player->m_samples.size()) return nullptr;
|
Chris@0
|
270
|
Chris@0
|
271 static DSSI_Program_Descriptor descriptor;
|
Chris@0
|
272 static char name[60];
|
Chris@0
|
273
|
Chris@1465
|
274 strncpy(name, player->m_samples[program].first.toLocal8Bit().data(), 59);
|
Chris@0
|
275 name[59] = '\0';
|
Chris@0
|
276
|
Chris@0
|
277 descriptor.Bank = 0;
|
Chris@0
|
278 descriptor.Program = program;
|
Chris@0
|
279 descriptor.Name = name;
|
Chris@0
|
280
|
Chris@0
|
281 return &descriptor;
|
Chris@0
|
282 }
|
Chris@0
|
283
|
Chris@0
|
284 void
|
Chris@0
|
285 SamplePlayer::selectProgram(LADSPA_Handle handle,
|
Chris@1429
|
286 unsigned long,
|
Chris@1429
|
287 unsigned long program)
|
Chris@0
|
288 {
|
Chris@0
|
289 SamplePlayer *player = (SamplePlayer *)handle;
|
Chris@1039
|
290 player->m_pendingProgramChange = (int)program;
|
Chris@0
|
291 }
|
Chris@0
|
292
|
Chris@0
|
293 int
|
Chris@0
|
294 SamplePlayer::getMidiController(LADSPA_Handle, unsigned long port)
|
Chris@0
|
295 {
|
Chris@0
|
296 int controllers[PortCount] = {
|
Chris@1429
|
297 DSSI_NONE,
|
Chris@1429
|
298 DSSI_CC(12),
|
Chris@1429
|
299 DSSI_CC(13),
|
Chris@1429
|
300 DSSI_CC(64),
|
Chris@1429
|
301 DSSI_CC(72)
|
Chris@0
|
302 };
|
Chris@0
|
303
|
Chris@0
|
304 return controllers[port];
|
Chris@0
|
305 }
|
Chris@0
|
306
|
Chris@0
|
307 void
|
Chris@0
|
308 SamplePlayer::runSynth(LADSPA_Handle handle, unsigned long samples,
|
Chris@1429
|
309 snd_seq_event_t *events, unsigned long eventCount)
|
Chris@0
|
310 {
|
Chris@0
|
311 SamplePlayer *player = (SamplePlayer *)handle;
|
Chris@0
|
312
|
Chris@0
|
313 player->runImpl(samples, events, eventCount);
|
Chris@0
|
314 }
|
Chris@0
|
315
|
Chris@0
|
316 void
|
Chris@0
|
317 SamplePlayer::receiveHostDescriptor(const DSSI_Host_Descriptor *descriptor)
|
Chris@0
|
318 {
|
Chris@0
|
319 hostDescriptor = descriptor;
|
Chris@0
|
320 }
|
Chris@0
|
321
|
Chris@0
|
322 void
|
Chris@0
|
323 SamplePlayer::workThreadCallback(LADSPA_Handle handle)
|
Chris@0
|
324 {
|
Chris@0
|
325 SamplePlayer *player = (SamplePlayer *)handle;
|
Chris@0
|
326
|
Chris@0
|
327 if (player->m_pendingProgramChange >= 0) {
|
Chris@0
|
328
|
Chris@601
|
329 #ifdef DEBUG_SAMPLE_PLAYER
|
Chris@1429
|
330 SVDEBUG << "SamplePlayer::workThreadCallback: pending program change " << player->m_pendingProgramChange << endl;
|
Chris@601
|
331 #endif
|
Chris@0
|
332
|
Chris@1429
|
333 player->m_mutex.lock();
|
Chris@0
|
334
|
Chris@1429
|
335 int program = player->m_pendingProgramChange;
|
Chris@1429
|
336 player->m_pendingProgramChange = -1;
|
Chris@0
|
337
|
Chris@1429
|
338 if (!player->m_sampleSearchComplete) {
|
Chris@1429
|
339 player->searchSamples();
|
Chris@1429
|
340 }
|
Chris@1429
|
341
|
Chris@1429
|
342 if (program < int(player->m_samples.size())) {
|
Chris@1429
|
343 QString path = player->m_samples[program].second;
|
Chris@1429
|
344 QString programName = player->m_samples[program].first;
|
Chris@1429
|
345 if (programName != player->m_program) {
|
Chris@1429
|
346 player->m_program = programName;
|
Chris@1429
|
347 player->m_mutex.unlock();
|
Chris@1429
|
348 player->loadSampleData(path);
|
Chris@1429
|
349 } else {
|
Chris@1429
|
350 player->m_mutex.unlock();
|
Chris@1429
|
351 }
|
Chris@1429
|
352 }
|
Chris@0
|
353 }
|
Chris@0
|
354
|
Chris@0
|
355 if (!player->m_sampleSearchComplete) {
|
Chris@0
|
356
|
Chris@1429
|
357 QMutexLocker locker(&player->m_mutex);
|
Chris@0
|
358
|
Chris@1429
|
359 if (!player->m_sampleSearchComplete) {
|
Chris@1429
|
360 player->searchSamples();
|
Chris@1429
|
361 }
|
Chris@0
|
362 }
|
Chris@0
|
363 }
|
Chris@0
|
364
|
Chris@0
|
365 void
|
Chris@0
|
366 SamplePlayer::searchSamples()
|
Chris@0
|
367 {
|
Chris@0
|
368 if (m_sampleSearchComplete) return;
|
Chris@0
|
369
|
Chris@81
|
370 m_samples.clear();
|
Chris@81
|
371
|
Chris@601
|
372 #ifdef DEBUG_SAMPLE_PLAYER
|
Chris@690
|
373 SVDEBUG << "SamplePlayer::searchSamples: Directory is \""
|
Chris@1429
|
374 << m_sampleDir << "\"" << endl;
|
Chris@601
|
375 #endif
|
Chris@0
|
376
|
Chris@83
|
377 QDir dir(m_sampleDir, "*.wav");
|
Chris@83
|
378
|
Chris@83
|
379 for (unsigned int i = 0; i < dir.count(); ++i) {
|
Chris@83
|
380 QFileInfo file(dir.filePath(dir[i]));
|
Chris@83
|
381 if (file.isReadable()) {
|
Chris@83
|
382 m_samples.push_back(std::pair<QString, QString>
|
Chris@83
|
383 (file.baseName(), file.filePath()));
|
Chris@601
|
384 #ifdef DEBUG_SAMPLE_PLAYER
|
Chris@845
|
385 cerr << "Found: " << dir[i] << endl;
|
Chris@601
|
386 #endif
|
Chris@75
|
387 }
|
Chris@0
|
388 }
|
Chris@83
|
389
|
Chris@0
|
390 m_sampleSearchComplete = true;
|
Chris@0
|
391 }
|
Chris@0
|
392
|
Chris@0
|
393 void
|
Chris@0
|
394 SamplePlayer::loadSampleData(QString path)
|
Chris@0
|
395 {
|
Chris@0
|
396 SF_INFO info;
|
Chris@0
|
397 SNDFILE *file;
|
Chris@0
|
398 size_t samples = 0;
|
Chris@0
|
399 float *tmpFrames, *tmpSamples, *tmpResamples, *tmpOld;
|
Chris@0
|
400 size_t i;
|
Chris@0
|
401
|
Chris@0
|
402 info.format = 0;
|
Chris@1359
|
403 #ifdef Q_OS_WIN
|
Chris@1361
|
404 file = sf_wchar_open((LPCWSTR)path.utf16(), SFM_READ, &info);
|
Chris@1359
|
405 #else
|
Chris@0
|
406 file = sf_open(path.toLocal8Bit().data(), SFM_READ, &info);
|
Chris@1359
|
407 #endif
|
Chris@0
|
408 if (!file) {
|
Chris@1429
|
409 cerr << "SamplePlayer::loadSampleData: Failed to open file "
|
Chris@1429
|
410 << path << ": "
|
Chris@1429
|
411 << sf_strerror(file) << endl;
|
Chris@1429
|
412 return;
|
Chris@0
|
413 }
|
Chris@0
|
414
|
Chris@0
|
415 samples = info.frames;
|
Chris@0
|
416 tmpFrames = (float *)malloc(info.frames * info.channels * sizeof(float));
|
Chris@0
|
417 if (!tmpFrames) return;
|
Chris@0
|
418
|
Chris@0
|
419 sf_readf_float(file, tmpFrames, info.frames);
|
Chris@0
|
420 sf_close(file);
|
Chris@0
|
421
|
Chris@1582
|
422 tmpResamples = nullptr;
|
Chris@0
|
423
|
Chris@0
|
424 if (info.samplerate != m_sampleRate) {
|
Chris@1429
|
425
|
Chris@1429
|
426 double ratio = (double)m_sampleRate / (double)info.samplerate;
|
Chris@1429
|
427 size_t target = (size_t)(double(info.frames) * ratio);
|
Chris@1429
|
428 SRC_DATA data;
|
Chris@0
|
429
|
Chris@1429
|
430 tmpResamples = (float *)malloc(target * info.channels * sizeof(float));
|
Chris@1429
|
431 if (!tmpResamples) {
|
Chris@1429
|
432 free(tmpFrames);
|
Chris@1429
|
433 return;
|
Chris@1429
|
434 }
|
Chris@0
|
435
|
Chris@1429
|
436 memset(tmpResamples, 0, target * info.channels * sizeof(float));
|
Chris@0
|
437
|
Chris@1429
|
438 data.data_in = tmpFrames;
|
Chris@1429
|
439 data.data_out = tmpResamples;
|
Chris@1429
|
440 data.input_frames = info.frames;
|
Chris@1429
|
441 data.output_frames = target;
|
Chris@1429
|
442 data.src_ratio = ratio;
|
Chris@0
|
443
|
Chris@1429
|
444 if (!src_simple(&data, SRC_SINC_BEST_QUALITY, info.channels)) {
|
Chris@1429
|
445 free(tmpFrames);
|
Chris@1429
|
446 tmpFrames = tmpResamples;
|
Chris@1429
|
447 samples = target;
|
Chris@1429
|
448 } else {
|
Chris@1429
|
449 free(tmpResamples);
|
Chris@1429
|
450 }
|
Chris@0
|
451 }
|
Chris@0
|
452
|
Chris@0
|
453 /* add an extra sample for linear interpolation */
|
Chris@0
|
454 tmpSamples = (float *)malloc((samples + 1) * sizeof(float));
|
Chris@0
|
455 if (!tmpSamples) {
|
Chris@1429
|
456 free(tmpFrames);
|
Chris@1429
|
457 return;
|
Chris@0
|
458 }
|
Chris@0
|
459
|
Chris@0
|
460 for (i = 0; i < samples; ++i) {
|
Chris@1429
|
461 int j;
|
Chris@1429
|
462 tmpSamples[i] = 0.0f;
|
Chris@1429
|
463 for (j = 0; j < info.channels; ++j) {
|
Chris@1429
|
464 tmpSamples[i] += tmpFrames[i * info.channels + j];
|
Chris@1429
|
465 }
|
Chris@0
|
466 }
|
Chris@0
|
467
|
Chris@0
|
468 free(tmpFrames);
|
Chris@0
|
469
|
Chris@0
|
470 /* add an extra sample for linear interpolation */
|
Chris@0
|
471 tmpSamples[samples] = 0.0f;
|
Chris@0
|
472
|
Chris@0
|
473 QMutexLocker locker(&m_mutex);
|
Chris@0
|
474
|
Chris@0
|
475 tmpOld = m_sampleData;
|
Chris@0
|
476 m_sampleData = tmpSamples;
|
Chris@0
|
477 m_sampleCount = samples;
|
Chris@0
|
478
|
Chris@0
|
479 for (i = 0; i < Polyphony; ++i) {
|
Chris@1429
|
480 m_ons[i] = -1;
|
Chris@1429
|
481 m_offs[i] = -1;
|
Chris@1429
|
482 m_velocities[i] = 0;
|
Chris@0
|
483 }
|
Chris@0
|
484
|
Chris@0
|
485 if (tmpOld) free(tmpOld);
|
Chris@0
|
486
|
Chris@0
|
487 printf("%s: loaded %s (%ld samples from original %ld channels resampled from %ld frames at %ld Hz)\n", "sampler", path.toLocal8Bit().data(), (long)samples, (long)info.channels, (long)info.frames, (long)info.samplerate);
|
Chris@0
|
488 }
|
Chris@0
|
489
|
Chris@0
|
490 void
|
Chris@0
|
491 SamplePlayer::runImpl(unsigned long sampleCount,
|
Chris@1429
|
492 snd_seq_event_t *events,
|
Chris@1429
|
493 unsigned long eventCount)
|
Chris@0
|
494 {
|
Chris@0
|
495 unsigned long pos;
|
Chris@0
|
496 unsigned long count;
|
Chris@0
|
497 unsigned long event_pos;
|
Chris@0
|
498 int i;
|
Chris@0
|
499
|
Chris@0
|
500 memset(m_output, 0, sampleCount * sizeof(float));
|
Chris@0
|
501
|
Chris@0
|
502 if (!m_mutex.tryLock()) return;
|
Chris@0
|
503
|
Chris@0
|
504 if (!m_sampleData || !m_sampleCount) {
|
Chris@1429
|
505 m_sampleNo += sampleCount;
|
Chris@1429
|
506 m_mutex.unlock();
|
Chris@1429
|
507 return;
|
Chris@0
|
508 }
|
Chris@0
|
509
|
Chris@0
|
510 for (pos = 0, event_pos = 0; pos < sampleCount; ) {
|
Chris@0
|
511
|
Chris@1429
|
512 while (event_pos < eventCount
|
Chris@1429
|
513 && pos >= events[event_pos].time.tick) {
|
Chris@0
|
514
|
Chris@1429
|
515 if (events[event_pos].type == SND_SEQ_EVENT_NOTEON) {
|
Chris@601
|
516 #ifdef DEBUG_SAMPLE_PLAYER
|
Chris@843
|
517 cerr << "SamplePlayer: found NOTEON at time "
|
Chris@843
|
518 << events[event_pos].time.tick << endl;
|
Chris@601
|
519 #endif
|
Chris@1429
|
520 snd_seq_ev_note_t n = events[event_pos].data.note;
|
Chris@1429
|
521 if (n.velocity > 0) {
|
Chris@1429
|
522 m_ons[n.note] =
|
Chris@1429
|
523 m_sampleNo + events[event_pos].time.tick;
|
Chris@1429
|
524 m_offs[n.note] = -1;
|
Chris@1429
|
525 m_velocities[n.note] = n.velocity;
|
Chris@1429
|
526 } else {
|
Chris@1429
|
527 if (!m_sustain || (*m_sustain < 0.001)) {
|
Chris@1429
|
528 m_offs[n.note] =
|
Chris@1429
|
529 m_sampleNo + events[event_pos].time.tick;
|
Chris@1429
|
530 }
|
Chris@1429
|
531 }
|
Chris@1429
|
532 } else if (events[event_pos].type == SND_SEQ_EVENT_NOTEOFF &&
|
Chris@1429
|
533 (!m_sustain || (*m_sustain < 0.001))) {
|
Chris@601
|
534 #ifdef DEBUG_SAMPLE_PLAYER
|
Chris@843
|
535 cerr << "SamplePlayer: found NOTEOFF at time "
|
Chris@843
|
536 << events[event_pos].time.tick << endl;
|
Chris@601
|
537 #endif
|
Chris@1429
|
538 snd_seq_ev_note_t n = events[event_pos].data.note;
|
Chris@1429
|
539 m_offs[n.note] =
|
Chris@1429
|
540 m_sampleNo + events[event_pos].time.tick;
|
Chris@1429
|
541 }
|
Chris@0
|
542
|
Chris@1429
|
543 ++event_pos;
|
Chris@1429
|
544 }
|
Chris@0
|
545
|
Chris@1429
|
546 count = sampleCount - pos;
|
Chris@1429
|
547 if (event_pos < eventCount &&
|
Chris@1429
|
548 events[event_pos].time.tick < sampleCount) {
|
Chris@1429
|
549 count = events[event_pos].time.tick - pos;
|
Chris@1429
|
550 }
|
Chris@0
|
551
|
Chris@601
|
552 int notecount = 0;
|
Chris@601
|
553
|
Chris@1429
|
554 for (i = 0; i < Polyphony; ++i) {
|
Chris@1429
|
555 if (m_ons[i] >= 0) {
|
Chris@601
|
556 ++notecount;
|
Chris@1429
|
557 addSample(i, pos, count);
|
Chris@1429
|
558 }
|
Chris@1429
|
559 }
|
Chris@0
|
560
|
Chris@601
|
561 #ifdef DEBUG_SAMPLE_PLAYER
|
Chris@843
|
562 cerr << "SamplePlayer: have " << notecount << " note(s) sounding currently" << endl;
|
Chris@601
|
563 #endif
|
Chris@601
|
564
|
Chris@1429
|
565 pos += count;
|
Chris@0
|
566 }
|
Chris@0
|
567
|
Chris@0
|
568 m_sampleNo += sampleCount;
|
Chris@0
|
569 m_mutex.unlock();
|
Chris@0
|
570 }
|
Chris@0
|
571
|
Chris@0
|
572 void
|
Chris@0
|
573 SamplePlayer::addSample(int n, unsigned long pos, unsigned long count)
|
Chris@0
|
574 {
|
Chris@143
|
575 float ratio = 1.f;
|
Chris@143
|
576 float gain = 1.f;
|
Chris@0
|
577 unsigned long i, s;
|
Chris@0
|
578
|
Chris@0
|
579 if (m_retune && *m_retune) {
|
Chris@143
|
580 if (m_concertA) {
|
Chris@143
|
581 ratio *= *m_concertA / 440.f;
|
Chris@143
|
582 }
|
Chris@1429
|
583 if (m_basePitch && float(n) != *m_basePitch) {
|
Chris@1429
|
584 ratio *= powf(1.059463094f, float(n) - *m_basePitch);
|
Chris@1429
|
585 }
|
Chris@0
|
586 }
|
Chris@0
|
587
|
Chris@0
|
588 if (long(pos + m_sampleNo) < m_ons[n]) return;
|
Chris@0
|
589
|
Chris@0
|
590 gain = (float)m_velocities[n] / 127.0f;
|
Chris@0
|
591
|
Chris@0
|
592 for (i = 0, s = pos + m_sampleNo - m_ons[n];
|
Chris@1429
|
593 i < count;
|
Chris@1429
|
594 ++i, ++s) {
|
Chris@0
|
595
|
Chris@1429
|
596 float lgain = gain;
|
Chris@1429
|
597 float rs = float(s) * ratio;
|
Chris@1429
|
598 unsigned long rsi = lrintf(floorf(rs));
|
Chris@0
|
599
|
Chris@1429
|
600 if (rsi >= m_sampleCount) {
|
Chris@601
|
601 #ifdef DEBUG_SAMPLE_PLAYER
|
Chris@843
|
602 cerr << "Note " << n << " has run out of samples (were " << m_sampleCount << " available at ratio " << ratio << "), ending" << endl;
|
Chris@601
|
603 #endif
|
Chris@1429
|
604 m_ons[n] = -1;
|
Chris@1429
|
605 break;
|
Chris@1429
|
606 }
|
Chris@0
|
607
|
Chris@1429
|
608 if (m_offs[n] >= 0 &&
|
Chris@1429
|
609 long(pos + i + m_sampleNo) > m_offs[n]) {
|
Chris@0
|
610
|
Chris@1429
|
611 unsigned long dist =
|
Chris@1429
|
612 pos + i + m_sampleNo - m_offs[n];
|
Chris@0
|
613
|
Chris@1429
|
614 unsigned long releaseFrames = 200;
|
Chris@1429
|
615 if (m_release) {
|
Chris@1429
|
616 releaseFrames = long(*m_release * float(m_sampleRate) + 0.0001f);
|
Chris@1429
|
617 }
|
Chris@0
|
618
|
Chris@1429
|
619 if (dist > releaseFrames) {
|
Chris@601
|
620 #ifdef DEBUG_SAMPLE_PLAYER
|
Chris@843
|
621 cerr << "Note " << n << " has expired its release time (" << releaseFrames << " frames), ending" << endl;
|
Chris@601
|
622 #endif
|
Chris@1429
|
623 m_ons[n] = -1;
|
Chris@1429
|
624 break;
|
Chris@1429
|
625 } else {
|
Chris@1429
|
626 lgain = lgain * (float)(releaseFrames - dist) /
|
Chris@1429
|
627 (float)releaseFrames;
|
Chris@1429
|
628 }
|
Chris@1429
|
629 }
|
Chris@1429
|
630
|
Chris@1429
|
631 float sample = m_sampleData[rsi] +
|
Chris@1429
|
632 ((m_sampleData[rsi + 1] -
|
Chris@1429
|
633 m_sampleData[rsi]) *
|
Chris@1429
|
634 (rs - (float)rsi));
|
Chris@0
|
635
|
Chris@1429
|
636 m_output[pos + i] += lgain * sample;
|
Chris@0
|
637 }
|
Chris@0
|
638 }
|