svcore  1.9
SamplePlayer.cpp
Go to the documentation of this file.
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 }