svcore
1.9
|
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ 00002 00003 /* 00004 Sonic Visualiser 00005 An audio file viewer and annotation editor. 00006 Centre for Digital Music, Queen Mary, University of London. 00007 00008 This program is free software; you can redistribute it and/or 00009 modify it under the terms of the GNU General Public License as 00010 published by the Free Software Foundation; either version 2 of the 00011 License, or (at your option) any later version. See the file 00012 COPYING included with this distribution for more information. 00013 */ 00014 00015 /* 00016 Based on trivial_sampler from the DSSI distribution 00017 (by Chris Cannam, public domain). 00018 */ 00019 00020 #include "SamplePlayer.h" 00021 #include "system/System.h" 00022 00023 #include "../api/dssi.h" 00024 00025 #include <cmath> 00026 #include <cstdlib> 00027 00028 #include <QMutexLocker> 00029 #include <QDir> 00030 #include <QFileInfo> 00031 00032 #include <sndfile.h> 00033 #include <samplerate.h> 00034 #include <iostream> 00035 00036 //#define DEBUG_SAMPLE_PLAYER 1 00037 00038 const char *const 00039 SamplePlayer::portNames[PortCount] = 00040 { 00041 "Output", 00042 "Tuned (on/off)", 00043 "Base Pitch (MIDI)", 00044 "Tuning of A (Hz)", 00045 "Sustain (on/off)", 00046 "Release time (s)" 00047 }; 00048 00049 const LADSPA_PortDescriptor 00050 SamplePlayer::ports[PortCount] = 00051 { 00052 LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, 00053 LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, 00054 LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, 00055 LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, 00056 LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL 00057 }; 00058 00059 const LADSPA_PortRangeHint 00060 SamplePlayer::hints[PortCount] = 00061 { 00062 { 0, 0, 0 }, 00063 { LADSPA_HINT_DEFAULT_MAXIMUM | LADSPA_HINT_INTEGER | 00064 LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 0, 1 }, 00065 { LADSPA_HINT_DEFAULT_MIDDLE | LADSPA_HINT_INTEGER | 00066 LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 0, 120 }, 00067 { LADSPA_HINT_DEFAULT_440 | LADSPA_HINT_LOGARITHMIC | 00068 LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 400, 499 }, 00069 { LADSPA_HINT_DEFAULT_MINIMUM | LADSPA_HINT_INTEGER | 00070 LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 0, 1 }, 00071 { LADSPA_HINT_DEFAULT_MINIMUM | LADSPA_HINT_LOGARITHMIC | 00072 LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 0.001, 2.0 } 00073 }; 00074 00075 const LADSPA_Properties 00076 SamplePlayer::properties = LADSPA_PROPERTY_HARD_RT_CAPABLE; 00077 00078 const LADSPA_Descriptor 00079 SamplePlayer::ladspaDescriptor = 00080 { 00081 0, // "Unique" ID 00082 "sample_player", // Label 00083 properties, 00084 "Library Sample Player", // Name 00085 "Chris Cannam", // Maker 00086 "GPL", // Copyright 00087 PortCount, 00088 ports, 00089 portNames, 00090 hints, 00091 0, // Implementation data 00092 instantiate, 00093 connectPort, 00094 activate, 00095 run, 00096 0, // Run adding 00097 0, // Set run adding gain 00098 deactivate, 00099 cleanup 00100 }; 00101 00102 const DSSI_Descriptor 00103 SamplePlayer::dssiDescriptor = 00104 { 00105 2, // DSSI API version 00106 &ladspaDescriptor, 00107 configure, 00108 getProgram, 00109 selectProgram, 00110 getMidiController, 00111 runSynth, 00112 0, // Run synth adding 00113 0, // Run multiple synths 00114 0, // Run multiple synths adding 00115 receiveHostDescriptor 00116 }; 00117 00118 const DSSI_Host_Descriptor * 00119 SamplePlayer::hostDescriptor = 0; 00120 00121 00122 const DSSI_Descriptor * 00123 SamplePlayer::getDescriptor(unsigned long index) 00124 { 00125 if (index == 0) return &dssiDescriptor; 00126 return 0; 00127 } 00128 00129 SamplePlayer::SamplePlayer(int sampleRate) : 00130 m_output(0), 00131 m_retune(0), 00132 m_basePitch(0), 00133 m_concertA(0), 00134 m_sustain(0), 00135 m_release(0), 00136 m_sampleData(0), 00137 m_sampleCount(0), 00138 m_sampleRate(sampleRate), 00139 m_sampleNo(0), 00140 m_sampleDir("samples"), 00141 m_sampleSearchComplete(false), 00142 m_pendingProgramChange(-1) 00143 { 00144 } 00145 00146 SamplePlayer::~SamplePlayer() 00147 { 00148 if (m_sampleData) free(m_sampleData); 00149 } 00150 00151 LADSPA_Handle 00152 SamplePlayer::instantiate(const LADSPA_Descriptor *, unsigned long rate) 00153 { 00154 if (!hostDescriptor || !hostDescriptor->request_non_rt_thread) { 00155 SVDEBUG << "SamplePlayer::instantiate: Host does not provide request_non_rt_thread, not instantiating" << endl; 00156 return 0; 00157 } 00158 00159 SamplePlayer *player = new SamplePlayer(rate); 00160 // std::cerr << "Instantiated sample player " << std::endl; 00161 00162 if (hostDescriptor->request_non_rt_thread(player, workThreadCallback)) { 00163 SVDEBUG << "SamplePlayer::instantiate: Host rejected request_non_rt_thread call, not instantiating" << endl; 00164 delete player; 00165 return 0; 00166 } 00167 00168 return player; 00169 } 00170 00171 void 00172 SamplePlayer::connectPort(LADSPA_Handle handle, 00173 unsigned long port, LADSPA_Data *location) 00174 { 00175 SamplePlayer *player = (SamplePlayer *)handle; 00176 00177 float **ports[PortCount] = { 00178 &player->m_output, 00179 &player->m_retune, 00180 &player->m_basePitch, 00181 &player->m_concertA, 00182 &player->m_sustain, 00183 &player->m_release 00184 }; 00185 00186 *ports[port] = (float *)location; 00187 } 00188 00189 void 00190 SamplePlayer::activate(LADSPA_Handle handle) 00191 { 00192 SamplePlayer *player = (SamplePlayer *)handle; 00193 QMutexLocker locker(&player->m_mutex); 00194 00195 player->m_sampleNo = 0; 00196 00197 for (size_t i = 0; i < Polyphony; ++i) { 00198 player->m_ons[i] = -1; 00199 player->m_offs[i] = -1; 00200 player->m_velocities[i] = 0; 00201 } 00202 } 00203 00204 void 00205 SamplePlayer::run(LADSPA_Handle handle, unsigned long samples) 00206 { 00207 runSynth(handle, samples, 0, 0); 00208 } 00209 00210 void 00211 SamplePlayer::deactivate(LADSPA_Handle handle) 00212 { 00213 activate(handle); // both functions just reset the plugin 00214 } 00215 00216 void 00217 SamplePlayer::cleanup(LADSPA_Handle handle) 00218 { 00219 delete (SamplePlayer *)handle; 00220 } 00221 00222 char * 00223 SamplePlayer::configure(LADSPA_Handle handle, const char *key, const char *value) 00224 { 00225 if (key && !strcmp(key, "sampledir")) { 00226 00227 SamplePlayer *player = (SamplePlayer *)handle; 00228 00229 QMutexLocker locker(&player->m_mutex); 00230 00231 if (QFileInfo(value).exists() && 00232 QFileInfo(value).isDir()) { 00233 00234 player->m_sampleDir = value; 00235 00236 if (player->m_sampleSearchComplete) { 00237 player->m_sampleSearchComplete = false; 00238 player->searchSamples(); 00239 } 00240 00241 return 0; 00242 00243 } else { 00244 char *buffer = (char *)malloc(strlen(value) + 80); 00245 sprintf(buffer, "Sample directory \"%s\" does not exist, leaving unchanged", value); 00246 return buffer; 00247 } 00248 } 00249 00250 return strdup("Unknown configure key"); 00251 } 00252 00253 const DSSI_Program_Descriptor * 00254 SamplePlayer::getProgram(LADSPA_Handle handle, unsigned long program) 00255 { 00256 SamplePlayer *player = (SamplePlayer *)handle; 00257 00258 if (!player->m_sampleSearchComplete) { 00259 QMutexLocker locker(&player->m_mutex); 00260 if (!player->m_sampleSearchComplete) { 00261 player->searchSamples(); 00262 } 00263 } 00264 if (program >= player->m_samples.size()) return 0; 00265 00266 static DSSI_Program_Descriptor descriptor; 00267 static char name[60]; 00268 00269 strncpy(name, player->m_samples[program].first.toLocal8Bit().data(), 60); 00270 name[59] = '\0'; 00271 00272 descriptor.Bank = 0; 00273 descriptor.Program = program; 00274 descriptor.Name = name; 00275 00276 return &descriptor; 00277 } 00278 00279 void 00280 SamplePlayer::selectProgram(LADSPA_Handle handle, 00281 unsigned long, 00282 unsigned long program) 00283 { 00284 SamplePlayer *player = (SamplePlayer *)handle; 00285 player->m_pendingProgramChange = program; 00286 } 00287 00288 int 00289 SamplePlayer::getMidiController(LADSPA_Handle, unsigned long port) 00290 { 00291 int controllers[PortCount] = { 00292 DSSI_NONE, 00293 DSSI_CC(12), 00294 DSSI_CC(13), 00295 DSSI_CC(64), 00296 DSSI_CC(72) 00297 }; 00298 00299 return controllers[port]; 00300 } 00301 00302 void 00303 SamplePlayer::runSynth(LADSPA_Handle handle, unsigned long samples, 00304 snd_seq_event_t *events, unsigned long eventCount) 00305 { 00306 SamplePlayer *player = (SamplePlayer *)handle; 00307 00308 player->runImpl(samples, events, eventCount); 00309 } 00310 00311 void 00312 SamplePlayer::receiveHostDescriptor(const DSSI_Host_Descriptor *descriptor) 00313 { 00314 hostDescriptor = descriptor; 00315 } 00316 00317 void 00318 SamplePlayer::workThreadCallback(LADSPA_Handle handle) 00319 { 00320 SamplePlayer *player = (SamplePlayer *)handle; 00321 00322 if (player->m_pendingProgramChange >= 0) { 00323 00324 #ifdef DEBUG_SAMPLE_PLAYER 00325 SVDEBUG << "SamplePlayer::workThreadCallback: pending program change " << player->m_pendingProgramChange << endl; 00326 #endif 00327 00328 player->m_mutex.lock(); 00329 00330 int program = player->m_pendingProgramChange; 00331 player->m_pendingProgramChange = -1; 00332 00333 if (!player->m_sampleSearchComplete) { 00334 player->searchSamples(); 00335 } 00336 00337 if (program < int(player->m_samples.size())) { 00338 QString path = player->m_samples[program].second; 00339 QString programName = player->m_samples[program].first; 00340 if (programName != player->m_program) { 00341 player->m_program = programName; 00342 player->m_mutex.unlock(); 00343 player->loadSampleData(path); 00344 } else { 00345 player->m_mutex.unlock(); 00346 } 00347 } 00348 } 00349 00350 if (!player->m_sampleSearchComplete) { 00351 00352 QMutexLocker locker(&player->m_mutex); 00353 00354 if (!player->m_sampleSearchComplete) { 00355 player->searchSamples(); 00356 } 00357 } 00358 } 00359 00360 void 00361 SamplePlayer::searchSamples() 00362 { 00363 if (m_sampleSearchComplete) return; 00364 00365 m_samples.clear(); 00366 00367 #ifdef DEBUG_SAMPLE_PLAYER 00368 SVDEBUG << "SamplePlayer::searchSamples: Directory is \"" 00369 << m_sampleDir << "\"" << endl; 00370 #endif 00371 00372 QDir dir(m_sampleDir, "*.wav"); 00373 00374 for (unsigned int i = 0; i < dir.count(); ++i) { 00375 QFileInfo file(dir.filePath(dir[i])); 00376 if (file.isReadable()) { 00377 m_samples.push_back(std::pair<QString, QString> 00378 (file.baseName(), file.filePath())); 00379 #ifdef DEBUG_SAMPLE_PLAYER 00380 cerr << "Found: " << dir[i] << endl; 00381 #endif 00382 } 00383 } 00384 00385 m_sampleSearchComplete = true; 00386 } 00387 00388 void 00389 SamplePlayer::loadSampleData(QString path) 00390 { 00391 SF_INFO info; 00392 SNDFILE *file; 00393 size_t samples = 0; 00394 float *tmpFrames, *tmpSamples, *tmpResamples, *tmpOld; 00395 size_t i; 00396 00397 info.format = 0; 00398 file = sf_open(path.toLocal8Bit().data(), SFM_READ, &info); 00399 if (!file) { 00400 cerr << "SamplePlayer::loadSampleData: Failed to open file " 00401 << path << ": " 00402 << sf_strerror(file) << endl; 00403 return; 00404 } 00405 00406 samples = info.frames; 00407 tmpFrames = (float *)malloc(info.frames * info.channels * sizeof(float)); 00408 if (!tmpFrames) return; 00409 00410 sf_readf_float(file, tmpFrames, info.frames); 00411 sf_close(file); 00412 00413 tmpResamples = 0; 00414 00415 if (info.samplerate != m_sampleRate) { 00416 00417 double ratio = (double)m_sampleRate / (double)info.samplerate; 00418 size_t target = (size_t)(info.frames * ratio); 00419 SRC_DATA data; 00420 00421 tmpResamples = (float *)malloc(target * info.channels * sizeof(float)); 00422 if (!tmpResamples) { 00423 free(tmpFrames); 00424 return; 00425 } 00426 00427 memset(tmpResamples, 0, target * info.channels * sizeof(float)); 00428 00429 data.data_in = tmpFrames; 00430 data.data_out = tmpResamples; 00431 data.input_frames = info.frames; 00432 data.output_frames = target; 00433 data.src_ratio = ratio; 00434 00435 if (!src_simple(&data, SRC_SINC_BEST_QUALITY, info.channels)) { 00436 free(tmpFrames); 00437 tmpFrames = tmpResamples; 00438 samples = target; 00439 } else { 00440 free(tmpResamples); 00441 } 00442 } 00443 00444 /* add an extra sample for linear interpolation */ 00445 tmpSamples = (float *)malloc((samples + 1) * sizeof(float)); 00446 if (!tmpSamples) { 00447 free(tmpFrames); 00448 return; 00449 } 00450 00451 for (i = 0; i < samples; ++i) { 00452 int j; 00453 tmpSamples[i] = 0.0f; 00454 for (j = 0; j < info.channels; ++j) { 00455 tmpSamples[i] += tmpFrames[i * info.channels + j]; 00456 } 00457 } 00458 00459 free(tmpFrames); 00460 00461 /* add an extra sample for linear interpolation */ 00462 tmpSamples[samples] = 0.0f; 00463 00464 QMutexLocker locker(&m_mutex); 00465 00466 tmpOld = m_sampleData; 00467 m_sampleData = tmpSamples; 00468 m_sampleCount = samples; 00469 00470 for (i = 0; i < Polyphony; ++i) { 00471 m_ons[i] = -1; 00472 m_offs[i] = -1; 00473 m_velocities[i] = 0; 00474 } 00475 00476 if (tmpOld) free(tmpOld); 00477 00478 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); 00479 } 00480 00481 void 00482 SamplePlayer::runImpl(unsigned long sampleCount, 00483 snd_seq_event_t *events, 00484 unsigned long eventCount) 00485 { 00486 unsigned long pos; 00487 unsigned long count; 00488 unsigned long event_pos; 00489 int i; 00490 00491 memset(m_output, 0, sampleCount * sizeof(float)); 00492 00493 if (!m_mutex.tryLock()) return; 00494 00495 if (!m_sampleData || !m_sampleCount) { 00496 m_sampleNo += sampleCount; 00497 m_mutex.unlock(); 00498 return; 00499 } 00500 00501 for (pos = 0, event_pos = 0; pos < sampleCount; ) { 00502 00503 while (event_pos < eventCount 00504 && pos >= events[event_pos].time.tick) { 00505 00506 if (events[event_pos].type == SND_SEQ_EVENT_NOTEON) { 00507 #ifdef DEBUG_SAMPLE_PLAYER 00508 cerr << "SamplePlayer: found NOTEON at time " 00509 << events[event_pos].time.tick << endl; 00510 #endif 00511 snd_seq_ev_note_t n = events[event_pos].data.note; 00512 if (n.velocity > 0) { 00513 m_ons[n.note] = 00514 m_sampleNo + events[event_pos].time.tick; 00515 m_offs[n.note] = -1; 00516 m_velocities[n.note] = n.velocity; 00517 } else { 00518 if (!m_sustain || (*m_sustain < 0.001)) { 00519 m_offs[n.note] = 00520 m_sampleNo + events[event_pos].time.tick; 00521 } 00522 } 00523 } else if (events[event_pos].type == SND_SEQ_EVENT_NOTEOFF && 00524 (!m_sustain || (*m_sustain < 0.001))) { 00525 #ifdef DEBUG_SAMPLE_PLAYER 00526 cerr << "SamplePlayer: found NOTEOFF at time " 00527 << events[event_pos].time.tick << endl; 00528 #endif 00529 snd_seq_ev_note_t n = events[event_pos].data.note; 00530 m_offs[n.note] = 00531 m_sampleNo + events[event_pos].time.tick; 00532 } 00533 00534 ++event_pos; 00535 } 00536 00537 count = sampleCount - pos; 00538 if (event_pos < eventCount && 00539 events[event_pos].time.tick < sampleCount) { 00540 count = events[event_pos].time.tick - pos; 00541 } 00542 00543 int notecount = 0; 00544 00545 for (i = 0; i < Polyphony; ++i) { 00546 if (m_ons[i] >= 0) { 00547 ++notecount; 00548 addSample(i, pos, count); 00549 } 00550 } 00551 00552 #ifdef DEBUG_SAMPLE_PLAYER 00553 cerr << "SamplePlayer: have " << notecount << " note(s) sounding currently" << endl; 00554 #endif 00555 00556 pos += count; 00557 } 00558 00559 m_sampleNo += sampleCount; 00560 m_mutex.unlock(); 00561 } 00562 00563 void 00564 SamplePlayer::addSample(int n, unsigned long pos, unsigned long count) 00565 { 00566 float ratio = 1.f; 00567 float gain = 1.f; 00568 unsigned long i, s; 00569 00570 if (m_retune && *m_retune) { 00571 if (m_concertA) { 00572 ratio *= *m_concertA / 440.f; 00573 } 00574 if (m_basePitch && n != *m_basePitch) { 00575 ratio *= powf(1.059463094f, n - *m_basePitch); 00576 } 00577 } 00578 00579 if (long(pos + m_sampleNo) < m_ons[n]) return; 00580 00581 gain = (float)m_velocities[n] / 127.0f; 00582 00583 for (i = 0, s = pos + m_sampleNo - m_ons[n]; 00584 i < count; 00585 ++i, ++s) { 00586 00587 float lgain = gain; 00588 float rs = s * ratio; 00589 unsigned long rsi = lrintf(floor(rs)); 00590 00591 if (rsi >= m_sampleCount) { 00592 #ifdef DEBUG_SAMPLE_PLAYER 00593 cerr << "Note " << n << " has run out of samples (were " << m_sampleCount << " available at ratio " << ratio << "), ending" << endl; 00594 #endif 00595 m_ons[n] = -1; 00596 break; 00597 } 00598 00599 if (m_offs[n] >= 0 && 00600 long(pos + i + m_sampleNo) > m_offs[n]) { 00601 00602 unsigned long dist = 00603 pos + i + m_sampleNo - m_offs[n]; 00604 00605 unsigned long releaseFrames = 200; 00606 if (m_release) { 00607 releaseFrames = long(*m_release * m_sampleRate + 0.0001); 00608 } 00609 00610 if (dist > releaseFrames) { 00611 #ifdef DEBUG_SAMPLE_PLAYER 00612 cerr << "Note " << n << " has expired its release time (" << releaseFrames << " frames), ending" << endl; 00613 #endif 00614 m_ons[n] = -1; 00615 break; 00616 } else { 00617 lgain = lgain * (float)(releaseFrames - dist) / 00618 (float)releaseFrames; 00619 } 00620 } 00621 00622 float sample = m_sampleData[rsi] + 00623 ((m_sampleData[rsi + 1] - 00624 m_sampleData[rsi]) * 00625 (rs - (float)rsi)); 00626 00627 m_output[pos + i] += lgain * sample; 00628 } 00629 }