annotate plugin/plugins/SamplePlayer.cpp @ 1196:c7b9c902642f spectrogram-minor-refactor

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