annotate plugin/plugins/SamplePlayer.cpp @ 1359:1c9bbbb6116a 3.0-integration

Use W64 instead of WAV for decoded files; use Ogg reader in preference to WAV one for Ogg files (WAV reader works, via libsndfile, but doesn't load metadata); fix Ogg reader to use QFile open instead of non-Win32-compatible API; add more encoder tests, audio writer test, midi reader test
author Chris Cannam
date Tue, 10 Jan 2017 10:58:25 +0000
parents 63b73a21bccd
children 49b43306778b
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@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@0 96 0, // Implementation data
Chris@0 97 instantiate,
Chris@0 98 connectPort,
Chris@0 99 activate,
Chris@0 100 run,
Chris@0 101 0, // Run adding
Chris@0 102 0, // 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@0 117 0, // Run synth adding
Chris@0 118 0, // Run multiple synths
Chris@0 119 0, // Run multiple synths adding
Chris@0 120 receiveHostDescriptor
Chris@0 121 };
Chris@0 122
Chris@0 123 const DSSI_Host_Descriptor *
Chris@0 124 SamplePlayer::hostDescriptor = 0;
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@0 131 return 0;
Chris@0 132 }
Chris@0 133
Chris@0 134 SamplePlayer::SamplePlayer(int sampleRate) :
Chris@0 135 m_output(0),
Chris@0 136 m_retune(0),
Chris@0 137 m_basePitch(0),
Chris@143 138 m_concertA(0),
Chris@0 139 m_sustain(0),
Chris@0 140 m_release(0),
Chris@0 141 m_sampleData(0),
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@690 160 SVDEBUG << "SamplePlayer::instantiate: Host does not provide request_non_rt_thread, not instantiating" << endl;
Chris@0 161 return 0;
Chris@0 162 }
Chris@0 163
Chris@1039 164 SamplePlayer *player = new SamplePlayer(int(rate));
gyorgyf@788 165 // std::cerr << "Instantiated sample player " << std::endl;
Chris@0 166
Chris@0 167 if (hostDescriptor->request_non_rt_thread(player, workThreadCallback)) {
Chris@690 168 SVDEBUG << "SamplePlayer::instantiate: Host rejected request_non_rt_thread call, not instantiating" << endl;
Chris@0 169 delete player;
Chris@0 170 return 0;
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@0 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@0 183 &player->m_output,
Chris@0 184 &player->m_retune,
Chris@0 185 &player->m_basePitch,
Chris@143 186 &player->m_concertA,
Chris@0 187 &player->m_sustain,
Chris@0 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@0 203 player->m_ons[i] = -1;
Chris@0 204 player->m_offs[i] = -1;
Chris@0 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@0 212 runSynth(handle, samples, 0, 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@75 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@83 246 return 0;
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@0 264 QMutexLocker locker(&player->m_mutex);
Chris@0 265 if (!player->m_sampleSearchComplete) {
Chris@0 266 player->searchSamples();
Chris@0 267 }
Chris@0 268 }
Chris@0 269 if (program >= player->m_samples.size()) return 0;
Chris@0 270
Chris@0 271 static DSSI_Program_Descriptor descriptor;
Chris@0 272 static char name[60];
Chris@0 273
Chris@0 274 strncpy(name, player->m_samples[program].first.toLocal8Bit().data(), 60);
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@0 286 unsigned long,
Chris@0 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@0 297 DSSI_NONE,
Chris@0 298 DSSI_CC(12),
Chris@0 299 DSSI_CC(13),
Chris@0 300 DSSI_CC(64),
Chris@0 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@0 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@690 330 SVDEBUG << "SamplePlayer::workThreadCallback: pending program change " << player->m_pendingProgramChange << endl;
Chris@601 331 #endif
Chris@0 332
Chris@0 333 player->m_mutex.lock();
Chris@0 334
Chris@0 335 int program = player->m_pendingProgramChange;
Chris@0 336 player->m_pendingProgramChange = -1;
Chris@0 337
Chris@0 338 if (!player->m_sampleSearchComplete) {
Chris@0 339 player->searchSamples();
Chris@0 340 }
Chris@0 341
Chris@0 342 if (program < int(player->m_samples.size())) {
Chris@0 343 QString path = player->m_samples[program].second;
Chris@0 344 QString programName = player->m_samples[program].first;
Chris@0 345 if (programName != player->m_program) {
Chris@0 346 player->m_program = programName;
Chris@0 347 player->m_mutex.unlock();
Chris@0 348 player->loadSampleData(path);
Chris@0 349 } else {
Chris@0 350 player->m_mutex.unlock();
Chris@0 351 }
Chris@0 352 }
Chris@0 353 }
Chris@0 354
Chris@0 355 if (!player->m_sampleSearchComplete) {
Chris@0 356
Chris@0 357 QMutexLocker locker(&player->m_mutex);
Chris@0 358
Chris@0 359 if (!player->m_sampleSearchComplete) {
Chris@0 360 player->searchSamples();
Chris@0 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@845 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@1359 404 file = sf_wchar_open((LPCWSTR)path.utf16(), SFM_READ, &m_fileInfo);
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@843 409 cerr << "SamplePlayer::loadSampleData: Failed to open file "
Chris@845 410 << path << ": "
Chris@843 411 << sf_strerror(file) << endl;
Chris@0 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@0 422 tmpResamples = 0;
Chris@0 423
Chris@0 424 if (info.samplerate != m_sampleRate) {
Chris@0 425
Chris@0 426 double ratio = (double)m_sampleRate / (double)info.samplerate;
Chris@1039 427 size_t target = (size_t)(double(info.frames) * ratio);
Chris@0 428 SRC_DATA data;
Chris@0 429
Chris@0 430 tmpResamples = (float *)malloc(target * info.channels * sizeof(float));
Chris@0 431 if (!tmpResamples) {
Chris@0 432 free(tmpFrames);
Chris@0 433 return;
Chris@0 434 }
Chris@0 435
Chris@0 436 memset(tmpResamples, 0, target * info.channels * sizeof(float));
Chris@0 437
Chris@0 438 data.data_in = tmpFrames;
Chris@0 439 data.data_out = tmpResamples;
Chris@0 440 data.input_frames = info.frames;
Chris@0 441 data.output_frames = target;
Chris@0 442 data.src_ratio = ratio;
Chris@0 443
Chris@0 444 if (!src_simple(&data, SRC_SINC_BEST_QUALITY, info.channels)) {
Chris@0 445 free(tmpFrames);
Chris@0 446 tmpFrames = tmpResamples;
Chris@0 447 samples = target;
Chris@0 448 } else {
Chris@0 449 free(tmpResamples);
Chris@0 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@0 456 free(tmpFrames);
Chris@0 457 return;
Chris@0 458 }
Chris@0 459
Chris@0 460 for (i = 0; i < samples; ++i) {
Chris@0 461 int j;
Chris@0 462 tmpSamples[i] = 0.0f;
Chris@0 463 for (j = 0; j < info.channels; ++j) {
Chris@0 464 tmpSamples[i] += tmpFrames[i * info.channels + j];
Chris@0 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@0 480 m_ons[i] = -1;
Chris@0 481 m_offs[i] = -1;
Chris@0 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@0 492 snd_seq_event_t *events,
Chris@0 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@0 505 m_sampleNo += sampleCount;
Chris@0 506 m_mutex.unlock();
Chris@0 507 return;
Chris@0 508 }
Chris@0 509
Chris@0 510 for (pos = 0, event_pos = 0; pos < sampleCount; ) {
Chris@0 511
Chris@0 512 while (event_pos < eventCount
Chris@0 513 && pos >= events[event_pos].time.tick) {
Chris@0 514
Chris@0 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@0 520 snd_seq_ev_note_t n = events[event_pos].data.note;
Chris@0 521 if (n.velocity > 0) {
Chris@0 522 m_ons[n.note] =
Chris@0 523 m_sampleNo + events[event_pos].time.tick;
Chris@0 524 m_offs[n.note] = -1;
Chris@0 525 m_velocities[n.note] = n.velocity;
Chris@0 526 } else {
Chris@0 527 if (!m_sustain || (*m_sustain < 0.001)) {
Chris@0 528 m_offs[n.note] =
Chris@0 529 m_sampleNo + events[event_pos].time.tick;
Chris@0 530 }
Chris@0 531 }
Chris@0 532 } else if (events[event_pos].type == SND_SEQ_EVENT_NOTEOFF &&
Chris@0 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@0 538 snd_seq_ev_note_t n = events[event_pos].data.note;
Chris@0 539 m_offs[n.note] =
Chris@0 540 m_sampleNo + events[event_pos].time.tick;
Chris@0 541 }
Chris@0 542
Chris@0 543 ++event_pos;
Chris@0 544 }
Chris@0 545
Chris@0 546 count = sampleCount - pos;
Chris@0 547 if (event_pos < eventCount &&
Chris@0 548 events[event_pos].time.tick < sampleCount) {
Chris@0 549 count = events[event_pos].time.tick - pos;
Chris@0 550 }
Chris@0 551
Chris@601 552 int notecount = 0;
Chris@601 553
Chris@0 554 for (i = 0; i < Polyphony; ++i) {
Chris@0 555 if (m_ons[i] >= 0) {
Chris@601 556 ++notecount;
Chris@0 557 addSample(i, pos, count);
Chris@0 558 }
Chris@0 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@0 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@1189 583 if (m_basePitch && float(n) != *m_basePitch) {
Chris@1039 584 ratio *= powf(1.059463094f, float(n) - *m_basePitch);
Chris@0 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@0 593 i < count;
Chris@0 594 ++i, ++s) {
Chris@0 595
Chris@0 596 float lgain = gain;
Chris@1039 597 float rs = float(s) * ratio;
Chris@1039 598 unsigned long rsi = lrintf(floorf(rs));
Chris@0 599
Chris@0 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@0 604 m_ons[n] = -1;
Chris@0 605 break;
Chris@0 606 }
Chris@0 607
Chris@0 608 if (m_offs[n] >= 0 &&
Chris@0 609 long(pos + i + m_sampleNo) > m_offs[n]) {
Chris@0 610
Chris@0 611 unsigned long dist =
Chris@0 612 pos + i + m_sampleNo - m_offs[n];
Chris@0 613
Chris@0 614 unsigned long releaseFrames = 200;
Chris@0 615 if (m_release) {
Chris@1039 616 releaseFrames = long(*m_release * float(m_sampleRate) + 0.0001f);
Chris@0 617 }
Chris@0 618
Chris@0 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@0 623 m_ons[n] = -1;
Chris@0 624 break;
Chris@0 625 } else {
Chris@0 626 lgain = lgain * (float)(releaseFrames - dist) /
Chris@0 627 (float)releaseFrames;
Chris@0 628 }
Chris@0 629 }
Chris@0 630
Chris@0 631 float sample = m_sampleData[rsi] +
Chris@0 632 ((m_sampleData[rsi + 1] -
Chris@0 633 m_sampleData[rsi]) *
Chris@0 634 (rs - (float)rsi));
Chris@0 635
Chris@0 636 m_output[pos + i] += lgain * sample;
Chris@0 637 }
Chris@0 638 }