hydrogen 1.2.3
AudioEngine.cpp
Go to the documentation of this file.
1/*
2 * Hydrogen
3 * Copyright(c) 2002-2008 by Alex >Comix< Cominu [comix@users.sourceforge.net]
4 * Copyright(c) 2008-2024 The hydrogen development team [hydrogen-devel@lists.sourceforge.net]
5 *
6 * http://www.hydrogen-music.org
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY, without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see https://www.gnu.org/licenses
20 *
21 */
22
25
26#ifdef WIN32
27# include "core/Timehelper.h"
28#else
29# include <unistd.h>
30# include <sys/time.h>
31#endif
32
33#include <core/EventQueue.h>
34#include <core/FX/Effects.h>
35#include <core/Basics/Song.h>
36#include <core/Basics/Pattern.h>
38#include <core/Basics/Note.h>
46#include <core/Helpers/Random.h>
47
48#include <core/IO/AudioOutput.h>
50#include <core/IO/NullDriver.h>
51#include <core/IO/MidiInput.h>
52#include <core/IO/MidiOutput.h>
54#include <core/IO/OssDriver.h>
55#include <core/IO/FakeDriver.h>
64
65#include <core/Hydrogen.h>
67
68#include <limits>
69
70namespace H2Core
71{
72
75inline timeval currentTime2()
76{
77 struct timeval now;
78 gettimeofday( &now, nullptr );
79 return now;
80}
81
83 : m_pSampler( nullptr )
84 , m_pSynth( nullptr )
85 , m_pAudioDriver( nullptr )
86 , m_pMidiDriver( nullptr )
87 , m_pMidiDriverOut( nullptr )
88 , m_state( State::Initialized )
89 , m_pMetronomeInstrument( nullptr )
90 , m_fSongSizeInTicks( 0 )
91 , m_nRealtimeFrame( 0 )
92 , m_fMasterPeak_L( 0.0f )
93 , m_fMasterPeak_R( 0.0f )
94 , m_nextState( State::Ready )
95 , m_fProcessTime( 0.0f )
96 , m_fLadspaTime( 0.0f )
97 , m_fMaxProcessTime( 0.0f )
98 , m_fNextBpm( 120 )
99 , m_pLocker({nullptr, 0, nullptr})
100 , m_fLastTickEnd( 0 )
101 , m_bLookaheadApplied( false )
102 , m_nLoopsDone( 0 )
103{
104 m_pTransportPosition = std::make_shared<TransportPosition>( "Transport" );
105 m_pQueuingPosition = std::make_shared<TransportPosition>( "Queuing" );
106
107 m_pSampler = new Sampler;
108 m_pSynth = new Synth;
109
110 m_pEventQueue = EventQueue::get_instance();
111
112 srand( time( nullptr ) );
113
114 // Create metronome instrument
115 // Get the path to the file of the metronome sound.
116 QString sMetronomeFilename = Filesystem::click_file_path();
117 m_pMetronomeInstrument = std::make_shared<Instrument>( METRONOME_INSTR_ID, "metronome" );
118
119 auto pLayer = std::make_shared<InstrumentLayer>( Sample::load( sMetronomeFilename ) );
120 auto pCompo = std::make_shared<InstrumentComponent>( 0 );
121 pCompo->set_layer(pLayer, 0);
122 m_pMetronomeInstrument->get_components()->push_back( pCompo );
123 m_pMetronomeInstrument->set_is_metronome_instrument(true);
124 m_pMetronomeInstrument->set_volume(
125 Preferences::get_instance()->m_fMetronomeVolume );
126
127 m_AudioProcessCallback = &audioEngine_process;
128
129 // Has to be done before assigning the supported audio drivers.
130 checkJackSupport();
131
132 // The order of the assigned drivers is important as Hydrogen uses
133 // it when trying different drivers in case "Auto" was selected.
134#if defined(WIN32)
135 #ifdef H2CORE_HAVE_PORTAUDIO
136 m_supportedAudioDrivers << "PortAudio";
137 #endif
138 if ( m_bJackSupported ) {
139 m_supportedAudioDrivers << "JACK";
140 }
141#elif defined(__APPLE__)
142 #ifdef H2CORE_HAVE_COREAUDIO
143 m_supportedAudioDrivers << "CoreAudio";
144 #endif
145 if ( m_bJackSupported ) {
146 m_supportedAudioDrivers << "JACK";
147 }
148 #ifdef H2CORE_HAVE_PULSEAUDIO
149 m_supportedAudioDrivers << "PulseAudio";
150 #endif
151 #ifdef H2CORE_HAVE_PORTAUDIO
152 m_supportedAudioDrivers << "PortAudio";
153 #endif
154#else /* Linux */
155 if ( m_bJackSupported ) {
156 m_supportedAudioDrivers << "JACK";
157 }
158 #ifdef H2CORE_HAVE_PULSEAUDIO
159 m_supportedAudioDrivers << "PulseAudio";
160 #endif
161 #ifdef H2CORE_HAVE_ALSA
162 m_supportedAudioDrivers << "ALSA";
163 #endif
164 #ifdef H2CORE_HAVE_OSS
165 m_supportedAudioDrivers << "OSS";
166 #endif
167 #ifdef H2CORE_HAVE_PORTAUDIO
168 m_supportedAudioDrivers << "PortAudio";
169 #endif
170#endif
171
172#ifdef H2CORE_HAVE_LADSPA
174#endif
175}
176
178{
180 if ( getState() != State::Initialized ) {
181 ERRORLOG( "Error the audio engine is not in State::Initialized" );
182 return;
183 }
185
186 this->lock( RIGHT_HERE );
187 INFOLOG( "*** Hydrogen audio engine shutdown ***" );
188
190
192
193 m_pTransportPosition->reset();
194 m_pTransportPosition = nullptr;
195 m_pQueuingPosition->reset();
196 m_pQueuingPosition = nullptr;
197
198 m_pMetronomeInstrument = nullptr;
199
200 this->unlock();
201
202#ifdef H2CORE_HAVE_LADSPA
203 delete Effects::get_instance();
204#endif
205
206 delete m_pSampler;
207 delete m_pSynth;
208}
209
211{
212 assert(m_pSampler);
213 return m_pSampler;
214}
215
217{
218 assert(m_pSynth);
219 return m_pSynth;
220}
221
222void AudioEngine::lock( const char* file, unsigned int line, const char* function )
223{
224 #ifdef H2CORE_HAVE_DEBUG
226 __logger->log( Logger::Locks, _class_name(), __FUNCTION__,
227 QString( "by %1 : %2 : %3" ).arg( function ).arg( line ).arg( file ) );
228 }
229 #endif
230
231 m_EngineMutex.lock();
232 m_pLocker.file = file;
233 m_pLocker.line = line;
234 m_pLocker.function = function;
235 m_LockingThread = std::this_thread::get_id();
236}
237
238bool AudioEngine::tryLock( const char* file, unsigned int line, const char* function )
239{
240 #ifdef H2CORE_HAVE_DEBUG
242 __logger->log( Logger::Locks, _class_name(), __FUNCTION__,
243 QString( "by %1 : %2 : %3" ).arg( function ).arg( line ).arg( file ) );
244 }
245 #endif
246 bool res = m_EngineMutex.try_lock();
247 if ( !res ) {
248 // Lock not obtained
249 return false;
250 }
251 m_pLocker.file = file;
252 m_pLocker.line = line;
253 m_pLocker.function = function;
254 m_LockingThread = std::this_thread::get_id();
255 #ifdef H2CORE_HAVE_DEBUG
257 __logger->log( Logger::Locks, _class_name(), __FUNCTION__, QString( "locked" ) );
258 }
259 #endif
260 return true;
261}
262
263bool AudioEngine::tryLockFor( std::chrono::microseconds duration, const char* file, unsigned int line, const char* function )
264{
265 #ifdef H2CORE_HAVE_DEBUG
267 __logger->log( Logger::Locks, _class_name(), __FUNCTION__,
268 QString( "by %1 : %2 : %3" ).arg( function ).arg( line ).arg( file ) );
269 }
270 #endif
271 bool res = m_EngineMutex.try_lock_for( duration );
272 if ( !res ) {
273 // Lock not obtained
274 WARNINGLOG( QString( "Lock timeout: lock timeout %1:%2:%3, lock held by %4:%5:%6" )
275 .arg( file ).arg( function ).arg( line )
276 .arg( m_pLocker.file ).arg( m_pLocker.function ).arg( m_pLocker.line ));
277 return false;
278 }
279 m_pLocker.file = file;
280 m_pLocker.line = line;
281 m_pLocker.function = function;
282 m_LockingThread = std::this_thread::get_id();
283
284 #ifdef H2CORE_HAVE_DEBUG
286 __logger->log( Logger::Locks, _class_name(), __FUNCTION__, QString( "locked" ) );
287 }
288 #endif
289 return true;
290}
291
293{
294 // Leave "__locker" dirty.
295 m_LockingThread = std::thread::id();
296 m_EngineMutex.unlock();
297 #ifdef H2CORE_HAVE_DEBUG
299 __logger->log( Logger::Locks, _class_name(), __FUNCTION__, QString( "" ) );
300 }
301 #endif
302}
303
305{
306 INFOLOG( "" );
307
308 if ( getState() != State::Ready ) {
309 ERRORLOG( "Error the audio engine is not in State::Ready" );
310 return;
311 }
312
314
316}
317
319{
320 INFOLOG( "" );
321
322 if ( getState() != State::Playing ) {
323 ERRORLOG( QString( "Error the audio engine is not in State::Playing but [%1]" )
324 .arg( static_cast<int>( getState() ) ) );
325 return;
326 }
327
329}
330
331void AudioEngine::reset( bool bWithJackBroadcast ) {
332 const auto pHydrogen = Hydrogen::get_instance();
333
335
336 m_fMasterPeak_L = 0.0f;
337 m_fMasterPeak_R = 0.0f;
338
339#ifdef H2CORE_HAVE_LADSPA
340 for ( int ii = 0; ii < MAX_FX; ++ii ) {
341 m_fFXPeak_L[ ii ] = 0;
342 m_fFXPeak_R[ ii ] = 0;
343 }
344#endif
345
346 m_fLastTickEnd = 0;
347 m_nLoopsDone = 0;
348 m_bLookaheadApplied = false;
349
351 setNextBpm( 120 );
352
353 m_pTransportPosition->reset();
354 m_pQueuingPosition->reset();
355
358
360
361#ifdef H2CORE_HAVE_JACK
362 if ( pHydrogen->hasJackTransport() && bWithJackBroadcast ) {
363 // Tell the JACK server to locate to the beginning as well
364 // (done in the next run of audioEngine_process()).
365 static_cast<JackAudioDriver*>( m_pAudioDriver )->locateTransport( 0 );
366 }
367#endif
368}
369
370float AudioEngine::computeTickSize( const int nSampleRate, const float fBpm, const int nResolution)
371{
372 float fTickSize = nSampleRate * 60.0 / fBpm / nResolution;
373
374 return fTickSize;
375}
376
377double AudioEngine::computeDoubleTickSize( const int nSampleRate, const float fBpm, const int nResolution)
378{
379 double fTickSize = static_cast<double>(nSampleRate) * 60.0 /
380 static_cast<double>(fBpm) /
381 static_cast<double>(nResolution);
382
383 return fTickSize;
384}
385
387
388 const auto pHydrogen = Hydrogen::get_instance();
389 const auto pDriver = pHydrogen->getAudioOutput();
390
391 if ( pDriver == nullptr || pDriver->getSampleRate() == 0 ) {
392 return 0;
393 }
394
395 return ( m_pTransportPosition->getFrame() -
396 m_pTransportPosition->getFrameOffsetTempo() )/
397 static_cast<float>(pDriver->getSampleRate());
398}
399
400void AudioEngine::locate( const double fTick, bool bWithJackBroadcast ) {
401 const auto pHydrogen = Hydrogen::get_instance();
402
403 // DEBUGLOG( QString( "fTick: %1" ).arg( fTick ) );
404
405#ifdef H2CORE_HAVE_JACK
406 // In case Hydrogen is using the JACK server to sync transport, it
407 // is up to the server to relocate to a different position. It
408 // does so after the current cycle of audioEngine_process() and we
409 // will pick it up at the beginning of the next one.
410 if ( pHydrogen->hasJackTransport() && bWithJackBroadcast ) {
411 double fTickMismatch;
412 const long long nNewFrame = TransportPosition::computeFrameFromTick(
413 fTick, &fTickMismatch );
414 static_cast<JackAudioDriver*>( m_pAudioDriver )->locateTransport( nNewFrame );
415 return;
416 }
417#endif
418
419 resetOffsets();
420 m_fLastTickEnd = fTick;
421 const long long nNewFrame = TransportPosition::computeFrameFromTick(
422 fTick, &m_pTransportPosition->m_fTickMismatch );
423
426
428}
429
430void AudioEngine::locateToFrame( const long long nFrame ) {
431
432 // DEBUGLOG( QString( "nFrame: %1" ).arg( nFrame ) );
433
434 resetOffsets();
435
436 double fNewTick = TransportPosition::computeTickFromFrame( nFrame );
437
438 // As the tick mismatch is lost when converting a sought location
439 // from ticks into frames, sending it to the JACK server,
440 // receiving it in a callback, and providing it here, we will use
441 // a heuristic in order to not experience any glitches upon
442 // relocation.
443 if ( std::fmod( fNewTick, std::floor( fNewTick ) ) >= 0.97 ) {
444 INFOLOG( QString( "Computed tick [%1] will be rounded to [%2] in order to avoid glitches" )
445 .arg( fNewTick, 0, 'E', -1 ).arg( std::round( fNewTick ) ) );
446 fNewTick = std::round( fNewTick );
447 }
448 m_fLastTickEnd = fNewTick;
449
450 // Assure tick<->frame can be converted properly using mismatch.
451 const long long nNewFrame = TransportPosition::computeFrameFromTick(
452 fNewTick, &m_pTransportPosition->m_fTickMismatch );
453
454 updateTransportPosition( fNewTick, nNewFrame, m_pTransportPosition );
456
458
459 // While the locate() function is wrapped by a caller in the
460 // CoreActionController - which takes care of queuing the
461 // relocation event - this function is only meant to be used in
462 // very specific circumstances and has to queue it itself.
464}
465
468
469 m_fLastTickEnd = 0;
470 m_nLoopsDone = 0;
471 m_bLookaheadApplied = false;
472
473 m_pTransportPosition->setFrameOffsetTempo( 0 );
474 m_pTransportPosition->setTickOffsetQueuing( 0 );
475 m_pTransportPosition->setTickOffsetSongSize( 0 );
476 m_pTransportPosition->setLastLeadLagFactor( 0 );
477 m_pQueuingPosition->setFrameOffsetTempo( 0 );
478 m_pQueuingPosition->setTickOffsetQueuing( 0 );
479 m_pQueuingPosition->setTickOffsetSongSize( 0 );
480 m_pQueuingPosition->setLastLeadLagFactor( 0 );
481}
482
484 auto pSong = Hydrogen::get_instance()->getSong();
485
486 if ( pSong == nullptr ) {
487 return;
488 }
489
490 const long long nNewFrame = m_pTransportPosition->getFrame() + nFrames;
491 const double fNewTick = TransportPosition::computeTickFromFrame( nNewFrame );
492 m_pTransportPosition->m_fTickMismatch = 0;
493
494 // DEBUGLOG( QString( "nFrames: %1, old frame: %2, new frame: %3, old tick: %4, new tick: %5, ticksize: %6" )
495 // .arg( nFrames )
496 // .arg( m_pTransportPosition->getFrame() )
497 // .arg( nNewFrame )
498 // .arg( m_pTransportPosition->getDoubleTick(), 0, 'f' )
499 // .arg( fNewTick, 0, 'f' )
500 // .arg( m_pTransportPosition->getTickSize(), 0, 'f' ) );
501
502 updateTransportPosition( fNewTick, nNewFrame, m_pTransportPosition );
503
504 // We are not updating the queuing position in here. This will be
505 // done in updateNoteQueue().
506}
507
508bool AudioEngine::isEndOfSongReached( std::shared_ptr<TransportPosition> pPos ) const {
509 const auto pSong = Hydrogen::get_instance()->getSong();
510 if ( pSong->getMode() == Song::Mode::Song &&
511 ( pSong->getLoopMode() == Song::LoopMode::Disabled &&
512 pPos->getDoubleTick() >= m_fSongSizeInTicks ||
513 pSong->getLoopMode() == Song::LoopMode::Finishing &&
514 pPos->getDoubleTick() >= m_fSongSizeInTicks *
515 (1 + static_cast<double>(m_nLoopsDone)) ) ) {
516 return true;
517 }
518
519 return false;
520}
521
522void AudioEngine::updateTransportPosition( double fTick, long long nFrame, std::shared_ptr<TransportPosition> pPos ) {
523
524 const auto pHydrogen = Hydrogen::get_instance();
525 const auto pSong = pHydrogen->getSong();
526
527 assert( pSong );
528
529 // WARNINGLOG( QString( "[Before] fTick: %1, nFrame: %2, pos: %3" )
530 // .arg( fTick, 0, 'f' )
531 // .arg( nFrame )
532 // .arg( pPos->toQString( "", true ) ) );
533
534 if ( pHydrogen->getMode() == Song::Mode::Song ) {
535 updateSongTransportPosition( fTick, nFrame, pPos );
536 }
537 else { // Song::Mode::Pattern
538 updatePatternTransportPosition( fTick, nFrame, pPos );
539 }
540
541 updateBpmAndTickSize( pPos );
542
543
544 // Beat - Bar (- Tick) information is a coarse grained position
545 // information and might not change on small position increments.
546 bool bBBTChanged = false;
547 const int nBar = std::max( pPos->getColumn(), 0 ) + 1;
548 if ( nBar != pPos->getBar() ) {
549 pPos->setBar( nBar );
550 bBBTChanged = true;
551 }
552
553 const int nBeat = static_cast<int>(
554 std::floor(static_cast<float>(pPos->getPatternTickPosition()) / 48 )) + 1;
555 if ( pPos->getBeat() != nBeat ) {
556 pPos->setBeat( nBeat );
557 bBBTChanged = true;
558 }
559
560 if ( pPos == m_pTransportPosition && bBBTChanged ) {
562 }
563
564 // WARNINGLOG( QString( "[After] fTick: %1, nFrame: %2, pos: %3, frame: %4" )
565 // .arg( fTick, 0, 'f' )
566 // .arg( nFrame )
567 // .arg( pPos->toQString( "", true ) )
568 // .arg( pPos->getFrame() ) );
569
570}
571
572void AudioEngine::updatePatternTransportPosition( double fTick, long long nFrame, std::shared_ptr<TransportPosition> pPos ) {
573
574 auto pHydrogen = Hydrogen::get_instance();
575
576 pPos->setTick( fTick );
577 pPos->setFrame( nFrame );
578
579 const double fPatternStartTick =
580 static_cast<double>(pPos->getPatternStartTick());
581 const int nPatternSize = pPos->getPatternSize();
582
583 if ( fTick >= fPatternStartTick + static_cast<double>(nPatternSize) ||
584 fTick < fPatternStartTick ) {
585 // Transport went past the end of the pattern or Pattern mode
586 // was just activated.
587 pPos->setPatternStartTick( pPos->getPatternStartTick() +
588 static_cast<long>(std::floor( ( fTick - fPatternStartTick ) /
589 static_cast<double>(nPatternSize) )) *
590 nPatternSize );
591
592 // In stacked pattern mode we will only update the playing
593 // patterns if the transport of the original pattern is looped
594 // back to the beginning. This way all patterns start fresh.
595 //
596 // In selected pattern mode pattern change does occur
597 // asynchonically by user interaction.
598 if ( pHydrogen->getPatternMode() == Song::PatternMode::Stacked ) {
600 }
601 }
602
603 long nPatternTickPosition = static_cast<long>(std::floor( fTick )) -
604 pPos->getPatternStartTick();
605 if ( nPatternTickPosition > nPatternSize ) {
606 nPatternTickPosition = ( static_cast<long>(std::floor( fTick ))
607 - pPos->getPatternStartTick() ) %
608 nPatternSize;
609 }
610 pPos->setPatternTickPosition( nPatternTickPosition );
611}
612
613void AudioEngine::updateSongTransportPosition( double fTick, long long nFrame, std::shared_ptr<TransportPosition> pPos ) {
614
615 // WARNINGLOG( QString( "[Before] fTick: %1, nFrame: %2, m_fSongSizeInTicks: %3, pos: %4" )
616 // .arg( fTick, 0, 'f' )
617 // .arg( nFrame )
618 // .arg( m_fSongSizeInTicks, 0, 'f' )
619 // .arg( pPos->toQString( "", true ) ) );
620
621 const auto pHydrogen = Hydrogen::get_instance();
622 const auto pSong = pHydrogen->getSong();
623
624 pPos->setTick( fTick );
625 pPos->setFrame( nFrame );
626
627 if ( fTick < 0 ) {
628 ERRORLOG( QString( "[%1] Provided tick [%2] is negative!" )
629 .arg( pPos->getLabel() )
630 .arg( fTick, 0, 'f' ) );
631 return;
632 }
633
634 int nNewColumn;
635 if ( pSong->getPatternGroupVector()->size() == 0 ) {
636 // There are no patterns in song.
637 pPos->setPatternStartTick( 0 );
638 pPos->setPatternTickPosition( 0 );
639 nNewColumn = 0;
640 }
641 else {
642 long nPatternStartTick;
643 nNewColumn = pHydrogen->getColumnForTick(
644 std::floor( fTick ), pSong->isLoopEnabled(), &nPatternStartTick );
645 pPos->setPatternStartTick( nPatternStartTick );
646
647 // While the current tick position is constantly increasing,
648 // m_nPatternStartTick is only defined between 0 and
649 // m_fSongSizeInTicks. We will take care of the looping next.
650 if ( fTick >= m_fSongSizeInTicks && m_fSongSizeInTicks != 0 ) {
651 pPos->setPatternTickPosition(
652 std::fmod( std::floor( fTick ) - nPatternStartTick,
654 }
655 else {
656 pPos->setPatternTickPosition( std::floor( fTick ) - nPatternStartTick );
657 }
658 }
659
660 if ( pPos->getColumn() != nNewColumn ) {
661 pPos->setColumn( nNewColumn );
662
665 }
666
667 // WARNINGLOG( QString( "[After] fTick: %1, nFrame: %2, m_fSongSizeInTicks: %3, pos: %4, frame: %5" )
668 // .arg( fTick, 0, 'f' )
669 // .arg( nFrame )
670 // .arg( m_fSongSizeInTicks, 0, 'f' )
671 // .arg( pPos->toQString( "", true ) )
672 // .arg( pPos->getFrame() ) );
673
674}
675
676void AudioEngine::updateBpmAndTickSize( std::shared_ptr<TransportPosition> pPos ) {
677 if ( ! ( m_state == State::Playing ||
679 m_state == State::Testing ) ) {
680 return;
681 }
682
683 auto pHydrogen = Hydrogen::get_instance();
684 auto pSong = pHydrogen->getSong();
685
686 const float fOldBpm = pPos->getBpm();
687
688 const float fNewBpm = getBpmAtColumn( pPos->getColumn() );
689 if ( fNewBpm != fOldBpm ) {
690 pPos->setBpm( fNewBpm );
692 }
693
694 const float fOldTickSize = pPos->getTickSize();
695 const float fNewTickSize =
697 fNewBpm, pSong->getResolution() );
698 // Nothing changed - avoid recomputing
699#if defined(WIN32) and !defined(WIN64)
700 // For some reason two identical numbers (according to their
701 // values when printing them) are not equal to each other in 32bit
702 // Windows. Course graining the tick change in here will do no
703 // harm except of for preventing tiny tempo changes. Integer value
704 // changes should not be affected.
705 if ( std::abs( fNewTickSize - fOldTickSize ) < 1e-2 ) {
706#else
707 if ( fNewTickSize == fOldTickSize ) {
708#endif
709 return;
710 }
711
712 if ( fNewTickSize == 0 ) {
713 ERRORLOG( QString( "[%1] Something went wrong while calculating the tick size. [oldTS: %2, newTS: %3]" )
714 .arg( pPos->getLabel() )
715 .arg( fOldTickSize, 0, 'f' ).arg( fNewTickSize, 0, 'f' ) );
716 return;
717 }
718
719 // The lookahead in updateNoteQueue is tempo dependent (since it
720 // contains both tick and frame components). By resetting this
721 // variable we allow that the next one calculated to have
722 // arbitrary values.
723 pPos->setLastLeadLagFactor( 0 );
724
725 // DEBUGLOG(QString( "[%1] [%7,%8] sample rate: %2, tick size: %3 -> %4, bpm: %5 -> %6" )
726 // .arg( pPos->getLabel() )
727 // .arg( static_cast<float>(m_pAudioDriver->getSampleRate()))
728 // .arg( fOldTickSize, 0, 'f' )
729 // .arg( fNewTickSize, 0, 'f' )
730 // .arg( fOldBpm, 0, 'f' )
731 // .arg( pPos->getBpm(), 0, 'f' )
732 // .arg( pPos->getFrame() )
733 // .arg( pPos->getDoubleTick(), 0, 'f' ) );
734
735 pPos->setTickSize( fNewTickSize );
736
738}
739
740void AudioEngine::calculateTransportOffsetOnBpmChange( std::shared_ptr<TransportPosition> pPos ) {
741
742 // If we deal with a single speed for the whole song, the frames
743 // since the beginning of the song are tempo-dependent and have to
744 // be recalculated.
745 const long long nNewFrame =
746 TransportPosition::computeFrameFromTick( pPos->getDoubleTick(),
747 &pPos->m_fTickMismatch );
748 pPos->setFrameOffsetTempo( nNewFrame - pPos->getFrame() +
749 pPos->getFrameOffsetTempo() );
750
751 if ( m_bLookaheadApplied ) {
752 // if ( m_fLastTickEnd != 0 ) {
753 const long long nNewLookahead =
754 getLeadLagInFrames( pPos->getDoubleTick() ) +
756 const double fNewTickEnd = TransportPosition::computeTickFromFrame(
757 nNewFrame + nNewLookahead ) + pPos->getTickMismatch();
758 pPos->setTickOffsetQueuing( fNewTickEnd - m_fLastTickEnd );
759
760 // DEBUGLOG( QString( "[%1 : [%2] timeline] old frame: %3, new frame: %4, tick: %5, nNewLookahead: %6, pPos->getFrameOffsetTempo(): %7, pPos->getTickOffsetQueuing(): %8, fNewTickEnd: %9, m_fLastTickEnd: %10" )
761 // .arg( pPos->getLabel() )
762 // .arg( Hydrogen::get_instance()->isTimelineEnabled() )
763 // .arg( pPos->getFrame() )
764 // .arg( nNewFrame )
765 // .arg( pPos->getDoubleTick(), 0, 'f' )
766 // .arg( nNewLookahead )
767 // .arg( pPos->getFrameOffsetTempo() )
768 // .arg( pPos->getTickOffsetQueuing(), 0, 'f' )
769 // .arg( fNewTickEnd, 0, 'f' )
770 // .arg( m_fLastTickEnd, 0, 'f' )
771 // );
772
773 }
774
775 // Happens when the Timeline was either toggled or tempo
776 // changed while the former was deactivated.
777 if ( pPos->getFrame() != nNewFrame ) {
778 pPos->setFrame( nNewFrame );
779 }
780
782}
783
784void AudioEngine::clearAudioBuffers( uint32_t nFrames )
785{
787 float *pBuffer_L, *pBuffer_R;
788
789 // clear main out Left and Right
790 if ( m_pAudioDriver != nullptr ) {
791 pBuffer_L = m_pAudioDriver->getOut_L();
792 pBuffer_R = m_pAudioDriver->getOut_R();
793 assert( pBuffer_L != nullptr && pBuffer_R != nullptr );
794 memset( pBuffer_L, 0, nFrames * sizeof( float ) );
795 memset( pBuffer_R, 0, nFrames * sizeof( float ) );
796 }
797
798#ifdef H2CORE_HAVE_JACK
799 if ( Hydrogen::get_instance()->hasJackAudioDriver() ) {
800 JackAudioDriver* pJackAudioDriver = static_cast<JackAudioDriver*>(m_pAudioDriver);
801
802 if ( pJackAudioDriver != nullptr ) {
803 pJackAudioDriver->clearPerTrackAudioBuffers( nFrames );
804 }
805 }
806#endif
807
808 m_MutexOutputPointer.unlock();
809
810#ifdef H2CORE_HAVE_LADSPA
811 if ( getState() == State::Ready ||
813 getState() == State::Testing ) {
814 Effects* pEffects = Effects::get_instance();
815 for ( unsigned i = 0; i < MAX_FX; ++i ) { // clear FX buffers
816 LadspaFX* pFX = pEffects->getLadspaFX( i );
817 if ( pFX != nullptr ) {
818 assert( pFX->m_pBuffer_L );
819 assert( pFX->m_pBuffer_R );
820 memset( pFX->m_pBuffer_L, 0, nFrames * sizeof( float ) );
821 memset( pFX->m_pBuffer_R, 0, nFrames * sizeof( float ) );
822 }
823 }
824 }
825#endif
826}
827
829{
830 INFOLOG( QString( "Creating driver [%1]" ).arg( sDriver ) );
831
832 auto pPref = Preferences::get_instance();
833 auto pHydrogen = Hydrogen::get_instance();
834 auto pSong = pHydrogen->getSong();
835 AudioOutput *pAudioDriver = nullptr;
836
837 if ( sDriver == "OSS" ) {
838 pAudioDriver = new OssDriver( m_AudioProcessCallback );
839 } else if ( sDriver == "JACK" ) {
840 pAudioDriver = new JackAudioDriver( m_AudioProcessCallback );
841#ifdef H2CORE_HAVE_JACK
842 if ( auto pJackDriver = dynamic_cast<JackAudioDriver*>( pAudioDriver ) ) {
843 pJackDriver->setConnectDefaults(
844 Preferences::get_instance()->m_bJackConnectDefaults
845 );
846 }
847#endif
848 }
849 else if ( sDriver == "ALSA" ) {
850 pAudioDriver = new AlsaAudioDriver( m_AudioProcessCallback );
851 }
852 else if ( sDriver == "PortAudio" ) {
853 pAudioDriver = new PortAudioDriver( m_AudioProcessCallback );
854 }
855 else if ( sDriver == "CoreAudio" ) {
856 pAudioDriver = new CoreAudioDriver( m_AudioProcessCallback );
857 }
858 else if ( sDriver == "PulseAudio" ) {
859 pAudioDriver = new PulseAudioDriver( m_AudioProcessCallback );
860 }
861 else if ( sDriver == "Fake" ) {
862 WARNINGLOG( "*** Using FAKE audio driver ***" );
863 pAudioDriver = new FakeDriver( m_AudioProcessCallback );
864 }
865 else if ( sDriver == "DiskWriterDriver" ) {
866 pAudioDriver = new DiskWriterDriver( m_AudioProcessCallback );
867 }
868 else if ( sDriver == "NullDriver" ) {
869 pAudioDriver = new NullDriver( m_AudioProcessCallback );
870 }
871 else {
872 ERRORLOG( QString( "Unknown driver [%1]" ).arg( sDriver ) );
874 return nullptr;
875 }
876
877 if ( pAudioDriver == nullptr ) {
878 INFOLOG( QString( "Unable to create driver [%1]" ).arg( sDriver ) );
879 return nullptr;
880 }
881
882 // Initialize the audio driver
883 int nRes = pAudioDriver->init( pPref->m_nBufferSize );
884 if ( nRes != 0 ) {
885 ERRORLOG( QString( "Error code [%2] while initializing audio driver [%1]." )
886 .arg( sDriver ).arg( nRes ) );
887 delete pAudioDriver;
888 return nullptr;
889 }
890
891 this->lock( RIGHT_HERE );
893
894 // Some audio drivers require to be already registered in the
895 // AudioEngine while being connected.
896 m_pAudioDriver = pAudioDriver;
897
898 if ( pSong != nullptr ) {
900 } else {
902 }
903
904 // Unlocking earlier might execute the jack process() callback before we
905 // are fully initialized.
906 m_MutexOutputPointer.unlock();
907 this->unlock();
908
909 nRes = m_pAudioDriver->connect();
910 if ( nRes != 0 ) {
912 ERRORLOG( QString( "Error code [%2] while connecting audio driver [%1]." )
913 .arg( sDriver ).arg( nRes ) );
914
915 this->lock( RIGHT_HERE );
917
918 delete m_pAudioDriver;
919 m_pAudioDriver = nullptr;
920
921 m_MutexOutputPointer.unlock();
922 this->unlock();
923
924 return nullptr;
925 }
926
927 if ( pSong != nullptr && pHydrogen->hasJackAudioDriver() ) {
928 pHydrogen->renameJackPorts( pSong );
929 }
930
932
933 if ( pSong != nullptr ) {
935 }
936
938
939 return pAudioDriver;
940}
941
943{
944 INFOLOG("");
946
947 if ( getState() != State::Initialized ) {
948 ERRORLOG( QString( "Audio engine is not in State::Initialized but [%1]" )
949 .arg( static_cast<int>( getState() ) ) );
950 return;
951 }
952
953 if ( m_pAudioDriver != nullptr ) { // check if audio driver is still alive
954 ERRORLOG( "The audio driver is still alive" );
955 }
956 if ( m_pMidiDriver != nullptr ) { // check if midi driver is still alive
957 ERRORLOG( "The MIDI driver is still active" );
958 }
959
960 QString sAudioDriver = pPref->m_sAudioDriver;
961
962 if ( sAudioDriver != "Auto" ) {
963 createAudioDriver( sAudioDriver );
964 }
965 else {
966 AudioOutput* pAudioDriver;
967 for ( QString sDriver : getSupportedAudioDrivers() ) {
968 if ( ( pAudioDriver = createAudioDriver( sDriver ) ) != nullptr ) {
969 break;
970 }
971 }
972 }
973
974 if ( m_pAudioDriver == nullptr ) {
975 ERRORLOG( QString( "Couldn't start audio driver [%1], falling back to NullDriver" )
976 .arg( sAudioDriver ) );
977 createAudioDriver( "NullDriver" );
978 }
979
980 this->lock( RIGHT_HERE );
982
983 if ( pPref->m_sMidiDriver == "ALSA" ) {
984#ifdef H2CORE_HAVE_ALSA
985 AlsaMidiDriver *alsaMidiDriver = new AlsaMidiDriver();
986 m_pMidiDriverOut = alsaMidiDriver;
987 m_pMidiDriver = alsaMidiDriver;
989 m_pMidiDriver->setActive( true );
990#endif
991 } else if ( pPref->m_sMidiDriver == "PortMidi" ) {
992#ifdef H2CORE_HAVE_PORTMIDI
993 PortMidiDriver* pPortMidiDriver = new PortMidiDriver();
994 m_pMidiDriver = pPortMidiDriver;
995 m_pMidiDriverOut = pPortMidiDriver;
997 m_pMidiDriver->setActive( true );
998#endif
999 } else if ( pPref->m_sMidiDriver == "CoreMIDI" ) {
1000#ifdef H2CORE_HAVE_COREMIDI
1001 CoreMidiDriver *coreMidiDriver = new CoreMidiDriver();
1002 m_pMidiDriver = coreMidiDriver;
1003 m_pMidiDriverOut = coreMidiDriver;
1005 m_pMidiDriver->setActive( true );
1006#endif
1007 } else if ( pPref->m_sMidiDriver == "JACK-MIDI" ) {
1008#ifdef H2CORE_HAVE_JACK
1009 JackMidiDriver *jackMidiDriver = new JackMidiDriver();
1010 m_pMidiDriverOut = jackMidiDriver;
1011 m_pMidiDriver = jackMidiDriver;
1013 m_pMidiDriver->setActive( true );
1014#endif
1015 }
1016
1017 m_MutexOutputPointer.unlock();
1018 this->unlock();
1019}
1020
1022{
1023 INFOLOG( "" );
1024
1025 this->lock( RIGHT_HERE );
1026
1027 if ( m_state == State::Playing ) {
1028 this->stopPlayback();
1029 }
1030
1031 if ( ( m_state != State::Prepared )
1032 && ( m_state != State::Ready ) ) {
1033 ERRORLOG( QString( "Audio engine is not in State::Prepared or State::Ready but [%1]" )
1034 .arg( static_cast<int>(m_state) ) );
1035 this->unlock();
1036 return;
1037 }
1038
1039
1041
1042 if ( m_pMidiDriver != nullptr ) {
1044 delete m_pMidiDriver;
1045 m_pMidiDriver = nullptr;
1046 m_pMidiDriverOut = nullptr;
1047 }
1048
1049 if ( m_pAudioDriver != nullptr ) {
1051 m_MutexOutputPointer.lock();
1052 delete m_pAudioDriver;
1053 m_pAudioDriver = nullptr;
1054 m_MutexOutputPointer.unlock();
1055 }
1056
1057 this->unlock();
1058}
1059
1061{
1062 bool bPlaying = m_state == State::Playing;
1063 if ( m_pAudioDriver != nullptr ) {
1065 }
1067 if ( bPlaying ) {
1068 this->startPlayback();
1069 }
1070
1071}
1072
1074 auto pSong = Hydrogen::get_instance()->getSong();
1075 if ( pSong->getLoopMode() == Song::LoopMode::Finishing ) {
1076 m_nLoopsDone = static_cast<int>(std::floor(
1077 m_pTransportPosition->getDoubleTick() / m_fSongSizeInTicks ));
1078 }
1079}
1080
1082
1083 if ( Hydrogen::get_instance()->getSong() == nullptr ) {
1084 WARNINGLOG( "no song set yet" );
1085 return;
1086 }
1087
1089}
1090
1091float AudioEngine::getBpmAtColumn( int nColumn ) {
1092
1093 auto pHydrogen = Hydrogen::get_instance();
1094 auto pAudioEngine = pHydrogen->getAudioEngine();
1095 auto pSong = pHydrogen->getSong();
1096
1097 if ( pSong == nullptr ) {
1098 WARNINGLOG( "no song set yet" );
1099 return MIN_BPM;
1100 }
1101
1102 float fBpm = pAudioEngine->getTransportPosition()->getBpm();
1103
1104 if ( pHydrogen->getJackTimebaseState() == JackAudioDriver::Timebase::Slave &&
1105 pHydrogen->getMode() == Song::Mode::Song ) {
1106 // Hydrogen is using the BPM broadcasted by the JACK
1107 // server. This one does solely depend on external
1108 // applications and will NOT be stored in the Song.
1109 const float fJackMasterBpm = pHydrogen->getMasterBpm();
1110 if ( ! std::isnan( fJackMasterBpm ) && fBpm != fJackMasterBpm ) {
1111 fBpm = fJackMasterBpm;
1112 // DEBUGLOG( QString( "Tempo update by the JACK server [%1]").arg( fJackMasterBpm ) );
1113 }
1114 }
1115 else if ( pSong->getIsTimelineActivated() &&
1116 pHydrogen->getMode() == Song::Mode::Song ) {
1117
1118 const float fTimelineBpm = pHydrogen->getTimeline()->getTempoAtColumn( nColumn );
1119 if ( fTimelineBpm != fBpm ) {
1120 // DEBUGLOG( QString( "Set tempo to timeline value [%1]").arg( fTimelineBpm ) );
1121 fBpm = fTimelineBpm;
1122 }
1123 }
1124 else {
1125 // Change in speed due to user interaction with the BPM widget
1126 // or corresponding MIDI or OSC events.
1127 if ( pAudioEngine->getNextBpm() != fBpm ) {
1128 // DEBUGLOG( QString( "BPM changed via Widget, OSC, or MIDI from [%1] to [%2]." )
1129 // .arg( fBpm ).arg( pAudioEngine->getNextBpm() ) );
1130 fBpm = pAudioEngine->getNextBpm();
1131 }
1132 }
1133 return fBpm;
1134}
1135
1137{
1138 Hydrogen* pHydrogen = Hydrogen::get_instance();
1139 std::shared_ptr<Song> pSong = pHydrogen->getSong();
1140 if ( pSong == nullptr ) {
1141 return;
1142 }
1143
1144#ifdef H2CORE_HAVE_LADSPA
1145 for ( unsigned nFX = 0; nFX < MAX_FX; ++nFX ) {
1147 if ( pFX == nullptr ) {
1148 return;
1149 }
1150
1151 pFX->deactivate();
1152
1153 pFX->connectAudioPorts( pFX->m_pBuffer_L, pFX->m_pBuffer_R,
1154 pFX->m_pBuffer_L, pFX->m_pBuffer_R );
1155 pFX->activate();
1156 }
1157#endif
1158}
1159
1160void AudioEngine::raiseError( unsigned nErrorCode )
1161{
1162 m_pEventQueue->push_event( EVENT_ERROR, nErrorCode );
1163}
1164
1166
1167 const auto pHydrogen = Hydrogen::get_instance();
1168 const auto pSong = pHydrogen->getSong();
1169
1170 if ( pHydrogen->isPatternEditorLocked() ) {
1171
1172 // Default value is used to deselect the current pattern in
1173 // case none was found.
1174 int nPatternNumber = -1;
1175
1176 const int nColumn = std::max( m_pTransportPosition->getColumn(), 0 );
1177 if ( nColumn < (*pSong->getPatternGroupVector()).size() ) {
1178
1179 const auto pPatternList = pSong->getPatternList();
1180 if ( pPatternList != nullptr ) {
1181
1182 const auto pColumn = ( *pSong->getPatternGroupVector() )[ nColumn ];
1183
1184 int nIndex;
1185 for ( const auto& pattern : *pColumn ) {
1186 nIndex = pPatternList->index( pattern );
1187
1188 if ( nIndex > nPatternNumber ) {
1189 nPatternNumber = nIndex;
1190 }
1191 }
1192 }
1193 }
1194
1195 pHydrogen->setSelectedPatternNumber( nPatternNumber, true, true );
1196 }
1197}
1198
1200 reset( true );
1201
1202 const auto pSong = Hydrogen::get_instance()->getSong();
1203 if ( pSong == nullptr ) {
1204 ERRORLOG( "no song set" );
1205 return;
1206 }
1207
1208 m_fSongSizeInTicks = pSong->lengthInTicks();
1209 setNextBpm( pSong->getBpm() );
1210}
1211
1212void AudioEngine::processPlayNotes( unsigned long nframes )
1213{
1214 Hydrogen* pHydrogen = Hydrogen::get_instance();
1215 std::shared_ptr<Song> pSong = pHydrogen->getSong();
1216
1217 long long nFrame;
1218 if ( getState() == State::Playing || getState() == State::Testing ) {
1219 // Current transport position.
1220 nFrame = m_pTransportPosition->getFrame();
1221 } else {
1222 // In case the playback is stopped we pretend it is still
1223 // rolling using the realtime ticks while disregarding tempo
1224 // changes in the Timeline. This is important as we want to
1225 // continue playing back notes in the sampler and process
1226 // realtime events, by e.g. MIDI or Hydrogen's virtual
1227 // keyboard.
1228 nFrame = getRealtimeFrame();
1229 }
1230
1231 while ( !m_songNoteQueue.empty() ) {
1232 Note *pNote = m_songNoteQueue.top();
1233 const long long nNoteStartInFrames = pNote->getNoteStart();
1234
1235 // DEBUGLOG( QString( "m_pTransportPosition->getDoubleTick(): %1, m_pTransportPosition->getFrame(): %2, nframes: %3, " )
1236 // .arg( m_pTransportPosition->getDoubleTick() )
1237 // .arg( m_pTransportPosition->getFrame() )
1238 // .arg( nframes )
1239 // .append( pNote->toQString( "", true ) ) );
1240
1241 if ( nNoteStartInFrames < nFrame + static_cast<long long>(nframes) ) {
1242
1243 float fNoteProbability = pNote->get_probability();
1244 if ( fNoteProbability != 1. ) {
1245 // Current note is skipped with a certain probability.
1246 if ( fNoteProbability < (float) rand() / (float) RAND_MAX ) {
1247 m_songNoteQueue.pop();
1248 pNote->get_instrument()->dequeue();
1249 continue;
1250 }
1251 }
1252
1253 /*
1254 * Check if the current instrument has the property "Stop-Note" set.
1255 * If yes, a NoteOff note is generated automatically after each note.
1256 */
1257 auto pNoteInstrument = pNote->get_instrument();
1258 if ( pNoteInstrument->is_stop_notes() ){
1259 Note *pOffNote = new Note( pNoteInstrument );
1260 pOffNote->set_note_off( true );
1261 m_pSampler->noteOn( pOffNote );
1262 delete pOffNote;
1263 }
1264
1265 if ( ! pNote->get_instrument()->hasSamples() ) {
1266 m_songNoteQueue.pop();
1267 pNote->get_instrument()->dequeue();
1268 continue;
1269 }
1270
1271 if ( pNoteInstrument == m_pMetronomeInstrument ) {
1273 pNote->get_pitch() == 0 ? 1 : 0 );
1274 }
1275
1276 m_pSampler->noteOn( pNote );
1277 m_songNoteQueue.pop();
1278 pNote->get_instrument()->dequeue();
1279
1280 const int nInstrument = pSong->getInstrumentList()->index( pNote->get_instrument() );
1281 if( pNote->get_note_off() ){
1282 delete pNote;
1283 }
1284
1285 // Check whether the instrument could be found.
1286 if ( nInstrument != -1 ) {
1287 m_pEventQueue->push_event( EVENT_NOTEON, nInstrument );
1288 }
1289
1290 continue;
1291 } else {
1292 // this note will not be played
1293 break;
1294 }
1295 }
1296}
1297
1299{
1300 // delete all copied notes in the note queues
1301 while ( !m_songNoteQueue.empty() ) {
1302 m_songNoteQueue.top()->get_instrument()->dequeue();
1303 delete m_songNoteQueue.top();
1304 m_songNoteQueue.pop();
1305 }
1306
1307 for ( unsigned i = 0; i < m_midiNoteQueue.size(); ++i ) {
1308 delete m_midiNoteQueue[i];
1309 }
1310 m_midiNoteQueue.clear();
1311}
1312
1313int AudioEngine::audioEngine_process( uint32_t nframes, void* /*arg*/ )
1314{
1316 // For the JACK driver it is very important (#1867) to not do anything while
1317 // the JACK client is stopped/closed. Otherwise it will segfault on mutex
1318 // locking or message logging.
1319 if ( ! ( pAudioEngine->getState() == AudioEngine::State::Ready ||
1320 pAudioEngine->getState() == AudioEngine::State::Playing ) ) {
1321 return 0;
1322 }
1323 timeval startTimeval = currentTime2();
1324
1325 pAudioEngine->clearAudioBuffers( nframes );
1326
1327 // Calculate maximum time to wait for audio engine lock. Using the
1328 // last calculated processing time as an estimate of the expected
1329 // processing time for this frame.
1330 float sampleRate = static_cast<float>(pAudioEngine->m_pAudioDriver->getSampleRate());
1331 pAudioEngine->m_fMaxProcessTime = 1000.0 / ( sampleRate / nframes );
1332 float fSlackTime = pAudioEngine->m_fMaxProcessTime - pAudioEngine->m_fProcessTime;
1333
1334 // If we expect to take longer than the available time to process,
1335 // require immediate locking or not at all: we're bound to drop a
1336 // buffer anyway.
1337 if ( fSlackTime < 0.0 ) {
1338 fSlackTime = 0.0;
1339 }
1340
1341 /*
1342 * The "try_lock" was introduced for Bug #164 (Deadlock after during
1343 * alsa driver shutdown). The try_lock *should* only fail in rare circumstances
1344 * (like shutting down drivers). In such cases, it seems to be ok to interrupt
1345 * audio processing. Returning the special return value "2" enables the disk
1346 * writer driver to repeat the processing of the current data.
1347 */
1348 if ( !pAudioEngine->tryLockFor( std::chrono::microseconds( (int)(1000.0*fSlackTime) ),
1349 RIGHT_HERE ) ) {
1350 ___ERRORLOG( QString( "Failed to lock audioEngine in allowed %1 ms, missed buffer" ).arg( fSlackTime ) );
1351
1352 if ( dynamic_cast<DiskWriterDriver*>(pAudioEngine->m_pAudioDriver) != nullptr ) {
1353 return 2; // inform the caller that we could not acquire the lock
1354 }
1355
1356 return 0;
1357 }
1358
1359 // Now that the engine is locked we properly check its state.
1360 if ( ! ( pAudioEngine->getState() == AudioEngine::State::Ready ||
1361 pAudioEngine->getState() == AudioEngine::State::Playing ) ) {
1362 pAudioEngine->unlock();
1363 return 0;
1364 }
1365
1366 Hydrogen* pHydrogen = Hydrogen::get_instance();
1367 std::shared_ptr<Song> pSong = pHydrogen->getSong();
1368 if ( pSong == nullptr ) {
1369 assert( pSong );
1370 ERRORLOG( "Invalid song" );
1371 return 1;
1372 }
1373
1374 // Sync transport with server (in case the current audio driver is
1375 // designed that way)
1376#ifdef H2CORE_HAVE_JACK
1377 if ( Hydrogen::get_instance()->hasJackTransport() ) {
1378 auto pAudioDriver = pHydrogen->getAudioOutput();
1379 if ( pAudioDriver == nullptr ) {
1380 ERRORLOG( "AudioDriver is not ready!" );
1381 assert( pAudioDriver );
1382 return 1;
1383 }
1384 static_cast<JackAudioDriver*>( pAudioDriver )->updateTransportPosition();
1385 }
1386#endif
1387
1388 // Check whether the tempo was changed.
1389 pAudioEngine->updateBpmAndTickSize( pAudioEngine->m_pTransportPosition );
1390 pAudioEngine->updateBpmAndTickSize( pAudioEngine->m_pQueuingPosition );
1391
1392 // Update the state of the audio engine depending on whether it
1393 // was started or stopped by the user.
1394 if ( pAudioEngine->getNextState() == State::Playing ) {
1395 if ( pAudioEngine->getState() == State::Ready ) {
1396 pAudioEngine->startPlayback();
1397 }
1398
1399 pAudioEngine->setRealtimeFrame( pAudioEngine->m_pTransportPosition->getFrame() );
1400 } else {
1401 if ( pAudioEngine->getState() == State::Playing ) {
1402 pAudioEngine->stopPlayback();
1403 }
1404
1405 // go ahead and increment the realtimeframes by nFrames
1406 // to support our realtime keyboard and midi event timing
1407 pAudioEngine->setRealtimeFrame( pAudioEngine->getRealtimeFrame() +
1408 static_cast<long long>(nframes) );
1409 }
1410
1411 // always update note queue.. could come from pattern or realtime input
1412 // (midi, keyboard)
1413 pAudioEngine->updateNoteQueue( nframes );
1414
1415 pAudioEngine->processAudio( nframes );
1416
1417 if ( pAudioEngine->getState() == AudioEngine::State::Playing ) {
1418
1419 // Check whether the end of the song has been reached.
1420 if ( pAudioEngine->isEndOfSongReached(
1421 pAudioEngine->m_pTransportPosition ) ) {
1422
1423 ___INFOLOG( "End of song received" );
1424
1425 if ( pHydrogen->getMidiOutput() != nullptr ) {
1426 pHydrogen->getMidiOutput()->handleQueueAllNoteOff();
1427 }
1428
1429 pAudioEngine->stop();
1430 pAudioEngine->stopPlayback();
1431 pAudioEngine->locate( 0 );
1432
1433 // Tell GUI to move the playhead position to the beginning of
1434 // the song again since it only updates it in case transport
1435 // is rolling.
1437
1438 if ( dynamic_cast<FakeDriver*>(pAudioEngine->m_pAudioDriver) !=
1439 nullptr ) {
1440 ___INFOLOG( "End of song." );
1441
1442 // TODO This part of the code might not be reached
1443 // anymore.
1444 pAudioEngine->unlock();
1445 return 1; // kill the audio AudioDriver thread
1446 }
1447 }
1448 else {
1449 // We are not at the end of the song, keep rolling.
1450 pAudioEngine->incrementTransportPosition( nframes );
1451 }
1452 }
1453
1454 timeval finishTimeval = currentTime2();
1455 pAudioEngine->m_fProcessTime =
1456 ( finishTimeval.tv_sec - startTimeval.tv_sec ) * 1000.0
1457 + ( finishTimeval.tv_usec - startTimeval.tv_usec ) / 1000.0;
1458
1459#ifdef CONFIG_DEBUG
1460 if ( pAudioEngine->m_fProcessTime > pAudioEngine->m_fMaxProcessTime ) {
1461 ___WARNINGLOG( "" );
1462 ___WARNINGLOG( "----XRUN----" );
1463 ___WARNINGLOG( QString( "XRUN of %1 msec (%2 > %3)" )
1464 .arg( ( pAudioEngine->m_fProcessTime - pAudioEngine->m_fMaxProcessTime ) )
1465 .arg( pAudioEngine->m_fProcessTime ).arg( pAudioEngine->m_fMaxProcessTime ) );
1466 ___WARNINGLOG( QString( "Ladspa process time = %1" ).arg( fLadspaTime ) );
1467 ___WARNINGLOG( "------------" );
1468 ___WARNINGLOG( "" );
1469
1471 }
1472#endif
1473
1474 pAudioEngine->unlock();
1475
1476 return 0;
1477}
1478
1479void AudioEngine::processAudio( uint32_t nFrames ) {
1480
1481 auto pSong = Hydrogen::get_instance()->getSong();
1482
1483 processPlayNotes( nFrames );
1484
1485 float *pBuffer_L = m_pAudioDriver->getOut_L(),
1486 *pBuffer_R = m_pAudioDriver->getOut_R();
1487 assert( pBuffer_L != nullptr && pBuffer_R != nullptr );
1488
1489 getSampler()->process( nFrames );
1490 float* out_L = getSampler()->m_pMainOut_L;
1491 float* out_R = getSampler()->m_pMainOut_R;
1492 for ( unsigned i = 0; i < nFrames; ++i ) {
1493 pBuffer_L[ i ] += out_L[ i ];
1494 pBuffer_R[ i ] += out_R[ i ];
1495 }
1496
1497 getSynth()->process( nFrames );
1498 out_L = getSynth()->m_pOut_L;
1499 out_R = getSynth()->m_pOut_R;
1500 for ( unsigned i = 0; i < nFrames; ++i ) {
1501 pBuffer_L[ i ] += out_L[ i ];
1502 pBuffer_R[ i ] += out_R[ i ];
1503 }
1504
1505 timeval ladspaTime_start = currentTime2();
1506
1507#ifdef H2CORE_HAVE_LADSPA
1508 for ( unsigned nFX = 0; nFX < MAX_FX; ++nFX ) {
1510 if ( ( pFX ) && ( pFX->isEnabled() ) ) {
1511 pFX->processFX( nFrames );
1512
1513 float *buf_L, *buf_R;
1514 if ( pFX->getPluginType() == LadspaFX::STEREO_FX ) {
1515 buf_L = pFX->m_pBuffer_L;
1516 buf_R = pFX->m_pBuffer_R;
1517 } else { // MONO FX
1518 buf_L = pFX->m_pBuffer_L;
1519 buf_R = buf_L;
1520 }
1521
1522 for ( unsigned i = 0; i < nFrames; ++i ) {
1523 pBuffer_L[ i ] += buf_L[ i ];
1524 pBuffer_R[ i ] += buf_R[ i ];
1525 if ( buf_L[ i ] > m_fFXPeak_L[nFX] ) {
1526 m_fFXPeak_L[nFX] = buf_L[ i ];
1527 }
1528
1529 if ( buf_R[ i ] > m_fFXPeak_R[nFX] ) {
1530 m_fFXPeak_R[nFX] = buf_R[ i ];
1531 }
1532 }
1533 }
1534 }
1535#endif
1536 timeval ladspaTime_end = currentTime2();
1538 ( ladspaTime_end.tv_sec - ladspaTime_start.tv_sec ) * 1000.0
1539 + ( ladspaTime_end.tv_usec - ladspaTime_start.tv_usec ) / 1000.0;
1540
1541 float val_L, val_R;
1542 for ( unsigned i = 0; i < nFrames; ++i ) {
1543 val_L = pBuffer_L[i];
1544 val_R = pBuffer_R[i];
1545
1546 if ( val_L > m_fMasterPeak_L ) {
1547 m_fMasterPeak_L = val_L;
1548 }
1549
1550 if ( val_R > m_fMasterPeak_R ) {
1551 m_fMasterPeak_R = val_R;
1552 }
1553 }
1554
1555 for ( auto component : *pSong->getComponents() ) {
1556 DrumkitComponent *pComponent = component.get();
1557 for ( unsigned i = 0; i < nFrames; ++i ) {
1558 float compo_val_L = pComponent->get_out_L(i);
1559 float compo_val_R = pComponent->get_out_R(i);
1560
1561 if( compo_val_L > pComponent->get_peak_l() ) {
1562 pComponent->set_peak_l( compo_val_L );
1563 }
1564 if( compo_val_R > pComponent->get_peak_r() ) {
1565 pComponent->set_peak_r( compo_val_R );
1566 }
1567 }
1568 }
1569
1570}
1571
1573 m_state = state;
1574 EventQueue::get_instance()->push_event( EVENT_STATE, static_cast<int>(state) );
1575}
1576
1577void AudioEngine::setNextBpm( float fNextBpm ) {
1578 if ( fNextBpm > MAX_BPM ) {
1580 WARNINGLOG( QString( "Provided bpm %1 is too high. Assigning upper bound %2 instead" )
1581 .arg( fNextBpm ).arg( MAX_BPM ) );
1582 }
1583 else if ( fNextBpm < MIN_BPM ) {
1585 WARNINGLOG( QString( "Provided bpm %1 is too low. Assigning lower bound %2 instead" )
1586 .arg( fNextBpm ).arg( MIN_BPM ) );
1587 }
1588
1589 m_fNextBpm = fNextBpm;
1590}
1591
1592void AudioEngine::setSong( std::shared_ptr<Song> pNewSong )
1593{
1594 auto pHydrogen = Hydrogen::get_instance();
1595
1596 INFOLOG( QString( "Set song: %1" ).arg( pNewSong->getName() ) );
1597
1598 this->lock( RIGHT_HERE );
1599
1600 if ( getState() != State::Prepared ) {
1601 ERRORLOG( QString( "Error the audio engine is not in State::Prepared but [%1]" )
1602 .arg( static_cast<int>( getState() ) ) );
1603 }
1604
1605 if ( m_pAudioDriver != nullptr ) {
1606 setupLadspaFX();
1607 }
1608
1609 // Reset (among other things) the transport position. This causes
1610 // the locate() call below to update the playing patterns.
1611 reset( false );
1612 setNextBpm( pNewSong->getBpm() );
1613 m_fSongSizeInTicks = static_cast<double>( pNewSong->lengthInTicks() );
1614
1615 pHydrogen->renameJackPorts( pNewSong );
1616
1618 // Will also adapt the audio engine to the new song's BPM.
1619 locate( 0 );
1620
1621 pHydrogen->setTimeline( pNewSong->getTimeline() );
1622 pHydrogen->getTimeline()->activate();
1623
1625
1626 this->unlock();
1627}
1628
1630{
1631 this->lock( RIGHT_HERE );
1632
1633 if ( getState() == State::Playing ) {
1634 stop();
1635 this->stopPlayback();
1636 }
1637
1638 if ( getState() != State::Ready ) {
1639 ERRORLOG( QString( "Error the audio engine is not in State::Ready but [%1]" )
1640 .arg( static_cast<int>( getState() ) ) );
1641 this->unlock();
1642 return;
1643 }
1644
1646 reset();
1647
1649 this->unlock();
1650}
1651
1653
1654 auto pHydrogen = Hydrogen::get_instance();
1655 auto pSong = pHydrogen->getSong();
1656
1657 if ( pSong == nullptr ) {
1658 ERRORLOG( "No song set yet" );
1659 return;
1660 }
1661
1662 auto updatePatternSize = []( std::shared_ptr<TransportPosition> pPos ) {
1663 if ( pPos->getPlayingPatterns()->size() > 0 ) {
1664 // No virtual pattern resolution in here
1665 pPos->setPatternSize( pPos->getPlayingPatterns()->longest_pattern_length( false ) );
1666 } else {
1667 pPos->setPatternSize( MAX_NOTES );
1668 }
1669 };
1670 updatePatternSize( m_pTransportPosition );
1671 updatePatternSize( m_pQueuingPosition );
1672
1673 if ( pHydrogen->getMode() == Song::Mode::Pattern ) {
1674 m_fSongSizeInTicks = static_cast<double>( pSong->lengthInTicks() );
1675
1677 return;
1678 }
1679
1680 // Expected behavior:
1681 // - changing any part of the song except of the pattern currently
1682 // playing shouldn't affect transport position
1683 // - the current transport position is defined as current column +
1684 // current pattern tick position
1685 // - there shouldn't be a difference in behavior whether the song
1686 // was already looped or not
1687 const double fNewSongSizeInTicks = static_cast<double>( pSong->lengthInTicks() );
1688
1689 // Indicates that the song contains no patterns (before or after
1690 // song size did change).
1691 const bool bEmptySong =
1692 m_fSongSizeInTicks == 0 || fNewSongSizeInTicks == 0;
1693
1694 double fNewStrippedTick, fRepetitions;
1695 if ( m_fSongSizeInTicks != 0 ) {
1696 // Strip away all repetitions when in loop mode but keep their
1697 // number. nPatternStartTick and nColumn are only defined
1698 // between 0 and fSongSizeInTicks.
1699 fNewStrippedTick = std::fmod( m_pTransportPosition->getDoubleTick(),
1701 fRepetitions =
1702 std::floor( m_pTransportPosition->getDoubleTick() / m_fSongSizeInTicks );
1703 }
1704 else {
1705 // No patterns in song prior to song size change.
1706 fNewStrippedTick = m_pTransportPosition->getDoubleTick();
1707 fRepetitions = 0;
1708 }
1709
1710 const int nOldColumn = m_pTransportPosition->getColumn();
1711
1712 // WARNINGLOG( QString( "[Before] fNewStrippedTick: %1, fRepetitions: %2, m_fSongSizeInTicks: %3, fNewSongSizeInTicks: %4, transport: %5, queuing: %6" )
1713 // .arg( fNewStrippedTick, 0, 'f' )
1714 // .arg( fRepetitions )
1715 // .arg( m_fSongSizeInTicks )
1716 // .arg( fNewSongSizeInTicks )
1717 // .arg( m_pTransportPosition->toQString( "", true ) )
1718 // .arg( m_pQueuingPosition->toQString( "", true ) )
1719 // );
1720
1721 m_fSongSizeInTicks = fNewSongSizeInTicks;
1722
1723 auto endOfSongReached = [&](){
1724 if ( getState() == State::Playing ) {
1725 stop();
1726 stopPlayback();
1727 }
1728 locate( 0 );
1729
1730 // WARNINGLOG( QString( "[End of song reached] fNewStrippedTick: %1, fRepetitions: %2, m_fSongSizeInTicks: %3, fNewSongSizeInTicks: %4, transport: %5, queuing: %6" )
1731 // .arg( fNewStrippedTick, 0, 'f' )
1732 // .arg( fRepetitions )
1733 // .arg( m_fSongSizeInTicks )
1734 // .arg( fNewSongSizeInTicks )
1735 // .arg( m_pTransportPosition->toQString( "", true ) )
1736 // .arg( m_pQueuingPosition->toQString( "", true ) )
1737 // );
1738
1740 };
1741
1742 if ( nOldColumn >= pSong->getPatternGroupVector()->size() &&
1743 pSong->getLoopMode() != Song::LoopMode::Enabled ) {
1744 // Old column exceeds the new song size.
1745 endOfSongReached();
1746 return;
1747 }
1748
1749
1750 const long nNewPatternStartTick = pHydrogen->getTickForColumn( nOldColumn );
1751
1752 if ( nNewPatternStartTick == -1 &&
1753 pSong->getLoopMode() != Song::LoopMode::Enabled ) {
1754 // Failsave in case old column exceeds the new song size.
1755 endOfSongReached();
1756 return;
1757 }
1758
1759 if ( nNewPatternStartTick != m_pTransportPosition->getPatternStartTick() &&
1760 ! bEmptySong ) {
1761 // A pattern prior to the current position was toggled,
1762 // enlarged, or shrunk. We need to compensate this in order to
1763 // keep the current pattern tick position constant.
1764
1765 // DEBUGLOG( QString( "[nPatternStartTick mismatch] old: %1, new: %2" )
1766 // .arg( m_pTransportPosition->getPatternStartTick() )
1767 // .arg( nNewPatternStartTick ) );
1768
1769 fNewStrippedTick +=
1770 static_cast<double>(nNewPatternStartTick -
1771 m_pTransportPosition->getPatternStartTick());
1772 }
1773
1774#ifdef H2CORE_HAVE_DEBUG
1775 const long nNewPatternTickPosition =
1776 static_cast<long>(std::floor( fNewStrippedTick )) - nNewPatternStartTick;
1777 if ( nNewPatternTickPosition != m_pTransportPosition->getPatternTickPosition() &&
1778 ! bEmptySong ) {
1779 ERRORLOG( QString( "[nPatternTickPosition mismatch] old: %1, new: %2" )
1780 .arg( m_pTransportPosition->getPatternTickPosition() )
1781 .arg( nNewPatternTickPosition ) );
1782 }
1783#endif
1784
1785 // Incorporate the looped transport again
1786 const double fNewTick = fNewStrippedTick + fRepetitions * fNewSongSizeInTicks;
1787 const long long nNewFrame = TransportPosition::computeFrameFromTick(
1788 fNewTick, &m_pTransportPosition->m_fTickMismatch );
1789
1790 double fTickOffset = fNewTick - m_pTransportPosition->getDoubleTick();
1791
1792 // The tick interval end covered in updateNoteQueue() is stored as
1793 // double and needs to be more precise (hence updated before
1794 // rounding).
1795 m_fLastTickEnd += fTickOffset;
1796
1797 // Small rounding noise introduced in the calculation does spoil
1798 // things as we floor the resulting tick offset later on. Hence,
1799 // we round it to a specific precision.
1800 fTickOffset *= 1e8;
1801 fTickOffset = std::round( fTickOffset );
1802 fTickOffset *= 1e-8;
1803 m_pTransportPosition->setTickOffsetSongSize( fTickOffset );
1804
1805 // Moves all notes currently processed by Hydrogen with respect to
1806 // the offsets calculated above.
1808
1809 m_pTransportPosition->setFrameOffsetTempo(
1810 nNewFrame - m_pTransportPosition->getFrame() +
1811 m_pTransportPosition->getFrameOffsetTempo() );
1812
1813 // INFOLOG(QString( "[update] nNewFrame: %1, m_pTransportPosition->getFrame() (old): %2, m_pTransportPosition->getFrameOffsetTempo(): %3, fNewTick: %4, m_pTransportPosition->getDoubleTick() (old): %5, m_pTransportPosition->getTickOffsetSongSize() : %6, tick offset (without rounding): %7, fNewSongSizeInTicks: %8, fRepetitions: %9, fNewStrippedTick: %10, nNewPatternStartTick: %11")
1814 // .arg( nNewFrame )
1815 // .arg( m_pTransportPosition->getFrame() )
1816 // .arg( m_pTransportPosition->getFrameOffsetTempo() )
1817 // .arg( fNewTick, 0, 'g', 30 )
1818 // .arg( m_pTransportPosition->getDoubleTick(), 0, 'g', 30 )
1819 // .arg( m_pTransportPosition->getTickOffsetSongSize(), 0, 'g', 30 )
1820 // .arg( fNewTick - m_pTransportPosition->getDoubleTick(), 0, 'g', 30 )
1821 // .arg( fNewSongSizeInTicks, 0, 'g', 30 )
1822 // .arg( fRepetitions, 0, 'f' )
1823 // .arg( fNewStrippedTick, 0, 'f' )
1824 // .arg( nNewPatternStartTick )
1825 // );
1826
1827 const auto fOldTickSize = m_pTransportPosition->getTickSize();
1828 updateTransportPosition( fNewTick, nNewFrame, m_pTransportPosition );
1829
1830 // Ensure the tick offset is calculated as well (we do not expect
1831 // the tempo to change hence the following call is most likely not
1832 // executed during updateTransportPosition()).
1833#if defined(WIN32) and !defined(WIN64)
1834 // For some reason two identical numbers (according to their
1835 // values when printing them) are not equal to each other in 32bit
1836 // Windows. Course graining the tick change in here will do no
1837 // harm except of for preventing tiny tempo changes. Integer value
1838 // changes should not be affected.
1839 if ( std::abs( m_pTransportPosition->getTickSize() - fOldTickSize ) < 1e-2 ) {
1840#else
1841 if ( fOldTickSize == m_pTransportPosition->getTickSize() ) {
1842#endif
1844 }
1845
1846 // Updating the queuing position by the same offset to keep them
1847 // approximately in sync.
1848 const double fNewTickQueuing = m_pQueuingPosition->getDoubleTick() +
1849 fTickOffset;
1850 const long long nNewFrameQueuing = TransportPosition::computeFrameFromTick(
1851 fNewTickQueuing, &m_pQueuingPosition->m_fTickMismatch );
1852 // Use offsets calculated above.
1854 updateTransportPosition( fNewTickQueuing, nNewFrameQueuing,
1856
1858
1859#ifdef H2CORE_HAVE_DEBUG
1860 if ( nOldColumn != m_pTransportPosition->getColumn() && ! bEmptySong &&
1861 nOldColumn != -1 && m_pTransportPosition->getColumn() != -1 ) {
1862 ERRORLOG( QString( "[nColumn mismatch] old: %1, new: %2" )
1863 .arg( nOldColumn )
1864 .arg( m_pTransportPosition->getColumn() ) );
1865 }
1866#endif
1867
1868 if ( m_pQueuingPosition->getColumn() == -1 &&
1869 pSong->getLoopMode() != Song::LoopMode::Enabled ) {
1870 endOfSongReached();
1871 return;
1872 }
1873
1874 // WARNINGLOG( QString( "[After] fNewTick: %1, fRepetitions: %2, m_fSongSizeInTicks: %3, fNewSongSizeInTicks: %4, transport: %5, queuing: %6" )
1875 // .arg( fNewTick, 0, 'g', 30 )
1876 // .arg( fRepetitions, 0, 'f' )
1877 // .arg( m_fSongSizeInTicks )
1878 // .arg( fNewSongSizeInTicks )
1879 // .arg( m_pTransportPosition->toQString( "", true ) )
1880 // .arg( m_pQueuingPosition->toQString( "", true ) )
1881 // );
1882
1884}
1885
1887 auto removePattern = [&]( std::shared_ptr<TransportPosition> pPos ) {
1888 auto pPlayingPatterns = pPos->getPlayingPatterns();
1889
1890 for ( int ii = 0; ii < pPlayingPatterns->size(); ++ii ) {
1891 if ( pPlayingPatterns->get( ii ) == pPattern ) {
1892 pPlayingPatterns->del( ii );
1893 break;
1894 }
1895 }
1896 };
1897
1898 removePattern( m_pTransportPosition );
1899 removePattern( m_pQueuingPosition );
1900}
1901
1906
1907void AudioEngine::updatePlayingPatternsPos( std::shared_ptr<TransportPosition> pPos ) {
1908 auto pHydrogen = Hydrogen::get_instance();
1909 auto pSong = pHydrogen->getSong();
1910 auto pPlayingPatterns = pPos->getPlayingPatterns();
1911
1912 // DEBUGLOG( QString( "pre: %1" ).arg( pPos->toQString() ) );
1913
1914 if ( pHydrogen->getMode() == Song::Mode::Song ) {
1915
1916 const auto nPrevPatternNumber = pPlayingPatterns->size();
1917
1918 pPlayingPatterns->clear();
1919
1920 if ( pSong->getPatternGroupVector()->size() == 0 ) {
1921 // No patterns in current song.
1922 if ( nPrevPatternNumber > 0 ) {
1924 }
1925 return;
1926 }
1927
1928 auto nColumn = std::max( pPos->getColumn(), 0 );
1929 if ( nColumn >= pSong->getPatternGroupVector()->size() ) {
1930 ERRORLOG( QString( "Provided column [%1] exceeds allowed range [0,%2]. Using 0 as fallback." )
1931 .arg( nColumn ).arg( pSong->getPatternGroupVector()->size() - 1 ) );
1932 nColumn = 0;
1933 }
1934
1935 for ( const auto& ppattern : *( *( pSong->getPatternGroupVector() ) )[ nColumn ] ) {
1936 if ( ppattern != nullptr ) {
1937 pPlayingPatterns->add( ppattern, true );
1938 }
1939 }
1940
1941 // GUI does not care about the internals of the audio engine
1942 // and just moves along the transport position.
1943 // We omit the event when passing from one empty column to the
1944 // next.
1945 if ( pPos == m_pTransportPosition &&
1946 ( nPrevPatternNumber != 0 || pPlayingPatterns->size() != 0 ) ) {
1948 }
1949 }
1950 else if ( pHydrogen->getPatternMode() == Song::PatternMode::Selected ) {
1951
1952 auto pSelectedPattern =
1953 pSong->getPatternList()->get( pHydrogen->getSelectedPatternNumber() );
1954
1955 if ( pSelectedPattern != nullptr &&
1956 ! ( pPlayingPatterns->size() == 1 &&
1957 pPlayingPatterns->get( 0 ) == pSelectedPattern ) ) {
1958 pPlayingPatterns->clear();
1959 pPlayingPatterns->add( pSelectedPattern, true );
1960
1961 // GUI does not care about the internals of the audio
1962 // engine and just moves along the transport position.
1963 if ( pPos == m_pTransportPosition ) {
1965 }
1966 }
1967 }
1968 else if ( pHydrogen->getPatternMode() == Song::PatternMode::Stacked ) {
1969
1970 auto pNextPatterns = pPos->getNextPatterns();
1971
1972 if ( pNextPatterns->size() > 0 ) {
1973 for ( const auto& ppattern : *pNextPatterns ) {
1974 if ( ppattern == nullptr ) {
1975 continue;
1976 }
1977
1978 if ( ( pPlayingPatterns->del( ppattern ) ) == nullptr ) {
1979 // pPattern was not present yet. It will
1980 // be added
1981 pPlayingPatterns->add( ppattern, true );
1982 } else {
1983 // pPattern was already present. It will
1984 // be deleted.
1985 ppattern->removeFlattenedVirtualPatterns( pPlayingPatterns );
1986 }
1987
1988 // GUI does not care about the internals of the audio
1989 // engine and just moves along the transport position.
1990 if ( pPos == m_pTransportPosition ) {
1992 }
1993 }
1994 pNextPatterns->clear();
1995 }
1996 }
1997
1998 if ( pPlayingPatterns->size() > 0 ) {
1999 // No virtual pattern resolution in here
2000 pPos->setPatternSize( pPlayingPatterns->longest_pattern_length( false ) );
2001 } else {
2002 pPos->setPatternSize( MAX_NOTES );
2003 }
2004
2005 // DEBUGLOG( QString( "post: %1" ).arg( pPos->toQString() ) );
2006
2007}
2008
2009void AudioEngine::toggleNextPattern( int nPatternNumber ) {
2010 auto pHydrogen = Hydrogen::get_instance();
2011 auto pSong = pHydrogen->getSong();
2012 auto pPatternList = pSong->getPatternList();
2013 auto pPattern = pPatternList->get( nPatternNumber );
2014 if ( pPattern == nullptr ) {
2015 return;
2016 }
2017
2018 if ( m_pTransportPosition->getNextPatterns()->del( pPattern ) == nullptr ) {
2019 m_pTransportPosition->getNextPatterns()->add( pPattern );
2020 }
2021 if ( m_pQueuingPosition->getNextPatterns()->del( pPattern ) == nullptr ) {
2022 m_pQueuingPosition->getNextPatterns()->add( pPattern );
2023 }
2024}
2025
2027 m_pTransportPosition->getNextPatterns()->clear();
2028 m_pQueuingPosition->getNextPatterns()->clear();
2029}
2030
2031void AudioEngine::flushAndAddNextPattern( int nPatternNumber ) {
2032 auto pHydrogen = Hydrogen::get_instance();
2033 auto pSong = pHydrogen->getSong();
2034 auto pPatternList = pSong->getPatternList();
2035
2036 bool bAlreadyPlaying = false;
2037
2038 // Note: we will not perform a bound check on the provided pattern
2039 // number. This way the user can use the SELECT_ONLY_NEXT_PATTERN
2040 // MIDI or OSC command to flush all playing patterns.
2041 auto pRequestedPattern = pPatternList->get( nPatternNumber );
2042
2043 auto flushAndAddNext = [&]( std::shared_ptr<TransportPosition> pPos ) {
2044
2045 auto pNextPatterns = pPos->getNextPatterns();
2046 auto pPlayingPatterns = pPos->getPlayingPatterns();
2047
2048 pNextPatterns->clear();
2049 for ( int ii = 0; ii < pPlayingPatterns->size(); ++ii ) {
2050
2051 auto pPlayingPattern = pPlayingPatterns->get( ii );
2052 if ( pPlayingPattern != pRequestedPattern ) {
2053 pNextPatterns->add( pPlayingPattern );
2054 }
2055 else if ( pRequestedPattern != nullptr ) {
2056 bAlreadyPlaying = true;
2057 }
2058 }
2059
2060 // Appending the requested pattern.
2061 if ( ! bAlreadyPlaying && pRequestedPattern != nullptr ) {
2062 pNextPatterns->add( pRequestedPattern );
2063 }
2064 };
2065
2066 flushAndAddNext( m_pTransportPosition );
2067 flushAndAddNext( m_pQueuingPosition );
2068}
2069
2071
2072 if ( Hydrogen::get_instance()->getPatternMode() == Song::PatternMode::Stacked ) {
2073 auto copyPlayingPatterns = [&]( std::shared_ptr<TransportPosition> pPos ) {
2074 auto pPlayingPatterns = pPos->getPlayingPatterns();
2075 auto pNextPatterns = pPos->getNextPatterns();
2076
2077 for ( const auto& ppPattern : *pPlayingPatterns ) {
2078 pNextPatterns->add( ppPattern );
2079 }
2080 };
2081 copyPlayingPatterns( m_pTransportPosition );
2082 copyPlayingPatterns( m_pQueuingPosition );
2083 }
2084
2085 m_pTransportPosition->getPlayingPatterns()->clear();
2086 m_pQueuingPosition->getPlayingPatterns()->clear();
2087
2090}
2091
2093
2094 // INFOLOG( QString( "before:\n%1\n%2" )
2095 // .arg( m_pTransportPosition->toQString() )
2096 // .arg( m_pQueuingPosition->toQString() ) );
2097
2098 const auto fOldTickSize = m_pTransportPosition->getTickSize();
2101
2102#if defined(WIN32) and !defined(WIN64)
2103 // For some reason two identical numbers (according to their
2104 // values when printing them) are not equal to each other in 32bit
2105 // Windows. Course graining the tick change in here will do no
2106 // harm except of for preventing tiny tempo changes. Integer value
2107 // changes should not be affected.
2108 if ( std::abs( m_pTransportPosition->getTickSize() - fOldTickSize ) < 1e-2 ) {
2109#else
2110 if ( fOldTickSize == m_pTransportPosition->getTickSize() ) {
2111#endif
2112 // As tempo did not change during the Timeline activation, no
2113 // update of the offsets took place. This, however, is not
2114 // good, as it makes a significant difference to be located at
2115 // tick X with e.g. 120 bpm tempo and at X with a 120 bpm
2116 // tempo marker active but several others located prior to X.
2118 }
2119
2120 // INFOLOG( QString( "after:\n%1\n%2" )
2121 // .arg( m_pTransportPosition->toQString() )
2122 // .arg( m_pQueuingPosition->toQString() ) );
2123}
2124
2126 if ( m_songNoteQueue.size() != 0 ) {
2127
2128 std::vector<Note*> notes;
2129 for ( ; ! m_songNoteQueue.empty(); m_songNoteQueue.pop() ) {
2130 notes.push_back( m_songNoteQueue.top() );
2131 }
2132
2133 if ( notes.size() > 0 ) {
2134 for ( auto nnote : notes ) {
2135 nnote->computeNoteStart();
2136 m_songNoteQueue.push( nnote );
2137 }
2138 }
2139
2140 notes.clear();
2141 while ( m_midiNoteQueue.size() > 0 ) {
2142 notes.push_back( m_midiNoteQueue[ 0 ] );
2143 m_midiNoteQueue.pop_front();
2144 }
2145
2146 if ( notes.size() > 0 ) {
2147 for ( auto nnote : notes ) {
2148 nnote->computeNoteStart();
2149 m_midiNoteQueue.push_back( nnote );
2150 }
2151 }
2152 }
2153
2155}
2156
2158 if ( m_songNoteQueue.size() != 0 ) {
2159
2160 std::vector<Note*> notes;
2161 for ( ; ! m_songNoteQueue.empty(); m_songNoteQueue.pop() ) {
2162 notes.push_back( m_songNoteQueue.top() );
2163 }
2164
2165 const long nTickOffset =
2166 static_cast<long>(std::floor(m_pTransportPosition->getTickOffsetSongSize()));
2167
2168 if ( notes.size() > 0 ) {
2169 for ( auto nnote : notes ) {
2170
2171 // DEBUGLOG( QString( "[song queue] name: %1, pos: %2 -> %3, tick offset: %4, tick offset floored: %5" )
2172 // .arg( nnote->get_instrument()->get_name() )
2173 // .arg( nnote->get_position() )
2174 // .arg( std::max( nnote->get_position() + nTickOffset,
2175 // static_cast<long>(0) ) )
2176 // .arg( m_pTransportPosition->getTickOffsetSongSize(), 0, 'f' )
2177 // .arg( nTickOffset ) );
2178
2179 nnote->set_position( std::max( nnote->get_position() + nTickOffset,
2180 static_cast<long>(0) ) );
2181 nnote->computeNoteStart();
2182 m_songNoteQueue.push( nnote );
2183 }
2184 }
2185
2186 notes.clear();
2187 while ( m_midiNoteQueue.size() > 0 ) {
2188 notes.push_back( m_midiNoteQueue[ 0 ] );
2189 m_midiNoteQueue.pop_front();
2190 }
2191
2192 if ( notes.size() > 0 ) {
2193 for ( auto nnote : notes ) {
2194
2195 // DEBUGLOG( QString( "[midi queue] name: %1, pos: %2 -> %3, tick offset: %4, tick offset floored: %5" )
2196 // .arg( nnote->get_instrument()->get_name() )
2197 // .arg( nnote->get_position() )
2198 // .arg( std::max( nnote->get_position() + nTickOffset,
2199 // static_cast<long>(0) ) )
2200 // .arg( m_pTransportPosition->getTickOffsetSongSize(), 0, 'f' )
2201 // .arg( nTickOffset ) );
2202
2203 nnote->set_position( std::max( nnote->get_position() + nTickOffset,
2204 static_cast<long>(0) ) );
2205 nnote->computeNoteStart();
2206 m_midiNoteQueue.push_back( nnote );
2207 }
2208 }
2209 }
2210
2212}
2213
2214long long AudioEngine::computeTickInterval( double* fTickStart, double* fTickEnd, unsigned nIntervalLengthInFrames ) {
2215
2216 const auto pHydrogen = Hydrogen::get_instance();
2217 const auto pTimeline = pHydrogen->getTimeline();
2218 auto pPos = m_pTransportPosition;
2219
2220 long long nFrameStart, nFrameEnd;
2221
2222 if ( getState() == State::Ready ) {
2223 // In case the playback is stopped we pretend it is still
2224 // rolling using the realtime ticks while disregarding tempo
2225 // changes in the Timeline. This is important as we want to
2226 // continue playing back notes in the sampler and process
2227 // realtime events, by e.g. MIDI or Hydrogen's virtual
2228 // keyboard.
2229 nFrameStart = getRealtimeFrame();
2230 } else {
2231 // Enters here when either transport is rolling or the unit
2232 // tests are run.
2233 nFrameStart = pPos->getFrame();
2234 }
2235
2236 long long nLeadLagFactor = getLeadLagInFrames( pPos->getDoubleTick() );
2237
2238 // Timeline disabled:
2239 // Due to rounding errors in tick<->frame conversions the leadlag
2240 // factor in frames can differ by +/-1 even if the corresponding
2241 // lead lag in ticks is exactly the same.
2242 //
2243 // Timeline enabled:
2244 // With Tempo markers being present the lookahead is not constant
2245 // anymore. As it determines the position X frames and Y ticks
2246 // into the future, imagine it being process cycle after cycle
2247 // moved across a marker. The amount of frames covered by the
2248 // first and the second tick size will always change and so does
2249 // the resulting lookahead.
2250 //
2251 // This, however, would result in holes and overlaps in tick
2252 // coverage for the queuing position and note enqueuing in
2253 // updateNoteQueue(). That's why we stick to a single lead lag
2254 // factor invalidated each time the tempo of the song does change.
2255 if ( pPos->getLastLeadLagFactor() != 0 ) {
2256 if ( pPos->getLastLeadLagFactor() != nLeadLagFactor ) {
2257 nLeadLagFactor = pPos->getLastLeadLagFactor();
2258 }
2259 } else {
2260 pPos->setLastLeadLagFactor( nLeadLagFactor );
2261 }
2262
2263 const long long nLookahead = nLeadLagFactor +
2265
2266 nFrameEnd = nFrameStart + nLookahead +
2267 static_cast<long long>(nIntervalLengthInFrames);
2268
2269 // Checking whether transport and queuing position are identical
2270 // is not enough in here. For specific audio driver parameters and
2271 // very tiny buffersizes used by drivers with dynamic buffer sizes
2272 // they both can be identical.
2273 if ( m_bLookaheadApplied ) {
2274 nFrameStart += nLookahead;
2275 }
2276
2277 *fTickStart = ( TransportPosition::computeTickFromFrame( nFrameStart ) +
2278 pPos->getTickMismatch() ) - pPos->getTickOffsetQueuing() ;
2279 *fTickEnd = TransportPosition::computeTickFromFrame( nFrameEnd ) -
2280 pPos->getTickOffsetQueuing();
2281
2282 // INFOLOG( QString( "nFrame: [%1,%2], fTick: [%3, %4], fTick (without offset): [%5,%6], m_pTransportPosition->getTickOffsetQueuing(): %7, nLookahead: %8, nIntervalLengthInFrames: %9, m_pTransportPosition: %10, m_pQueuingPosition: %11,_bLookaheadApplied: %12" )
2283 // .arg( nFrameStart )
2284 // .arg( nFrameEnd )
2285 // .arg( *fTickStart, 0, 'f' )
2286 // .arg( *fTickEnd, 0, 'f' )
2287 // .arg( TransportPosition::computeTickFromFrame( nFrameStart ), 0, 'f' )
2288 // .arg( TransportPosition::computeTickFromFrame( nFrameEnd ), 0, 'f' )
2289 // .arg( pPos->getTickOffsetQueuing(), 0, 'f' )
2290 // .arg( nLookahead )
2291 // .arg( nIntervalLengthInFrames )
2292 // .arg( pPos->toQString() )
2293 // .arg( m_pQueuingPosition->toQString() )
2294 // .arg( m_bLookaheadApplied )
2295 // );
2296
2297 return nLeadLagFactor;
2298}
2299
2300 // Ideally we just floor the provided tick. When relocating to
2301 // a specific tick, it's converted counterpart is stored as the
2302 // transport position in frames, which is then used to calculate
2303 // the tick start again. These conversions back and forth can
2304 // introduce rounding error that get larger for larger tick
2305 // numbers and could result in a computed start tick of
2306 // 86753.999999934 when transport was relocated to 86754. As we do
2307 // not want to cover notes prior to our current transport
2308 // position, we have to account for such rounding errors.
2309double AudioEngine::coarseGrainTick( double fTick ) {
2310 if ( std::ceil( fTick ) - fTick > 0 &&
2311 std::ceil( fTick ) - fTick < 1E-6 ) {
2312 return std::floor( fTick ) + 1;
2313 }
2314 else {
2315 return std::floor( fTick );
2316 }
2317 }
2318
2319void AudioEngine::updateNoteQueue( unsigned nIntervalLengthInFrames )
2320{
2321 Hydrogen* pHydrogen = Hydrogen::get_instance();
2322 std::shared_ptr<Song> pSong = pHydrogen->getSong();
2323
2324 double fTickStartComp, fTickEndComp;
2325
2326 long long nLeadLagFactor =
2327 computeTickInterval( &fTickStartComp, &fTickEndComp, nIntervalLengthInFrames );
2328
2329 // MIDI events get put into the `m_songNoteQueue` as well.
2330 while ( m_midiNoteQueue.size() > 0 ) {
2331 Note *pNote = m_midiNoteQueue[0];
2332 if ( pNote->get_position() >
2333 static_cast<int>(coarseGrainTick( fTickEndComp )) ) {
2334 break;
2335 }
2336
2337 m_midiNoteQueue.pop_front();
2338 pNote->get_instrument()->enqueue();
2339 pNote->computeNoteStart();
2340 pNote->humanize();
2341 m_songNoteQueue.push( pNote );
2342 }
2343
2344 if ( getState() != State::Playing && getState() != State::Testing ) {
2345 return;
2346 }
2347
2348 AutomationPath* pAutomationPath = pSong->getVelocityAutomationPath();
2349
2350 // computeTickInterval() is always called regardless whether
2351 // transport is rolling or not. But we only mark the lookahead
2352 // consumed if the associated tick interval was actually traversed
2353 // by the queuing position.
2354 if ( ! m_bLookaheadApplied ) {
2355 m_bLookaheadApplied = true;
2356 }
2357
2358 const long nTickStart = static_cast<long>(coarseGrainTick( fTickStartComp ));
2359 const long nTickEnd = static_cast<long>(coarseGrainTick( fTickEndComp ));
2360
2361 // Only store the last tick interval end if transport is
2362 // rolling. Else the realtime frame processing will mess things
2363 // up.
2364 m_fLastTickEnd = fTickEndComp;
2365
2366 // WARNINGLOG( QString( "tick interval (floor): [%1,%2], tick interval (computed): [%3,%4], nLeadLagFactor: %5, m_fSongSizeInTicks: %6, m_pTransportPosition: %7, m_pQueuingPosition: %8")
2367 // .arg( nTickStart ).arg( nTickEnd )
2368 // .arg( fTickStartComp, 0, 'f' ).arg( fTickEndComp, 0, 'f' )
2369 // .arg( nLeadLagFactor )
2370 // .arg( m_fSongSizeInTicks, 0, 'f' )
2371 // .arg( m_pTransportPosition->toQString() )
2372 // .arg( m_pQueuingPosition->toQString() ) );
2373
2374 // We loop over integer ticks to ensure that all notes encountered
2375 // between two iterations belong to the same pattern.
2376 for ( long nnTick = nTickStart; nnTick < nTickEnd; ++nnTick ) {
2377
2379 // Update queuing position and playing patterns.
2380 if ( pHydrogen->getMode() == Song::Mode::Song ) {
2381
2382 const long nPreviousPosition = m_pQueuingPosition->getPatternStartTick() +
2383 m_pQueuingPosition->getPatternTickPosition();
2384
2385 const long long nNewFrame = TransportPosition::computeFrameFromTick(
2386 static_cast<double>(nnTick),
2387 &m_pQueuingPosition->m_fTickMismatch );
2388 updateSongTransportPosition( static_cast<double>(nnTick),
2389 nNewFrame, m_pQueuingPosition );
2390
2392 // Queueing reached end of the song.
2393 return;
2394 }
2395 }
2396 else if ( pHydrogen->getMode() == Song::Mode::Pattern ) {
2397
2398 const long long nNewFrame = TransportPosition::computeFrameFromTick(
2399 static_cast<double>(nnTick),
2400 &m_pQueuingPosition->m_fTickMismatch );
2401 updatePatternTransportPosition( static_cast<double>(nnTick),
2402 nNewFrame, m_pQueuingPosition );
2403 }
2404
2406 // Metronome
2407 // Only trigger the metronome at a predefined rate.
2408 int nMetronomeTickPosition;
2409 if ( pSong->getPatternGroupVector()->size() == 0 ) {
2410 nMetronomeTickPosition = nnTick;
2411 } else {
2412 nMetronomeTickPosition = m_pQueuingPosition->getPatternTickPosition();
2413 }
2414
2415 if ( nMetronomeTickPosition % 48 == 0 ) {
2416 float fPitch;
2417 float fVelocity;
2418
2419 // Depending on whether the metronome beat will be issued
2420 // at the beginning or in the remainder of the pattern,
2421 // two different sounds and events will be used.
2422 if ( nMetronomeTickPosition == 0 ) {
2423 fPitch = 3;
2424 fVelocity = 1.0;
2425 } else {
2426 fPitch = 0;
2427 fVelocity = 0.8;
2428 }
2429
2430 // Only trigger the sounds if the user enabled the
2431 // metronome.
2432 if ( Preferences::get_instance()->m_bUseMetronome ) {
2433 Note *pMetronomeNote = new Note( m_pMetronomeInstrument,
2434 nnTick,
2435 fVelocity,
2436 0.f, // pan
2437 -1,
2438 fPitch );
2439 m_pMetronomeInstrument->enqueue();
2440 pMetronomeNote->computeNoteStart();
2441 m_songNoteQueue.push( pMetronomeNote );
2442 }
2443 }
2444
2445 if ( pHydrogen->getMode() == Song::Mode::Song &&
2446 pSong->getPatternGroupVector()->size() == 0 ) {
2447 // No patterns in song. We let transport roll in case
2448 // patterns will be added again and still use metronome.
2449 if ( Preferences::get_instance()->m_bUseMetronome ) {
2450 continue;
2451 } else {
2452 return;
2453 }
2454 }
2456 // Update the notes queue.
2457 //
2458 // Supporting ticks with float precision:
2459 // - make FOREACH_NOTE_CST_IT_BOUND loop over all notes
2460 // `(_it)->first >= (_bound) && (_it)->first < (_bound + 1)`
2461 // - add remainder of pNote->get_position() % 1 when setting
2462 // nnTick as new position.
2463 //
2464 const auto pPlayingPatterns = m_pQueuingPosition->getPlayingPatterns();
2465 if ( pPlayingPatterns->size() != 0 ) {
2466 for ( auto nPat = 0; nPat < pPlayingPatterns->size(); ++nPat ) {
2467 Pattern *pPattern = pPlayingPatterns->get( nPat );
2468 assert( pPattern != nullptr );
2469 Pattern::notes_t* notes = (Pattern::notes_t*)pPattern->get_notes();
2470
2471 // Loop over all notes at tick nPatternTickPosition
2472 // (associated tick is determined by Note::__position
2473 // at the time of insertion into the Pattern).
2475 m_pQueuingPosition->getPatternTickPosition(),
2476 pPattern ) {
2477 Note *pNote = it->second;
2478 if ( pNote != nullptr ) {
2479 pNote->set_just_recorded( false );
2480
2481 Note *pCopiedNote = new Note( pNote );
2482
2483 // Lead or Lag.
2484 // This property is set within the
2485 // NotePropertiesRuler and only applies to
2486 // notes picked up from patterns within
2487 // Hydrogen during transport.
2488 pCopiedNote->set_humanize_delay(
2489 pCopiedNote->get_humanize_delay() +
2490 static_cast<int>(
2491 static_cast<float>(pNote->get_lead_lag()) *
2492 static_cast<float>(nLeadLagFactor) ));
2493
2494 pCopiedNote->set_position( nnTick );
2495 pCopiedNote->humanize();
2496
2504 if ( ( ( m_pQueuingPosition->getPatternTickPosition() %
2505 ( MAX_NOTES / 16 ) ) == 0 ) &&
2506 ( ( m_pQueuingPosition->getPatternTickPosition() %
2507 ( MAX_NOTES / 8 ) ) != 0 ) ) {
2508 pCopiedNote->swing();
2509 }
2510
2511 // This must be done _after_ setting the
2512 // position, humanization, and swing.
2513 pCopiedNote->computeNoteStart();
2514
2515 if ( pHydrogen->getMode() == Song::Mode::Song ) {
2516 const float fPos = static_cast<float>( m_pQueuingPosition->getColumn() ) +
2517 pCopiedNote->get_position() % 192 / 192.f;
2518 pCopiedNote->set_velocity( pCopiedNote->get_velocity() *
2519 pAutomationPath->get_value( fPos ) );
2520 }
2521
2522 // Ensure the custom length of the note does not exceed
2523 // the length of the current pattern.
2524 if ( pCopiedNote->get_length() != -1 ) {
2525 pCopiedNote->set_length(
2526 std::min(
2527 static_cast<long>(pCopiedNote->get_length()),
2528 static_cast<long>(pPattern->get_length()) -
2529 m_pQueuingPosition->getPatternTickPosition() ) );
2530 }
2531
2532 // DEBUGLOG( QString( "m_pQueuingPosition: %1, new note: %2" )
2533 // .arg( m_pQueuingPosition->toQString() )
2534 // .arg( pCopiedNote->toQString() ) );
2535
2536 pCopiedNote->get_instrument()->enqueue();
2537 m_songNoteQueue.push( pCopiedNote );
2538 }
2539 }
2540 }
2541 }
2542 }
2543
2544 return;
2545}
2546
2548{
2549 if ( ! ( getState() == State::Playing ||
2550 getState() == State::Ready ||
2551 getState() == State::Testing ) ) {
2552 ERRORLOG( QString( "Error the audio engine is not in State::Ready, State::Playing, or State::Testing but [%1]" )
2553 .arg( static_cast<int>( getState() ) ) );
2554 delete note;
2555 return;
2556 }
2557
2558 m_midiNoteQueue.push_back( note );
2559}
2560
2562 return pNote1->getNoteStart() > pNote2->getNoteStart();
2563}
2564
2566
2567 assert( m_pAudioDriver );
2568
2569#ifdef H2CORE_HAVE_JACK
2570 if ( Hydrogen::get_instance()->hasJackTransport() ) {
2571 // Tell all other JACK clients to start as well and wait for
2572 // the JACK server to give the signal.
2573 static_cast<JackAudioDriver*>( m_pAudioDriver )->startTransport();
2574 return;
2575 }
2576#endif
2577
2579
2580 if ( dynamic_cast<FakeDriver*>(m_pAudioDriver) != nullptr ) {
2581 static_cast<FakeDriver*>( m_pAudioDriver )->processCallback();
2582 }
2583}
2584
2586 assert( m_pAudioDriver );
2587
2588#ifdef H2CORE_HAVE_JACK
2589 if ( Hydrogen::get_instance()->hasJackTransport() ) {
2590 // Tell all other JACK clients to stop as well and wait for
2591 // the JACK server to give the signal.
2592 static_cast<JackAudioDriver*>( m_pAudioDriver )->stopTransport();
2593 return;
2594 }
2595#endif
2596
2598}
2599
2601 return 5;
2602}
2603
2604long long AudioEngine::getLeadLagInFrames( double fTick ) {
2605 double fTmp;
2606 const long long nFrameStart =
2608 const long long nFrameEnd =
2611 &fTmp );
2612
2613 // WARNINGLOG( QString( "nFrameStart: %1, nFrameEnd: %2, diff: %3, fTick: %4" )
2614 // .arg( nFrameStart ).arg( nFrameEnd )
2615 // .arg( nFrameEnd - nFrameStart ).arg( fTick, 0, 'f' ) );
2616
2617 return nFrameEnd - nFrameStart;
2618}
2619
2621 if ( m_pTransportPosition != nullptr ) {
2622 return m_pTransportPosition->getPlayingPatterns();
2623 }
2624 return nullptr;
2625}
2626
2628 if ( m_pTransportPosition != nullptr ) {
2629 return m_pTransportPosition->getNextPatterns();
2630 }
2631 return nullptr;
2632}
2633
2635#ifdef H2CORE_HAVE_JACK
2636 #ifdef H2CORE_HAVE_DYNAMIC_JACK_CHECK
2637 // As this function is only executed during startup, we can
2638 // override the dynamic JACK support check by either setting the
2639 // audio driver to "Jack" in the hydrogen.conf file manually (or
2640 // importing the file) or by passing the `-d jack` CLI option.
2641 if ( Preferences::get_instance()->m_sAudioDriver != "JACK" ) {
2643 WARNINGLOG( "JACK support disabled." );
2644 m_bJackSupported = false;
2645 return;
2646 }
2647
2648 INFOLOG( "JACK support enabled." );
2649 }
2650 else {
2651 INFOLOG( "Dynamic JACK support skipped. JACK support enabled." );
2652 }
2653 #endif
2654
2655 m_bJackSupported = true;
2656 return;
2657
2658#else
2659 INFOLOG( "Hydrogen was compiled without JACK support." );
2660 m_bJackSupported = false;
2661 return;
2662#endif
2663}
2664
2665QString AudioEngine::toQString( const QString& sPrefix, bool bShort ) const {
2666 QString s = Base::sPrintIndention;
2667
2668 QString sOutput;
2669 if ( ! bShort ) {
2670 sOutput = QString( "%1[AudioEngine]\n" ).arg( sPrefix )
2671 .append( "%1%2m_pTransportPosition:\n").arg( sPrefix ).arg( s );
2672 if ( m_pTransportPosition != nullptr ) {
2673 sOutput.append( QString( "%1" )
2674 .arg( m_pTransportPosition->toQString( sPrefix + s, bShort ) ) );
2675 } else {
2676 sOutput.append( QString( "nullptr\n" ) );
2677 }
2678 sOutput.append( QString( "%1%2m_pQueuingPosition:\n").arg( sPrefix ).arg( s ) );
2679 if ( m_pQueuingPosition != nullptr ) {
2680 sOutput.append( QString( "%1" )
2681 .arg( m_pQueuingPosition->toQString( sPrefix + s, bShort ) ) );
2682 } else {
2683 sOutput.append( QString( "nullptr\n" ) );
2684 }
2685 sOutput.append( QString( "%1%2m_fNextBpm: %3\n" ).arg( sPrefix ).arg( s ).arg( m_fNextBpm, 0, 'f' ) )
2686 .append( QString( "%1%2m_state: %3\n" ).arg( sPrefix ).arg( s ).arg( static_cast<int>(m_state) ) )
2687 .append( QString( "%1%2m_nextState: %3\n" ).arg( sPrefix ).arg( s ).arg( static_cast<int>(m_nextState) ) )
2688 .append( QString( "%1%2m_fSongSizeInTicks: %3\n" ).arg( sPrefix ).arg( s ).arg( m_fSongSizeInTicks, 0, 'f' ) )
2689 .append( QString( "%1%2m_fLastTickEnd: %3\n" ).arg( sPrefix ).arg( s ).arg( m_fLastTickEnd, 0, 'f' ) )
2690 .append( QString( "%1%2m_bLookaheadApplied: %3\n" ).arg( sPrefix ).arg( s ).arg( m_bLookaheadApplied ) )
2691 .append( QString( "%1%2m_pSampler: stringification not implemented\n" ).arg( sPrefix ).arg( s ) )
2692 .append( QString( "%1%2m_pSynth: stringification not implemented\n" ).arg( sPrefix ).arg( s ) )
2693 .append( QString( "%1%2m_pAudioDriver: stringification not implemented\n" ).arg( sPrefix ).arg( s ) )
2694 .append( QString( "%1%2m_pMidiDriver: stringification not implemented\n" ).arg( sPrefix ).arg( s ) )
2695 .append( QString( "%1%2m_pMidiDriverOut: stringification not implemented\n" ).arg( sPrefix ).arg( s ) )
2696 .append( QString( "%1%2m_pEventQueue: stringification not implemented\n" ).arg( sPrefix ).arg( s ) );
2697#ifdef H2CORE_HAVE_LADSPA
2698 sOutput.append( QString( "%1%2m_fFXPeak_L: [" ).arg( sPrefix ).arg( s ) );
2699 for ( auto ii : m_fFXPeak_L ) {
2700 sOutput.append( QString( " %1" ).arg( ii ) );
2701 }
2702 sOutput.append( QString( "]\n%1%2m_fFXPeak_R: [" ).arg( sPrefix ).arg( s ) );
2703 for ( auto ii : m_fFXPeak_R ) {
2704 sOutput.append( QString( " %1" ).arg( ii ) );
2705 }
2706 sOutput.append( QString( " ]\n" ) );
2707#endif
2708 sOutput.append( QString( "%1%2m_fMasterPeak_L: %3\n" ).arg( sPrefix ).arg( s ).arg( m_fMasterPeak_L ) )
2709 .append( QString( "%1%2m_fMasterPeak_R: %3\n" ).arg( sPrefix ).arg( s ).arg( m_fMasterPeak_R ) )
2710 .append( QString( "%1%2m_fProcessTime: %3\n" ).arg( sPrefix ).arg( s ).arg( m_fProcessTime ) )
2711 .append( QString( "%1%2m_fMaxProcessTime: %3\n" ).arg( sPrefix ).arg( s ).arg( m_fMaxProcessTime ) )
2712 .append( QString( "%1%2m_fLadspaTime: %3\n" ).arg( sPrefix ).arg( s ).arg( m_fLadspaTime ) )
2713 .append( QString( "%1%2m_nRealtimeFrame: %3\n" ).arg( sPrefix ).arg( s ).arg( m_nRealtimeFrame ) )
2714 .append( QString( "%1%2m_AudioProcessCallback: stringification not implemented\n" ).arg( sPrefix ).arg( s ) )
2715 .append( QString( "%1%2m_songNoteQueue: length = %3\n" ).arg( sPrefix ).arg( s ).arg( m_songNoteQueue.size() ) );
2716 sOutput.append( QString( "%1%2m_midiNoteQueue: [\n" ).arg( sPrefix ).arg( s ) );
2717 for ( const auto& nn : m_midiNoteQueue ) {
2718 sOutput.append( nn->toQString( sPrefix + s, bShort ) );
2719 }
2720 sOutput.append( QString( "]\n%1%2m_pMetronomeInstrument: %3\n" ).arg( sPrefix ).arg( s ).arg( m_pMetronomeInstrument->toQString( sPrefix + s, bShort ) ) )
2721 .append( QString( "%1%2nMaxTimeHumanize: %3\n" ).arg( sPrefix ).arg( s ).arg( AudioEngine::nMaxTimeHumanize ) )
2722 .append( QString( "%1%2fHumanizeVelocitySD: %3\n" ).arg( sPrefix ).arg( s ).arg( AudioEngine::fHumanizeVelocitySD ) )
2723 .append( QString( "%1%2fHumanizePitchSD: %3\n" ).arg( sPrefix ).arg( s ).arg( AudioEngine::fHumanizePitchSD ) )
2724 .append( QString( "%1%2fHumanizeTimingSD: %3\n" ).arg( sPrefix ).arg( s ).arg( AudioEngine::fHumanizeTimingSD ) );
2725
2726 }
2727 else {
2728 sOutput = QString( "%1[AudioEngine]" ).arg( sPrefix )
2729 .append( ", m_pTransportPosition:\n");
2730 if ( m_pTransportPosition != nullptr ) {
2731 sOutput.append( QString( "%1" )
2732 .arg( m_pTransportPosition->toQString( sPrefix, bShort ) ) );
2733 } else {
2734 sOutput.append( QString( "nullptr\n" ) );
2735 }
2736 sOutput.append( ", m_pQueuingPosition:\n");
2737 if ( m_pQueuingPosition != nullptr ) {
2738 sOutput.append( QString( "%1" )
2739 .arg( m_pQueuingPosition->toQString( sPrefix, bShort ) ) );
2740 } else {
2741 sOutput.append( QString( "nullptr\n" ) );
2742 }
2743 sOutput.append( QString( ", m_fNextBpm: %1" ).arg( m_fNextBpm, 0, 'f' ) )
2744 .append( QString( ", m_state: %1" ).arg( static_cast<int>(m_state) ) )
2745 .append( QString( ", m_nextState: %1" ).arg( static_cast<int>(m_nextState) ) )
2746 .append( QString( ", m_fSongSizeInTicks: %1" ).arg( m_fSongSizeInTicks, 0, 'f' ) )
2747 .append( QString( ", m_fLastTickEnd: %1" ).arg( m_fLastTickEnd, 0, 'f' ) )
2748 .append( QString( ", m_bLookaheadApplied: %1" ).arg( m_bLookaheadApplied ) )
2749 .append( QString( ", m_pSampler: ..." ) )
2750 .append( QString( ", m_pSynth: ..." ) )
2751 .append( QString( ", m_pAudioDriver: ..." ) )
2752 .append( QString( ", m_pMidiDriver: ..." ) )
2753 .append( QString( ", m_pMidiDriverOut: ..." ) )
2754 .append( QString( ", m_pEventQueue: ..." ) );
2755#ifdef H2CORE_HAVE_LADSPA
2756 sOutput.append( QString( ", m_fFXPeak_L: [" ) );
2757 for ( auto ii : m_fFXPeak_L ) {
2758 sOutput.append( QString( " %1" ).arg( ii ) );
2759 }
2760 sOutput.append( QString( "], m_fFXPeak_R: [" ) );
2761 for ( auto ii : m_fFXPeak_R ) {
2762 sOutput.append( QString( " %1" ).arg( ii ) );
2763 }
2764 sOutput.append( QString( " ]" ) );
2765#endif
2766 sOutput.append( QString( ", m_fMasterPeak_L: %1" ).arg( m_fMasterPeak_L ) )
2767 .append( QString( ", m_fMasterPeak_R: %1" ).arg( m_fMasterPeak_R ) )
2768 .append( QString( ", m_fProcessTime: %1" ).arg( m_fProcessTime ) )
2769 .append( QString( ", m_fMaxProcessTime: %1" ).arg( m_fMaxProcessTime ) )
2770 .append( QString( ", m_fLadspaTime: %1" ).arg( m_fLadspaTime ) )
2771 .append( QString( ", m_nRealtimeFrame: %1" ).arg( m_nRealtimeFrame ) )
2772 .append( QString( ", m_AudioProcessCallback: ..." ) )
2773 .append( QString( ", m_songNoteQueue: length = %1" ).arg( m_songNoteQueue.size() ) );
2774 sOutput.append( QString( ", m_midiNoteQueue: [" ) );
2775 for ( const auto& nn : m_midiNoteQueue ) {
2776 sOutput.append( nn->toQString( sPrefix + s, bShort ) );
2777 }
2778 sOutput.append( QString( "], m_pMetronomeInstrument: id = %1" ).arg( m_pMetronomeInstrument->get_id() ) )
2779 .append( QString( ", nMaxTimeHumanize: id %1" ).arg( AudioEngine::nMaxTimeHumanize ) )
2780 .append( QString( ", fHumanizeVelocitySD: id %1" ).arg( AudioEngine::fHumanizeVelocitySD ) )
2781 .append( QString( ", fHumanizePitchSD: id %1" ).arg( AudioEngine::fHumanizePitchSD ) )
2782 .append( QString( ", fHumanizeTimingSD: id %1" ).arg( AudioEngine::fHumanizeTimingSD ) );
2783 }
2784
2785 return sOutput;
2786}
2787
2789{
2790#ifndef NDEBUG
2791 if ( m_bNeedsLock ) {
2793 }
2794#endif
2795}
2796
2797}; // namespace H2Core
#define RIGHT_HERE
Macro intended to be used for the logging of the locking of the H2Core::AudioEngine.
Definition AudioEngine.h:59
#define METRONOME_INSTR_ID
Created Instrument will be used as metronome.
Definition Instrument.h:36
#define ___WARNINGLOG(x)
Definition Object.h:256
#define INFOLOG(x)
Definition Object.h:237
#define WARNINGLOG(x)
Definition Object.h:238
#define ERRORLOG(x)
Definition Object.h:239
#define ___INFOLOG(x)
Definition Object.h:255
#define ___ERRORLOG(x)
Definition Object.h:257
#define FOREACH_NOTE_CST_IT_BOUND_LENGTH(_notes, _it, _bound, _pattern)
Iterate over all notes in column _bound in an immutable way if it is contained in _pattern.
Definition Pattern.h:290
int gettimeofday(struct timeval *tv, struct timezone *tz)
Alsa Midi Driver Based on Matthias Nagorni alsa sequencer example.
void assertAudioEngineLocked() const
Assert that the AudioEngine lock is held if needed.
The audio engine deals with two distinct #TransportPosition.
Definition AudioEngine.h:97
int m_nLoopsDone
Indicates how many loops the transport already did when the user presses the Loop button again.
void updateBpmAndTickSize(std::shared_ptr< TransportPosition > pTransportPosition)
void handleTimelineChange()
Updates the transport states and all notes in m_songNoteQueue and m_midiNoteQueue after adding or del...
void flushAndAddNextPattern(int nPatternNumber)
Add pattern nPatternNumber to #m_pNextPatterns as well as the whole content of #m_pPlayingPatterns.
std::shared_ptr< TransportPosition > m_pTransportPosition
MidiOutput * m_pMidiDriverOut
friend void Hydrogen::removeSong()
Is allowed to call removeSong().
void setRealtimeFrame(long long nFrame)
bool tryLock(const char *file, unsigned int line, const char *function)
Mutex locking of the AudioEngine.
friend void Hydrogen::setSong(std::shared_ptr< Song > pSong, bool bRelinking)
Is allowed to call setSong().
AudioOutput * m_pAudioDriver
void handleSelectedPattern()
Keeps the selected pattern in line with the one the transport position resides in while in Song::Mode...
const PatternList * getNextPatterns() const
std::deque< Note * > m_midiNoteQueue
Midi Note FIFO.
static float getBpmAtColumn(int nColumn)
void clearAudioBuffers(uint32_t nFrames)
Clear all audio buffers.
void setNextState(State state)
static constexpr float fHumanizePitchSD
Maximum value the standard deviation of the Gaussian distribution the random pitch contribution will ...
audioProcessCallback m_AudioProcessCallback
void handleLoopModeChanged()
In order to properly support H2Core::Song::LoopMode::Finishing - transport was already looped a coupl...
void toggleNextPattern(int nPatternNumber)
Add pattern nPatternNumber to #m_pNextPatterns or deletes it in case it is already present.
void setState(State state)
std::timed_mutex m_EngineMutex
Mutex for synchronizing the access to the Song object and the AudioEngine.
void removePlayingPattern(Pattern *pPattern)
long long m_nRealtimeFrame
Variable keeping track of the transport position in realtime.
@ Prepared
Drivers are set up, but not ready to process audio.
@ Initialized
Not ready, but most pointers are now valid or NULL.
@ Playing
Transport is rolling.
@ Ready
Ready to process audio.
@ Uninitialized
Not even the constructors have been called.
@ Testing
State used during the unit tests of the AudioEngine.
float getElapsedTime() const
void setNextBpm(float fNextBpm)
Stores the new speed into a separate variable which will be adopted during the next processing cycle.
State m_nextState
State assigned during the next call to processTransport().
void locateToFrame(const long long nFrame)
Version of the locate() function intended to be directly used by frame-based audio drivers / servers.
void updatePlayingPatternsPos(std::shared_ptr< TransportPosition > pPos)
AudioOutput * createAudioDriver(const QString &sDriver)
Create an audio driver using audioEngine_process() as its argument based on the provided choice and c...
void checkJackSupport()
Attempts to dynamically load the JACK 2 shared library and stores the result in m_bJackSupported.
friend void JackAudioDriver::updateTransportPosition()
long long getLeadLagInFrames(double fTick)
Calculates lead lag factor (in frames) relative to the transport position fTick.
void play()
Marks the audio engine to be started during the next call of the audioEngine_process() callback funct...
static constexpr float fHumanizeTimingSD
Maximum value the standard deviation of the Gaussian distribution the random pitch contribution will ...
void updatePlayingPatterns()
Update the list of currently played patterns associated with m_pTransportPosition and m_pQueuingPosit...
static constexpr int nMaxTimeHumanize
Maximum time (in frames) a note's position can be off due to the humanization (lead-lag).
std::priority_queue< Note *, std::deque< Note * >, compare_pNotes > m_songNoteQueue
void handleTempoChange()
Updates all notes in m_songNoteQueue and m_midiNoteQueue to be still valid after a tempo change.
float m_fFXPeak_L[MAX_FX]
bool tryLockFor(std::chrono::microseconds duration, const char *file, unsigned int line, const char *function)
Mutex locking of the AudioEngine.
const QStringList getSupportedAudioDrivers() const
State getNextState() const
bool m_bJackSupported
Whether or not the shared library of the JACK server could be found on the system at runtime.
void handleSongModeChanged()
Called whenever Hydrogen switches from Song::Mode::Song into Song::Mode::Pattern or the other way aro...
EventQueue * m_pEventQueue
QMutex m_MutexOutputPointer
Mutex for locking the pointer to the audio output buffer, allowing multiple readers.
double m_fSongSizeInTicks
Set to the total number of ticks in a Song.
bool isEndOfSongReached(std::shared_ptr< TransportPosition > pPos) const
void stop()
Marks the audio engine to be stopped during the next call of the audioEngine_process() callback funct...
void calculateTransportOffsetOnBpmChange(std::shared_ptr< TransportPosition > pTransportPosition)
void unlock()
Mutex unlocking of the AudioEngine.
void lock(const char *file, unsigned int line, const char *function)
Mutex locking of the AudioEngine.
State getState() const
const PatternList * getPlayingPatterns() const
State m_state
Current state of the H2Core::AudioEngine.
double coarseGrainTick(double fTick)
Ideally we just floor the provided tick.
static double getLeadLagInTicks()
Maximum lead lag factor in ticks.
std::shared_ptr< TransportPosition > m_pQueuingPosition
static constexpr float fHumanizeVelocitySD
Maximum value the standard deviation of the Gaussian distribution the random velocity contribution wi...
static float computeTickSize(const int nSampleRate, const float fBpm, const int nResolution)
Calculates the number of frames that make up a tick.
void updatePatternTransportPosition(double fTick, long long nFrame, std::shared_ptr< TransportPosition > pPos)
static int audioEngine_process(uint32_t nframes, void *arg)
Main audio processing function called by the audio drivers whenever there is work to do.
struct H2Core::AudioEngine::_locker_struct m_pLocker
void locate(const double fTick, bool bWithJackBroadcast=true)
void processPlayNotes(unsigned long nframes)
void updateSongSize()
Function to be called every time the length of the current song does change, e.g.
void assertLocked()
Assert that the calling thread is the current holder of the AudioEngine lock.
Sampler * getSampler() const
void stopAudioDrivers()
Stops all audio and MIDI drivers.
QString toQString(const QString &sPrefix="", bool bShort=true) const override
Formatted string version for debugging purposes.
void noteOn(Note *note)
std::shared_ptr< Instrument > m_pMetronomeInstrument
Pointer to the metronome.
void handleSongSizeChange()
Updates all notes in m_songNoteQueue to be still valid after a change in song size.
float m_fFXPeak_R[MAX_FX]
void handleDriverChange()
The audio driver was changed what possible changed the tick size - which depends on both the sample r...
void reset(bool bWithJackBroadcast=true)
MidiInput * m_pMidiDriver
void updateSongTransportPosition(double fTick, long long nFrame, std::shared_ptr< TransportPosition > pPos)
long long computeTickInterval(double *fTickStart, double *fTickEnd, unsigned nIntervalLengthInFrames)
void startAudioDrivers()
Creation and initialization of all audio and MIDI drivers called in Hydrogen::Hydrogen().
void processAudio(uint32_t nFrames)
Synth * getSynth() const
void incrementTransportPosition(uint32_t nFrames)
void updateNoteQueue(unsigned nIntervalLengthInFrames)
Takes all notes from the currently playing patterns, from the MIDI queue m_midiNoteQueue,...
static double computeDoubleTickSize(const int nSampleRate, const float fBpm, const int nResolution)
void raiseError(unsigned nErrorCode)
long long getRealtimeFrame() const
std::thread::id m_LockingThread
Thread ID of the current holder of the AudioEngine lock.
Base abstract class for audio output classes.
Definition AudioOutput.h:39
virtual unsigned getSampleRate()=0
virtual int init(unsigned nBufferSize)=0
virtual void disconnect()=0
virtual float * getOut_L()=0
virtual float * getOut_R()=0
virtual int connect()=0
float get_value(float x) const noexcept
Get value at given location.
static QString sPrintIndention
String used to format the debugging string output of some core classes.
Definition Object.h:127
static Logger * __logger
Definition Object.h:154
static const char * _class_name()
return the class name
Definition Object.h:78
Driver for export audio to disk.
float get_out_L(int nBufferPos)
float get_out_R(int nBufferPos)
static Effects * get_instance()
Returns a pointer to the current Effects singleton stored in __instance.
Definition Effects.h:54
static void create_instance()
If __instance equals 0, a new Effects singleton will be created and stored in it.
Definition Effects.cpp:63
LadspaFX * getLadspaFX(int nFX) const
Definition Effects.cpp:91
static EventQueue * get_instance()
Returns a pointer to the current EventQueue singleton stored in __instance.
Definition EventQueue.h:224
void push_event(const EventType type, const int nValue)
Queues the next event into the EventQueue.
Fake audio driver.
Definition FakeDriver.h:37
static QString click_file_path()
Returns a string containing the path to the click.wav file used in the metronome.
Hydrogen Audio Engine.
Definition Hydrogen.h:54
std::shared_ptr< Song > getSong() const
Get the current song.
Definition Hydrogen.h:122
Song::Mode getMode() const
static Hydrogen * get_instance()
Returns the current Hydrogen instance __instance.
Definition Hydrogen.h:83
MidiOutput * getMidiOutput() const
Definition Hydrogen.cpp:730
AudioEngine * getAudioEngine() const
Definition Hydrogen.h:649
@ UNKNOWN_DRIVER
The provided input string in createDriver() does not match any of the choices for Preferences::m_sAud...
Definition Hydrogen.h:245
@ ERROR_STARTING_DRIVER
Unable to connect the audio driver stored in H2Core::AudioEngine::m_pAudioDriver in audioEngine_start...
Definition Hydrogen.h:252
AudioOutput * getAudioOutput() const
Used to display audio driver info.
Definition Hydrogen.cpp:719
JACK (Jack Audio Connection Kit) server driver.
void clearPerTrackAudioBuffers(uint32_t nFrames)
Resets the buffers contained in m_pTrackOutputPortsL and m_pTrackOutputPortsR.
@ Slave
An external program is timebase master and Hydrogen will disregard all tempo markers on the Timeline ...
static bool checkSupport()
Attempts to call several JACK executables in order to check for existing JACK support.
float * m_pBuffer_L
Definition LadspaFX.h:128
int getPluginType() const
Definition LadspaFX.h:162
float * m_pBuffer_R
Definition LadspaFX.h:129
bool isEnabled() const
Definition LadspaFX.h:155
void connectAudioPorts(float *pIn_L, float *pIn_R, float *pOut_L, float *pOut_R)
Definition LadspaFx.cpp:399
void processFX(unsigned nFrames)
Definition LadspaFx.cpp:439
void log(unsigned level, const QString &class_name, const char *func_name, const QString &msg)
the log function
Definition Logger.cpp:144
bool should_log(unsigned lvl) const
return true if the level is set in the bitmask
Definition Logger.h:83
virtual void open()=0
void setActive(bool isActive)
Definition MidiInput.h:49
virtual void close()=0
virtual void handleQueueAllNoteOff()=0
A note plays an associated instrument with a velocity left and right pan.
Definition Note.h:102
int get_position() const
__position accessor
Definition Note.h:535
void set_length(int value)
__length setter
Definition Note.h:555
void swing()
Add swing contribution to __humanize_delay.
Definition Note.cpp:462
void set_humanize_delay(int value)
__humanize_delay setter
Definition Note.cpp:157
int get_humanize_delay() const
__humanize_delay accessor
Definition Note.h:630
void set_position(int value)
__position setter
Definition Note.h:530
std::shared_ptr< Instrument > get_instrument()
__instrument accessor
Definition Note.h:500
float get_lead_lag() const
__lead_lag accessor
Definition Note.h:550
float get_pitch() const
__pitch accessor
Definition Note.h:570
void humanize()
Add random contributions to __pitch, __humanize_delay, and __velocity.
Definition Note.cpp:433
void set_just_recorded(bool value)
__just_recorded setter
Definition Note.h:600
int get_length() const
__length accessor
Definition Note.h:560
float get_velocity() const
__velocity accessor
Definition Note.h:540
bool get_note_off() const
__note_off accessor
Definition Note.h:580
void computeNoteStart()
Calculates the m_nNoteStart in frames corresponding to the __position in ticks and storing the used t...
Definition Note.cpp:234
void set_velocity(float value)
__velocity setter
Definition Note.cpp:143
long long getNoteStart() const
Definition Note.h:746
void set_note_off(bool value)
__note_off setter
Definition Note.h:575
float get_probability() const
Definition Note.h:610
OSS Audio Driver.
Definition OssDriver.h:65
PatternList is a collection of patterns.
Definition PatternList.h:43
Pattern class is a Note container.
Definition Pattern.h:46
int get_length() const
set the denominator of the pattern
Definition Pattern.h:340
const notes_t * get_notes() const
get the virtual pattern set
Definition Pattern.h:355
std::multimap< int, Note * > notes_t
< multimap note type
Definition Pattern.h:50
Manager for User Preferences File (singleton)
Definition Preferences.h:78
static Preferences * get_instance()
Returns a pointer to the current Preferences singleton stored in __instance.
QString m_sAudioDriver
Audio driver.
QString m_sMidiDriver
MIDI driver.
static std::shared_ptr< Sample > load(const QString &filepath, const License &license=License())
Definition Sample.cpp:136
Waveform based sampler.
Definition Sampler.h:51
void process(uint32_t nFrames)
Definition Sampler.cpp:111
void handleTimelineOrTempoChange()
Recalculates all note starts to make them valid again after a TempoMarker was added to or deleted fro...
Definition Sampler.cpp:426
float * m_pMainOut_L
sampler main out (left channel)
Definition Sampler.h:163
float * m_pMainOut_R
sampler main out (right channel)
Definition Sampler.h:164
void noteOn(Note *pNote)
Start playing a note.
Definition Sampler.cpp:190
void stopPlayingNotes(std::shared_ptr< Instrument > pInstr=nullptr)
Definition Sampler.cpp:1341
void handleSongSizeChange()
Recalculates all note starts and positions to make them valid again after the song size changed,...
Definition Sampler.cpp:487
@ Finishing
Transport is still in loop mode (frames and ticks larger than song size are allowed) but playback end...
@ Stacked
An arbitrary number of pattern can be played.
@ Selected
Only one pattern - the one currently selected in the GUI - will be played back.
A simple synthetizer...
Definition Synth.h:43
void process(uint32_t nFrames)
Definition Synth.cpp:94
float * m_pOut_R
Definition Synth.h:47
float * m_pOut_L
Definition Synth.h:46
static long long computeFrameFromTick(double fTick, double *fTickMismatch, int nSampleRate=0)
Calculates frame equivalent of fTick.
static double computeTickFromFrame(long long nFrame, int nSampleRate=0)
Calculates tick equivalent of nFrame.
#define MAX_NOTES
Maximum number of notes.
Definition config.dox:79
#define MAX_FX
Maximum number of effects.
Definition config.dox:83
#define MAX_BPM
Definition Globals.h:36
#define MIN_BPM
Definition Globals.h:35
@ EVENT_RELOCATION
Triggered in case there is a relocation of the transport position while trasnsport is not rolling.
Definition EventQueue.h:166
@ EVENT_XRUN
Definition EventQueue.h:85
@ EVENT_METRONOME
Triggered when a metronome note is passed to the H2Core::Sampler.
Definition EventQueue.h:97
@ EVENT_STATE
Definition EventQueue.h:43
@ EVENT_PLAYING_PATTERNS_CHANGED
The list of currently played patterns (AudioEngine::getPlayingPatterns()) did change.
Definition EventQueue.h:57
@ EVENT_TEMPO_CHANGED
Definition EventQueue.h:103
@ EVENT_SONG_SIZE_CHANGED
Definition EventQueue.h:173
@ EVENT_BBT_CHANGED
The coarse grained transport position in beats and bars did change.
Definition EventQueue.h:172
@ EVENT_NOTEON
Definition EventQueue.h:86
@ EVENT_ERROR
Definition EventQueue.h:87
@ EVENT_DRIVER_CHANGED
Definition EventQueue.h:174
timeval currentTime2()
Gets the current time.
bool operator()(Note *pNote1, Note *pNote2)