hydrogen 1.2.6
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-2025 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 <sstream>
34
35#include <core/EventQueue.h>
36#include <core/FX/Effects.h>
37#include <core/Basics/Song.h>
38#include <core/Basics/Pattern.h>
40#include <core/Basics/Note.h>
48#include <core/Helpers/Random.h>
49
50#include <core/IO/AudioOutput.h>
52#include <core/IO/NullDriver.h>
53#include <core/IO/MidiInput.h>
54#include <core/IO/MidiOutput.h>
56#include <core/IO/OssDriver.h>
57#include <core/IO/FakeDriver.h>
66
67#include <core/Hydrogen.h>
69
70#include <limits>
71
72#define AUDIO_ENGINE_DEBUG 0
73
74namespace H2Core
75{
76
77#define AE_INFOLOG(x) INFOLOG( QString( "[%1] %2" ) \
78 .arg( Hydrogen::get_instance()->getAudioEngine()->getDriverNames() ).arg( x ) );
79#define AE_WARNINGLOG(x) WARNINGLOG( QString( "[%1] %2" ) \
80 .arg( Hydrogen::get_instance()->getAudioEngine()->getDriverNames() ).arg( x ) );
81#define AE_ERRORLOG(x) ERRORLOG( QString( "[%1] %2" ) \
82 .arg( Hydrogen::get_instance()->getAudioEngine()->getDriverNames() ).arg( x ) );
83#define AE_DEBUGLOG(x) if ( __logger->should_log( Logger::Debug ) ) { \
84 __logger->log( Logger::Debug, _class_name(), __FUNCTION__, \
85 QString( "%1" ).arg( x ), "\033[34;1m" ); }
86
89inline timeval currentTime2()
90{
91 struct timeval now;
92 gettimeofday( &now, nullptr );
93 return now;
94}
95
97 : m_pSampler( nullptr )
98 , m_pSynth( nullptr )
99 , m_pAudioDriver( nullptr )
100 , m_pMidiDriver( nullptr )
101 , m_pMidiDriverOut( nullptr )
103 , m_pMetronomeInstrument( nullptr )
105 , m_nRealtimeFrame( 0 )
106 , m_fMasterPeak_L( 0.0f )
107 , m_fMasterPeak_R( 0.0f )
109 , m_fProcessTime( 0.0f )
110 , m_fLadspaTime( 0.0f )
111 , m_fMaxProcessTime( 0.0f )
112 , m_fNextBpm( 120 )
113 , m_pLocker({nullptr, 0, nullptr})
114 , m_fLastTickEnd( 0 )
115 , m_bLookaheadApplied( false )
116 , m_nLoopsDone( 0 )
117{
118 m_pTransportPosition = std::make_shared<TransportPosition>( "Transport" );
119 m_pQueuingPosition = std::make_shared<TransportPosition>( "Queuing" );
120
121 m_pSampler = new Sampler;
122 m_pSynth = new Synth;
123
124 m_pEventQueue = EventQueue::get_instance();
125
126 srand( time( nullptr ) );
127
128 // Create metronome instrument
129 // Get the path to the file of the metronome sound.
130 QString sMetronomeFilename = Filesystem::click_file_path();
131 m_pMetronomeInstrument = std::make_shared<Instrument>( METRONOME_INSTR_ID, "metronome" );
132
133 auto pLayer = std::make_shared<InstrumentLayer>( Sample::load( sMetronomeFilename ) );
134 auto pCompo = std::make_shared<InstrumentComponent>( 0 );
135 pCompo->set_layer(pLayer, 0);
136 m_pMetronomeInstrument->get_components()->push_back( pCompo );
137 m_pMetronomeInstrument->set_is_metronome_instrument(true);
138 m_pMetronomeInstrument->set_volume(
139 Preferences::get_instance()->m_fMetronomeVolume );
140
141 m_AudioProcessCallback = &audioEngine_process;
142
143#ifdef H2CORE_HAVE_LADSPA
145#endif
146}
147
149{
151 if ( getState() != State::Initialized ) {
152 AE_ERRORLOG( "Error the audio engine is not in State::Initialized" );
153 return;
154 }
155 m_pSampler->stopPlayingNotes();
156
157 this->lock( RIGHT_HERE );
158 AE_INFOLOG( "*** Hydrogen audio engine shutdown ***" );
159
161
163
164 m_pTransportPosition->reset();
165 m_pTransportPosition = nullptr;
166 m_pQueuingPosition->reset();
167 m_pQueuingPosition = nullptr;
168
169 m_pMetronomeInstrument = nullptr;
170
171 this->unlock();
172
173#ifdef H2CORE_HAVE_LADSPA
174 delete Effects::get_instance();
175#endif
176
177 delete m_pSampler;
178 delete m_pSynth;
179}
180
182{
183 assert(m_pSampler);
184 return m_pSampler;
185}
186
188{
189 assert(m_pSynth);
190 return m_pSynth;
191}
192
193void AudioEngine::lock( const char* file, unsigned int line, const char* function )
194{
195#ifdef H2CORE_HAVE_DEBUG
196 // Is there a more convenient way to convert the thread id to QSTring?
197 std::stringstream tmpStream;
198 tmpStream << std::this_thread::get_id();
199 if (__logger->should_log(Logger::Locks)) {
200 __logger->log( Logger::Locks, _class_name(), __FUNCTION__,
201 QString( "[thread id: %1] : %2 : [line: %3] : %4" )
202 .arg( QString::fromStdString( tmpStream.str() ) )
203 .arg( function ).arg( line ).arg( file ) );
204 }
205#endif
206
207 m_EngineMutex.lock();
208 m_pLocker.file = file;
209 m_pLocker.line = line;
210 m_pLocker.function = function;
211 m_LockingThread = std::this_thread::get_id();
212
213#ifdef H2CORE_HAVE_DEBUG
214 if ( __logger->should_log( Logger::Locks ) ) {
215 __logger->log( Logger::Locks, _class_name(), __FUNCTION__,
216 QString( "[thread id: %1] locked" )
217 .arg( QString::fromStdString( tmpStream.str() ) ) );
218 }
219#endif
220}
221
222bool AudioEngine::tryLock( const char* file, unsigned int line, const char* function )
223{
224#ifdef H2CORE_HAVE_DEBUG
225 // Is there a more convenient way to convert the thread id to QSTring?
226 std::stringstream tmpStream;
227 tmpStream << std::this_thread::get_id();
228 if ( __logger->should_log( Logger::Locks ) ) {
229 __logger->log( Logger::Locks, _class_name(), __FUNCTION__,
230 QString( "[thread id: %1] : %2 : [line: %3] : %4" )
231 .arg( QString::fromStdString( tmpStream.str() ) )
232 .arg( function ).arg( line ).arg( file ) );
233 }
234#endif
235 bool res = m_EngineMutex.try_lock();
236 if ( !res ) {
237 // Lock not obtained
238 return false;
239 }
240 m_pLocker.file = file;
241 m_pLocker.line = line;
242 m_pLocker.function = function;
243 m_LockingThread = std::this_thread::get_id();
244
245#ifdef H2CORE_HAVE_DEBUG
246 if ( __logger->should_log( Logger::Locks ) ) {
247 __logger->log( Logger::Locks, _class_name(), __FUNCTION__,
248 QString( "[thread id: %1] locked" )
249 .arg( QString::fromStdString( tmpStream.str() ) ) );
250 }
251#endif
252
253 return true;
254}
255
256bool AudioEngine::tryLockFor( std::chrono::microseconds duration, const char* file, unsigned int line, const char* function )
257{
258 std::stringstream tmpStream;
259 tmpStream << std::this_thread::get_id();
260#ifdef H2CORE_HAVE_DEBUG
261 if ( __logger->should_log( Logger::Locks ) ) {
262 __logger->log( Logger::Locks, _class_name(), __FUNCTION__,
263 QString( "[thread id: %1] : %2 : [line: %3] : %4" )
264 .arg( QString::fromStdString( tmpStream.str() ) )
265 .arg( function ).arg( line ).arg( file ) );
266 }
267#endif
268
269 bool res = m_EngineMutex.try_lock_for( duration );
270 if ( !res ) {
271 // Lock not obtained
272 AE_WARNINGLOG( QString( "[thread id: %1] : Lock timeout: lock timeout %2:%3:%4, lock held by %5:%6:%7" )
273 .arg( QString::fromStdString( tmpStream.str() ) )
274 .arg( file ).arg( function ).arg( line )
275 .arg( m_pLocker.file ).arg( m_pLocker.function )
276 .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
285 if ( __logger->should_log( Logger::Locks ) ) {
286 __logger->log( Logger::Locks, _class_name(), __FUNCTION__,
287 QString( "[thread id: %1] locked" )
288 .arg( QString::fromStdString( tmpStream.str() ) ) );
289 }
290#endif
291
292 return true;
293}
294
296{
297 // Leave "__locker" dirty.
298 m_LockingThread = std::thread::id();
299 m_EngineMutex.unlock();
300
301#ifdef H2CORE_HAVE_DEBUG
302 std::stringstream tmpStream;
303 tmpStream << std::this_thread::get_id();
304 if ( __logger->should_log( Logger::Locks ) ) {
305 __logger->log( Logger::Locks, _class_name(), __FUNCTION__,
306 QString( "[thread id: %1]" )
307 .arg( QString::fromStdString( tmpStream.str() ) ) );
308 }
309#endif
310}
311
313{
314 AE_INFOLOG( "" );
315
316 if ( getState() != State::Ready ) {
317 AE_ERRORLOG( "Error the audio engine is not in State::Ready" );
318 return;
319 }
320
322
324}
325
327{
328 AE_INFOLOG( "" );
329
330 if ( getState() != State::Playing ) {
331 AE_ERRORLOG( QString( "Error the audio engine is not in State::Playing but [%1]" )
332 .arg( static_cast<int>( getState() ) ) );
333 return;
334 }
335
337}
338
339void AudioEngine::reset( bool bWithJackBroadcast ) {
340 const auto pHydrogen = Hydrogen::get_instance();
341
343
344 m_fMasterPeak_L = 0.0f;
345 m_fMasterPeak_R = 0.0f;
346
347#ifdef H2CORE_HAVE_LADSPA
348 for ( int ii = 0; ii < MAX_FX; ++ii ) {
349 m_fFXPeak_L[ ii ] = 0;
350 m_fFXPeak_R[ ii ] = 0;
351 }
352#endif
353
354 m_fLastTickEnd = 0;
355 m_nLoopsDone = 0;
356 m_bLookaheadApplied = false;
357
358 setNextBpm( 120 );
359
360 m_pTransportPosition->reset();
361 m_pQueuingPosition->reset();
362
365
367
368#ifdef H2CORE_HAVE_JACK
369 if ( pHydrogen->hasJackTransport() && bWithJackBroadcast ) {
370 // Tell the JACK server to locate to the beginning as well
371 // (done in the next run of audioEngine_process()).
372 static_cast<JackAudioDriver*>( m_pAudioDriver )->locateTransport( 0 );
373 }
374#endif
375}
376
377float AudioEngine::computeTickSize( const int nSampleRate, const float fBpm, const int nResolution)
378{
379 float fTickSize = nSampleRate * 60.0 / fBpm / nResolution;
380
381 return fTickSize;
382}
383
384double AudioEngine::computeDoubleTickSize( const int nSampleRate, const float fBpm, const int nResolution)
385{
386 double fTickSize = static_cast<double>(nSampleRate) * 60.0 /
387 static_cast<double>(fBpm) /
388 static_cast<double>(nResolution);
389
390 return fTickSize;
391}
392
394
395 const auto pHydrogen = Hydrogen::get_instance();
396 const auto pDriver = pHydrogen->getAudioOutput();
397
398 if ( pDriver == nullptr || pDriver->getSampleRate() == 0 ) {
399 return 0;
400 }
401
402 return ( m_pTransportPosition->getFrame() -
403 m_pTransportPosition->getFrameOffsetTempo() )/
404 static_cast<float>(pDriver->getSampleRate());
405}
406
407void AudioEngine::locate( const double fTick, bool bWithJackBroadcast ) {
408 const auto pHydrogen = Hydrogen::get_instance();
409
410#ifdef H2CORE_HAVE_JACK
411 // In case Hydrogen is using the JACK server to sync transport, it
412 // is up to the server to relocate to a different position. It
413 // does so after the current cycle of audioEngine_process() and we
414 // will pick it up at the beginning of the next one.
415 if ( pHydrogen->hasJackTransport() && bWithJackBroadcast ) {
416
417 double fNewTick = fTick;
418 // As the tick mismatch is lost when converting a sought location from
419 // ticks into frames, sending it to the JACK server, receiving it in a
420 // callback, and providing it here, we will use a heuristic in order to
421 // not experience any glitches upon relocation.
422 if ( std::fmod( fTick, std::floor( fTick ) ) >= 0.97 ) {
423 fNewTick = std::round( fTick );
424 AE_INFOLOG( QString( "Tick [%1] will be rounded to [%2] in order to avoid glitches" )
425 .arg( fTick, 0, 'E', -1 ).arg( fNewTick ) );
426 }
427
428 double fTickMismatch;
429 const long long nNewFrame = TransportPosition::computeFrameFromTick(
430 fNewTick, &fTickMismatch );
431
432#if AUDIO_ENGINE_DEBUG
433 AE_DEBUGLOG( QString( "[Locate via JACK server] fTick: %1, fNewTick: %2, nNewFrame: %3" )
434 .arg( fTick ).arg( fNewTick ) .arg( nNewFrame ) );
435#endif
436
437 static_cast<JackAudioDriver*>( m_pAudioDriver )->
438 locateTransport( nNewFrame );
439 return;
440 }
441#endif
442
443 resetOffsets();
444 m_fLastTickEnd = fTick;
445 const long long nNewFrame = TransportPosition::computeFrameFromTick(
446 fTick, &m_pTransportPosition->m_fTickMismatch );
447
448#if AUDIO_ENGINE_DEBUG
449 AE_DEBUGLOG( QString( "fTick: %1, nNewFrame: %2" )
450 .arg( fTick ).arg( nNewFrame ) );
451#endif
452
455
457}
458
459void AudioEngine::locateToFrame( const long long nFrame ) {
460
461#if AUDIO_ENGINE_DEBUG
462 AE_DEBUGLOG( QString( "nFrame: %1" ).arg( nFrame ) );
463#endif
464
465 resetOffsets();
466
467 const double fNewTick = TransportPosition::computeTickFromFrame( nFrame );
468 m_fLastTickEnd = fNewTick;
469
470 // Assure tick<->frame can be converted properly using mismatch.
471 const long long nNewFrame = TransportPosition::computeFrameFromTick(
472 fNewTick, &m_pTransportPosition->m_fTickMismatch );
473
474 updateTransportPosition( fNewTick, nNewFrame, m_pTransportPosition );
476
478
479 // While the locate() function is wrapped by a caller in the
480 // CoreActionController - which takes care of queuing the
481 // relocation event - this function is only meant to be used in
482 // very specific circumstances and has to queue it itself.
484}
485
488
489 m_fLastTickEnd = 0;
490 m_nLoopsDone = 0;
491 m_bLookaheadApplied = false;
492
493 m_pTransportPosition->setFrameOffsetTempo( 0 );
494 m_pTransportPosition->setTickOffsetQueuing( 0 );
495 m_pTransportPosition->setTickOffsetSongSize( 0 );
496 m_pTransportPosition->setLastLeadLagFactor( 0 );
497 m_pQueuingPosition->setFrameOffsetTempo( 0 );
498 m_pQueuingPosition->setTickOffsetQueuing( 0 );
499 m_pQueuingPosition->setTickOffsetSongSize( 0 );
500 m_pQueuingPosition->setLastLeadLagFactor( 0 );
501}
502
504 const long long nNewFrame = m_pTransportPosition->getFrame() + nFrames;
505 const double fNewTick = TransportPosition::computeTickFromFrame( nNewFrame );
506 m_pTransportPosition->m_fTickMismatch = 0;
507
508#if AUDIO_ENGINE_DEBUG
509 AE_DEBUGLOG( QString( "nFrames: %1, old frame: %2, new frame: %3, old tick: %4, new tick: %5, ticksize: %6" )
510 .arg( nFrames )
511 .arg( m_pTransportPosition->getFrame() )
512 .arg( nNewFrame )
513 .arg( m_pTransportPosition->getDoubleTick(), 0, 'f' )
514 .arg( fNewTick, 0, 'f' )
515 .arg( m_pTransportPosition->getTickSize(), 0, 'f' ) );
516#endif
517
518 updateTransportPosition( fNewTick, nNewFrame, m_pTransportPosition );
519
520 // We are not updating the queuing position in here. This will be
521 // done in updateNoteQueue().
522}
523
524bool AudioEngine::isEndOfSongReached( std::shared_ptr<TransportPosition> pPos ) const {
525 const auto pSong = Hydrogen::get_instance()->getSong();
526 if ( pSong != nullptr && pSong->getMode() == Song::Mode::Song &&
527 ( pSong->getLoopMode() == Song::LoopMode::Disabled &&
528 pPos->getDoubleTick() >= m_fSongSizeInTicks ||
529 pSong->getLoopMode() == Song::LoopMode::Finishing &&
530 pPos->getDoubleTick() >= m_fSongSizeInTicks *
531 (1 + static_cast<double>(m_nLoopsDone)) ) ) {
532 return true;
533 }
534
535 return false;
536}
537
538void AudioEngine::makeTrackPorts( std::shared_ptr<Song> pSong ) {
539
540#ifdef H2CORE_HAVE_JACK
541 auto pJackAudioDriver = dynamic_cast<JackAudioDriver*>( m_pAudioDriver );
542 if ( pJackAudioDriver != nullptr ) {
543 // We have to guard this call using the special output buffer mutex to
544 // avoid `JackAudioDriver::makeTrackPorts` being called in parallel to
545 // `JackAudioDriver::clearPerTrackAudioBuffers` (called without an
546 // AudioEngine lock).
548
549 pJackAudioDriver->makeTrackOutputs( pSong );
550
551 m_MutexOutputPointer.unlock();
552 }
553#endif
554}
555
556void AudioEngine::updateTransportPosition( double fTick, long long nFrame, std::shared_ptr<TransportPosition> pPos ) {
557
558 const auto pHydrogen = Hydrogen::get_instance();
559
560#if AUDIO_ENGINE_DEBUG
561 AE_DEBUGLOG( QString( "[Before] fTick: %1, nFrame: %2, pos: %3" )
562 .arg( fTick, 0, 'f' )
563 .arg( nFrame )
564 .arg( pPos->toQString( "", true ) ) );
565#endif
566
567 if ( pHydrogen->getMode() == Song::Mode::Song ) {
568 updateSongTransportPosition( fTick, nFrame, pPos );
569 }
570 else { // Song::Mode::Pattern
571 updatePatternTransportPosition( fTick, nFrame, pPos );
572 }
573
574 updateBpmAndTickSize( pPos );
575
576
577 // Beat - Bar (- Tick) information is a coarse grained position
578 // information and might not change on small position increments.
579 bool bBBTChanged = false;
580 const int nBar = std::max( pPos->getColumn(), 0 ) + 1;
581 if ( nBar != pPos->getBar() ) {
582 pPos->setBar( nBar );
583 bBBTChanged = true;
584 }
585
586 const int nBeat = static_cast<int>(
587 std::floor(static_cast<float>(pPos->getPatternTickPosition()) / 48 )) + 1;
588 if ( pPos->getBeat() != nBeat ) {
589 pPos->setBeat( nBeat );
590 bBBTChanged = true;
591 }
592
593 if ( pPos == m_pTransportPosition && bBBTChanged ) {
595 }
596
597#if AUDIO_ENGINE_DEBUG
598 AE_DEBUGLOG( QString( "[After] fTick: %1, nFrame: %2, pos: %3, frame: %4" )
599 .arg( fTick, 0, 'f' )
600 .arg( nFrame )
601 .arg( pPos->toQString( "", true ) )
602 .arg( pPos->getFrame() ) );
603#endif
604
605}
606
607void AudioEngine::updatePatternTransportPosition( double fTick, long long nFrame, std::shared_ptr<TransportPosition> pPos ) {
608
609 auto pHydrogen = Hydrogen::get_instance();
610
611 pPos->setTick( fTick );
612 pPos->setFrame( nFrame );
613
614 const double fPatternStartTick =
615 static_cast<double>(pPos->getPatternStartTick());
616 const int nPatternSize = pPos->getPatternSize();
617
618 if ( fTick >= fPatternStartTick + static_cast<double>(nPatternSize) ||
619 fTick < fPatternStartTick ) {
620 // Transport went past the end of the pattern or Pattern mode
621 // was just activated.
622 pPos->setPatternStartTick( pPos->getPatternStartTick() +
623 static_cast<long>(std::floor( ( fTick - fPatternStartTick ) /
624 static_cast<double>(nPatternSize) )) *
625 nPatternSize );
626
627 // In stacked pattern mode we will only update the playing
628 // patterns if the transport of the original pattern is looped
629 // back to the beginning. This way all patterns start fresh.
630 //
631 // In selected pattern mode pattern change does occur
632 // asynchonically by user interaction.
633 if ( pHydrogen->getPatternMode() == Song::PatternMode::Stacked ) {
635 }
636 }
637
638 long nPatternTickPosition = static_cast<long>(std::floor( fTick )) -
639 pPos->getPatternStartTick();
640 if ( nPatternTickPosition > nPatternSize ) {
641 nPatternTickPosition = ( static_cast<long>(std::floor( fTick ))
642 - pPos->getPatternStartTick() ) %
643 nPatternSize;
644 }
645 pPos->setPatternTickPosition( nPatternTickPosition );
646}
647
648void AudioEngine::updateSongTransportPosition( double fTick, long long nFrame, std::shared_ptr<TransportPosition> pPos ) {
649
650#if AUDIO_ENGINE_DEBUG
651 AE_DEBUGLOG( QString( "[Before] fTick: %1, nFrame: %2, m_fSongSizeInTicks: %3, pos: %4" )
652 .arg( fTick, 0, 'f' )
653 .arg( nFrame )
654 .arg( m_fSongSizeInTicks, 0, 'f' )
655 .arg( pPos->toQString( "", true ) ) );
656#endif
657
658 const auto pHydrogen = Hydrogen::get_instance();
659 const auto pSong = pHydrogen->getSong();
660
661 pPos->setTick( fTick );
662 pPos->setFrame( nFrame );
663
664 if ( fTick < 0 ) {
665 AE_ERRORLOG( QString( "[%1] Provided tick [%2] is negative!" )
666 .arg( pPos->getLabel() )
667 .arg( fTick, 0, 'f' ) );
668 return;
669 }
670
671 int nNewColumn;
672 if ( pSong == nullptr || pSong->getPatternGroupVector()->size() == 0 ) {
673 // There are no patterns in song.
674 pPos->setPatternStartTick( 0 );
675 pPos->setPatternTickPosition( 0 );
676 nNewColumn = 0;
677 }
678 else {
679 long nPatternStartTick;
680 nNewColumn = pHydrogen->getColumnForTick(
681 std::floor( fTick ), pSong->isLoopEnabled(), &nPatternStartTick );
682 pPos->setPatternStartTick( nPatternStartTick );
683
684 // While the current tick position is constantly increasing,
685 // m_nPatternStartTick is only defined between 0 and
686 // m_fSongSizeInTicks. We will take care of the looping next.
687 if ( nNewColumn == -1 ) {
688 // Loop mode is not activated and the new position lies beyond the
689 // end of the song. We jump to the beginning to indicated transport
690 // is "finished".
691 pPos->setPatternTickPosition( 0 );
692 }
693 else if ( fTick >= m_fSongSizeInTicks && m_fSongSizeInTicks != 0 ) {
694 pPos->setPatternTickPosition(
695 std::fmod( std::floor( fTick ) - nPatternStartTick,
697 }
698 else {
699 pPos->setPatternTickPosition( std::floor( fTick ) - nPatternStartTick );
700 }
701 }
702
703 if ( pPos->getColumn() != nNewColumn ) {
704 pPos->setColumn( nNewColumn );
705
707
708 if ( pPos == m_pTransportPosition ) {
710 }
711 }
712
713#if AUDIO_ENGINE_DEBUG
714 AE_DEBUGLOG( QString( "[After] fTick: %1, nFrame: %2, m_fSongSizeInTicks: %3, pos: %4, frame: %5" )
715 .arg( fTick, 0, 'f' )
716 .arg( nFrame )
717 .arg( m_fSongSizeInTicks, 0, 'f' )
718 .arg( pPos->toQString( "", true ) )
719 .arg( pPos->getFrame() ) );
720#endif
721
722}
723
724void AudioEngine::updateBpmAndTickSize( std::shared_ptr<TransportPosition> pPos ) {
725 if ( ! ( m_state == State::Playing ||
727 m_state == State::Testing ) ) {
728 return;
729 }
730
731 auto pHydrogen = Hydrogen::get_instance();
732 auto pSong = pHydrogen->getSong();
733
734 const float fOldBpm = pPos->getBpm();
735
736 float fNewBpm = getBpmAtColumn( pPos->getColumn() );
737 // If we are in Pattern mode or Timeline is not activated in Song Mode +
738 // there is no external application controling tempo of Hydrogen, we are
739 // free to apply a new tempo. This was set by the user either via UI or
740 // MIDI/OSC message.
741 if ( pHydrogen->getJackTimebaseState() !=
743 ( ( pSong != nullptr && ! pSong->getIsTimelineActivated() ) ||
744 pHydrogen->getMode() != Song::Mode::Song ) &&
745 fNewBpm != m_fNextBpm ) {
746 fNewBpm = m_fNextBpm;
747 }
748
749 if ( fNewBpm != fOldBpm ) {
750 pPos->setBpm( fNewBpm );
751 if ( pPos == m_pTransportPosition ) {
753 }
754 }
755
756 int nResolution;
757 if ( pSong != nullptr ) {
758 nResolution = pSong->getResolution();
759 } else {
760 nResolution = Song::nDefaultResolution;
761 }
762
763 const float fOldTickSize = pPos->getTickSize();
764 const float fNewTickSize = AudioEngine::computeTickSize(
765 static_cast<float>(m_pAudioDriver->getSampleRate()), fNewBpm,
766 nResolution );
767
768 // Nothing changed - avoid recomputing
769#if defined(WIN32) and !defined(WIN64)
770 // For some reason two identical numbers (according to their
771 // values when printing them) are not equal to each other in 32bit
772 // Windows. Course graining the tick change in here will do no
773 // harm except of for preventing tiny tempo changes. Integer value
774 // changes should not be affected.
775 if ( std::abs( fNewTickSize - fOldTickSize ) < 1e-2 ) {
776#else
777 if ( fNewTickSize == fOldTickSize ) {
778#endif
779 return;
780 }
781
782 if ( fNewTickSize == 0 ) {
783 AE_ERRORLOG( QString( "[%1] Something went wrong while calculating the tick size. [oldTS: %2, newTS: %3]" )
784 .arg( pPos->getLabel() )
785 .arg( fOldTickSize, 0, 'f' ).arg( fNewTickSize, 0, 'f' ) );
786 return;
787 }
788
789 // The lookahead in updateNoteQueue is tempo dependent (since it
790 // contains both tick and frame components). By resetting this
791 // variable we allow that the next one calculated to have
792 // arbitrary values.
793 pPos->setLastLeadLagFactor( 0 );
794
795#if AUDIO_ENGINE_DEBUG
796 AE_DEBUGLOG(QString( "[%1] [%7,%8] sample rate: %2, tick size: %3 -> %4, bpm: %5 -> %6" )
797 .arg( pPos->getLabel() )
798 .arg( static_cast<float>(m_pAudioDriver->getSampleRate()))
799 .arg( fOldTickSize, 0, 'f' )
800 .arg( fNewTickSize, 0, 'f' )
801 .arg( fOldBpm, 0, 'f' )
802 .arg( pPos->getBpm(), 0, 'f' )
803 .arg( pPos->getFrame() )
804 .arg( pPos->getDoubleTick(), 0, 'f' ) );
805#endif
806
807 pPos->setTickSize( fNewTickSize );
808
810}
811
812void AudioEngine::calculateTransportOffsetOnBpmChange( std::shared_ptr<TransportPosition> pPos ) {
813
814 // If we deal with a single speed for the whole song, the frames
815 // since the beginning of the song are tempo-dependent and have to
816 // be recalculated.
817 const long long nNewFrame =
818 TransportPosition::computeFrameFromTick( pPos->getDoubleTick(),
819 &pPos->m_fTickMismatch );
820 pPos->setFrameOffsetTempo( nNewFrame - pPos->getFrame() +
821 pPos->getFrameOffsetTempo() );
822
823 if ( m_bLookaheadApplied ) {
824 // if ( m_fLastTickEnd != 0 ) {
825 const long long nNewLookahead =
826 getLeadLagInFrames( pPos->getDoubleTick() ) +
828 const double fNewTickEnd = TransportPosition::computeTickFromFrame(
829 nNewFrame + nNewLookahead ) + pPos->getTickMismatch();
830 pPos->setTickOffsetQueuing( fNewTickEnd - m_fLastTickEnd );
831
832#if AUDIO_ENGINE_DEBUG
833 AE_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" )
834 .arg( pPos->getLabel() )
835 .arg( Hydrogen::get_instance()->isTimelineEnabled() )
836 .arg( pPos->getFrame() )
837 .arg( nNewFrame )
838 .arg( pPos->getDoubleTick(), 0, 'f' )
839 .arg( nNewLookahead )
840 .arg( pPos->getFrameOffsetTempo() )
841 .arg( pPos->getTickOffsetQueuing(), 0, 'f' )
842 .arg( fNewTickEnd, 0, 'f' )
843 .arg( m_fLastTickEnd, 0, 'f' )
844 );
845#endif
846
847 }
848
849 // Happens when the Timeline was either toggled or tempo
850 // changed while the former was deactivated.
851 if ( pPos->getFrame() != nNewFrame ) {
852 pPos->setFrame( nNewFrame );
853 }
854
855 if ( pPos == m_pTransportPosition ) {
857 }
858}
859
860void AudioEngine::clearAudioBuffers( uint32_t nFrames )
861{
863 float *pBuffer_L, *pBuffer_R;
864
865 // clear main out Left and Right
866 if ( m_pAudioDriver != nullptr ) {
867 pBuffer_L = m_pAudioDriver->getOut_L();
868 pBuffer_R = m_pAudioDriver->getOut_R();
869 assert( pBuffer_L != nullptr && pBuffer_R != nullptr );
870 memset( pBuffer_L, 0, nFrames * sizeof( float ) );
871 memset( pBuffer_R, 0, nFrames * sizeof( float ) );
872 }
873
874#ifdef H2CORE_HAVE_JACK
875 if ( Hydrogen::get_instance()->hasJackAudioDriver() ) {
876 JackAudioDriver* pJackAudioDriver = static_cast<JackAudioDriver*>(m_pAudioDriver);
877
878 if ( pJackAudioDriver != nullptr ) {
879 pJackAudioDriver->clearPerTrackAudioBuffers( nFrames );
880 }
881 }
882#endif
883
884 m_MutexOutputPointer.unlock();
885
886#ifdef H2CORE_HAVE_LADSPA
887 if ( getState() == State::Ready ||
889 getState() == State::Testing ) {
890 Effects* pEffects = Effects::get_instance();
891 for ( unsigned i = 0; i < MAX_FX; ++i ) { // clear FX buffers
892 LadspaFX* pFX = pEffects->getLadspaFX( i );
893 if ( pFX != nullptr ) {
894 assert( pFX->m_pBuffer_L );
895 assert( pFX->m_pBuffer_R );
896 memset( pFX->m_pBuffer_L, 0, nFrames * sizeof( float ) );
897 memset( pFX->m_pBuffer_R, 0, nFrames * sizeof( float ) );
898 }
899 }
900 }
901#endif
902}
903
905{
906 AE_INFOLOG( QString( "Creating driver [%1]" )
907 .arg( Preferences::audioDriverToQString( driver ) ) );
908
909 auto pPref = Preferences::get_instance();
910 auto pHydrogen = Hydrogen::get_instance();
911 auto pSong = pHydrogen->getSong();
912 AudioOutput *pAudioDriver = nullptr;
913
914 if ( driver == Preferences::AudioDriver::Oss ) {
915 pAudioDriver = new OssDriver( m_AudioProcessCallback );
916 }
917 else if ( driver == Preferences::AudioDriver::Jack ) {
918 pAudioDriver = new JackAudioDriver( m_AudioProcessCallback );
919#ifdef H2CORE_HAVE_JACK
920 if ( auto pJackDriver = dynamic_cast<JackAudioDriver*>( pAudioDriver ) ) {
921 pJackDriver->setConnectDefaults(
922 Preferences::get_instance()->m_bJackConnectDefaults
923 );
924 }
925#endif
926 }
927 else if ( driver == Preferences::AudioDriver::Alsa ) {
928 pAudioDriver = new AlsaAudioDriver( m_AudioProcessCallback );
929 }
930 else if ( driver == Preferences::AudioDriver::PortAudio ) {
931 pAudioDriver = new PortAudioDriver( m_AudioProcessCallback );
932 }
933 else if ( driver == Preferences::AudioDriver::CoreAudio ) {
934 pAudioDriver = new CoreAudioDriver( m_AudioProcessCallback );
935 }
936 else if ( driver == Preferences::AudioDriver::PulseAudio ) {
937 pAudioDriver = new PulseAudioDriver( m_AudioProcessCallback );
938 }
939 else if ( driver == Preferences::AudioDriver::Fake ) {
940 AE_WARNINGLOG( "*** Using FAKE audio driver ***" );
941 pAudioDriver = new FakeDriver( m_AudioProcessCallback );
942 }
943 else if ( driver == Preferences::AudioDriver::Disk ) {
944 pAudioDriver = new DiskWriterDriver( m_AudioProcessCallback );
945 }
946 else if ( driver == Preferences::AudioDriver::Null ) {
947 pAudioDriver = new NullDriver( m_AudioProcessCallback );
948 }
949 else {
950 AE_ERRORLOG( QString( "Unknown driver [%1]" )
951 .arg( Preferences::audioDriverToQString( driver ) ) );
953 return nullptr;
954 }
955
956 if ( pAudioDriver == nullptr ) {
957 AE_INFOLOG( QString( "Unable to create driver [%1]" )
958 .arg( Preferences::audioDriverToQString( driver ) ) );
959 return nullptr;
960 }
961
962 // Initialize the audio driver
963 int nRes = pAudioDriver->init( pPref->m_nBufferSize );
964 if ( nRes != 0 ) {
965 AE_ERRORLOG( QString( "Error code [%2] while initializing audio driver [%1]." )
966 .arg( Preferences::audioDriverToQString( driver ) ).arg( nRes ) );
967 delete pAudioDriver;
968 return nullptr;
969 }
970
971 this->lock( RIGHT_HERE );
973
974 // Some audio drivers require to be already registered in the
975 // AudioEngine while being connected.
976 m_pAudioDriver = pAudioDriver;
977
978 if ( pSong != nullptr ) {
980 } else {
982 }
983
984 // Unlocking earlier might execute the jack process() callback before we
985 // are fully initialized.
986 m_MutexOutputPointer.unlock();
987 this->unlock();
988
989 nRes = m_pAudioDriver->connect();
990 if ( nRes != 0 ) {
992 AE_ERRORLOG( QString( "Error code [%2] while connecting audio driver [%1]." )
993 .arg( Preferences::audioDriverToQString( driver ) ).arg( nRes ) );
994
995 this->lock( RIGHT_HERE );
997
998 delete m_pAudioDriver;
999 m_pAudioDriver = nullptr;
1000
1001 m_MutexOutputPointer.unlock();
1002 this->unlock();
1003
1004 return nullptr;
1005 }
1006
1007 if ( pSong != nullptr && pHydrogen->hasJackAudioDriver() ) {
1008 pHydrogen->renameJackPorts( pSong );
1009 }
1010
1011 lock( RIGHT_HERE );
1012 setupLadspaFX();
1013
1014 if ( pSong != nullptr ) {
1016 }
1017 unlock();
1018
1020
1021 return pAudioDriver;
1022}
1023
1025{
1026 AE_INFOLOG("");
1028
1029 if ( getState() != State::Initialized ) {
1030 AE_ERRORLOG( QString( "Audio engine is not in State::Initialized but [%1]" )
1031 .arg( static_cast<int>( getState() ) ) );
1032 return;
1033 }
1034
1035 if ( m_pAudioDriver != nullptr ) { // check if audio driver is still alive
1036 AE_ERRORLOG( "The audio driver is still alive" );
1037 }
1038 if ( m_pMidiDriver != nullptr ) { // check if midi driver is still alive
1039 AE_ERRORLOG( "The MIDI driver is still active" );
1040 }
1041
1042 const auto audioDriver = pPref->m_audioDriver;
1043
1044 if ( audioDriver != Preferences::AudioDriver::Auto ) {
1045 createAudioDriver( audioDriver );
1046 }
1047 else {
1048 AudioOutput* pAudioDriver;
1049 for ( const auto& ddriver : Preferences::getSupportedAudioDrivers() ) {
1050 if ( ( pAudioDriver = createAudioDriver( ddriver ) ) != nullptr ) {
1051 break;
1052 }
1053 }
1054 }
1055
1056 if ( m_pAudioDriver == nullptr ) {
1057 AE_ERRORLOG( QString( "Couldn't start audio driver [%1], falling back to NullDriver" )
1058 .arg( Preferences::audioDriverToQString( audioDriver ) ) );
1060 }
1061
1062 this->lock( RIGHT_HERE );
1063 m_MutexOutputPointer.lock();
1064
1065 if ( pPref->m_sMidiDriver == "ALSA" ) {
1066#ifdef H2CORE_HAVE_ALSA
1067 AlsaMidiDriver *alsaMidiDriver = new AlsaMidiDriver();
1068 m_pMidiDriverOut = alsaMidiDriver;
1069 m_pMidiDriver = alsaMidiDriver;
1070 m_pMidiDriver->open();
1071 m_pMidiDriver->setActive( true );
1072#endif
1073 } else if ( pPref->m_sMidiDriver == "PortMidi" ) {
1074#ifdef H2CORE_HAVE_PORTMIDI
1075 PortMidiDriver* pPortMidiDriver = new PortMidiDriver();
1076 m_pMidiDriver = pPortMidiDriver;
1077 m_pMidiDriverOut = pPortMidiDriver;
1078 m_pMidiDriver->open();
1079 m_pMidiDriver->setActive( true );
1080#endif
1081 } else if ( pPref->m_sMidiDriver == "CoreMIDI" ) {
1082#ifdef H2CORE_HAVE_COREMIDI
1083 CoreMidiDriver *coreMidiDriver = new CoreMidiDriver();
1084 m_pMidiDriver = coreMidiDriver;
1085 m_pMidiDriverOut = coreMidiDriver;
1086 m_pMidiDriver->open();
1087 m_pMidiDriver->setActive( true );
1088#endif
1089 } else if ( pPref->m_sMidiDriver == "JACK-MIDI" ) {
1090#ifdef H2CORE_HAVE_JACK
1091 JackMidiDriver *jackMidiDriver = new JackMidiDriver();
1092 m_pMidiDriverOut = jackMidiDriver;
1093 m_pMidiDriver = jackMidiDriver;
1094 m_pMidiDriver->open();
1095 m_pMidiDriver->setActive( true );
1096#endif
1097 }
1098
1099 m_MutexOutputPointer.unlock();
1100 this->unlock();
1101}
1102
1104{
1105 AE_INFOLOG( "" );
1106
1107 this->lock( RIGHT_HERE );
1108
1109 if ( m_state == State::Playing ) {
1110 this->stopPlayback();
1111 }
1112
1113 if ( ( m_state != State::Prepared )
1114 && ( m_state != State::Ready ) ) {
1115 AE_ERRORLOG( QString( "Audio engine is not in State::Prepared or State::Ready but [%1]" )
1116 .arg( static_cast<int>(m_state) ) );
1117 this->unlock();
1118 return;
1119 }
1120
1121
1123
1124 if ( m_pMidiDriver != nullptr ) {
1125 m_pMidiDriver->close();
1126 delete m_pMidiDriver;
1127 m_pMidiDriver = nullptr;
1128 m_pMidiDriverOut = nullptr;
1129 }
1130
1131 if ( m_pAudioDriver != nullptr ) {
1132 m_pAudioDriver->disconnect();
1133 m_MutexOutputPointer.lock();
1134 delete m_pAudioDriver;
1135 m_pAudioDriver = nullptr;
1136 m_MutexOutputPointer.unlock();
1137 }
1138
1139 this->unlock();
1140}
1141
1143{
1144 bool bPlaying = m_state == State::Playing;
1145 if ( m_pAudioDriver != nullptr ) {
1147 }
1149 if ( bPlaying ) {
1150 this->startPlayback();
1151 }
1152
1153}
1154
1156 auto pSong = Hydrogen::get_instance()->getSong();
1157 if ( pSong != nullptr &&
1158 pSong->getLoopMode() == Song::LoopMode::Finishing ) {
1159 m_nLoopsDone = static_cast<int>(std::floor(
1160 m_pTransportPosition->getDoubleTick() / m_fSongSizeInTicks ));
1161 }
1162}
1163
1165
1166 if ( Hydrogen::get_instance()->getSong() == nullptr ) {
1167 AE_WARNINGLOG( "no song set yet" );
1168 return;
1169 }
1170
1172}
1173
1174float AudioEngine::getBpmAtColumn( int nColumn ) {
1175
1176 auto pHydrogen = Hydrogen::get_instance();
1177 auto pAudioEngine = pHydrogen->getAudioEngine();
1178 auto pSong = pHydrogen->getSong();
1179
1180 if ( pSong == nullptr ) {
1181 AE_WARNINGLOG( "no song set yet" );
1182 return MIN_BPM;
1183 }
1184
1185 float fBpm = pAudioEngine->getTransportPosition()->getBpm();
1186
1187 if ( pHydrogen->getJackTimebaseState() ==
1189 // Hydrogen is using the BPM broadcasted by the JACK
1190 // server. This one does solely depend on external
1191 // applications and will NOT be stored in the Song.
1192 const float fJackTimebaseBpm = pHydrogen->getJackTimebaseControllerBpm();
1193 if ( ! std::isnan( fJackTimebaseBpm ) ) {
1194 if ( fBpm != fJackTimebaseBpm ) {
1195 fBpm = fJackTimebaseBpm;
1196#if AUDIO_ENGINE_DEBUG
1197 AE_DEBUGLOG( QString( "Tempo update by the JACK server [%1]")
1198 .arg( fJackTimebaseBpm ) );
1199#endif
1200 }
1201 } else {
1202 AE_ERRORLOG( "Unable to retrieve tempo from JACK server" );
1203 }
1204 }
1205 else if ( pSong->getIsTimelineActivated() &&
1206 pHydrogen->getMode() == Song::Mode::Song ) {
1207
1208 const float fTimelineBpm = pHydrogen->getTimeline()->getTempoAtColumn( nColumn );
1209 if ( fTimelineBpm != fBpm ) {
1210#if AUDIO_ENGINE_DEBUG
1211 AE_DEBUGLOG( QString( "Set tempo to timeline value [%1]")
1212 .arg( fTimelineBpm ) );
1213#endif
1214 fBpm = fTimelineBpm;
1215 }
1216 }
1217 else {
1218 // Change in speed due to user interaction with the BPM widget
1219 // or corresponding MIDI or OSC events.
1220 if ( pAudioEngine->getNextBpm() != fBpm ) {
1221#if AUDIO_ENGINE_DEBUG
1222 AE_DEBUGLOG( QString( "BPM changed via Widget, OSC, or MIDI from [%1] to [%2]." )
1223 .arg( fBpm ).arg( pAudioEngine->getNextBpm() ) );
1224 // We do not return AudioEngine::m_fNextBpm since it is considered
1225 // transient until applied in updateBpmAndTickSize(). It's not the
1226 // current tempo.
1227#endif
1228 }
1229 }
1230 return fBpm;
1231}
1232
1234{
1235 Hydrogen* pHydrogen = Hydrogen::get_instance();
1236 std::shared_ptr<Song> pSong = pHydrogen->getSong();
1237 if ( pSong == nullptr ) {
1238 return;
1239 }
1240
1241#ifdef H2CORE_HAVE_LADSPA
1242 for ( unsigned nFX = 0; nFX < MAX_FX; ++nFX ) {
1244 if ( pFX == nullptr ) {
1245 return;
1246 }
1247
1248 pFX->deactivate();
1249
1250 pFX->connectAudioPorts( pFX->m_pBuffer_L, pFX->m_pBuffer_R,
1251 pFX->m_pBuffer_L, pFX->m_pBuffer_R );
1252 pFX->activate();
1253 }
1254#endif
1255}
1256
1257void AudioEngine::raiseError( unsigned nErrorCode )
1258{
1259 m_pEventQueue->push_event( EVENT_ERROR, nErrorCode );
1260}
1261
1263
1264 const auto pHydrogen = Hydrogen::get_instance();
1265 const auto pSong = pHydrogen->getSong();
1266
1267 if ( pSong != nullptr && pHydrogen->isPatternEditorLocked() ) {
1268
1269 // Default value is used to deselect the current pattern in
1270 // case none was found.
1271 int nPatternNumber = -1;
1272
1273 const int nColumn = std::max( m_pTransportPosition->getColumn(), 0 );
1274 if ( nColumn < (*pSong->getPatternGroupVector()).size() ) {
1275
1276 const auto pPatternList = pSong->getPatternList();
1277 if ( pPatternList != nullptr ) {
1278
1279 const auto pColumn = ( *pSong->getPatternGroupVector() )[ nColumn ];
1280
1281 int nIndex;
1282 for ( const auto& pattern : *pColumn ) {
1283 nIndex = pPatternList->index( pattern );
1284
1285 if ( nIndex > nPatternNumber ) {
1286 nPatternNumber = nIndex;
1287 }
1288 }
1289 }
1290 }
1291
1292 pHydrogen->setSelectedPatternNumber( nPatternNumber, true, true );
1293 }
1294}
1295
1297 const auto pSong = Hydrogen::get_instance()->getSong();
1298 if ( pSong == nullptr ) {
1299 AE_ERRORLOG( "no song set" );
1300 return;
1301 }
1302
1303 m_fSongSizeInTicks = pSong->lengthInTicks();
1304 reset( true );
1305 setNextBpm( pSong->getBpm() );
1306}
1307
1308void AudioEngine::processPlayNotes( unsigned long nframes )
1309{
1310 Hydrogen* pHydrogen = Hydrogen::get_instance();
1311 std::shared_ptr<Song> pSong = pHydrogen->getSong();
1312 if ( pSong == nullptr ) {
1313 return;
1314 }
1315
1316 long long nFrame;
1317 if ( getState() == State::Playing || getState() == State::Testing ) {
1318 // Current transport position.
1319 nFrame = m_pTransportPosition->getFrame();
1320 } else {
1321 // In case the playback is stopped we pretend it is still
1322 // rolling using the realtime ticks while disregarding tempo
1323 // changes in the Timeline. This is important as we want to
1324 // continue playing back notes in the sampler and process
1325 // realtime events, by e.g. MIDI or Hydrogen's virtual
1326 // keyboard.
1327 nFrame = getRealtimeFrame();
1328 }
1329
1330 while ( !m_songNoteQueue.empty() ) {
1331 Note *pNote = m_songNoteQueue.top();
1332 const long long nNoteStartInFrames = pNote->getNoteStart();
1333
1334#if AUDIO_ENGINE_DEBUG
1335 AE_DEBUGLOG( QString( "m_pTransportPosition->getDoubleTick(): %1, m_pTransportPosition->getFrame(): %2, nframes: %3, " )
1336 .arg( m_pTransportPosition->getDoubleTick() )
1337 .arg( m_pTransportPosition->getFrame() )
1338 .arg( nframes )
1339 .append( pNote->toQString( "", true ) ) );
1340#endif
1341
1342 if ( nNoteStartInFrames < nFrame + static_cast<long long>(nframes) ) {
1343
1344 float fNoteProbability = pNote->get_probability();
1345 if ( fNoteProbability != 1. ) {
1346 // Current note is skipped with a certain probability.
1347 if ( fNoteProbability < (float) rand() / (float) RAND_MAX ) {
1348 m_songNoteQueue.pop();
1349 pNote->get_instrument()->dequeue();
1350 delete pNote;
1351 continue;
1352 }
1353 }
1354
1355 /*
1356 * Check if the current instrument has the property "Stop-Note" set.
1357 * If yes, a NoteOff note is generated automatically after each note.
1358 */
1359 auto pNoteInstrument = pNote->get_instrument();
1360 if ( pNoteInstrument->is_stop_notes() ){
1361 Note *pOffNote = new Note( pNoteInstrument );
1362 pOffNote->set_note_off( true );
1363 m_pSampler->noteOn( pOffNote );
1364 delete pOffNote;
1365 }
1366
1367 if ( ! pNote->get_instrument()->hasSamples() ) {
1368 m_songNoteQueue.pop();
1369 pNote->get_instrument()->dequeue();
1370 continue;
1371 }
1372
1373 if ( pNoteInstrument == m_pMetronomeInstrument ) {
1374 m_pEventQueue->push_event( EVENT_METRONOME,
1375 pNote->get_pitch() == 0 ? 1 : 0 );
1376 }
1377
1378 m_pSampler->noteOn( pNote );
1379 m_songNoteQueue.pop();
1380 pNote->get_instrument()->dequeue();
1381
1382 const int nInstrument = pSong->getInstrumentList()->index( pNote->get_instrument() );
1383 if( pNote->get_note_off() ){
1384 delete pNote;
1385 }
1386
1387 // Check whether the instrument could be found.
1388 if ( nInstrument != -1 ) {
1389 m_pEventQueue->push_event( EVENT_NOTEON, nInstrument );
1390 }
1391
1392 continue;
1393 } else {
1394 // this note will not be played
1395 break;
1396 }
1397 }
1398}
1399
1401{
1402 // delete all copied notes in the note queues
1403 while ( !m_songNoteQueue.empty() ) {
1404 m_songNoteQueue.top()->get_instrument()->dequeue();
1405 delete m_songNoteQueue.top();
1406 m_songNoteQueue.pop();
1407 }
1408
1409 for ( unsigned i = 0; i < m_midiNoteQueue.size(); ++i ) {
1410 delete m_midiNoteQueue[i];
1411 }
1412 m_midiNoteQueue.clear();
1413}
1414
1415int AudioEngine::audioEngine_process( uint32_t nframes, void* /*arg*/ )
1416{
1418 // For the JACK driver it is very important (#1867) to not do anything while
1419 // the JACK client is stopped/closed. Otherwise it will segfault on mutex
1420 // locking or message logging.
1421 if ( pAudioEngine->m_pAudioDriver == nullptr ||
1422 ( ! ( pAudioEngine->getState() == AudioEngine::State::Ready ||
1423 pAudioEngine->getState() == AudioEngine::State::Playing ) &&
1424 dynamic_cast<JackAudioDriver*>(pAudioEngine->m_pAudioDriver) != nullptr ) ) {
1425 return 0;
1426 }
1427 timeval startTimeval = currentTime2();
1428 const auto sDrivers = pAudioEngine->getDriverNames();
1429
1430 pAudioEngine->clearAudioBuffers( nframes );
1431
1432 // Calculate maximum time to wait for audio engine lock. Using the
1433 // last calculated processing time as an estimate of the expected
1434 // processing time for this frame.
1435 float sampleRate = static_cast<float>(pAudioEngine->m_pAudioDriver->getSampleRate());
1436 pAudioEngine->m_fMaxProcessTime = 1000.0 / ( sampleRate / nframes );
1437 float fSlackTime = pAudioEngine->m_fMaxProcessTime - pAudioEngine->m_fProcessTime;
1438
1439 // If we expect to take longer than the available time to process,
1440 // require immediate locking or not at all: we're bound to drop a
1441 // buffer anyway.
1442 if ( fSlackTime < 0.0 ) {
1443 fSlackTime = 0.0;
1444 }
1445
1446 /*
1447 * The "try_lock" was introduced for Bug #164 (Deadlock after during
1448 * alsa driver shutdown). The try_lock *should* only fail in rare circumstances
1449 * (like shutting down drivers). In such cases, it seems to be ok to interrupt
1450 * audio processing. Returning the special return value "2" enables the disk
1451 * writer driver to repeat the processing of the current data.
1452 */
1453 if ( !pAudioEngine->tryLockFor( std::chrono::microseconds( (int)(1000.0*fSlackTime) ),
1454 RIGHT_HERE ) ) {
1455 ___ERRORLOG( QString( "[%1] Failed to lock audioEngine in allowed %2 ms, missed buffer" )
1456 .arg( sDrivers ).arg( fSlackTime ) );
1457
1458 if ( dynamic_cast<DiskWriterDriver*>(pAudioEngine->m_pAudioDriver) != nullptr ) {
1459 return 2; // inform the caller that we could not acquire the lock
1460 }
1461
1462 return 0;
1463 }
1464
1465 // Now that the engine is locked we properly check its state.
1466 if ( ! ( pAudioEngine->getState() == AudioEngine::State::Ready ||
1467 pAudioEngine->getState() == AudioEngine::State::Playing ) ) {
1468 pAudioEngine->unlock();
1469 return 0;
1470 }
1471
1472 Hydrogen* pHydrogen = Hydrogen::get_instance();
1473
1474 // Sync transport with server (in case the current audio driver is
1475 // designed that way)
1476#ifdef H2CORE_HAVE_JACK
1477 if ( Hydrogen::get_instance()->hasJackTransport() ) {
1478 auto pAudioDriver = pHydrogen->getAudioOutput();
1479 if ( pAudioDriver == nullptr ) {
1480 ___ERRORLOG( QString( "[%1] AudioDriver is not ready!" )
1481 .arg( sDrivers ) );
1482 assert( pAudioDriver );
1483 return 1;
1484 }
1485 static_cast<JackAudioDriver*>( pAudioDriver )->updateTransportPosition();
1486 }
1487#endif
1488
1489 // Check whether the tempo was changed.
1490 pAudioEngine->updateBpmAndTickSize( pAudioEngine->m_pTransportPosition );
1491 pAudioEngine->updateBpmAndTickSize( pAudioEngine->m_pQueuingPosition );
1492
1493 // Update the state of the audio engine depending on whether it
1494 // was started or stopped by the user.
1495 if ( pAudioEngine->getNextState() == State::Playing ) {
1496 if ( pAudioEngine->getState() == State::Ready ) {
1497 pAudioEngine->startPlayback();
1498 }
1499
1500 pAudioEngine->setRealtimeFrame( pAudioEngine->m_pTransportPosition->getFrame() );
1501 } else {
1502 if ( pAudioEngine->getState() == State::Playing ) {
1503 pAudioEngine->stopPlayback();
1504 }
1505
1506 // go ahead and increment the realtimeframes by nFrames
1507 // to support our realtime keyboard and midi event timing
1508 pAudioEngine->setRealtimeFrame( pAudioEngine->getRealtimeFrame() +
1509 static_cast<long long>(nframes) );
1510 }
1511
1512 // always update note queue.. could come from pattern or realtime input
1513 // (midi, keyboard)
1514 pAudioEngine->updateNoteQueue( nframes );
1515
1516 pAudioEngine->processAudio( nframes );
1517
1518 if ( pAudioEngine->getState() == AudioEngine::State::Playing ) {
1519
1520 // Check whether the end of the song has been reached.
1521 if ( pAudioEngine->isEndOfSongReached(
1522 pAudioEngine->m_pTransportPosition ) ) {
1523
1524 ___INFOLOG( QString( "[%1] End of song received" ).arg( sDrivers ) );
1525
1526 if ( pHydrogen->getMidiOutput() != nullptr ) {
1527 pHydrogen->getMidiOutput()->handleQueueAllNoteOff();
1528 }
1529
1530 pAudioEngine->stop();
1531 pAudioEngine->stopPlayback();
1532 pAudioEngine->locate( 0 );
1533
1534 // Tell GUI to move the playhead position to the beginning of
1535 // the song again since it only updates it in case transport
1536 // is rolling.
1538
1539 if ( dynamic_cast<FakeDriver*>(pAudioEngine->m_pAudioDriver) !=
1540 nullptr ) {
1541 ___INFOLOG( QString( "[%1] End of song." ).arg( sDrivers ) );
1542
1543 // TODO This part of the code might not be reached
1544 // anymore.
1545 pAudioEngine->unlock();
1546 return 1; // kill the audio AudioDriver thread
1547 }
1548 }
1549 else {
1550 // We are not at the end of the song, keep rolling.
1551 pAudioEngine->incrementTransportPosition( nframes );
1552 }
1553 }
1554
1555 timeval finishTimeval = currentTime2();
1556 pAudioEngine->m_fProcessTime =
1557 ( finishTimeval.tv_sec - startTimeval.tv_sec ) * 1000.0
1558 + ( finishTimeval.tv_usec - startTimeval.tv_usec ) / 1000.0;
1559
1560#ifdef CONFIG_DEBUG
1561 if ( pAudioEngine->m_fProcessTime > pAudioEngine->m_fMaxProcessTime ) {
1562 ___WARNINGLOG( "" );
1563 ___WARNINGLOG( "----XRUN----" );
1564 ___WARNINGLOG( QString( "[%1] XRUN of %2 msec (%3 > %4)" )
1565 .arg( sDrivers )
1566 .arg( ( pAudioEngine->m_fProcessTime - pAudioEngine->m_fMaxProcessTime ) )
1567 .arg( pAudioEngine->m_fProcessTime )
1568 .arg( pAudioEngine->m_fMaxProcessTime ) );
1569 ___WARNINGLOG( QString( "Ladspa process time = %1" ).arg( fLadspaTime ) );
1570 ___WARNINGLOG( "------------" );
1571 ___WARNINGLOG( "" );
1572
1574 }
1575#endif
1576
1577 pAudioEngine->unlock();
1578
1579 return 0;
1580}
1581
1582void AudioEngine::processAudio( uint32_t nFrames ) {
1583
1584 auto pSong = Hydrogen::get_instance()->getSong();
1585 if ( pSong == nullptr ) {
1586 return;
1587 }
1588
1589 processPlayNotes( nFrames );
1590
1591 float *pBuffer_L = m_pAudioDriver->getOut_L(),
1592 *pBuffer_R = m_pAudioDriver->getOut_R();
1593 assert( pBuffer_L != nullptr && pBuffer_R != nullptr );
1594
1595 getSampler()->process( nFrames );
1596 float* out_L = getSampler()->m_pMainOut_L;
1597 float* out_R = getSampler()->m_pMainOut_R;
1598 for ( unsigned i = 0; i < nFrames; ++i ) {
1599 pBuffer_L[ i ] += out_L[ i ];
1600 pBuffer_R[ i ] += out_R[ i ];
1601 }
1602
1603 getSynth()->process( nFrames );
1604 out_L = getSynth()->m_pOut_L;
1605 out_R = getSynth()->m_pOut_R;
1606 for ( unsigned i = 0; i < nFrames; ++i ) {
1607 pBuffer_L[ i ] += out_L[ i ];
1608 pBuffer_R[ i ] += out_R[ i ];
1609 }
1610
1611 timeval ladspaTime_start = currentTime2();
1612
1613#ifdef H2CORE_HAVE_LADSPA
1614 for ( unsigned nFX = 0; nFX < MAX_FX; ++nFX ) {
1616 if ( ( pFX ) && ( pFX->isEnabled() ) ) {
1617 pFX->processFX( nFrames );
1618
1619 float *buf_L, *buf_R;
1620 if ( pFX->getPluginType() == LadspaFX::STEREO_FX ) {
1621 buf_L = pFX->m_pBuffer_L;
1622 buf_R = pFX->m_pBuffer_R;
1623 } else { // MONO FX
1624 buf_L = pFX->m_pBuffer_L;
1625 buf_R = buf_L;
1626 }
1627
1628 for ( unsigned i = 0; i < nFrames; ++i ) {
1629 pBuffer_L[ i ] += buf_L[ i ];
1630 pBuffer_R[ i ] += buf_R[ i ];
1631 if ( buf_L[ i ] > m_fFXPeak_L[nFX] ) {
1632 m_fFXPeak_L[nFX] = buf_L[ i ];
1633 }
1634
1635 if ( buf_R[ i ] > m_fFXPeak_R[nFX] ) {
1636 m_fFXPeak_R[nFX] = buf_R[ i ];
1637 }
1638 }
1639 }
1640 }
1641#endif
1642 timeval ladspaTime_end = currentTime2();
1644 ( ladspaTime_end.tv_sec - ladspaTime_start.tv_sec ) * 1000.0
1645 + ( ladspaTime_end.tv_usec - ladspaTime_start.tv_usec ) / 1000.0;
1646
1647 float val_L, val_R;
1648 for ( unsigned i = 0; i < nFrames; ++i ) {
1649 val_L = pBuffer_L[i];
1650 val_R = pBuffer_R[i];
1651
1652 if ( val_L > m_fMasterPeak_L ) {
1653 m_fMasterPeak_L = val_L;
1654 }
1655
1656 if ( val_R > m_fMasterPeak_R ) {
1657 m_fMasterPeak_R = val_R;
1658 }
1659 }
1660
1661 for ( auto component : *pSong->getComponents() ) {
1662 DrumkitComponent *pComponent = component.get();
1663 for ( unsigned i = 0; i < nFrames; ++i ) {
1664 float compo_val_L = pComponent->get_out_L(i);
1665 float compo_val_R = pComponent->get_out_R(i);
1666
1667 if( compo_val_L > pComponent->get_peak_l() ) {
1668 pComponent->set_peak_l( compo_val_L );
1669 }
1670 if( compo_val_R > pComponent->get_peak_r() ) {
1671 pComponent->set_peak_r( compo_val_R );
1672 }
1673 }
1674 }
1675
1676}
1677
1679 m_state = state;
1680 EventQueue::get_instance()->push_event( EVENT_STATE, static_cast<int>(state) );
1681}
1682
1683void AudioEngine::setNextBpm( float fNextBpm ) {
1684 if ( fNextBpm > MAX_BPM ) {
1686 AE_WARNINGLOG( QString( "Provided bpm %1 is too high. Assigning upper bound %2 instead" )
1687 .arg( fNextBpm ).arg( MAX_BPM ) );
1688 }
1689 else if ( fNextBpm < MIN_BPM ) {
1691 AE_WARNINGLOG( QString( "Provided bpm %1 is too low. Assigning lower bound %2 instead" )
1692 .arg( fNextBpm ).arg( MIN_BPM ) );
1693 }
1694
1695 m_fNextBpm = fNextBpm;
1696}
1697
1698void AudioEngine::setSong( std::shared_ptr<Song> pNewSong )
1699{
1700 auto pHydrogen = Hydrogen::get_instance();
1701
1702 AE_INFOLOG( QString( "Set song: %1" )
1703 .arg( pNewSong != nullptr ? pNewSong->getName() : "nullptr" ) );
1704
1705 if ( getState() != State::Prepared ) {
1706 AE_ERRORLOG( QString( "Error the audio engine is not in State::Prepared but [%1]" )
1707 .arg( static_cast<int>( getState() ) ) );
1708 }
1709
1710 if ( m_pAudioDriver != nullptr ) {
1711 setupLadspaFX();
1712 }
1713
1714 float fNextBpm;
1715 if ( pNewSong != nullptr ) {
1716 fNextBpm = pNewSong->getBpm();
1717 m_fSongSizeInTicks = static_cast<double>( pNewSong->lengthInTicks() );
1718 }
1719 else {
1720 fNextBpm = MIN_BPM;
1722 }
1723 // Reset (among other things) the transport position. This causes
1724 // the locate() call below to update the playing patterns.
1725 reset( false );
1726 setNextBpm( fNextBpm );
1727
1728 pHydrogen->renameJackPorts( pNewSong );
1729
1731 // Will also adapt the audio engine to the new song's BPM.
1732 locate( 0 );
1733
1734 if ( pNewSong != nullptr ) {
1735 pHydrogen->setTimeline( pNewSong->getTimeline() );
1736 pHydrogen->getTimeline()->activate();
1737 }
1738 else {
1739 pHydrogen->setTimeline( nullptr );
1740 }
1741
1743}
1744
1746{
1747 if ( getState() == State::Playing ) {
1748 stop();
1749 this->stopPlayback();
1750 }
1751
1752 if ( getState() != State::Ready ) {
1753 AE_ERRORLOG( QString( "Error the audio engine is not in State::Ready but [%1]" )
1754 .arg( static_cast<int>( getState() ) ) );
1755 return;
1756 }
1757
1758 m_pSampler->stopPlayingNotes();
1759 reset();
1761
1763}
1764
1766
1767 auto pHydrogen = Hydrogen::get_instance();
1768 auto pSong = pHydrogen->getSong();
1769
1770 if ( pSong == nullptr ) {
1771 AE_ERRORLOG( "No song set yet" );
1772 return;
1773 }
1774
1775 auto updatePatternSize = []( std::shared_ptr<TransportPosition> pPos ) {
1776 if ( pPos->getPlayingPatterns()->size() > 0 ) {
1777 // No virtual pattern resolution in here
1778 pPos->setPatternSize( pPos->getPlayingPatterns()->longest_pattern_length( false ) );
1779 } else {
1780 pPos->setPatternSize( MAX_NOTES );
1781 }
1782 };
1783 updatePatternSize( m_pTransportPosition );
1784 updatePatternSize( m_pQueuingPosition );
1785
1786 if ( pHydrogen->getMode() == Song::Mode::Pattern ) {
1787 AE_INFOLOG( QString( "[Pattern] Size update: [%1] -> [%2]" )
1788 .arg( m_fSongSizeInTicks )
1789 .arg( static_cast<double>( pSong->lengthInTicks() ) ) );
1790 m_fSongSizeInTicks = static_cast<double>( pSong->lengthInTicks() );
1791
1793 return;
1794 }
1795
1796 // Expected behavior:
1797 // - changing any part of the song except of the pattern currently
1798 // playing shouldn't affect transport position
1799 // - the current transport position is defined as current column +
1800 // current pattern tick position
1801 // - there shouldn't be a difference in behavior whether the song
1802 // was already looped or not
1803 const double fNewSongSizeInTicks = static_cast<double>( pSong->lengthInTicks() );
1804
1805 // Indicates that the song contains no patterns (before or after
1806 // song size did change).
1807 const bool bEmptySong =
1808 m_fSongSizeInTicks == 0 || fNewSongSizeInTicks == 0;
1809
1810 double fNewStrippedTick, fRepetitions;
1811 if ( m_fSongSizeInTicks != 0 ) {
1812 // Strip away all repetitions when in loop mode but keep their
1813 // number. nPatternStartTick and nColumn are only defined
1814 // between 0 and fSongSizeInTicks.
1815 fNewStrippedTick = std::fmod( m_pTransportPosition->getDoubleTick(),
1817 fRepetitions =
1818 std::floor( m_pTransportPosition->getDoubleTick() / m_fSongSizeInTicks );
1819 }
1820 else {
1821 // No patterns in song prior to song size change.
1822 fNewStrippedTick = m_pTransportPosition->getDoubleTick();
1823 fRepetitions = 0;
1824 }
1825
1826 const int nOldColumn = m_pTransportPosition->getColumn();
1827
1828#if AUDIO_ENGINE_DEBUG
1829 AE_DEBUGLOG( QString( "[Before] fNewStrippedTick: %1, fRepetitions: %2, m_fSongSizeInTicks: %3, fNewSongSizeInTicks: %4, transport: %5, queuing: %6" )
1830 .arg( fNewStrippedTick, 0, 'f' )
1831 .arg( fRepetitions )
1832 .arg( m_fSongSizeInTicks )
1833 .arg( fNewSongSizeInTicks )
1834 .arg( m_pTransportPosition->toQString( "", true ) )
1835 .arg( m_pQueuingPosition->toQString( "", true ) )
1836 );
1837#endif
1838
1839 AE_INFOLOG( QString( "[Song] Size update: [%1] -> [%2]" )
1840 .arg( m_fSongSizeInTicks ).arg( fNewSongSizeInTicks ) );
1841
1842 m_fSongSizeInTicks = fNewSongSizeInTicks;
1843
1844 auto endOfSongReached = [&](){
1845 if ( getState() == State::Playing ) {
1846 stop();
1847 stopPlayback();
1848 }
1849 locate( 0 );
1850
1851#if AUDIO_ENGINE_DEBUG
1852 AE_DEBUGLOG( QString( "[End of song reached] fNewStrippedTick: %1, fRepetitions: %2, m_fSongSizeInTicks: %3, fNewSongSizeInTicks: %4, transport: %5, queuing: %6" )
1853 .arg( fNewStrippedTick, 0, 'f' )
1854 .arg( fRepetitions )
1855 .arg( m_fSongSizeInTicks )
1856 .arg( fNewSongSizeInTicks )
1857 .arg( m_pTransportPosition->toQString( "", true ) )
1858 .arg( m_pQueuingPosition->toQString( "", true ) )
1859 );
1860#endif
1861
1863 };
1864
1865 if ( nOldColumn >= pSong->getPatternGroupVector()->size() &&
1866 pSong->getLoopMode() != Song::LoopMode::Enabled ) {
1867 // Old column exceeds the new song size.
1868 endOfSongReached();
1869 return;
1870 }
1871
1872
1873 const long nNewPatternStartTick = pHydrogen->getTickForColumn( nOldColumn );
1874
1875 if ( nNewPatternStartTick == -1 &&
1876 pSong->getLoopMode() != Song::LoopMode::Enabled ) {
1877 // Failsave in case old column exceeds the new song size.
1878 endOfSongReached();
1879 return;
1880 }
1881
1882 if ( nNewPatternStartTick != m_pTransportPosition->getPatternStartTick() &&
1883 ! bEmptySong ) {
1884 // A pattern prior to the current position was toggled,
1885 // enlarged, or shrunk. We need to compensate this in order to
1886 // keep the current pattern tick position constant.
1887
1888#if AUDIO_ENGINE_DEBUG
1889 AE_DEBUGLOG( QString( "[nPatternStartTick mismatch] old: %1, new: %2" )
1890 .arg( m_pTransportPosition->getPatternStartTick() )
1891 .arg( nNewPatternStartTick ) );
1892#endif
1893
1894 fNewStrippedTick +=
1895 static_cast<double>(nNewPatternStartTick -
1896 m_pTransportPosition->getPatternStartTick());
1897 }
1898
1899#ifdef H2CORE_HAVE_DEBUG
1900 const long nNewPatternTickPosition =
1901 static_cast<long>(std::floor( fNewStrippedTick )) - nNewPatternStartTick;
1902 if ( nNewPatternTickPosition != m_pTransportPosition->getPatternTickPosition() &&
1903 ! bEmptySong ) {
1904 AE_ERRORLOG( QString( "[nPatternTickPosition mismatch] old: %1, new: %2" )
1905 .arg( m_pTransportPosition->getPatternTickPosition() )
1906 .arg( nNewPatternTickPosition ) );
1907 }
1908#endif
1909
1910 // Incorporate the looped transport again
1911 const double fNewTick = fNewStrippedTick + fRepetitions * fNewSongSizeInTicks;
1912 const long long nNewFrame = TransportPosition::computeFrameFromTick(
1913 fNewTick, &m_pTransportPosition->m_fTickMismatch );
1914
1915 double fTickOffset = fNewTick - m_pTransportPosition->getDoubleTick();
1916
1917 // The tick interval end covered in updateNoteQueue() is stored as
1918 // double and needs to be more precise (hence updated before
1919 // rounding).
1920 m_fLastTickEnd += fTickOffset;
1921
1922 // Small rounding noise introduced in the calculation does spoil
1923 // things as we floor the resulting tick offset later on. Hence,
1924 // we round it to a specific precision.
1925 fTickOffset *= 1e8;
1926 fTickOffset = std::round( fTickOffset );
1927 fTickOffset *= 1e-8;
1928 m_pTransportPosition->setTickOffsetSongSize( fTickOffset );
1929
1930 // Moves all notes currently processed by Hydrogen with respect to
1931 // the offsets calculated above.
1933
1934 m_pTransportPosition->setFrameOffsetTempo(
1935 nNewFrame - m_pTransportPosition->getFrame() +
1936 m_pTransportPosition->getFrameOffsetTempo() );
1937
1938#if AUDIO_ENGINE_DEBUG
1939 AE_DEBUGLOG(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")
1940 .arg( nNewFrame )
1941 .arg( m_pTransportPosition->getFrame() )
1942 .arg( m_pTransportPosition->getFrameOffsetTempo() )
1943 .arg( fNewTick, 0, 'g', 30 )
1944 .arg( m_pTransportPosition->getDoubleTick(), 0, 'g', 30 )
1945 .arg( m_pTransportPosition->getTickOffsetSongSize(), 0, 'g', 30 )
1946 .arg( fNewTick - m_pTransportPosition->getDoubleTick(), 0, 'g', 30 )
1947 .arg( fNewSongSizeInTicks, 0, 'g', 30 )
1948 .arg( fRepetitions, 0, 'f' )
1949 .arg( fNewStrippedTick, 0, 'f' )
1950 .arg( nNewPatternStartTick )
1951 );
1952#endif
1953
1954 const auto fOldTickSize = m_pTransportPosition->getTickSize();
1955 updateTransportPosition( fNewTick, nNewFrame, m_pTransportPosition );
1956
1957 // Ensure the tick offset is calculated as well (we do not expect
1958 // the tempo to change hence the following call is most likely not
1959 // executed during updateTransportPosition()).
1960#if defined(WIN32) and !defined(WIN64)
1961 // For some reason two identical numbers (according to their
1962 // values when printing them) are not equal to each other in 32bit
1963 // Windows. Course graining the tick change in here will do no
1964 // harm except of for preventing tiny tempo changes. Integer value
1965 // changes should not be affected.
1966 if ( std::abs( m_pTransportPosition->getTickSize() - fOldTickSize ) < 1e-2 ) {
1967#else
1968 if ( fOldTickSize == m_pTransportPosition->getTickSize() ) {
1969#endif
1971 }
1972
1973 // Updating the queuing position by the same offset to keep them
1974 // approximately in sync.
1975 const double fNewTickQueuing = m_pQueuingPosition->getDoubleTick() +
1976 fTickOffset;
1977 const long long nNewFrameQueuing = TransportPosition::computeFrameFromTick(
1978 fNewTickQueuing, &m_pQueuingPosition->m_fTickMismatch );
1979 // Use offsets calculated above.
1981 updateTransportPosition( fNewTickQueuing, nNewFrameQueuing,
1983
1985
1986#ifdef H2CORE_HAVE_DEBUG
1987 if ( nOldColumn != m_pTransportPosition->getColumn() && ! bEmptySong &&
1988 nOldColumn != -1 && m_pTransportPosition->getColumn() != -1 ) {
1989 AE_ERRORLOG( QString( "[nColumn mismatch] old: %1, new: %2" )
1990 .arg( nOldColumn )
1991 .arg( m_pTransportPosition->getColumn() ) );
1992 }
1993#endif
1994
1995 if ( m_pQueuingPosition->getColumn() == -1 &&
1996 pSong->getLoopMode() != Song::LoopMode::Enabled ) {
1997 endOfSongReached();
1998 return;
1999 }
2000
2001#if AUDIO_ENGINE_DEBUG
2002 AE_DEBUGLOG( QString( "[After] fNewTick: %1, fRepetitions: %2, m_fSongSizeInTicks: %3, fNewSongSizeInTicks: %4, transport: %5, queuing: %6" )
2003 .arg( fNewTick, 0, 'g', 30 )
2004 .arg( fRepetitions, 0, 'f' )
2005 .arg( m_fSongSizeInTicks )
2006 .arg( fNewSongSizeInTicks )
2007 .arg( m_pTransportPosition->toQString( "", true ) )
2008 .arg( m_pQueuingPosition->toQString( "", true ) )
2009 );
2010#endif
2011
2013}
2014
2016 auto removePattern = [&]( std::shared_ptr<TransportPosition> pPos ) {
2017 auto pPlayingPatterns = pPos->getPlayingPatterns();
2018
2019 for ( int ii = 0; ii < pPlayingPatterns->size(); ++ii ) {
2020 if ( pPlayingPatterns->get( ii ) == pPattern ) {
2021 pPlayingPatterns->del( ii );
2022 break;
2023 }
2024 }
2025 };
2026
2027 removePattern( m_pTransportPosition );
2028 removePattern( m_pQueuingPosition );
2029}
2030
2035
2036void AudioEngine::updatePlayingPatternsPos( std::shared_ptr<TransportPosition> pPos ) {
2037 auto pHydrogen = Hydrogen::get_instance();
2038 auto pSong = pHydrogen->getSong();
2039 auto pPlayingPatterns = pPos->getPlayingPatterns();
2040
2041#if AUDIO_ENGINE_DEBUG
2042 AE_DEBUGLOG( QString( "pre: %1" ).arg( pPos->toQString() ) );
2043#endif
2044
2045 if ( pSong == nullptr ) {
2046 pPlayingPatterns->clear();
2047 pPos->setPatternSize( MAX_NOTES );
2048 return;
2049 }
2050
2051 if ( pHydrogen->getMode() == Song::Mode::Song ) {
2052
2053 const auto nPrevPatternNumber = pPlayingPatterns->size();
2054
2055 pPlayingPatterns->clear();
2056
2057 if ( pSong->getPatternGroupVector()->size() == 0 ) {
2058 // No patterns in current song.
2059
2060 pPos->setPatternSize( MAX_NOTES );
2061
2062 if ( pPos == m_pTransportPosition && nPrevPatternNumber > 0 ) {
2064 }
2065 return;
2066 }
2067
2068 auto nColumn = std::max( pPos->getColumn(), 0 );
2069 if ( nColumn >= pSong->getPatternGroupVector()->size() ) {
2070 AE_ERRORLOG( QString( "Provided column [%1] exceeds allowed range [0,%2]. Using 0 as fallback." )
2071 .arg( nColumn )
2072 .arg( pSong->getPatternGroupVector()->size() - 1 ) );
2073 nColumn = 0;
2074 }
2075
2076 for ( const auto& ppattern : *( *( pSong->getPatternGroupVector() ) )[ nColumn ] ) {
2077 if ( ppattern != nullptr ) {
2078 pPlayingPatterns->add( ppattern, true );
2079 }
2080 }
2081
2082 // GUI does not care about the internals of the audio engine
2083 // and just moves along the transport position.
2084 // We omit the event when passing from one empty column to the
2085 // next.
2086 if ( pPos == m_pTransportPosition &&
2087 ( nPrevPatternNumber != 0 || pPlayingPatterns->size() != 0 ) ) {
2089 }
2090 }
2091 else if ( pHydrogen->getPatternMode() == Song::PatternMode::Selected ) {
2092
2093 auto pSelectedPattern =
2094 pSong->getPatternList()->get( pHydrogen->getSelectedPatternNumber() );
2095
2096 if ( pSelectedPattern != nullptr &&
2097 ! ( pPlayingPatterns->size() == 1 &&
2098 pPlayingPatterns->get( 0 ) == pSelectedPattern ) ) {
2099 pPlayingPatterns->clear();
2100 pPlayingPatterns->add( pSelectedPattern, true );
2101
2102 // GUI does not care about the internals of the audio
2103 // engine and just moves along the transport position.
2104 if ( pPos == m_pTransportPosition ) {
2106 }
2107 }
2108 }
2109 else if ( pHydrogen->getPatternMode() == Song::PatternMode::Stacked ) {
2110
2111 auto pNextPatterns = pPos->getNextPatterns();
2112
2113 if ( pNextPatterns->size() > 0 ) {
2114 for ( const auto& ppattern : *pNextPatterns ) {
2115 if ( ppattern == nullptr ) {
2116 continue;
2117 }
2118
2119 if ( ( pPlayingPatterns->del( ppattern ) ) == nullptr ) {
2120 // pPattern was not present yet. It will
2121 // be added
2122 pPlayingPatterns->add( ppattern, true );
2123 } else {
2124 // pPattern was already present. It will
2125 // be deleted.
2126 ppattern->removeFlattenedVirtualPatterns( pPlayingPatterns );
2127 }
2128
2129 // GUI does not care about the internals of the audio
2130 // engine and just moves along the transport position.
2131 if ( pPos == m_pTransportPosition ) {
2133 }
2134 }
2135 pNextPatterns->clear();
2136 }
2137 }
2138
2139 if ( pPlayingPatterns->size() > 0 ) {
2140 // No virtual pattern resolution in here
2141 pPos->setPatternSize( pPlayingPatterns->longest_pattern_length( false ) );
2142 } else {
2143 pPos->setPatternSize( MAX_NOTES );
2144 }
2145
2146#if AUDIO_ENGINE_DEBUG
2147 AE_DEBUGLOG( QString( "post: %1" ).arg( pPos->toQString() ) );
2148#endif
2149
2150}
2151
2152void AudioEngine::toggleNextPattern( int nPatternNumber ) {
2153 auto pHydrogen = Hydrogen::get_instance();
2154 auto pSong = pHydrogen->getSong();
2155 if ( pSong == nullptr ) {
2156 return;
2157 }
2158 auto pPatternList = pSong->getPatternList();
2159 auto pPattern = pPatternList->get( nPatternNumber );
2160 if ( pPattern == nullptr ) {
2161 return;
2162 }
2163
2164 if ( m_pTransportPosition->getNextPatterns()->del( pPattern ) == nullptr ) {
2165 m_pTransportPosition->getNextPatterns()->add( pPattern );
2166 }
2167 if ( m_pQueuingPosition->getNextPatterns()->del( pPattern ) == nullptr ) {
2168 m_pQueuingPosition->getNextPatterns()->add( pPattern );
2169 }
2170}
2171
2173 m_pTransportPosition->getNextPatterns()->clear();
2174 m_pQueuingPosition->getNextPatterns()->clear();
2175}
2176
2177void AudioEngine::flushAndAddNextPattern( int nPatternNumber ) {
2178 auto pHydrogen = Hydrogen::get_instance();
2179 auto pSong = pHydrogen->getSong();
2180 if ( pSong == nullptr ) {
2181 return;
2182 }
2183 auto pPatternList = pSong->getPatternList();
2184
2185 bool bAlreadyPlaying = false;
2186
2187 // Note: we will not perform a bound check on the provided pattern
2188 // number. This way the user can use the SELECT_ONLY_NEXT_PATTERN
2189 // MIDI or OSC command to flush all playing patterns.
2190 auto pRequestedPattern = pPatternList->get( nPatternNumber );
2191
2192 auto flushAndAddNext = [&]( std::shared_ptr<TransportPosition> pPos ) {
2193
2194 auto pNextPatterns = pPos->getNextPatterns();
2195 auto pPlayingPatterns = pPos->getPlayingPatterns();
2196
2197 pNextPatterns->clear();
2198 for ( int ii = 0; ii < pPlayingPatterns->size(); ++ii ) {
2199
2200 auto pPlayingPattern = pPlayingPatterns->get( ii );
2201 if ( pPlayingPattern != pRequestedPattern ) {
2202 pNextPatterns->add( pPlayingPattern );
2203 }
2204 else if ( pRequestedPattern != nullptr ) {
2205 bAlreadyPlaying = true;
2206 }
2207 }
2208
2209 // Appending the requested pattern.
2210 if ( ! bAlreadyPlaying && pRequestedPattern != nullptr ) {
2211 pNextPatterns->add( pRequestedPattern );
2212 }
2213 };
2214
2215 flushAndAddNext( m_pTransportPosition );
2216 flushAndAddNext( m_pQueuingPosition );
2217}
2218
2220
2221 if ( Hydrogen::get_instance()->getPatternMode() == Song::PatternMode::Stacked ) {
2222 auto copyPlayingPatterns = [&]( std::shared_ptr<TransportPosition> pPos ) {
2223 auto pPlayingPatterns = pPos->getPlayingPatterns();
2224 auto pNextPatterns = pPos->getNextPatterns();
2225
2226 for ( const auto& ppPattern : *pPlayingPatterns ) {
2227 pNextPatterns->add( ppPattern );
2228 }
2229 };
2230 copyPlayingPatterns( m_pTransportPosition );
2231 copyPlayingPatterns( m_pQueuingPosition );
2232 }
2233
2234 m_pTransportPosition->getPlayingPatterns()->clear();
2235 m_pQueuingPosition->getPlayingPatterns()->clear();
2236
2239}
2240
2242
2243#if AUDIO_ENGINE_DEBUG
2244 AE_DEBUGLOG( QString( "before:\n%1\n%2" )
2245 .arg( m_pTransportPosition->toQString() )
2246 .arg( m_pQueuingPosition->toQString() ) );
2247#endif
2248
2249 const auto fOldTickSize = m_pTransportPosition->getTickSize();
2252
2253#if defined(WIN32) and !defined(WIN64)
2254 // For some reason two identical numbers (according to their
2255 // values when printing them) are not equal to each other in 32bit
2256 // Windows. Course graining the tick change in here will do no
2257 // harm except of for preventing tiny tempo changes. Integer value
2258 // changes should not be affected.
2259 if ( std::abs( m_pTransportPosition->getTickSize() - fOldTickSize ) < 1e-2 ) {
2260#else
2261 if ( fOldTickSize == m_pTransportPosition->getTickSize() ) {
2262#endif
2263 // As tempo did not change during the Timeline activation, no
2264 // update of the offsets took place. This, however, is not
2265 // good, as it makes a significant difference to be located at
2266 // tick X with e.g. 120 bpm tempo and at X with a 120 bpm
2267 // tempo marker active but several others located prior to X.
2269 }
2270
2271#if AUDIO_ENGINE_DEBUG
2272 AE_DEBUGLOG( QString( "after:\n%1\n%2" )
2273 .arg( m_pTransportPosition->toQString() )
2274 .arg( m_pQueuingPosition->toQString() ) );
2275#endif
2276}
2277
2279 if ( m_songNoteQueue.size() != 0 ) {
2280
2281 std::vector<Note*> notes;
2282 for ( ; ! m_songNoteQueue.empty(); m_songNoteQueue.pop() ) {
2283 notes.push_back( m_songNoteQueue.top() );
2284 }
2285
2286 if ( notes.size() > 0 ) {
2287 for ( auto nnote : notes ) {
2288 nnote->computeNoteStart();
2289 m_songNoteQueue.push( nnote );
2290 }
2291 }
2292
2293 notes.clear();
2294 while ( m_midiNoteQueue.size() > 0 ) {
2295 notes.push_back( m_midiNoteQueue[ 0 ] );
2296 m_midiNoteQueue.pop_front();
2297 }
2298
2299 if ( notes.size() > 0 ) {
2300 for ( auto nnote : notes ) {
2301 nnote->computeNoteStart();
2302 m_midiNoteQueue.push_back( nnote );
2303 }
2304 }
2305 }
2306
2308}
2309
2311 if ( m_songNoteQueue.size() != 0 ) {
2312
2313 std::vector<Note*> notes;
2314 for ( ; ! m_songNoteQueue.empty(); m_songNoteQueue.pop() ) {
2315 notes.push_back( m_songNoteQueue.top() );
2316 }
2317
2318 const long nTickOffset =
2319 static_cast<long>(std::floor(m_pTransportPosition->getTickOffsetSongSize()));
2320
2321 if ( notes.size() > 0 ) {
2322 for ( auto nnote : notes ) {
2323
2324#if AUDIO_ENGINE_DEBUG
2325 AE_DEBUGLOG( QString( "[song queue] name: %1, pos: %2 -> %3, tick offset: %4, tick offset floored: %5" )
2326 .arg( nnote->get_instrument()->get_name() )
2327 .arg( nnote->get_position() )
2328 .arg( std::max( nnote->get_position() + nTickOffset,
2329 static_cast<long>(0) ) )
2330 .arg( m_pTransportPosition->getTickOffsetSongSize(), 0, 'f' )
2331 .arg( nTickOffset ) );
2332#endif
2333
2334 nnote->set_position( std::max( nnote->get_position() + nTickOffset,
2335 static_cast<long>(0) ) );
2336 nnote->computeNoteStart();
2337 m_songNoteQueue.push( nnote );
2338 }
2339 }
2340
2341 notes.clear();
2342 while ( m_midiNoteQueue.size() > 0 ) {
2343 notes.push_back( m_midiNoteQueue[ 0 ] );
2344 m_midiNoteQueue.pop_front();
2345 }
2346
2347 if ( notes.size() > 0 ) {
2348 for ( auto nnote : notes ) {
2349
2350#if AUDIO_ENGINE_DEBUG
2351 AE_DEBUGLOG( QString( "[midi queue] name: %1, pos: %2 -> %3, tick offset: %4, tick offset floored: %5" )
2352 .arg( nnote->get_instrument()->get_name() )
2353 .arg( nnote->get_position() )
2354 .arg( std::max( nnote->get_position() + nTickOffset,
2355 static_cast<long>(0) ) )
2356 .arg( m_pTransportPosition->getTickOffsetSongSize(), 0, 'f' )
2357 .arg( nTickOffset ) );
2358#endif
2359
2360 nnote->set_position( std::max( nnote->get_position() + nTickOffset,
2361 static_cast<long>(0) ) );
2362 nnote->computeNoteStart();
2363 m_midiNoteQueue.push_back( nnote );
2364 }
2365 }
2366 }
2367
2369}
2370
2371long long AudioEngine::computeTickInterval( double* fTickStart, double* fTickEnd, unsigned nIntervalLengthInFrames ) {
2372
2373 const auto pHydrogen = Hydrogen::get_instance();
2374 auto pPos = m_pTransportPosition;
2375
2376 long long nFrameStart, nFrameEnd;
2377
2378 if ( getState() == State::Ready ) {
2379 // In case the playback is stopped we pretend it is still
2380 // rolling using the realtime ticks while disregarding tempo
2381 // changes in the Timeline. This is important as we want to
2382 // continue playing back notes in the sampler and process
2383 // realtime events, by e.g. MIDI or Hydrogen's virtual
2384 // keyboard.
2385 nFrameStart = getRealtimeFrame();
2386 } else {
2387 // Enters here when either transport is rolling or the unit
2388 // tests are run.
2389 nFrameStart = pPos->getFrame();
2390 }
2391
2392 long long nLeadLagFactor = getLeadLagInFrames( pPos->getDoubleTick() );
2393
2394 // Timeline disabled:
2395 // Due to rounding errors in tick<->frame conversions the leadlag
2396 // factor in frames can differ by +/-1 even if the corresponding
2397 // lead lag in ticks is exactly the same.
2398 //
2399 // Timeline enabled:
2400 // With Tempo markers being present the lookahead is not constant
2401 // anymore. As it determines the position X frames and Y ticks
2402 // into the future, imagine it being process cycle after cycle
2403 // moved across a marker. The amount of frames covered by the
2404 // first and the second tick size will always change and so does
2405 // the resulting lookahead.
2406 //
2407 // This, however, would result in holes and overlaps in tick
2408 // coverage for the queuing position and note enqueuing in
2409 // updateNoteQueue(). That's why we stick to a single lead lag
2410 // factor invalidated each time the tempo of the song does change.
2411 if ( pPos->getLastLeadLagFactor() != 0 ) {
2412 if ( pPos->getLastLeadLagFactor() != nLeadLagFactor ) {
2413 nLeadLagFactor = pPos->getLastLeadLagFactor();
2414 }
2415 } else {
2416 pPos->setLastLeadLagFactor( nLeadLagFactor );
2417 }
2418
2419 const long long nLookahead = nLeadLagFactor +
2421
2422 nFrameEnd = nFrameStart + nLookahead +
2423 static_cast<long long>(nIntervalLengthInFrames);
2424
2425 // Checking whether transport and queuing position are identical
2426 // is not enough in here. For specific audio driver parameters and
2427 // very tiny buffersizes used by drivers with dynamic buffer sizes
2428 // they both can be identical.
2429 if ( m_bLookaheadApplied ) {
2430 nFrameStart += nLookahead;
2431 }
2432
2433 *fTickStart = ( TransportPosition::computeTickFromFrame( nFrameStart ) +
2434 pPos->getTickMismatch() ) - pPos->getTickOffsetQueuing() ;
2435 *fTickEnd = TransportPosition::computeTickFromFrame( nFrameEnd ) -
2436 pPos->getTickOffsetQueuing();
2437
2438#if AUDIO_ENGINE_DEBUG
2439 AE_DEBUGLOG( 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" )
2440 .arg( nFrameStart )
2441 .arg( nFrameEnd )
2442 .arg( *fTickStart, 0, 'f' )
2443 .arg( *fTickEnd, 0, 'f' )
2444 .arg( TransportPosition::computeTickFromFrame( nFrameStart ), 0, 'f' )
2445 .arg( TransportPosition::computeTickFromFrame( nFrameEnd ), 0, 'f' )
2446 .arg( pPos->getTickOffsetQueuing(), 0, 'f' )
2447 .arg( nLookahead )
2448 .arg( nIntervalLengthInFrames )
2449 .arg( pPos->toQString() )
2450 .arg( m_pQueuingPosition->toQString() )
2451 .arg( m_bLookaheadApplied )
2452 );
2453#endif
2454
2455 return nLeadLagFactor;
2456}
2457
2458 // Ideally we just floor the provided tick. When relocating to
2459 // a specific tick, it's converted counterpart is stored as the
2460 // transport position in frames, which is then used to calculate
2461 // the tick start again. These conversions back and forth can
2462 // introduce rounding error that get larger for larger tick
2463 // numbers and could result in a computed start tick of
2464 // 86753.999999934 when transport was relocated to 86754. As we do
2465 // not want to cover notes prior to our current transport
2466 // position, we have to account for such rounding errors.
2467double AudioEngine::coarseGrainTick( double fTick ) {
2468 if ( std::ceil( fTick ) - fTick > 0 &&
2469 std::ceil( fTick ) - fTick < 1E-6 ) {
2470 return std::floor( fTick ) + 1;
2471 }
2472 else {
2473 return std::floor( fTick );
2474 }
2475 }
2476
2477void AudioEngine::updateNoteQueue( unsigned nIntervalLengthInFrames )
2478{
2479 Hydrogen* pHydrogen = Hydrogen::get_instance();
2480 std::shared_ptr<Song> pSong = pHydrogen->getSong();
2481 if ( pSong == nullptr ) {
2482 return;
2483 }
2484
2485 double fTickStartComp, fTickEndComp;
2486
2487 long long nLeadLagFactor =
2488 computeTickInterval( &fTickStartComp, &fTickEndComp, nIntervalLengthInFrames );
2489
2490 // MIDI events get put into the `m_songNoteQueue` as well.
2491 while ( m_midiNoteQueue.size() > 0 ) {
2492 Note *pNote = m_midiNoteQueue[0];
2493 if ( pNote->get_position() >
2494 static_cast<int>(coarseGrainTick( fTickEndComp )) ) {
2495 break;
2496 }
2497
2498 m_midiNoteQueue.pop_front();
2499 pNote->get_instrument()->enqueue();
2500 pNote->computeNoteStart();
2501 pNote->humanize();
2502 m_songNoteQueue.push( pNote );
2503 }
2504
2505 if ( getState() != State::Playing && getState() != State::Testing ) {
2506 return;
2507 }
2508
2509 AutomationPath* pAutomationPath = pSong->getVelocityAutomationPath();
2510
2511 // computeTickInterval() is always called regardless whether
2512 // transport is rolling or not. But we only mark the lookahead
2513 // consumed if the associated tick interval was actually traversed
2514 // by the queuing position.
2515 if ( ! m_bLookaheadApplied ) {
2516 m_bLookaheadApplied = true;
2517 }
2518
2519 const long nTickStart = static_cast<long>(coarseGrainTick( fTickStartComp ));
2520 const long nTickEnd = static_cast<long>(coarseGrainTick( fTickEndComp ));
2521
2522 // Only store the last tick interval end if transport is
2523 // rolling. Else the realtime frame processing will mess things
2524 // up.
2525 m_fLastTickEnd = fTickEndComp;
2526
2527#if AUDIO_ENGINE_DEBUG
2528 AE_DEBUGLOG( QString( "tick interval (floor): [%1,%2], tick interval (computed): [%3,%4], nLeadLagFactor: %5, m_fSongSizeInTicks: %6, m_pTransportPosition: %7, m_pQueuingPosition: %8")
2529 .arg( nTickStart ).arg( nTickEnd )
2530 .arg( fTickStartComp, 0, 'f' ).arg( fTickEndComp, 0, 'f' )
2531 .arg( nLeadLagFactor )
2532 .arg( m_fSongSizeInTicks, 0, 'f' )
2533 .arg( m_pTransportPosition->toQString() )
2534 .arg( m_pQueuingPosition->toQString() ) );
2535#endif
2536
2537 // We loop over integer ticks to ensure that all notes encountered
2538 // between two iterations belong to the same pattern.
2539 for ( long nnTick = nTickStart; nnTick < nTickEnd; ++nnTick ) {
2540
2542 // Update queuing position and playing patterns.
2543 if ( pHydrogen->getMode() == Song::Mode::Song ) {
2544
2545 const long nPreviousPosition = m_pQueuingPosition->getPatternStartTick() +
2546 m_pQueuingPosition->getPatternTickPosition();
2547
2548 const long long nNewFrame = TransportPosition::computeFrameFromTick(
2549 static_cast<double>(nnTick),
2550 &m_pQueuingPosition->m_fTickMismatch );
2551 updateSongTransportPosition( static_cast<double>(nnTick),
2552 nNewFrame, m_pQueuingPosition );
2553
2555 // Queueing reached end of the song.
2556 return;
2557 }
2558 }
2559 else if ( pHydrogen->getMode() == Song::Mode::Pattern ) {
2560
2561 const long long nNewFrame = TransportPosition::computeFrameFromTick(
2562 static_cast<double>(nnTick),
2563 &m_pQueuingPosition->m_fTickMismatch );
2564 updatePatternTransportPosition( static_cast<double>(nnTick),
2565 nNewFrame, m_pQueuingPosition );
2566 }
2567
2569 // Metronome
2570 // Only trigger the metronome at a predefined rate.
2571 int nMetronomeTickPosition;
2572 if ( pSong->getPatternGroupVector()->size() == 0 ) {
2573 nMetronomeTickPosition = nnTick;
2574 } else {
2575 nMetronomeTickPosition = m_pQueuingPosition->getPatternTickPosition();
2576 }
2577
2578 if ( nMetronomeTickPosition % 48 == 0 ) {
2579 float fPitch;
2580 float fVelocity;
2581
2582 // Depending on whether the metronome beat will be issued
2583 // at the beginning or in the remainder of the pattern,
2584 // two different sounds and events will be used.
2585 if ( nMetronomeTickPosition == 0 ) {
2586 fPitch = 3;
2587 fVelocity = 1.0;
2588 } else {
2589 fPitch = 0;
2590 fVelocity = 0.8;
2591 }
2592
2593 // Only trigger the sounds if the user enabled the
2594 // metronome.
2595 if ( Preferences::get_instance()->m_bUseMetronome ) {
2596 Note *pMetronomeNote = new Note( m_pMetronomeInstrument,
2597 nnTick,
2598 fVelocity,
2599 0.f, // pan
2600 -1,
2601 fPitch );
2602 m_pMetronomeInstrument->enqueue();
2603 pMetronomeNote->computeNoteStart();
2604 m_songNoteQueue.push( pMetronomeNote );
2605 }
2606 }
2607
2608 if ( pHydrogen->getMode() == Song::Mode::Song &&
2609 pSong->getPatternGroupVector()->size() == 0 ) {
2610 // No patterns in song. We let transport roll in case
2611 // patterns will be added again and still use metronome.
2612 if ( Preferences::get_instance()->m_bUseMetronome ) {
2613 continue;
2614 } else {
2615 return;
2616 }
2617 }
2619 // Update the notes queue.
2620 //
2621 // Supporting ticks with float precision:
2622 // - make FOREACH_NOTE_CST_IT_BOUND loop over all notes
2623 // `(_it)->first >= (_bound) && (_it)->first < (_bound + 1)`
2624 // - add remainder of pNote->get_position() % 1 when setting
2625 // nnTick as new position.
2626 //
2627 const auto pPlayingPatterns = m_pQueuingPosition->getPlayingPatterns();
2628 if ( pPlayingPatterns->size() != 0 ) {
2629 for ( auto nPat = 0; nPat < pPlayingPatterns->size(); ++nPat ) {
2630 Pattern *pPattern = pPlayingPatterns->get( nPat );
2631 assert( pPattern != nullptr );
2632 Pattern::notes_t* notes = (Pattern::notes_t*)pPattern->get_notes();
2633
2634 // Loop over all notes at tick nPatternTickPosition
2635 // (associated tick is determined by Note::__position
2636 // at the time of insertion into the Pattern).
2638 m_pQueuingPosition->getPatternTickPosition(),
2639 pPattern ) {
2640 Note *pNote = it->second;
2641 if ( pNote != nullptr && pNote->get_instrument_id() != -1 &&
2642 pNote->get_instrument() != nullptr &&
2643 pNote->get_instrument()->get_id() != -1 ) {
2644
2645 pNote->set_just_recorded( false );
2646
2647 Note *pCopiedNote = new Note( pNote );
2648
2649 // Lead or Lag.
2650 // This property is set within the
2651 // NotePropertiesRuler and only applies to
2652 // notes picked up from patterns within
2653 // Hydrogen during transport.
2654 pCopiedNote->set_humanize_delay(
2655 pCopiedNote->get_humanize_delay() +
2656 static_cast<int>(
2657 static_cast<float>(pNote->get_lead_lag()) *
2658 static_cast<float>(nLeadLagFactor) ));
2659
2660 pCopiedNote->set_position( nnTick );
2661 pCopiedNote->humanize();
2662
2670 if ( ( ( m_pQueuingPosition->getPatternTickPosition() %
2671 ( MAX_NOTES / 16 ) ) == 0 ) &&
2672 ( ( m_pQueuingPosition->getPatternTickPosition() %
2673 ( MAX_NOTES / 8 ) ) != 0 ) ) {
2674 pCopiedNote->swing();
2675 }
2676
2677 // This must be done _after_ setting the
2678 // position, humanization, and swing.
2679 pCopiedNote->computeNoteStart();
2680
2681 if ( pHydrogen->getMode() == Song::Mode::Song ) {
2682 // Onset of the current column + the percentage by
2683 // which the current column was already passed. For
2684 // large patterns the cursor will move slower and
2685 // for shorter faster within a column.
2686 const float fPos =
2687 static_cast<float>( m_pQueuingPosition->getColumn() ) +
2688 ( ( pCopiedNote->get_position() -
2689 m_pQueuingPosition->getPatternStartTick() ) %
2690 m_pQueuingPosition->getPatternSize() ) /
2691 static_cast<float>(m_pQueuingPosition->getPatternSize());
2692
2693 pCopiedNote->set_velocity( pCopiedNote->get_velocity() *
2694 pAutomationPath->get_value( fPos ) );
2695 }
2696
2697 // Ensure the custom length of the note does not exceed
2698 // the length of the current pattern.
2699 if ( pCopiedNote->get_length() != -1 ) {
2700 pCopiedNote->set_length(
2701 std::min(
2702 static_cast<long>(pCopiedNote->get_length()),
2703 static_cast<long>(pPattern->get_length()) -
2704 m_pQueuingPosition->getPatternTickPosition() ) );
2705 }
2706
2707#if AUDIO_ENGINE_DEBUG
2708 AE_DEBUGLOG( QString( "m_pQueuingPosition: %1, new note: %2" )
2709 .arg( m_pQueuingPosition->toQString() )
2710 .arg( pCopiedNote->toQString() ) );
2711#endif
2712
2713 pCopiedNote->get_instrument()->enqueue();
2714 m_songNoteQueue.push( pCopiedNote );
2715 }
2716 }
2717 }
2718 }
2719 }
2720
2721 return;
2722}
2723
2725{
2726 if ( ! ( getState() == State::Playing ||
2727 getState() == State::Ready ||
2728 getState() == State::Testing ) ) {
2729 AE_ERRORLOG( QString( "Error the audio engine is not in State::Ready, State::Playing, or State::Testing but [%1]" )
2730 .arg( static_cast<int>( getState() ) ) );
2731 delete note;
2732 return;
2733 }
2734
2735 m_midiNoteQueue.push_back( note );
2736}
2737
2739 return pNote1->getNoteStart() > pNote2->getNoteStart();
2740}
2741
2743
2744 assert( m_pAudioDriver );
2745
2746#ifdef H2CORE_HAVE_JACK
2747 if ( Hydrogen::get_instance()->hasJackTransport() ) {
2748 // Tell all other JACK clients to start as well and wait for
2749 // the JACK server to give the signal.
2750 static_cast<JackAudioDriver*>( m_pAudioDriver )->startTransport();
2751 return;
2752 }
2753#endif
2754
2756
2757 if ( dynamic_cast<FakeDriver*>(m_pAudioDriver) != nullptr ) {
2758 static_cast<FakeDriver*>( m_pAudioDriver )->processCallback();
2759 }
2760}
2761
2763 assert( m_pAudioDriver );
2764
2765#ifdef H2CORE_HAVE_JACK
2766 if ( Hydrogen::get_instance()->hasJackTransport() ) {
2767
2768#if AUDIO_ENGINE_DEBUG
2769 AE_DEBUGLOG( "Stopping engine via JACK server" );
2770#endif
2771
2772 // Tell all other JACK clients to stop as well and wait for
2773 // the JACK server to give the signal.
2774 static_cast<JackAudioDriver*>( m_pAudioDriver )->stopTransport();
2775 return;
2776 }
2777#endif
2778
2779#if AUDIO_ENGINE_DEBUG
2780 AE_DEBUGLOG( "Mark audio engine to be stop" );
2781#endif
2782
2784}
2785
2787 return 5;
2788}
2789
2790long long AudioEngine::getLeadLagInFrames( double fTick ) {
2791 double fTmp;
2792 const long long nFrameStart =
2794 const long long nFrameEnd =
2797 &fTmp );
2798
2799#if AUDIO_ENGINE_DEBUG
2800 AE_DEBUGLOG( QString( "nFrameStart: %1, nFrameEnd: %2, diff: %3, fTick: %4" )
2801 .arg( nFrameStart ).arg( nFrameEnd )
2802 .arg( nFrameEnd - nFrameStart ).arg( fTick, 0, 'f' ) );
2803#endif
2804
2805 return nFrameEnd - nFrameStart;
2806}
2807
2809 if ( m_pTransportPosition != nullptr ) {
2810 return m_pTransportPosition->getPlayingPatterns();
2811 }
2812 return nullptr;
2813}
2814
2816 if ( m_pTransportPosition != nullptr ) {
2817 return m_pTransportPosition->getNextPatterns();
2818 }
2819 return nullptr;
2820}
2821
2822QString AudioEngine::StateToQString( const State& state ) {
2823 switch( state ) {
2825 return "Uninitialized";
2826 case State::Initialized:
2827 return "Initialized";
2828 case State::Prepared:
2829 return "Prepared";
2830 case State::Ready:
2831 return "Ready";
2832 case State::Playing:
2833 return "Playing";
2834 case State::Testing:
2835 return "Testing";
2836 default:
2837 return "Unknown state";
2838 }
2839}
2840
2841QString AudioEngine::toQString( const QString& sPrefix, bool bShort ) const {
2842 QString s = Base::sPrintIndention;
2843
2844 QString sOutput;
2845 if ( ! bShort ) {
2846 sOutput = QString( "%1[AudioEngine]\n" ).arg( sPrefix )
2847 .append( "%1%2m_pTransportPosition:\n").arg( sPrefix ).arg( s );
2848 if ( m_pTransportPosition != nullptr ) {
2849 sOutput.append( QString( "%1" )
2850 .arg( m_pTransportPosition->toQString( sPrefix + s, bShort ) ) );
2851 } else {
2852 sOutput.append( QString( "nullptr\n" ) );
2853 }
2854 sOutput.append( QString( "%1%2m_pQueuingPosition:\n").arg( sPrefix ).arg( s ) );
2855 if ( m_pQueuingPosition != nullptr ) {
2856 sOutput.append( QString( "%1" )
2857 .arg( m_pQueuingPosition->toQString( sPrefix + s, bShort ) ) );
2858 } else {
2859 sOutput.append( QString( "nullptr\n" ) );
2860 }
2861 sOutput.append( QString( "%1%2m_fNextBpm: %3\n" ).arg( sPrefix ).arg( s ).arg( m_fNextBpm, 0, 'f' ) )
2862 .append( QString( "%1%2m_state: %3\n" ).arg( sPrefix ).arg( s ).arg( static_cast<int>(m_state) ) )
2863 .append( QString( "%1%2m_nextState: %3\n" ).arg( sPrefix ).arg( s ).arg( static_cast<int>(m_nextState) ) )
2864 .append( QString( "%1%2m_fSongSizeInTicks: %3\n" ).arg( sPrefix ).arg( s ).arg( m_fSongSizeInTicks, 0, 'f' ) )
2865 .append( QString( "%1%2m_fLastTickEnd: %3\n" ).arg( sPrefix ).arg( s ).arg( m_fLastTickEnd, 0, 'f' ) )
2866 .append( QString( "%1%2m_bLookaheadApplied: %3\n" ).arg( sPrefix ).arg( s ).arg( m_bLookaheadApplied ) )
2867 .append( QString( "%1%2m_pSampler: stringification not implemented\n" ).arg( sPrefix ).arg( s ) )
2868 .append( QString( "%1%2m_pSynth: stringification not implemented\n" ).arg( sPrefix ).arg( s ) )
2869 .append( QString( "%1%2m_pAudioDriver: stringification not implemented\n" ).arg( sPrefix ).arg( s ) )
2870 .append( QString( "%1%2m_pMidiDriver: stringification not implemented\n" ).arg( sPrefix ).arg( s ) )
2871 .append( QString( "%1%2m_pMidiDriverOut: stringification not implemented\n" ).arg( sPrefix ).arg( s ) )
2872 .append( QString( "%1%2m_pEventQueue: stringification not implemented\n" ).arg( sPrefix ).arg( s ) );
2873#ifdef H2CORE_HAVE_LADSPA
2874 sOutput.append( QString( "%1%2m_fFXPeak_L: [" ).arg( sPrefix ).arg( s ) );
2875 for ( auto ii : m_fFXPeak_L ) {
2876 sOutput.append( QString( " %1" ).arg( ii ) );
2877 }
2878 sOutput.append( QString( "]\n%1%2m_fFXPeak_R: [" ).arg( sPrefix ).arg( s ) );
2879 for ( auto ii : m_fFXPeak_R ) {
2880 sOutput.append( QString( " %1" ).arg( ii ) );
2881 }
2882 sOutput.append( QString( " ]\n" ) );
2883#endif
2884 sOutput.append( QString( "%1%2m_fMasterPeak_L: %3\n" ).arg( sPrefix ).arg( s ).arg( m_fMasterPeak_L ) )
2885 .append( QString( "%1%2m_fMasterPeak_R: %3\n" ).arg( sPrefix ).arg( s ).arg( m_fMasterPeak_R ) )
2886 .append( QString( "%1%2m_fProcessTime: %3\n" ).arg( sPrefix ).arg( s ).arg( m_fProcessTime ) )
2887 .append( QString( "%1%2m_fMaxProcessTime: %3\n" ).arg( sPrefix ).arg( s ).arg( m_fMaxProcessTime ) )
2888 .append( QString( "%1%2m_fLadspaTime: %3\n" ).arg( sPrefix ).arg( s ).arg( m_fLadspaTime ) )
2889 .append( QString( "%1%2m_nRealtimeFrame: %3\n" ).arg( sPrefix ).arg( s ).arg( m_nRealtimeFrame ) )
2890 .append( QString( "%1%2m_AudioProcessCallback: stringification not implemented\n" ).arg( sPrefix ).arg( s ) )
2891 .append( QString( "%1%2m_songNoteQueue: length = %3\n" ).arg( sPrefix ).arg( s ).arg( m_songNoteQueue.size() ) );
2892 sOutput.append( QString( "%1%2m_midiNoteQueue: [\n" ).arg( sPrefix ).arg( s ) );
2893 for ( const auto& nn : m_midiNoteQueue ) {
2894 sOutput.append( nn->toQString( sPrefix + s, bShort ) );
2895 }
2896 sOutput.append( QString( "]\n%1%2m_pMetronomeInstrument: %3\n" ).arg( sPrefix ).arg( s ).arg( m_pMetronomeInstrument->toQString( sPrefix + s, bShort ) ) )
2897 .append( QString( "%1%2nMaxTimeHumanize: %3\n" ).arg( sPrefix ).arg( s ).arg( AudioEngine::nMaxTimeHumanize ) )
2898 .append( QString( "%1%2fHumanizeVelocitySD: %3\n" ).arg( sPrefix ).arg( s ).arg( AudioEngine::fHumanizeVelocitySD ) )
2899 .append( QString( "%1%2fHumanizePitchSD: %3\n" ).arg( sPrefix ).arg( s ).arg( AudioEngine::fHumanizePitchSD ) )
2900 .append( QString( "%1%2fHumanizeTimingSD: %3\n" ).arg( sPrefix ).arg( s ).arg( AudioEngine::fHumanizeTimingSD ) );
2901
2902 }
2903 else {
2904 sOutput = QString( "%1[AudioEngine]" ).arg( sPrefix )
2905 .append( ", m_pTransportPosition:\n");
2906 if ( m_pTransportPosition != nullptr ) {
2907 sOutput.append( QString( "%1" )
2908 .arg( m_pTransportPosition->toQString( sPrefix, bShort ) ) );
2909 } else {
2910 sOutput.append( QString( "nullptr\n" ) );
2911 }
2912 sOutput.append( ", m_pQueuingPosition:\n");
2913 if ( m_pQueuingPosition != nullptr ) {
2914 sOutput.append( QString( "%1" )
2915 .arg( m_pQueuingPosition->toQString( sPrefix, bShort ) ) );
2916 } else {
2917 sOutput.append( QString( "nullptr\n" ) );
2918 }
2919 sOutput.append( QString( ", m_fNextBpm: %1" ).arg( m_fNextBpm, 0, 'f' ) )
2920 .append( QString( ", m_state: %1" ).arg( static_cast<int>(m_state) ) )
2921 .append( QString( ", m_nextState: %1" ).arg( static_cast<int>(m_nextState) ) )
2922 .append( QString( ", m_fSongSizeInTicks: %1" ).arg( m_fSongSizeInTicks, 0, 'f' ) )
2923 .append( QString( ", m_fLastTickEnd: %1" ).arg( m_fLastTickEnd, 0, 'f' ) )
2924 .append( QString( ", m_bLookaheadApplied: %1" ).arg( m_bLookaheadApplied ) )
2925 .append( QString( ", m_pSampler: ..." ) )
2926 .append( QString( ", m_pSynth: ..." ) )
2927 .append( QString( ", m_pAudioDriver: ..." ) )
2928 .append( QString( ", m_pMidiDriver: ..." ) )
2929 .append( QString( ", m_pMidiDriverOut: ..." ) )
2930 .append( QString( ", m_pEventQueue: ..." ) );
2931#ifdef H2CORE_HAVE_LADSPA
2932 sOutput.append( QString( ", m_fFXPeak_L: [" ) );
2933 for ( auto ii : m_fFXPeak_L ) {
2934 sOutput.append( QString( " %1" ).arg( ii ) );
2935 }
2936 sOutput.append( QString( "], m_fFXPeak_R: [" ) );
2937 for ( auto ii : m_fFXPeak_R ) {
2938 sOutput.append( QString( " %1" ).arg( ii ) );
2939 }
2940 sOutput.append( QString( " ]" ) );
2941#endif
2942 sOutput.append( QString( ", m_fMasterPeak_L: %1" ).arg( m_fMasterPeak_L ) )
2943 .append( QString( ", m_fMasterPeak_R: %1" ).arg( m_fMasterPeak_R ) )
2944 .append( QString( ", m_fProcessTime: %1" ).arg( m_fProcessTime ) )
2945 .append( QString( ", m_fMaxProcessTime: %1" ).arg( m_fMaxProcessTime ) )
2946 .append( QString( ", m_fLadspaTime: %1" ).arg( m_fLadspaTime ) )
2947 .append( QString( ", m_nRealtimeFrame: %1" ).arg( m_nRealtimeFrame ) )
2948 .append( QString( ", m_AudioProcessCallback: ..." ) )
2949 .append( QString( ", m_songNoteQueue: length = %1" ).arg( m_songNoteQueue.size() ) );
2950 sOutput.append( QString( ", m_midiNoteQueue: [" ) );
2951 for ( const auto& nn : m_midiNoteQueue ) {
2952 sOutput.append( nn->toQString( sPrefix + s, bShort ) );
2953 }
2954 sOutput.append( QString( "], m_pMetronomeInstrument: id = %1" ).arg( m_pMetronomeInstrument->get_id() ) )
2955 .append( QString( ", nMaxTimeHumanize: id %1" ).arg( AudioEngine::nMaxTimeHumanize ) )
2956 .append( QString( ", fHumanizeVelocitySD: id %1" ).arg( AudioEngine::fHumanizeVelocitySD ) )
2957 .append( QString( ", fHumanizePitchSD: id %1" ).arg( AudioEngine::fHumanizePitchSD ) )
2958 .append( QString( ", fHumanizeTimingSD: id %1" ).arg( AudioEngine::fHumanizeTimingSD ) );
2959 }
2960
2961 return sOutput;
2962}
2963
2966 QString sMidiInputDriver( "unknown" );
2967 QString sMidiOutputDriver( "unknown" );
2968
2969 if ( m_pAudioDriver == nullptr ) {
2970 audioDriver = Preferences::AudioDriver::None;
2971 }
2972 else if ( dynamic_cast<JackAudioDriver*>(m_pAudioDriver) != nullptr ) {
2973 audioDriver = Preferences::AudioDriver::Jack;
2974 }
2975 else if ( dynamic_cast<PortAudioDriver*>(m_pAudioDriver) != nullptr ) {
2977 }
2978 else if ( dynamic_cast<CoreAudioDriver*>(m_pAudioDriver) != nullptr ) {
2980 }
2981 else if ( dynamic_cast<PulseAudioDriver*>(m_pAudioDriver) != nullptr ) {
2983 }
2984 else if ( dynamic_cast<OssDriver*>(m_pAudioDriver) != nullptr ) {
2985 audioDriver = Preferences::AudioDriver::Oss;
2986 }
2987 else if ( dynamic_cast<AlsaAudioDriver*>(m_pAudioDriver) != nullptr ) {
2988 audioDriver = Preferences::AudioDriver::Alsa;
2989 }
2990 else if ( dynamic_cast<FakeDriver*>(m_pAudioDriver) != nullptr ) {
2991 audioDriver = Preferences::AudioDriver::Fake;
2992 }
2993 else if ( dynamic_cast<NullDriver*>(m_pAudioDriver) != nullptr ) {
2994 audioDriver = Preferences::AudioDriver::Null;
2995 }
2996 else if ( dynamic_cast<DiskWriterDriver*>(m_pAudioDriver) != nullptr ) {
2997 audioDriver = Preferences::AudioDriver::Disk;
2998 }
2999
3000 if ( m_pMidiDriver == nullptr ) {
3001 sMidiInputDriver = "nullptr";
3002#ifdef H2CORE_HAVE_ALSA
3003 } else if ( dynamic_cast<AlsaMidiDriver*>(m_pMidiDriver) != nullptr ) {
3004 sMidiInputDriver = "ALSA";
3005#endif
3006#ifdef H2CORE_HAVE_PORTMIDI
3007 } else if ( dynamic_cast<PortMidiDriver*>(m_pMidiDriver) != nullptr ) {
3008 sMidiInputDriver = "PortMidi";
3009#endif
3010#ifdef H2CORE_HAVE_COREMIDI
3011 } else if ( dynamic_cast<CoreMidiDriver*>(m_pMidiDriver) != nullptr ) {
3012 sMidiInputDriver = "CoreMidi";
3013#endif
3014#ifdef H2CORE_HAVE_JACK
3015 } else if ( dynamic_cast<JackMidiDriver*>(m_pMidiDriver) != nullptr ) {
3016 sMidiInputDriver = "JACK";
3017#endif
3018 }
3019
3020 if ( m_pMidiDriverOut == nullptr ) {
3021 sMidiOutputDriver = "nullptr";
3022#ifdef H2CORE_HAVE_ALSA
3023 } else if ( dynamic_cast<AlsaMidiDriver*>(m_pMidiDriverOut) != nullptr ) {
3024 sMidiOutputDriver = "ALSA";
3025#endif
3026#ifdef H2CORE_HAVE_PORTMIDI
3027 } else if ( dynamic_cast<PortMidiDriver*>(m_pMidiDriverOut) != nullptr ) {
3028 sMidiOutputDriver = "PortMidi";
3029#endif
3030#ifdef H2CORE_HAVE_COREMIDI
3031 } else if ( dynamic_cast<CoreMidiDriver*>(m_pMidiDriverOut) != nullptr ) {
3032 sMidiOutputDriver = "CoreMidi";
3033#endif
3034#ifdef H2CORE_HAVE_JACK
3035 } else if ( dynamic_cast<JackMidiDriver*>(m_pMidiDriverOut) != nullptr ) {
3036 sMidiOutputDriver = "JACK";
3037#endif
3038 }
3039
3040 auto res = QString( "%1|" )
3041 .arg( Preferences::audioDriverToQString( audioDriver ) );
3042 if ( sMidiInputDriver == sMidiOutputDriver ) {
3043 res.append( QString( "%1" ).arg( sMidiInputDriver ) );
3044 } else {
3045 res.append( QString( "in: %1;out: %2" ).arg( sMidiInputDriver )
3046 .arg( sMidiOutputDriver ) );
3047 }
3048
3049 return std::move( res );
3050}
3051
3052
3053void AudioEngine::assertLocked( const QString& sClass, const char* sFunction,
3054 const QString& sMsg ) {
3055#ifndef NDEBUG
3056 if ( m_LockingThread != std::this_thread::get_id() ) {
3057 // Is there a more convenient way to convert the thread id to QSTring?
3058 std::stringstream tmpStream;
3059 tmpStream << std::this_thread::get_id();
3060 ERRORLOG( QString( "[thread id: %1] [%2::%3] %4" )
3061 .arg( QString::fromStdString( tmpStream.str() ) )
3062 .arg( sClass ).arg( sFunction ).arg( sMsg ) );
3063 __logger->flush();
3064 assert( false );
3065 }
3066#endif
3067}
3068
3070 const char* sFunction,
3071 const QString& sMsg ) const
3072{
3073#ifndef NDEBUG
3074 if ( m_bNeedsLock ) {
3076 assertLocked( sClass, sFunction, sMsg );
3077 }
3078#endif
3079}
3080
3081}; // namespace H2Core
#define AE_ERRORLOG(x)
#define AE_DEBUGLOG(x)
#define AE_WARNINGLOG(x)
#define AE_INFOLOG(x)
#define RIGHT_HERE
Macro intended to be used for the logging of the locking of the H2Core::AudioEngine.
Definition AudioEngine.h:61
#define METRONOME_INSTR_ID
Created Instrument will be used as metronome.
Definition Instrument.h:36
#define ___WARNINGLOG(x)
Definition Object.h:259
#define ERRORLOG(x)
Definition Object.h:242
#define ___INFOLOG(x)
Definition Object.h:258
#define ___ERRORLOG(x)
Definition Object.h:260
#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 QString &sClass, const char *sFunction, const QString &sMsg) const
Assert that the AudioEngine lock is held if needed.
int m_nLoopsDone
Indicates how many loops the transport already did when the user presses the Loop button again.
void makeTrackPorts(std::shared_ptr< Song > pSong)
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...
QString getDriverNames() const
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.
static QString StateToQString(const State &state)
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)
void updateTransportPosition(double fTick, long long nFrame, std::shared_ptr< TransportPosition > pPos)
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.
State getNextState() const
AudioOutput * createAudioDriver(const Preferences::AudioDriver &driver)
Create an audio driver using audioEngine_process() as its argument based on the provided choice and c...
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.
friend class JackAudioDriver
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.
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.
void assertLocked(const QString &sClass, const char *sFunction, const QString &sMsg)
Assert that the calling thread is the current holder of the AudioEngine lock.
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
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:156
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:123
Song::Mode getMode() const
static Hydrogen * get_instance()
Returns the current Hydrogen instance __instance.
Definition Hydrogen.h:84
MidiOutput * getMidiOutput() const
Definition Hydrogen.cpp:752
AudioEngine * getAudioEngine() const
Definition Hydrogen.h:663
@ UNKNOWN_DRIVER
The provided input in createDriver() does not match any of the choices for H2Core::Preferences::Audio...
Definition Hydrogen.h:247
@ ERROR_STARTING_DRIVER
Unable to connect the audio driver stored in H2Core::AudioEngine::m_pAudioDriver in audioEngine_start...
Definition Hydrogen.h:254
AudioOutput * getAudioOutput() const
Used to display audio driver info.
Definition Hydrogen.cpp:741
void clearPerTrackAudioBuffers(uint32_t nFrames)
Resets the buffers contained in m_pTrackOutputPortsL and m_pTrackOutputPortsR.
@ Listener
An external program is Timebase controller and Hydrogen will disregard all tempo markers on the Timel...
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
virtual void handleQueueAllNoteOff()=0
A note plays an associated instrument with a velocity left and right pan.
Definition Note.h:101
int get_position() const
__position accessor
Definition Note.h:534
void set_length(int value)
__length setter
Definition Note.h:554
void swing()
Add swing contribution to __humanize_delay.
Definition Note.cpp:460
void set_humanize_delay(int value)
__humanize_delay setter
Definition Note.cpp:157
int get_humanize_delay() const
__humanize_delay accessor
Definition Note.h:629
void set_position(int value)
__position setter
Definition Note.h:529
std::shared_ptr< Instrument > get_instrument()
__instrument accessor
Definition Note.h:499
float get_lead_lag() const
__lead_lag accessor
Definition Note.h:549
float get_pitch() const
__pitch accessor
Definition Note.h:569
void humanize()
Add random contributions to __pitch, __humanize_delay, and __velocity.
Definition Note.cpp:431
void set_just_recorded(bool value)
__just_recorded setter
Definition Note.h:599
int get_length() const
__length accessor
Definition Note.h:559
float get_velocity() const
__velocity accessor
Definition Note.h:539
bool get_note_off() const
__note_off accessor
Definition Note.h:579
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
QString toQString(const QString &sPrefix="", bool bShort=true) const override
Formatted string version for debugging purposes.
Definition Note.cpp:534
long long getNoteStart() const
Definition Note.h:746
void set_note_off(bool value)
__note_off setter
Definition Note.h:574
float get_probability() const
Definition Note.h:609
int get_instrument_id() const
__instrument_id accessor
Definition Note.h:514
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:79
static Preferences * get_instance()
Returns a pointer to the current Preferences singleton stored in __instance.
static std::vector< AudioDriver > getSupportedAudioDrivers()
static QString audioDriverToQString(const AudioDriver &driver)
QString m_sMidiDriver
MIDI driver.
AudioDriver m_audioDriver
Audio 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:106
void handleTimelineOrTempoChange()
Recalculates all note starts to make them valid again after a TempoMarker was added to or deleted fro...
Definition Sampler.cpp:421
float * m_pMainOut_L
sampler main out (left channel)
Definition Sampler.h:162
float * m_pMainOut_R
sampler main out (right channel)
Definition Sampler.h:163
void handleSongSizeChange()
Recalculates all note starts and positions to make them valid again after the song size changed,...
Definition Sampler.cpp:481
static constexpr int nDefaultResolution
Definition Song.h:135
@ Finishing
Transport is still in loop mode (frames and ticks larger than song size are allowed) but playback end...
Definition Song.h:94
@ Stacked
An arbitrary number of pattern can be played.
Definition Song.h:103
@ Selected
Only one pattern - the one currently selected in the GUI - will be played back.
Definition Song.h:106
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)