hydrogen 1.2.6
JackAudioDriver.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
24#if defined(H2CORE_HAVE_JACK) || _DOXYGEN_
25
26#include <sys/types.h>
27#include <unistd.h>
28#include <cstdlib>
29#include <cassert>
30#include <algorithm>
31#include <cmath>
32#include <jack/metadata.h>
33
34#include <core/Hydrogen.h>
41#include <core/Basics/Pattern.h>
44#include <core/Basics/Song.h>
45#include <core/Helpers/Files.h>
48#include <core/Globals.h>
49#include <core/EventQueue.h>
50
51#ifdef H2CORE_HAVE_LASH
53#endif
54
55#define JACK_DEBUG 0
56
57#define J_DEBUGLOG(x) if ( __logger->should_log( Logger::Debug ) ) { \
58 __logger->log( Logger::Debug, _class_name(), __FUNCTION__, \
59 QString( "%1" ).arg( x ), "\033[37m" ); }
60
61namespace H2Core {
62
63int JackAudioDriver::jackDriverSampleRate( jack_nframes_t nframes, void* param ){
64 // Used for logging.
65 Base * __object = ( Base * )param;
66 // The __INFOLOG macro uses the Base *__object and not the
67 // Object instance as INFOLOG does. It will call
68 // __object->logger()->log( H2Core::Logger::Info, ..., msg )
69 // (see object.h).
70 __INFOLOG( QString("New JACK sample rate: [%1]/sec")
71 .arg( QString::number( static_cast<int>(nframes) ) ) );
73 return 0;
74}
75
76int JackAudioDriver::jackDriverBufferSize( jack_nframes_t nframes, void* param ){
77 // This function does _NOT_ have to be realtime safe.
78 Base * __object = ( Base * )param;
79 __INFOLOG( QString("new JACK buffer size: [%1]")
80 .arg( QString::number( static_cast<int>(nframes) ) ) );
82 return 0;
83}
84
86{
87 UNUSED( arg );
88
89#if JACK_DEBUG
90 ___INFOLOG( "" );
91#endif
92
93 JackAudioDriver::pJackDriverInstance->m_pClient = nullptr;
95}
97 UNUSED( arg );
99
100#if JACK_DEBUG
101 ___INFOLOG( QString( "New XRun. [%1] in total" )
103#endif
104
105#ifdef HAVE_INTEGRATION_TESTS
106 // Xruns do mess up the current transport position and we might get the same
107 // frame two times in a row while the audio engine is already at a new
108 // position.
109 JackAudioDriver::m_nIntegrationLastRelocationFrame = -1;
110#endif
112 return 0;
113}
114
118#ifdef HAVE_INTEGRATION_TESTS
119long JackAudioDriver::m_nIntegrationLastRelocationFrame = -1;
120#endif
122
124 : AudioOutput()
125 , m_nTrackPortCount( 0 )
126 , m_pClient( nullptr )
127 , m_pOutputPort1( nullptr )
128 , m_pOutputPort2( nullptr )
131 , m_fLastTimebaseBpm( 120 )
134#ifdef HAVE_INTEGRATION_TESTS
135 , m_bIntegrationRelocationLoop( false )
136 , m_bIntegrationCheckRelocationLoop( false )
137#endif
138{
139 auto pPreferences = Preferences::get_instance();
140
141 m_bConnectDefaults = pPreferences->m_bJackConnectDefaults;
142
144 this->m_processCallback = m_processCallback;
145
146
147 // Destination ports the output of Hydrogen will be connected
148 // to.
149 m_sOutputPortName1 = pPreferences->m_sJackPortName1;
150 m_sOutputPortName2 = pPreferences->m_sJackPortName2;
151
152 memset( m_pTrackOutputPortsL, 0, sizeof(m_pTrackOutputPortsL) );
153 memset( m_pTrackOutputPortsR, 0, sizeof(m_pTrackOutputPortsR) );
154
155 m_JackTransportState = JackTransportStopped;
156}
157
162
164{
165 INFOLOG( "connect" );
166
167 // The `jack_activate' function is defined in the jack/jack.h
168 // header files and tells the JACK server that the program is
169 // ready to start processing audio. It returns 0 on success
170 // and a non-zero error code otherwise.
171 if ( jack_activate( m_pClient ) ) {
173 return 1;
174 }
175
176 bool bConnectDefaults = m_bConnectDefaults;
177
178#ifdef H2CORE_HAVE_LASH
179 if ( Preferences::get_instance()->useLash() ){
180 LashClient* lashClient = LashClient::get_instance();
181 if (lashClient && lashClient->isConnected()){
182 // INFOLOG( "[LASH] Sending JACK client name to LASH server" );
183 lashClient->sendJackClientName();
184
185 if (!lashClient->isNewProject()){
186 bConnectDefaults = false;
187 }
188 }
189 }
190#endif
191
192 if ( bConnectDefaults ) {
193 // Connect the left and right default ports of Hydrogen.
194 //
195 // The `jack_connect' function is defined in the
196 // jack/jack.h file. It establishes a connection between
197 // two ports. When a connection exists, data written
198 // to the source port will be available to be read at
199 // the destination port. Returns 0 on success, exits
200 // if the connection is already made, and returns a
201 // non-zero error code otherwise.
202 // Syntax: jack_connect( jack_client_t jack_client,
203 // const char *source_port )
204 // const char *destination_port
205 // )
206 // The `jack_port_name' function is also defined in
207 // the jack/jack.h header returns the full name of a
208 // provided port of type jack_port_t.
209 if ( jack_connect( m_pClient, jack_port_name( m_pOutputPort1 ),
210 m_sOutputPortName1.toLocal8Bit() ) == 0 &&
211 jack_connect( m_pClient, jack_port_name( m_pOutputPort2 ),
212 m_sOutputPortName2.toLocal8Bit() ) == 0 ) {
213 return 0;
214 }
215
216 WARNINGLOG( "Could not connect to the saved output ports. Connect to the first pair of input ports instead." );
217 // The `jack_get_ports' is defined in the jack/jack.h
218 // header file and performs a lookup of ports of the
219 // JACK server based on their e.g. flags. It returns a
220 // NULL-terminated array of ports that match the
221 // specified arguments. The caller is responsible for
222 // calling jack_free() any non-NULL returned
223 // value.
224 const char ** portnames = jack_get_ports( m_pClient, nullptr, nullptr, JackPortIsInput );
225 if ( !portnames || !portnames[0] || !portnames[1] ) {
226 ERRORLOG( "Couldn't locate two Jack input ports" );
228 return 2;
229 }
230 if ( jack_connect( m_pClient, jack_port_name( m_pOutputPort1 ),
231 portnames[0] ) != 0 ||
232 jack_connect( m_pClient, jack_port_name( m_pOutputPort2 ),
233 portnames[1] ) != 0 ) {
234 ERRORLOG( "Couldn't connect to first pair of Jack input ports" );
236 return 2;
237 }
238 free( portnames );
239 }
240
241 return 0;
242}
243
245{
246 INFOLOG( "disconnect" );
247
248 deactivate();
249
250 jack_client_t* pOldClient = m_pClient;
251
252 m_pClient = nullptr;
253
254 if ( pOldClient != nullptr ) {
255 int nReturnCode = jack_client_close( pOldClient );
256 if ( nReturnCode != 0 ) {
257 ERRORLOG( "Error in jack_client_close" );
259 }
260 }
261 m_pClient = nullptr;
262}
263
265{
266 if ( m_pClient != nullptr ) {
267 int nReturnCode = jack_deactivate( m_pClient );
268 if ( nReturnCode != 0 ) {
269 ERRORLOG( "Error in jack_deactivate" );
270 }
271 }
272 memset( m_pTrackOutputPortsL, 0, sizeof(m_pTrackOutputPortsL) );
273 memset( m_pTrackOutputPortsR, 0, sizeof(m_pTrackOutputPortsR) );
274}
275
280
285
287{
288 if ( m_pClient != nullptr &&
289 Preferences::get_instance()->m_bJackTrackOuts ) {
290 float* pBuffer;
291
292 for ( int ii = 0; ii < m_nTrackPortCount; ++ii ) {
293 pBuffer = getTrackOut_L( ii );
294 if ( pBuffer != nullptr ) {
295 memset( pBuffer, 0, nFrames * sizeof( float ) );
296 }
297 pBuffer = getTrackOut_R( ii );
298 if ( pBuffer != nullptr ) {
299 memset( pBuffer, 0, nFrames * sizeof( float ) );
300 }
301 }
302 }
303}
304
305const jack_position_t& JackAudioDriver::getJackPosition() const {
306 return m_JackTransportPos;
307}
308
309bool JackAudioDriver::isBBTValid( const jack_position_t& pos ) {
310 if ( ! ( pos.valid & JackPositionBBT ) ) {
311 // No BBT information
312 return false;
313 }
314
315 // Sometime the JACK server does send seemingly random nuisance.
316 if ( pos.beat_type < 1 ||
317 pos.bar < 1 ||
318 pos.beat < 1 ||
319 pos.beat > pos.beats_per_bar ||
320 pos.beats_per_bar < 1 ||
321 pos.beats_per_minute < MIN_BPM ||
322 pos.beats_per_minute > MAX_BPM ||
323 pos.tick < 0 ||
324 pos.tick >= pos.ticks_per_beat ||
325 pos.ticks_per_beat < 1 ||
326 std::isnan( pos.bar_start_tick ) ||
327 std::isnan( pos.beats_per_bar ) ||
328 std::isnan( pos.beat_type ) ||
329 std::isnan( pos.ticks_per_beat ) ||
330 std::isnan( pos.beats_per_minute ) ) {
331#if JACK_DEBUG
332 J_DEBUGLOG( QString( "Invalid BBT content. beat_type: %1, bar: %2, beat: %3, tick: %4, beats_per_bar: %5, beats_per_minute: %6, ticks_per_beat: %7, bar_start_tick: %8, beat_type: %9" )
333 .arg( pos.beat_type ).arg( pos.bar ).arg( pos.beat )
334 .arg( pos.tick ).arg( pos.beats_per_bar )
335 .arg( pos.beats_per_minute ).arg( pos.ticks_per_beat )
336 .arg( pos.bar_start_tick ).arg( pos.beat_type ) );
337#endif
338 ERRORLOG( "Invalid timebase information. Hydrogen falls back to frame-based relocation. In case you encounter this error frequently, you might considering to disabling JACK timebase support in the Preferences in order to avoid glitches." );
339 return false;
340 }
341
342 return true;
343}
344
345double JackAudioDriver::bbtToTick( const jack_position_t& pos ) {
346
347 auto pHydrogen = Hydrogen::get_instance();
348
349 int nResolution = Song::nDefaultResolution;
351 long nSongSizeInTicks = 0;
352 auto pSong = pHydrogen->getSong();
353 if ( pSong != nullptr ) {
354 nResolution = pSong->getResolution();
355 loopMode = pSong->getLoopMode();
356 nSongSizeInTicks = pSong->lengthInTicks();
357#if JACK_DEBUG
358 } else {
359 WARNINGLOG( "No song set" );
360#endif
361 }
362
363 auto pAudioEngine = pHydrogen->getAudioEngine();
364
365 const double fTicksPerBeat =
366 static_cast<double>( nResolution / pos.beat_type * 4 );
367
368 bool bEndOfSongReached = false;
369 long nBarTicks = 0;
370 if ( pHydrogen->getMode() == Song::Mode::Song ) {
371
372 // We disregard any relation between patterns/columns in Hydrogen
373 // and the bar information provided by JACK. Instead, we assume a
374 // constant measure for the whole song relocate to the tick encoded
375 // in BBT information.
376 //
377 // We also have to convert between the tick size used within
378 // Hydrogen and the one used by the current Timebase controller.
379 nBarTicks = pos.bar_start_tick * ( fTicksPerBeat / pos.ticks_per_beat );
380
381 // Check whether the resulting ticks exceeds the end of the song.
382 if ( ( loopMode == Song::LoopMode::Disabled ||
383 loopMode == Song::LoopMode::Finishing ) &&
384 nBarTicks >= nSongSizeInTicks ) {
385 bEndOfSongReached = true;
386 }
387 }
388
389 double fNewTick;
390 if ( bEndOfSongReached ) {
391 fNewTick = -1;
392#if JACK_DEBUG
393 J_DEBUGLOG( "[end of song reached]" );
394#endif
395 }
396 else {
397 fNewTick = static_cast<double>(nBarTicks) +
398 ( pos.beat - 1 ) * fTicksPerBeat +
399 pos.tick * ( fTicksPerBeat / pos.ticks_per_beat );
400 }
401
402#if JACK_DEBUG
403 J_DEBUGLOG( QString( "Calculated tick [%1] from pos.bar: %2, nBarTicks: %3, pos.beat: %4, fTicksPerBeat: %5, pos.tick: %6, pos.ticks_per_beat: %7, bEndOfSongReached: %8" )
404 .arg( fNewTick ).arg( pos.bar ).arg( nBarTicks )
405 .arg( pos.beat ).arg( fTicksPerBeat )
406 .arg( pos.tick ).arg( pos.ticks_per_beat )
407 .arg( bEndOfSongReached ) );
408#endif
409
410 return fNewTick;
411}
412
414 jack_position_t* pJackPosition ) {
415 int nResolution = Song::nDefaultResolution;
416 const auto pSong = Hydrogen::get_instance()->getSong();
417 if ( pSong != nullptr ) {
418 nResolution = pSong->getResolution();
419#if JACK_DEBUG
420 } else {
421 WARNINGLOG( "No song set" );
422#endif
423 }
424
425 // We use the longest playing pattern as reference.
426 Pattern* pPattern = nullptr;
427 int nPatternLength = 0;
428 auto pPatternList = transportPos.getPlayingPatterns();
429 for ( std::vector<Pattern*>::const_iterator ppPattern = pPatternList->cbegin();
430 ppPattern < pPatternList ->cend(); ppPattern++ ) {
431 if ( (*ppPattern)->get_length() > nPatternLength ) {
432 nPatternLength = (*ppPattern)->get_length();
433 pPattern = *ppPattern;
434 }
435
436 for ( const auto& ppVirtualPattern : *(*ppPattern)->get_flattened_virtual_patterns() ) {
437 if ( ppVirtualPattern->get_length() > nPatternLength ) {
438 nPatternLength = ppVirtualPattern->get_length();
439 pPattern = ppVirtualPattern;
440 }
441 }
442 }
443
444 float fNumerator, fDenumerator;
445 if ( pPattern != nullptr ) {
446 fNumerator = nPatternLength * pPattern->get_denominator() / MAX_NOTES;
447 fDenumerator = pPattern->get_denominator();
448 }
449 else {
450 fNumerator = 4;
451 fDenumerator = 4;
452 }
453 const float fTicksPerBeat =
454 static_cast<float>(nResolution) * 4 / fDenumerator;
455
456 pJackPosition->frame_rate =
458 pJackPosition->ticks_per_beat = fTicksPerBeat;
459 pJackPosition->valid = JackPositionBBT;
460 // Time signature "numerator"
461 pJackPosition->beats_per_bar = fNumerator;
462 // Time signature "denominator"
463 pJackPosition->beat_type = fDenumerator;
464 pJackPosition->beats_per_minute = static_cast<double>(transportPos.getBpm());
465
466 if ( transportPos.getFrame() < 1 || transportPos.getColumn() == -1 ) {
467 // We have to be careful about column == -1. In song mode with loop mode
468 // disabled Hydrogen will just stop transport and set column to -1 while
469 // still holding the former tick and frame values. (This is important to
470 // properly rendering fade outs and realtime event).
471 pJackPosition->bar = 1;
472 pJackPosition->beat = 1;
473 pJackPosition->tick = 0;
474 pJackPosition->bar_start_tick = 0;
475 }
476 else {
477 // +1 since the counting bars starts at 1.
478 pJackPosition->bar = transportPos.getColumn() + 1;
479
480 // Number of ticks that have elapsed between frame 0 and the
481 // first beat of the next measure.
482 pJackPosition->bar_start_tick = transportPos.getPatternStartTick();
483
484 pJackPosition->beat = static_cast<int>(std::floor(
485 static_cast<float>(transportPos.getPatternTickPosition()) /
486 static_cast<float>(pJackPosition->ticks_per_beat)));
487 // +1 since the counting beats starts at 1.
488 pJackPosition->beat++;
489
490 // Counting ticks starts at 0.
491 pJackPosition->tick = std::fmod(
492 static_cast<double>(transportPos.getPatternTickPosition()),
493 pJackPosition->ticks_per_beat );
494 }
495}
496
498{
499 if ( ! Preferences::get_instance()->m_bJackTimebaseEnabled ) {
500 ERRORLOG( "This function should not have been called with JACK timebase disabled in the Preferences" );
501 return;
502 }
504 ERRORLOG( QString( "Relocation using BBT information can only be used in the presence of another JACK Timebase controller" ) );
505 return;
506 }
507
508 Hydrogen* pHydrogen = Hydrogen::get_instance();
509 std::shared_ptr<Song> pSong = pHydrogen->getSong();
510 auto pAudioEngine = pHydrogen->getAudioEngine();
511
512 if ( pSong == nullptr ) {
513 // Expected behavior if Hydrogen is exited while playback is
514 // still running.
515#if JACK_DEBUG
516 J_DEBUGLOG( "No song set." );
517#endif
518 return;
519 }
520
521 const double fNewTick = bbtToTick( m_JackTransportPos );
522
523 if ( fNewTick == -1 ) {
524 // End of song reached.
525 if ( pAudioEngine->getState() == AudioEngine::State::Playing ) {
526 pAudioEngine->stop();
527 pAudioEngine->stopPlayback();
528 }
529
530#if JACK_DEBUG
531 J_DEBUGLOG( "Exceeding length of song. Locating back to start." );
532#endif
533
534 // It is important to relocate to the beginning of the song. If we would
535 // stay at the end or beyond, Hydrogen would stop every attempt to start
536 // playback again. And it's most probably not obvious to the user why it
537 // does so.
538 pAudioEngine->locate( 0, false );
539
540 // Reset the offset as we loose information in truncating the transport
541 // position.
543 }
544 else {
545
546#if JACK_DEBUG
547 J_DEBUGLOG( QString( "Locate to tick [%1]" ).arg( fNewTick ) );
548#endif
549
550 pAudioEngine->locate( fNewTick, false );
551 }
552
554
555 m_nTimebaseFrameOffset = pAudioEngine->getTransportPosition()->getFrame() -
556 m_JackTransportPos.frame;
557
558 return;
559}
560
562{
563 if ( Preferences::get_instance()->m_bJackTransportMode !=
565 return;
566 }
567
568 auto pHydrogen = Hydrogen::get_instance();
569 auto pAudioEngine = pHydrogen->getAudioEngine();
570
571 const bool bTimebaseEnabled = Preferences::get_instance()->m_bJackTimebaseEnabled;
572
573#ifdef HAVE_INTEGRATION_TESTS
574 const int nPreviousXruns = JackAudioDriver::jackServerXRuns;
575#endif
576
577 // jack_transport_query() (jack/transport.h) queries the
578 // current transport state and position. If called from the
579 // process thread, the second argument, which is a pointer to
580 // a structure for returning current transport, corresponds to
581 // the first frame of the current cycle and the state returned
582 // is valid for the entire cycle. #m_JackTransportPos.valid
583 // will show which fields contain valid data. If
584 // #m_JackTransportPos is NULL, do not return position
585 // information.
586 m_JackTransportState = jack_transport_query( m_pClient, &m_JackTransportPos );
587
588 switch ( m_JackTransportState ) {
589 case JackTransportStopped: // Transport is halted
590 pAudioEngine->setNextState( AudioEngine::State::Ready );
591 break;
592
593 case JackTransportRolling: // Transport is playing
594 pAudioEngine->setNextState( AudioEngine::State::Playing );
595 break;
596
597 case JackTransportStarting:
598 // Waiting for sync ready. If there are slow-sync clients,
599 // this can take more than one cycle.
600 pAudioEngine->setNextState( AudioEngine::State::Ready );
601 break;
602
603 default:
604 ERRORLOG( "Unknown jack transport state" );
605 }
606
607 if ( pHydrogen->getSong() == nullptr ) {
608 // Expected behavior if Hydrogen is exited while playback is
609 // still running.
610#if JACK_DEBUG
611 J_DEBUGLOG( "No song set." );
612#endif
613 return;
614 }
615
616 if ( m_JackTransportPos.valid & JackPositionBBT ) {
618 static_cast<float>(m_JackTransportPos.beats_per_minute );
619 }
620
621#if JACK_DEBUG
622 J_DEBUGLOG( QString( "JACK state: %1, TimebaseFrameOffset: %2, pos: %3" )
626 J_DEBUGLOG( QString( "Timebase state: %1, tracking: %2" )
629#endif
630
631 // We rely on the JackTimebaseCallback to give us a thumbs up every time it
632 // is called. But since this is not happening while transport is stopped or
633 // starting, we have to omit those cases.
634 if ( bTimebaseEnabled && m_JackTransportState == JackTransportRolling ) {
635 // Update the status regrading JACK Timebase.
639 }
640 else {
641 // JackTimebaseCallback not called anymore -> timebase
642 // listener/normal client
644 if ( m_JackTransportPos.valid & JackPositionBBT ) {
646 }
647 else {
649 }
650
651#if JACK_DEBUG
652 J_DEBUGLOG( QString( "Updating Timebase [0] [%1] -> [%2]" )
655#endif
656
660 static_cast<int>(m_timebaseState) );
661 }
662 }
663 else {
664 // Update state with respect to an external Timebase controller
665 if ( m_JackTransportPos.valid & JackPositionBBT ) {
666 // There is an external controller
668
669#if JACK_DEBUG
670 J_DEBUGLOG( QString( "Updating Timebase [1] [%1] -> [%2]" )
673#endif
674
679 static_cast<int>(m_timebaseState) );
680 }
683 }
684 }
685 else {
688 // There might have been a relocation by another listener (or
689 // us). We wait till the next processing cycle in order to
690 // decide whether to drop the BBT support or not.
692 }
693 else {
695
696#if JACK_DEBUG
697 J_DEBUGLOG( QString( "Updating Timebase [2] [%1] -> [%2]" )
700#endif
701
706 static_cast<int>(m_timebaseState) );
707 }
708 }
709 }
710 }
711
712 // The relocation could be either triggered by an user interaction
713 // (e.g. clicking the forward button or clicking somewhere on the
714 // timeline) or by a different JACK client.
715 const bool bRelocation =
716 ( pAudioEngine->getTransportPosition()->getFrame() -
717 pAudioEngine->getTransportPosition()->getFrameOffsetTempo() -
719 if ( bRelocation || ( m_lastTransportBits != m_JackTransportPos.valid &&
721
722#if JACK_DEBUG
723 if ( bRelocation ) {
724 J_DEBUGLOG( QString( "[relocation detected] frames: %1, offset: %2, Jack frames: %3, m_nTimebaseFrameOffset: %4, timebase mode: %5" )
725 .arg( pAudioEngine->getTransportPosition()->getFrame() )
726 .arg( pAudioEngine->getTransportPosition()->getFrameOffsetTempo() )
727 .arg( m_JackTransportPos.frame )
730 }
731 else {
732 J_DEBUGLOG( QString( "[BBT info available] update transport" ) );
733 }
734#endif
735
736 if ( bTimebaseEnabled && m_timebaseState == Timebase::Listener &&
739 }
740 else {
741 pAudioEngine->locateToFrame( m_JackTransportPos.frame );
743 }
744
746
747#ifdef HAVE_INTEGRATION_TESTS
748 // Used to check whether we can find the proper position right away
749 // during the integration tests. If e.g. an offset is off, we get
750 // trapped in a relocation loop.
751 //
752 // We only perform the check in case no XRun occurred over the course of
753 // this function. They would mess things up.
754 if ( m_bIntegrationCheckRelocationLoop && bRelocation &&
755 nPreviousXruns == JackAudioDriver::jackServerXRuns ) {
756 if ( JackAudioDriver::m_nIntegrationLastRelocationFrame !=
757 m_JackTransportPos.frame ) {
758 JackAudioDriver::m_nIntegrationLastRelocationFrame =
759 m_JackTransportPos.frame;
760 } else {
761 ERRORLOG( QString( "Relocation Loop! [%1] is detected as relocation a second time." )
762 .arg( m_JackTransportPos.frame ) );
763 m_bIntegrationRelocationLoop = true;
764 }
765 }
766#endif
767
768#if JACK_DEBUG
769 J_DEBUGLOG( QString( "[relocation done] m_nTimebaseFrameOffset: %1, new pos: %2" )
771 .arg( pAudioEngine->getTransportPosition()->toQString() ) );
772#endif
773 }
774
775 return;
776}
777
779{
788 jack_default_audio_sample_t *out = static_cast<jack_default_audio_sample_t*>(jack_port_get_buffer( m_pOutputPort1, JackAudioDriver::jackServerBufferSize ));
789 return out;
790}
791
793{
794 jack_default_audio_sample_t *out = static_cast<jack_default_audio_sample_t*>(jack_port_get_buffer( m_pOutputPort2, JackAudioDriver::jackServerBufferSize ));
795 return out;
796}
797
798float* JackAudioDriver::getTrackOut_L( unsigned nTrack )
799{
800 if ( nTrack > static_cast<unsigned>(m_nTrackPortCount) ) {
801 return nullptr;
802 }
803
804 jack_port_t* pPort = m_pTrackOutputPortsL[nTrack];
805 jack_default_audio_sample_t* out = nullptr;
806 if( pPort ) {
807 out = static_cast<jack_default_audio_sample_t*>(jack_port_get_buffer( pPort, JackAudioDriver::jackServerBufferSize));
808 }
809 return out;
810}
811
812float* JackAudioDriver::getTrackOut_R( unsigned nTrack )
813{
814 if( nTrack > static_cast<unsigned>(m_nTrackPortCount) ) {
815 return nullptr;
816 }
817
818 jack_port_t* pPort = m_pTrackOutputPortsR[nTrack];
819 jack_default_audio_sample_t* out = nullptr;
820 if( pPort ) {
821 out = static_cast<jack_default_audio_sample_t*>(jack_port_get_buffer( pPort, JackAudioDriver::jackServerBufferSize));
822 }
823 return out;
824}
825
826float* JackAudioDriver::getTrackOut_L( std::shared_ptr<Instrument> instr, std::shared_ptr<InstrumentComponent> pCompo)
827{
828 return getTrackOut_L(m_trackMap[instr->get_id()][pCompo->get_drumkit_componentID()]);
829}
830
831float* JackAudioDriver::getTrackOut_R( std::shared_ptr<Instrument> instr, std::shared_ptr<InstrumentComponent> pCompo)
832{
833 return getTrackOut_R(m_trackMap[instr->get_id()][pCompo->get_drumkit_componentID()]);
834}
835
836
837#define CLIENT_FAILURE(msg) { \
838 ERRORLOG("Could not connect to JACK server (" msg ")"); \
839 if ( m_pClient != nullptr ) { \
840 ERRORLOG("...but JACK returned a non-null pointer?"); \
841 m_pClient = nullptr; \
842 } \
843 if (nTries) ERRORLOG("...trying again."); \
844}
845
846
847#define CLIENT_SUCCESS(msg) { \
848 assert(m_pClient); \
849 INFOLOG(msg); \
850 nTries = 0; \
851}
852
853int JackAudioDriver::init( unsigned bufferSize )
854{
855 auto pPreferences = Preferences::get_instance();
856
857 QString sClientName = "Hydrogen";
858
859#ifdef H2CORE_HAVE_OSC
860 QString sNsmClientId = pPreferences->getNsmClientId();
861
862 if( !sNsmClientId.isEmpty() ){
863 sClientName = sNsmClientId;
864 }
865#endif
866 // The address of the status object will be used by JACK to
867 // return information from the open operation.
868 jack_status_t status;
869 // Sometimes jackd doesn't stop and start fast enough.
870 int nTries = 2;
871 while ( nTries > 0 ) {
872 --nTries;
873
874 // Open an external client session with the JACK
875 // server. The `jack_client_open' function is defined
876 // in the jack/jack.h header. With it, clients may
877 // choose which of several servers to connect, and
878 // control whether and how to start the server
879 // automatically, if it was not already running. Its
880 // first argument _client_name_ of is at most
881 // jack_client_name_size() characters. The name scope
882 // is local to each server. Unless forbidden by the
883 // JackUseExactName option, the server will modify
884 // this name to create a unique variant, if
885 // needed. The second argument _options_ is formed by
886 // OR-ing together JackOptions bits. Only the
887 // JackOpenOptions bits are allowed. _status_ (if
888 // non-NULL) is an address for JACK to return
889 // information from the open operation. This status
890 // word is formed by OR-ing together the relevant
891 // JackStatus bits. Depending on the _status_, an
892 // optional argument _server_name_ selects from among
893 // several possible concurrent server
894 // instances. Server names are unique to each user. It
895 // returns an opaque client handle if successful. If
896 // this is NULL, the open operation failed, *status
897 // includes JackFailure and the caller is not a JACK
898 // client.
899 m_pClient = jack_client_open( sClientName.toLocal8Bit(),
900 JackNullOption,
901 &status);
902
903 // Check what did happen during the opening of the
904 // client. CLIENT_SUCCESS sets the nTries variable
905 // to 0 while CLIENT_FAILURE resets m_pClient to the
906 // nullptr.
907 switch(status) {
908 case JackFailure:
909 CLIENT_FAILURE("unknown error");
910 break;
911 case JackInvalidOption:
912 CLIENT_FAILURE("invalid option");
913 break;
914 case JackNameNotUnique:
915 if ( m_pClient != nullptr ) {
916 sClientName = jack_get_client_name(m_pClient);
917 CLIENT_SUCCESS(QString("Jack assigned the client name '%1'").arg(sClientName));
918 } else {
919 CLIENT_FAILURE("name not unique");
920 }
921 break;
922 case JackServerStarted:
923 CLIENT_SUCCESS("JACK Server started for Hydrogen.");
924 break;
925 case JackServerFailed:
926 CLIENT_FAILURE("unable to connect");
927 break;
928 case JackServerError:
929 CLIENT_FAILURE("communication error");
930 break;
931 case JackNoSuchClient:
932 CLIENT_FAILURE("unknown client type");
933 break;
934 case JackLoadFailure:
935 CLIENT_FAILURE("can't load internal client");
936 break;
937 case JackInitFailure:
938 CLIENT_FAILURE("can't initialize client");
939 break;
940 case JackShmFailure:
941 CLIENT_FAILURE("unable to access shared memory");
942 break;
943 case JackVersionError:
944 CLIENT_FAILURE("client/server protocol version mismatch");
945 break;
946 default:
947 if (status) {
948 ERRORLOG("Unknown status with JACK server.");
949 if ( m_pClient != nullptr ) {
950 CLIENT_SUCCESS("Client pointer is *not* null..."
951 " assuming we're OK");
952 }
953 } else {
954 CLIENT_SUCCESS("Connected to JACK server");
955 }
956 }
957 }
958
959 if ( m_pClient == nullptr ) {
960 return -1;
961 }
962 JackAudioDriver::jackServerSampleRate = jack_get_sample_rate( m_pClient );
963 JackAudioDriver::jackServerBufferSize = jack_get_buffer_size( m_pClient );
964
965 pPreferences->m_nSampleRate = JackAudioDriver::jackServerSampleRate;
966 pPreferences->m_nBufferSize = JackAudioDriver::jackServerBufferSize;
967
968 /* tell the JACK server to call `process()' whenever
969 there is work to be done.
970 */
971 if ( jack_set_process_callback(
972 m_pClient, this->m_processCallback, nullptr ) != 0 ) {
973 ERRORLOG( "Unable to set process callback" );
974 }
975
976 /* tell the JACK server to call `srate()' whenever
977 the sample rate of the system changes.
978 */
979 if ( jack_set_sample_rate_callback(
980 m_pClient, jackDriverSampleRate, this ) != 0 ) {
981 ERRORLOG( "Unable to set sample rate callback" );
982 }
983
984 /* tell JACK server to update us if the buffer size
985 (frames per process cycle) changes.
986 */
987 if ( jack_set_buffer_size_callback(
988 m_pClient, jackDriverBufferSize, this ) != 0 ) {
989 ERRORLOG( "Unable to set buffersize callback" );
990 }
991
992 /* display an XRun event in the GUI.*/
993 if ( jack_set_xrun_callback( m_pClient, jackXRunCallback, nullptr ) != 0 ) {
994 ERRORLOG( "Unable to set XRun callback" );
995 }
996
997 /* tell the JACK server to call `jack_shutdown()' if
998 it ever shuts down, either entirely, or if it
999 just decides to stop calling us.
1000 */
1001 jack_on_shutdown( m_pClient, jackDriverShutdown, nullptr );
1002
1003 // Create two new ports for Hydrogen's client. These are
1004 // objects used for moving data of any type in or out of the
1005 // client. Ports may be connected in various ways. The
1006 // function `jack_port_register' (jack/jack.h) is called like
1007 // jack_port_register( jack_client_t *client,
1008 // const char *port_name,
1009 // const char *port_type,
1010 // unsigned long flags,
1011 // unsigned long buffer_size)
1012 //
1013 // All ports have a type, which may be any non-NULL and non-zero
1014 // length string, passed as an argument. Some port types are built
1015 // into the JACK API, currently only JACK_DEFAULT_AUDIO_TYPE.
1016 // It returns a _jack_port_t_ pointer on success, otherwise NULL.
1017 m_pOutputPort1 = jack_port_register( m_pClient, "out_L", JACK_DEFAULT_AUDIO_TYPE,
1018 JackPortIsOutput, 0 );
1019 if ( jack_set_property( m_pClient, jack_port_uuid( m_pOutputPort1 ),
1020 JACK_METADATA_PRETTY_NAME, "Main Output L",
1021 "text/plain" ) != 0 ) {
1022 INFOLOG( "Unable to set pretty name of left main output" );
1023 }
1024 m_pOutputPort2 = jack_port_register( m_pClient, "out_R", JACK_DEFAULT_AUDIO_TYPE,
1025 JackPortIsOutput, 0 );
1026 if ( jack_set_property( m_pClient, jack_port_uuid( m_pOutputPort2 ),
1027 JACK_METADATA_PRETTY_NAME, "Main Output R",
1028 "text/plain" ) != 0 ) {
1029 INFOLOG( "Unable to set pretty name of right main output" );
1030 }
1031 Hydrogen* pHydrogen = Hydrogen::get_instance();
1032 if ( ( m_pOutputPort1 == nullptr ) || ( m_pOutputPort2 == nullptr ) ) {
1034 return 4;
1035 }
1036
1037#ifdef H2CORE_HAVE_LASH
1038 if ( pPreferences->useLash() ){
1039 LashClient* lashClient = LashClient::get_instance();
1040 if ( lashClient->isConnected() ) {
1041 const auto sClientNameLocal8Bit = sClientName.toLocal8Bit();
1042 lashClient->setJackClientName(sClientNameLocal8Bit.constData());
1043 }
1044 }
1045#endif
1046
1047 if ( pPreferences->m_bJackTransportMode == Preferences::USE_JACK_TRANSPORT &&
1048 pPreferences->m_bJackTimebaseMode == Preferences::USE_JACK_TIMEBASE_CONTROL &&
1049 pPreferences->m_bJackTimebaseEnabled ){
1051 }
1052
1053 // TODO: Apart from the makeTrackOutputs() all other calls should
1054 // be redundant.
1055 //
1056 // Whenever there is a Song present, create per track outputs (if
1057 // activated in the Preferences).
1058 std::shared_ptr<Song> pSong = pHydrogen->getSong();
1059 if ( pSong != nullptr ) {
1060 makeTrackOutputs( pSong );
1061 }
1062
1063 return 0;
1064}
1065
1066void JackAudioDriver::makeTrackOutputs( std::shared_ptr<Song> pSong )
1067{
1068 if( Preferences::get_instance()->m_bJackTrackOuts == false ) {
1069 return;
1070 }
1071
1072 auto pInstrumentList = pSong->getInstrumentList();
1073 std::shared_ptr<Instrument> pInstrument;
1074 int nInstruments = static_cast<int>(pInstrumentList->size());
1075
1076 WARNINGLOG( QString( "Creating / renaming %1 ports" ).arg( nInstruments ) );
1077
1078 int nTrackCount = 0;
1079
1080 for( int i = 0 ; i < MAX_INSTRUMENTS ; i++ ){
1081 for ( int j = 0 ; j < MAX_COMPONENTS ; j++ ){
1082 m_trackMap[i][j] = 0;
1083 }
1084 }
1085 // Creates a new output track or reassigns an existing one for
1086 // each component of each instrument and stores the result in
1087 // the `m_trackMap'.
1088 std::shared_ptr<InstrumentComponent> pInstrumentComponent;
1089 for ( int n = 0; n <= nInstruments - 1; n++ ) {
1090 pInstrument = pInstrumentList->get( n );
1091 for ( auto& pInstrumentComponent : *pInstrument->get_components() ) {
1092 setTrackOutput( nTrackCount, pInstrument, pInstrumentComponent, pSong);
1093 m_trackMap[pInstrument->get_id()][pInstrumentComponent->get_drumkit_componentID()] =
1094 nTrackCount;
1095 nTrackCount++;
1096 }
1097 }
1098 // clean up unused ports
1099 jack_port_t *pPortL, *pPortR;
1100 for ( int n = nTrackCount; n < m_nTrackPortCount; n++ ) {
1101 pPortL = m_pTrackOutputPortsL[n];
1102 pPortR = m_pTrackOutputPortsR[n];
1103 m_pTrackOutputPortsL[n] = nullptr;
1104 if ( jack_port_unregister( m_pClient, pPortL ) != 0 ) {
1105 ERRORLOG( QString( "Unable to unregister left port [%1]" ).arg( n ) );
1106 }
1107 m_pTrackOutputPortsR[n] = nullptr;
1108 if ( jack_port_unregister( m_pClient, pPortR ) != 0 ) {
1109 ERRORLOG( QString( "Unable to unregister right port [%1]" ).arg( n ) );
1110 }
1111 }
1112
1113 m_nTrackPortCount = nTrackCount;
1114}
1115
1116void JackAudioDriver::setTrackOutput( int n, std::shared_ptr<Instrument> pInstrument, std::shared_ptr<InstrumentComponent> pInstrumentComponent, std::shared_ptr<Song> pSong )
1117{
1118 QString sComponentName;
1119
1120 // The function considers `m_nTrackPortCount' as the number of
1121 // ports already present. If it's smaller than `n', new ports
1122 // have to be created.
1123 if ( m_nTrackPortCount <= n ) {
1124 for ( int m = m_nTrackPortCount; m <= n; m++ ) {
1125 sComponentName = QString( "Track_%1_" ).arg( m + 1 );
1127 jack_port_register( m_pClient, ( sComponentName + "L" ).toLocal8Bit(),
1128 JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 );
1129
1131 jack_port_register( m_pClient, ( sComponentName + "R" ).toLocal8Bit(),
1132 JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 );
1133
1134 if ( ! m_pTrackOutputPortsR[m] || ! m_pTrackOutputPortsL[m] ) {
1136 }
1137 }
1138 m_nTrackPortCount = n + 1;
1139 }
1140
1141 // Now that we're sure there is an n'th port, rename it.
1142 auto pDrumkitComponent = pSong->getComponent( pInstrumentComponent->get_drumkit_componentID() );
1143 sComponentName = QString( "Track_%1_%2_%3_" ).arg( n + 1 )
1144 .arg( pInstrument->get_name() ).arg( pDrumkitComponent->get_name() );
1145
1146#ifdef HAVE_JACK_PORT_RENAME
1147 // This differs from jack_port_set_name() by triggering
1148 // PortRename notifications to clients that have registered a
1149 // port rename handler.
1150 if ( jack_port_rename( m_pClient, m_pTrackOutputPortsL[n],
1151 ( sComponentName + "L" ).toLocal8Bit() ) != 0 ) {
1152 ERRORLOG( QString( "Unable to rename left port of track [%1] to [%2]" )
1153 .arg( n ).arg( sComponentName + "L" ) );
1154 }
1155 if ( jack_port_rename( m_pClient, m_pTrackOutputPortsR[n],
1156 ( sComponentName + "R" ).toLocal8Bit() ) != 0 ) {
1157 ERRORLOG( QString( "Unable to rename right port of track [%1] to [%2]" )
1158 .arg( n ).arg( sComponentName + "R" ) );
1159 }
1160#else
1161 if ( jack_port_set_name( m_pTrackOutputPortsL[n],
1162 ( sComponentName + "L" ).toLocal8Bit() ) != 0 ) {
1163 ERRORLOG( QString( "Unable to set name of left port of track [%1] to [%2]" )
1164 .arg( n ).arg( sComponentName + "R" ) );
1165 }
1166 if ( jack_port_set_name( m_pTrackOutputPortsR[n],
1167 ( sComponentName + "R" ).toLocal8Bit() ) != 0 ) {
1168 ERRORLOG( QString( "Unable to set name of right port of track [%1] to [%2]" )
1169 .arg( n ).arg( sComponentName + "R" ) );
1170 }
1171#endif
1172}
1173
1175#if JACK_DEBUG
1176 J_DEBUGLOG( "" );
1177#endif
1178
1179 if ( m_pClient != nullptr ) {
1180 jack_transport_start( m_pClient );
1181 } else {
1182 ERRORLOG( "No client registered" );
1183 }
1184}
1185
1187#if JACK_DEBUG
1188 J_DEBUGLOG( "" );
1189#endif
1190
1191 if ( m_pClient != nullptr ) {
1192 jack_transport_stop( m_pClient );
1193 } else {
1194 ERRORLOG( "No client registered" );
1195 }
1196}
1197
1198void JackAudioDriver::locateTransport( long long nFrame )
1199{
1200 const auto pAudioEngine = Hydrogen::get_instance()->getAudioEngine();
1201
1202 if ( m_pClient != nullptr ) {
1204 // We have to provided all BBT information as well when relocating
1205 // as Timebase controller.
1206 m_nextJackTransportPos.frame = nFrame;
1207 transportToBBT( *pAudioEngine->getTransportPosition(),
1209#if JACK_DEBUG
1210 J_DEBUGLOG( QString( "Relocate to position: %1" )
1212#endif
1213
1214 if ( jack_transport_reposition( m_pClient,
1215 &m_nextJackTransportPos ) != 0 ) {
1216 ERRORLOG( QString( "Position rejected [%1]" )
1218 }
1219 }
1220 else {
1221 long long nNewFrame = nFrame;
1223 // We have to guard against negative values which themselves are
1224 // nothing bad. They just tell that time was rescaled by a
1225 // measure change in the controller in such a way, Hydrogen
1226 // expects the origin of transport beyond 0.
1227 nNewFrame = std::max( static_cast<long long>(0),
1228 nFrame - m_nTimebaseFrameOffset );
1229 }
1230#if JACK_DEBUG
1231 J_DEBUGLOG( QString( "Relocate to nFrame: %1, nNewFrame: %2, m_nTimebaseFrameOffset: %3, timebase state: %4" )
1232 .arg( nFrame ).arg( nNewFrame )
1235#endif
1236
1237 // jack_transport_locate() (jack/transport.h )
1238 // re-positions the transport to a new frame number. May
1239 // be called at any time by any client.
1240 if ( jack_transport_locate( m_pClient, nNewFrame ) != 0 ) {
1241 ERRORLOG( QString( "Invalid relocation request to frame [%1]" )
1242 .arg( nNewFrame ) );
1243 }
1244 }
1245 } else {
1246 ERRORLOG( "No client registered" );
1247 }
1248}
1249
1251{
1252 if ( m_pClient == nullptr ) {
1253 ERRORLOG( "No client yet" );
1254 return;
1255 }
1256
1257 if ( ! Preferences::get_instance()->m_bJackTimebaseEnabled ) {
1258 ERRORLOG( "This function should not have been called with JACK Timebase disabled in the Preferences" );
1259 return;
1260 }
1261
1262 Preferences* pPreferences = Preferences::get_instance();
1264 int nReturnValue = jack_set_timebase_callback(m_pClient, 0,
1265 JackTimebaseCallback, this);
1266 if ( nReturnValue != 0 ){
1268 WARNINGLOG( QString( "Hydrogen was not able to register itself as Timebase controller: [%1]" )
1269 .arg( nReturnValue ) );
1270 }
1271 else {
1273
1274#if JACK_DEBUG
1275 J_DEBUGLOG( QString( "Updating Timebase [2] [%1] -> [%2]" )
1278#endif
1279
1283 static_cast<int>(m_timebaseState) );
1284 }
1285 }
1286 else {
1287 WARNINGLOG( "Timebase control should currently not be requested by Hydrogen" );
1289 }
1290}
1291
1293{
1294 if ( m_pClient == nullptr ) {
1295 ERRORLOG( QString( "Not fully initialized yet" ) );
1296 return;
1297 }
1298
1299 if ( ! Preferences::get_instance()->m_bJackTimebaseEnabled ) {
1300 ERRORLOG( "This function should not have been called with JACK timebase disabled in the Preferences" );
1301 return;
1302 }
1303
1304 if ( jack_release_timebase( m_pClient ) ) {
1305 ERRORLOG( "Unable to release Timebase control" );
1306 }
1307
1309 if ( m_JackTransportPos.valid & JackPositionBBT &&
1311 // Having an external controller while this function is called should be
1312 // rarely the case. But we still have to handle it.
1314 }
1315 else {
1317 }
1318
1319#if JACK_DEBUG
1320 J_DEBUGLOG( QString( "Updating Timebase [%1] -> [%2]" )
1323#endif
1324
1327
1328}
1329
1330void JackAudioDriver::JackTimebaseCallback(jack_transport_state_t state,
1331 jack_nframes_t nFrames,
1332 jack_position_t* pJackPosition,
1333 int new_pos,
1334 void *arg)
1335{
1336 JackAudioDriver* pDriver = static_cast<JackAudioDriver*>(arg);
1337 if ( pDriver == nullptr ){
1338 return;
1339 }
1340
1341 auto pAudioEngine = Hydrogen::get_instance()->getAudioEngine();
1342 std::shared_ptr<TransportPosition> pPos = nullptr;
1343
1344 pAudioEngine->lock( RIGHT_HERE );
1345
1346 // Ensure the callback is not triggered during startup or teardown.
1347 if ( pAudioEngine->getState() != AudioEngine::State::Ready &&
1348 pAudioEngine->getState() != AudioEngine::State::Playing &&
1349 pAudioEngine->getState() != AudioEngine::State::Testing ) {
1350 pAudioEngine->unlock();
1351 return;
1352 }
1353
1354 const long long nInitialFrame = pJackPosition->frame;
1355
1356 const auto posFromFrame = [&]( long long nFrame,
1357 jack_position_t* pJackPosition ) {
1358 if ( nFrame == pAudioEngine->getTransportPosition()->getFrame() ) {
1359 // Requested transport position coincides with the current one of the
1360 // Audio Engine. We can reuse it.
1361 pPos = pAudioEngine->getTransportPosition();
1362 }
1363 else {
1364 pPos = std::make_shared<TransportPosition>( "JackTimebaseCallback" );
1365 const auto fTick = TransportPosition::computeTickFromFrame(
1366 nFrame );
1367 pAudioEngine->updateTransportPosition( fTick, nFrame, pPos );
1368 }
1369
1370 transportToBBT( *pPos, pJackPosition );
1371 };
1372
1373 // In the face of heavy load - can be triggered by enabling JackAudioDriver,
1374 // TransportPosition, and AudioEngine debug logs - XRuns occur and the frame
1375 // information provided by the JACK server glitches(!!!!). So, it just
1376 // changes under the hood in the pointer passed to this callback. The very
1377 // quantity we should calculate BBT information based on. Well we try a
1378 // second time in order to avoid glitches.
1379 posFromFrame( nInitialFrame, pJackPosition );
1380
1381 if ( nInitialFrame != pJackPosition->frame ) {
1382 ERRORLOG( QString( "Provided frame glitched! Tring again using new one..." ) );
1383 const long long nSecondFrame = pJackPosition->frame;
1384 posFromFrame( nSecondFrame, pJackPosition );
1385 }
1386
1387 // Tell Hydrogen it is still in Timebase control.
1388 if ( pDriver->m_timebaseTracking != TimebaseTracking::Valid ) {
1390 }
1391 if ( pDriver->m_timebaseState != Timebase::Controller ) {
1392
1393#if JACK_DEBUG
1394 J_DEBUGLOG( QString( "Updating Timebase [%1] -> [%2]" )
1395 .arg( TimebaseToQString( pDriver->m_timebaseState ) )
1397#endif
1398
1400
1403 static_cast<int>(pDriver->m_timebaseState) );
1404 }
1405
1406#if JACK_DEBUG
1407 J_DEBUGLOG( QString( "Interal transport pos: %1" )
1408 .arg( pPos->toQString() ) );
1409 J_DEBUGLOG( QString( "JACK pos: %1" )
1410 .arg( JackTransportPosToQString( *pJackPosition ) ) );
1411#endif
1412
1413 pAudioEngine->unlock();
1414}
1415
1416
1418 if ( Preferences::get_instance()->m_bJackTimebaseEnabled ) {
1419 return m_timebaseState;
1420 }
1421 return Timebase::None;
1422}
1423
1426 return std::nan("no tempo, no masters");
1427 }
1428
1429 return m_fLastTimebaseBpm;
1430}
1431
1435
1437
1438 auto pHydrogen = Hydrogen::get_instance();
1439
1440 J_DEBUGLOG( QString( "m_JackTransportState: %1,\n m_JackTransportPos: %2,\nm_timebaseState: %3, current pattern column: %4" )
1441 .arg( m_JackTransportState )
1443 .arg( static_cast<int>(m_timebaseState) )
1444 .arg( pHydrogen->getAudioEngine()->getTransportPosition()->
1445 getColumn() ) );
1446}
1447
1448QString JackAudioDriver::JackTransportPosToQString( const jack_position_t& pos ) {
1449 return QString( "frame: %1, frame_rate: %2, valid: %3, bar: %4, beat: %5, tick: %6, bar_start_tick: %7, beats_per_bar: %8, beat_type: %9, ticks_per_beat: %10, beats_per_minute: %11, frame_time: %12, next_time: %13" )
1450 .arg( pos.frame ).arg( pos.frame_rate )
1451 .arg( pos.valid, 8, 16, QLatin1Char( '0' ) ) // hex value
1452 .arg( pos.bar ).arg( pos.beat ).arg( pos.tick )
1453 .arg( pos.bar_start_tick ).arg( pos.beats_per_bar )
1454 .arg( pos.beat_type ).arg( pos.ticks_per_beat )
1455 .arg( pos.beats_per_minute ).arg( pos.frame_time )
1456 .arg( pos.next_time );
1457}
1458
1460 switch( t ) {
1461 case Timebase::None:
1462 return "None";
1463 case Timebase::Listener:
1464 return "Listener";
1466 return "Controller";
1467 default:
1468 return "Unknown";
1469 }
1470}
1471QString JackAudioDriver::JackTransportStateToQString( const jack_transport_state_t& t ) {
1472 switch( t ) {
1473 case JackTransportStopped:
1474 return "Stopped";
1475 case JackTransportRolling:
1476 return "Rolling";
1477 case JackTransportStarting:
1478 return "Starting";
1479 case JackTransportLooping:
1480 // Only used in old systems.
1481 return "Looping";
1482 default:
1483 // There is the JackTransportStarting state too. But we do not cover
1484 // it here (yet) for the sake of backward compatibility.
1485 return QString( "Unknown JackTransportState [%1]" )
1486 .arg( static_cast<int>(t) );
1487
1488 }
1489}
1490
1492 switch( t ) {
1494 return "Valid";
1496 return "OnHold";
1498 return "None";
1499 default:
1500 return "Unknown";
1501 }
1502}
1504 switch( nState ) {
1505 case static_cast<int>(Timebase::Listener):
1506 return Timebase::Listener;
1507 case static_cast<int>(Timebase::Controller):
1508 return Timebase::Controller;
1509 case static_cast<int>(Timebase::None):
1510 default:
1511 return Timebase::None;
1512 }
1513}
1514};
1515
1516#endif // H2CORE_HAVE_JACK
#define RIGHT_HERE
Macro intended to be used for the logging of the locking of the H2Core::AudioEngine.
Definition AudioEngine.h:61
#define CLIENT_FAILURE(msg)
#define J_DEBUGLOG(x)
#define CLIENT_SUCCESS(msg)
#define INFOLOG(x)
Definition Object.h:240
#define WARNINGLOG(x)
Definition Object.h:241
#define __INFOLOG(x)
Definition Object.h:252
#define ERRORLOG(x)
Definition Object.h:242
#define ___INFOLOG(x)
Definition Object.h:258
@ Playing
Transport is rolling.
@ Ready
Ready to process audio.
@ Testing
State used during the unit tests of the AudioEngine.
void lock(const char *file, unsigned int line, const char *function)
Mutex locking of the AudioEngine.
virtual unsigned getSampleRate()=0
Base class.
Definition Object.h:62
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.
Hydrogen Audio Engine.
Definition Hydrogen.h:54
std::shared_ptr< Song > getSong() const
Get the current song.
Definition Hydrogen.h:123
static Hydrogen * get_instance()
Returns the current Hydrogen instance __instance.
Definition Hydrogen.h:84
AudioEngine * getAudioEngine() const
Definition Hydrogen.h:663
@ JACK_CANNOT_ACTIVATE_CLIENT
Definition Hydrogen.h:256
@ JACK_ERROR_IN_PORT_REGISTER
Unable to register output ports for the JACK client using jack_port_register() (jack/jack....
Definition Hydrogen.h:280
@ JACK_CANNOT_CLOSE_CLIENT
The client of Hydrogen can not be disconnected from the JACK server using jack_client_close() (jack/j...
Definition Hydrogen.h:273
@ JACK_CANNOT_CONNECT_OUTPUT_PORT
Unable to connect either the JackAudioDriver::output_port_1 and the JackAudioDriver::output_port_name...
Definition Hydrogen.h:267
AudioOutput * getAudioOutput() const
Used to display audio driver info.
Definition Hydrogen.cpp:741
void raiseError(unsigned nErrorCode)
Definition Hydrogen.cpp:800
JACK (Jack Audio Connection Kit) server driver.
virtual void disconnect() override
Disconnects the JACK client of the Hydrogen from the JACK server.
int m_nTrackPortCount
Total number of output ports currently in use.
static QString JackTransportStateToQString(const jack_transport_state_t &pPos)
long long m_nTimebaseFrameOffset
Stores an intended deviation of our transport position from the one hold by the JACK server.
jack_port_t * m_pTrackOutputPortsL[MAX_INSTRUMENTS]
Vector of all left audio output ports currently used by the local JACK client.
jack_position_t m_nextJackTransportPos
Use for relocation if Hydrogen is Timebase controller (and needs to provide valid BBT information in ...
void initTimebaseControl()
Acquires control of JACK Timebase.
TimebaseTracking
Used internally to keep track of the current Timebase state.
@ Valid
Current timebase state is on par with JACK server.
@ OnHold
We are uncertain of the current timebase state and wait a processing cycle to determine what to do ne...
static void transportToBBT(const TransportPosition &transportPos, jack_position_t *pPos)
virtual float * getOut_L() override
Get content in the left stereo output port.
Timebase m_timebaseState
Whether Hydrogen is receiving relocation and tempo changes as part of BBT information,...
static QString JackTransportPosToQString(const jack_position_t &pPos)
virtual int init(unsigned bufferSize) override
Initializes the JACK audio driver.
JackAudioDriver(JackProcessCallback m_processCallback)
Constructor of the JACK server driver.
bool m_bConnectDefaults
Specifies whether the default left and right (master) audio JACK ports will be automatically connecte...
static unsigned long jackServerSampleRate
Sample rate of the JACK audio server.
static JackAudioDriver * pJackDriverInstance
Instance of the JackAudioDriver.
QString m_sOutputPortName2
Destination of the right source port m_pOutputPort2, for which a connection will be established in co...
QString m_sOutputPortName1
Destination of the left source port m_pOutputPort1, for which a connection will be established in con...
float * getTrackOut_R(unsigned nTrack)
Get content of right output port of a specific track.
float getTimebaseControllerBpm() const
static int jackDriverSampleRate(jack_nframes_t nframes, void *param)
Callback function for the JACK audio server to set the sample rate jackServerSampleRate.
void clearPerTrackAudioBuffers(uint32_t nFrames)
Resets the buffers contained in m_pTrackOutputPortsL and m_pTrackOutputPortsR.
const jack_position_t & getJackPosition() const
jack_port_t * m_pOutputPort2
Right source port.
int m_trackMap[MAX_INSTRUMENTS][MAX_COMPONENTS]
Matrix containing the track number of each component of all instruments.
void startTransport()
Tells the JACK server to start transport.
virtual float * getOut_R() override
Get content in the right stereo output port.
static int jackServerXRuns
Number of XRuns since the driver started.
virtual unsigned getBufferSize() override
static QString TimebaseTrackingToQString(const TimebaseTracking &t)
void releaseTimebaseControl()
Release Hydrogen from the JACK Timebase control.
void setTrackOutput(int n, std::shared_ptr< Instrument > instr, std::shared_ptr< InstrumentComponent > pCompo, std::shared_ptr< Song > pSong)
Renames the n 'th port of JACK client and creates it if it's not already present.
static double bbtToTick(const jack_position_t &pos)
Timebase
Whether Hydrogen or another program is in Timebase control.
@ Listener
An external program is Timebase controller and Hydrogen will disregard all tempo markers on the Timel...
@ None
Only normal clients registered.
@ Controller
Hydrogen itself is Timebase controller and provides its current tempo to other Timebase listeners.
jack_client_t * m_pClient
Object holding the external client session with the JACK server.
void deactivate()
Deactivates the JACK client of Hydrogen and disconnects all ports belonging to it.
static Timebase TimebaseFromInt(int nState)
float m_fLastTimebaseBpm
Stores the last tempo sent by an external Timebase controller.
int m_lastTransportBits
Remembers the BBT capability bit received in a JACK process cycle.
virtual int connect() override
Connects to output ports via the JACK server.
Timebase getTimebaseState() const
static QString TimebaseToQString(const Timebase &t)
jack_position_t m_JackTransportPos
Current transport position obtained using jack_transport_query() (jack/transport.h).
jack_port_t * m_pOutputPort1
Left source port.
static int jackDriverBufferSize(jack_nframes_t nframes, void *arg)
Callback function for the JACK audio server to set the buffer size jackServerBufferSize.
static jack_nframes_t jackServerBufferSize
Buffer size of the JACK audio server.
void stopTransport()
Tells the JACK server to stop transport.
void updateTransportPosition()
The function queries the transport position and additional information from the JACK server,...
static bool isBBTValid(const jack_position_t &pos)
static void jackDriverShutdown(void *arg)
Callback function for the JACK audio server to shutting down the JACK driver.
TimebaseTracking m_timebaseTracking
Whether the current timebase state is stable or about to change.
static int jackXRunCallback(void *arg)
Report an XRun event to the GUI.
void makeTrackOutputs(std::shared_ptr< Song > pSong)
Creates per component output ports for each instrument.
virtual unsigned getSampleRate() override
static void JackTimebaseCallback(jack_transport_state_t state, jack_nframes_t nFrames, jack_position_t *pJackPosition, int new_pos, void *arg)
Callback function for the JACK server to supply additional timebase information.
JackProcessCallback m_processCallback
Main process callback.
~JackAudioDriver()
Destructor of the JACK server driver.
void printState() const
Show debugging information.
float * getTrackOut_L(unsigned nTrack)
Get content of left output port of a specific track.
jack_transport_state_t m_JackTransportState
Current transport state returned by jack_transport_query() (jack/transport.h).
void relocateUsingBBT()
Uses the bar-beat-tick information to relocate the transport position.
jack_port_t * m_pTrackOutputPortsR[MAX_INSTRUMENTS]
Vector of all right audio output ports currently used by the local JACK client.
void locateTransport(long long nFrame)
Re-positions the transport position to nFrame.
virtual int getXRuns() const override
Get the number of XRuns that occurred since the audio driver has started.
Pattern class is a Note container.
Definition Pattern.h:46
int get_denominator() const
get the note multimap
Definition Pattern.h:350
Manager for User Preferences File (singleton)
Definition Preferences.h:79
int m_bJackTimebaseMode
Specifies if Hydrogen support the of JACK Timebase protocol.
static Preferences * get_instance()
Returns a pointer to the current Preferences singleton stored in __instance.
bool m_bJackTimebaseEnabled
External applications with a faulty JACK Timebase implementation can mess up the transport within Hyd...
@ USE_JACK_TRANSPORT
Specifies whether or not to use JACK transport capabilities.
Definition Preferences.h:90
@ NO_JACK_TIMEBASE_CONTROL
Specifies that Hydrogen should not be in control of JACK Timebase information.
@ USE_JACK_TIMEBASE_CONTROL
Specifies that Hydrogen should attempt to acquire JACK Timebase control.
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
Object holding most of the information about the transport state of the AudioEngine.
const PatternList * getPlayingPatterns() const
static double computeTickFromFrame(long long nFrame, int nSampleRate=0)
Calculates tick equivalent of nFrame.
bool isNewProject()
bool isConnected()
static LashClient * get_instance()
Definition LashClient.h:39
void setJackClientName(const std::string &jackClientName)
Set the name of the JACK client by modifying jackClientName.
void sendJackClientName()
#define MAX_INSTRUMENTS
Maximum number of instruments allowed in Hydrogen.
Definition config.dox:70
#define MAX_NOTES
Maximum number of notes.
Definition config.dox:79
#define MAX_COMPONENTS
Maximum number of components each Instrument is allowed to have.
Definition config.dox:75
#define MAX_BPM
Definition Globals.h:36
#define MIN_BPM
Definition Globals.h:35
#define UNUSED(v)
Definition Globals.h:42
@ 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_JACK_TIMEBASE_STATE_CHANGED
Toggles the button indicating the usage JACK Timebase control and informs the GUI about a state chang...
Definition EventQueue.h:144