hydrogen 1.2.3
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-2024 The hydrogen development team [hydrogen-devel@lists.sourceforge.net]
5 *
6 * http://www.hydrogen-music.org
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY, without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see https://www.gnu.org/licenses
20 *
21 */
22
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#include <QProcess>
34
35#include <core/Hydrogen.h>
42#include <core/Basics/Pattern.h>
45#include <core/Basics/Song.h>
46#include <core/Helpers/Files.h>
49#include <core/Globals.h>
50#include <core/EventQueue.h>
51
52#ifdef H2CORE_HAVE_LASH
54#endif
55
56namespace H2Core {
57
58int JackAudioDriver::jackDriverSampleRate( jack_nframes_t nframes, void* param ){
59 // Used for logging.
60 Base * __object = ( Base * )param;
61 // The __INFOLOG macro uses the Base *__object and not the
62 // Object instance as INFOLOG does. It will call
63 // __object->logger()->log( H2Core::Logger::Info, ..., msg )
64 // (see object.h).
65 __INFOLOG( QString("New JACK sample rate: [%1]/sec")
66 .arg( QString::number( static_cast<int>(nframes) ) ) );
68 return 0;
69}
70
71int JackAudioDriver::jackDriverBufferSize( jack_nframes_t nframes, void* param ){
72 // This function does _NOT_ have to be realtime safe.
73 Base * __object = ( Base * )param;
74 __INFOLOG( QString("new JACK buffer size: [%1]")
75 .arg( QString::number( static_cast<int>(nframes) ) ) );
77 return 0;
78}
79
93
98
99JackAudioDriver::JackAudioDriver( JackProcessCallback m_processCallback )
100 : AudioOutput(),
101 m_nTrackPortCount( 0 ),
102 m_pClient( nullptr ),
103 m_pOutputPort1( nullptr ),
104 m_pOutputPort2( nullptr ),
105 m_nTimebaseTracking( -1 ),
106 m_timebaseState( Timebase::None )
107{
108 auto pPreferences = Preferences::get_instance();
109
110 m_bConnectDefaults = pPreferences->m_bJackConnectDefaults;
111
113 this->m_processCallback = m_processCallback;
114
115
116 // Destination ports the output of Hydrogen will be connected
117 // to.
118 m_sOutputPortName1 = pPreferences->m_sJackPortName1;
119 m_sOutputPortName2 = pPreferences->m_sJackPortName2;
120
121 memset( m_pTrackOutputPortsL, 0, sizeof(m_pTrackOutputPortsL) );
122 memset( m_pTrackOutputPortsR, 0, sizeof(m_pTrackOutputPortsR) );
123
124 m_JackTransportState = JackTransportStopped;
125}
126
131
133{
134 INFOLOG( "connect" );
135
136 // The `jack_activate' function is defined in the jack/jack.h
137 // header files and tells the JACK server that the program is
138 // ready to start processing audio. It returns 0 on success
139 // and a non-zero error code otherwise.
140 if ( jack_activate( m_pClient ) ) {
142 return 1;
143 }
144
145 bool bConnectDefaults = m_bConnectDefaults;
146
147#ifdef H2CORE_HAVE_LASH
148 if ( Preferences::get_instance()->useLash() ){
149 LashClient* lashClient = LashClient::get_instance();
150 if (lashClient && lashClient->isConnected()){
151 // INFOLOG( "[LASH] Sending JACK client name to LASH server" );
152 lashClient->sendJackClientName();
153
154 if (!lashClient->isNewProject()){
155 bConnectDefaults = false;
156 }
157 }
158 }
159#endif
160
161 if ( bConnectDefaults ) {
162 // Connect the left and right default ports of Hydrogen.
163 //
164 // The `jack_connect' function is defined in the
165 // jack/jack.h file. It establishes a connection between
166 // two ports. When a connection exists, data written
167 // to the source port will be available to be read at
168 // the destination port. Returns 0 on success, exits
169 // if the connection is already made, and returns a
170 // non-zero error code otherwise.
171 // Syntax: jack_connect( jack_client_t jack_client,
172 // const char *source_port )
173 // const char *destination_port
174 // )
175 // The `jack_port_name' function is also defined in
176 // the jack/jack.h header returns the full name of a
177 // provided port of type jack_port_t.
178 if ( jack_connect( m_pClient, jack_port_name( m_pOutputPort1 ),
179 m_sOutputPortName1.toLocal8Bit() ) == 0 &&
180 jack_connect( m_pClient, jack_port_name( m_pOutputPort2 ),
181 m_sOutputPortName2.toLocal8Bit() ) == 0 ) {
182 return 0;
183 }
184
185 WARNINGLOG( "Could not connect to the saved output ports. Connect to the first pair of input ports instead." );
186 // The `jack_get_ports' is defined in the jack/jack.h
187 // header file and performs a lookup of ports of the
188 // JACK server based on their e.g. flags. It returns a
189 // NULL-terminated array of ports that match the
190 // specified arguments. The caller is responsible for
191 // calling jack_free() any non-NULL returned
192 // value.
193 const char ** portnames = jack_get_ports( m_pClient, nullptr, nullptr, JackPortIsInput );
194 if ( !portnames || !portnames[0] || !portnames[1] ) {
195 ERRORLOG( "Couldn't locate two Jack input ports" );
197 return 2;
198 }
199 if ( jack_connect( m_pClient, jack_port_name( m_pOutputPort1 ),
200 portnames[0] ) != 0 ||
201 jack_connect( m_pClient, jack_port_name( m_pOutputPort2 ),
202 portnames[1] ) != 0 ) {
203 ERRORLOG( "Couldn't connect to first pair of Jack input ports" );
205 return 2;
206 }
207 free( portnames );
208 }
209
210 return 0;
211}
212
214{
215 INFOLOG( "disconnect" );
216
217 deactivate();
218
219 jack_client_t* pOldClient = m_pClient;
220
221 m_pClient = nullptr;
222
223 if ( pOldClient != nullptr ) {
224 int nReturnCode = jack_client_close( pOldClient );
225 if ( nReturnCode != 0 ) {
226 ERRORLOG( "Error in jack_client_close" );
228 }
229 }
230 m_pClient = nullptr;
231}
232
234{
235 if ( m_pClient != nullptr ) {
236 int nReturnCode = jack_deactivate( m_pClient );
237 if ( nReturnCode != 0 ) {
238 ERRORLOG( "Error in jack_deactivate" );
239 }
240 }
241 memset( m_pTrackOutputPortsL, 0, sizeof(m_pTrackOutputPortsL) );
242 memset( m_pTrackOutputPortsR, 0, sizeof(m_pTrackOutputPortsR) );
243}
244
249
254
256{
257 if ( m_pClient != nullptr &&
258 Preferences::get_instance()->m_bJackTrackOuts ) {
259 float* pBuffer;
260
261 for ( int ii = 0; ii < m_nTrackPortCount; ++ii ) {
262 pBuffer = getTrackOut_L( ii );
263 if ( pBuffer != nullptr ) {
264 memset( pBuffer, 0, nFrames * sizeof( float ) );
265 }
266 pBuffer = getTrackOut_R( ii );
267 if ( pBuffer != nullptr ) {
268 memset( pBuffer, 0, nFrames * sizeof( float ) );
269 }
270 }
271 }
272}
273
275{
276 if ( ! Preferences::get_instance()->m_bJackTimebaseEnabled ) {
277 ERRORLOG( "This function should not have been called with JACK timebase disabled in the Preferences" );
278 return;
279 }
281 ERRORLOG( QString( "Relocation using BBT information can only be used in the presence of another Jack timebase master" ) );
282 return;
283 }
284
285 // Sometime the JACK server does send seemingly random nuisance.
286 if ( m_JackTransportPos.beat_type < 1 ||
287 m_JackTransportPos.bar < 1 ||
288 m_JackTransportPos.beat < 1 ||
289 m_JackTransportPos.beats_per_bar < 1 ||
290 m_JackTransportPos.beats_per_minute < MIN_BPM ||
291 m_JackTransportPos.beats_per_minute > MAX_BPM ||
292 m_JackTransportPos.ticks_per_beat < 1 ) {
293 ERRORLOG( QString( "Unsupported to BBT content. beat_type: %1, bar: %2, beat: %3, beats_per_bar: %4, beats_per_minute: %5, ticks_per_beat: %6" )
294 .arg( m_JackTransportPos.beat_type )
295 .arg( m_JackTransportPos.bar )
296 .arg( m_JackTransportPos.beat )
297 .arg( m_JackTransportPos.beats_per_bar )
298 .arg( m_JackTransportPos.beats_per_minute )
299 .arg( m_JackTransportPos.ticks_per_beat ) );
300 return;
301 }
302
303 Hydrogen* pHydrogen = Hydrogen::get_instance();
304 std::shared_ptr<Song> pSong = pHydrogen->getSong();
305 auto pAudioEngine = pHydrogen->getAudioEngine();
306
307 if ( pSong == nullptr ) {
308 // Expected behavior if Hydrogen is exited while playback is
309 // still running.
310 // DEBUGLOG( "No song set." );
311 return;
312 }
313
314 float fTicksPerBeat = static_cast<float>( pSong->getResolution() / m_JackTransportPos.beat_type * 4 );
315
316 long barTicks = 0;
317 float fAdditionalTicks = 0;
318 float fNumberOfBarsPassed = 0;
319 if ( pHydrogen->getMode() == Song::Mode::Song ) {
320
323 barTicks = pHydrogen->getTickForColumn( m_JackTransportPos.bar - 1 );
324
325 if ( barTicks < 0 ) {
326 barTicks = 0;
327 }
330 // Length of a pattern * fBarConversion provides the number of
331 // bars in Jack's point of view a Hydrogen pattern does cover.
332 float fBarConversion = pSong->getResolution() * 4 *
333 m_JackTransportPos.beats_per_bar /
334 m_JackTransportPos.beat_type;
335 float fNextIncrement = 0;
336 int nBarJack = m_JackTransportPos.bar - 1;
337 int nLargeNumber = 100000;
338 int nMinimumPatternLength = nLargeNumber;
339 int nNumberOfPatternsPassed = 0;
340
341 // Checking how many of Hydrogen's patterns are covered by the
342 // bar provided by Jack.
343 auto pPatternGroup = pSong->getPatternGroupVector();
344 for ( const PatternList* ppPatternList : *pPatternGroup ) {
345 nMinimumPatternLength = nLargeNumber;
346
347 // If there are multiple patterns at a single bar (in
348 // Hydrogen) the length of the shortest one used for
349 // playback.
350 for ( int ii = 0; ii < ppPatternList->size(); ++ii ) {
351 if ( ppPatternList->get( ii )->get_length() <
352 nMinimumPatternLength ) {
353 nMinimumPatternLength = ppPatternList->get( ii )->get_length();
354 }
355 }
356
357 if ( nMinimumPatternLength == nLargeNumber ){
358 fNextIncrement = 0;
359 } else {
360 fNextIncrement =
361 static_cast<float>(nMinimumPatternLength) /
362 fBarConversion;
363 }
364
365 if ( static_cast<float>(nBarJack) < ( fNumberOfBarsPassed + fNextIncrement ) ) {
366 break;
367 }
368
369 fNumberOfBarsPassed += fNextIncrement;
370 ++nNumberOfPatternsPassed;
371 }
372
373 // Position of the resulting pattern in ticks.
374 barTicks = pHydrogen->getTickForColumn( nNumberOfPatternsPassed );
375 if ( barTicks < 0 ) {
376 barTicks = 0;
377 } else if ( fNextIncrement > 1 &&
378 fNumberOfBarsPassed != nBarJack ) {
379 // If pattern is longer than what is considered a bar in
380 // Jack's point of view, some additional ticks have to be
381 // added whenever transport passes the first bar contained
382 // in the pattern.
383 fAdditionalTicks = fTicksPerBeat * 4 *
384 ( fNextIncrement - 1 );
385 }
386
387 // std::cout << "[relocateUsingBBT] "
388 // << "nNumberOfPatternsPassed: " << nNumberOfPatternsPassed
389 // << ", fAdditionalTicks: " << fAdditionalTicks
390 // << ", nBarJack: " << nBarJack
391 // << ", fNumberOfBarsPassed: " << fNumberOfBarsPassed
392 // << ", fBarConversion: " << fBarConversion
393 // << ", barTicks: " << barTicks
394 // << std::endl;
395 } else {
396 ERRORLOG( QString( "Unsupported m_JackBBTSync option [%1]" )
397 .arg( static_cast<int>(Preferences::get_instance()->m_JackBBTSync) ) );
398 }
399 }
400
401 float fNewTick = static_cast<float>(barTicks) + fAdditionalTicks +
402 ( m_JackTransportPos.beat - 1 ) * fTicksPerBeat +
403 m_JackTransportPos.tick * ( fTicksPerBeat / m_JackTransportPos.ticks_per_beat );
404
405 pAudioEngine->locate( fNewTick, false );
406}
407
409{
410 if ( ! Preferences::get_instance()->m_bJackTimebaseEnabled ) {
411 ERRORLOG( "This function should not have been called with JACK timebase disabled in the Preferences" );
412 }
413
414 if ( m_JackTransportPos.beats_per_minute !=
415 m_previousJackTransportPos.beats_per_minute ) {
416 // DEBUGLOG( QString( "Change in tempo from [%1] to [%2]" )
417 // .arg( m_previousJackTransportPos.beats_per_minute )
418 // .arg( m_JackTransportPos.beats_per_minute ) );
419 return false;
420 }
421
422 double expectedTickUpdate =
424 m_JackTransportPos.beats_per_minute *
425 m_JackTransportPos.ticks_per_beat /
426 m_JackTransportPos.frame_rate / 60;
427
428 int32_t nNewTick = m_previousJackTransportPos.tick +
429 floor( expectedTickUpdate );
430
431 // The rounding is the task of the external timebase master. So,
432 // we need to be a little generous in here to be sure to match its
433 // decision.
434 if ( m_JackTransportPos.tick != nNewTick &&
435 nNewTick + 1 >= m_JackTransportPos.ticks_per_beat ) {
436 nNewTick = remainder( nNewTick, m_JackTransportPos.ticks_per_beat );
437
438 if ( m_previousJackTransportPos.beat + 1 >
439 m_previousJackTransportPos.beats_per_bar ) {
440 if ( m_JackTransportPos.bar !=
442 m_JackTransportPos.beat != 1 ) {
443 // DEBUGLOG( QString( "Change in position from bar:beat [%1]:[%2] to [%3]:[%4]*" )
444 // .arg( m_previousJackTransportPos.bar )
445 // .arg( m_previousJackTransportPos.beat )
446 // .arg( m_JackTransportPos.bar )
447 // .arg( m_JackTransportPos.beat ) );
448 return false;
449 }
450 } else {
451 if ( m_JackTransportPos.bar !=
453 m_JackTransportPos.beat !=
454 m_previousJackTransportPos.beat + 1 ) {
455 // DEBUGLOG( QString( "Change in position from bar:beat [%1]:[%2] to [%3]:[%4]**" )
456 // .arg( m_previousJackTransportPos.bar )
457 // .arg( m_previousJackTransportPos.beat )
458 // .arg( m_JackTransportPos.bar )
459 // .arg( m_JackTransportPos.beat ) );
460 return false;
461 }
462 }
463 } else if ( m_JackTransportPos.bar !=
465 m_JackTransportPos.beat !=
467 // DEBUGLOG( QString( "Change in position from bar:beat [%1]:[%2] to [%3]:[%4]***" )
468 // .arg( m_previousJackTransportPos.bar )
469 // .arg( m_previousJackTransportPos.beat )
470 // .arg( m_JackTransportPos.bar )
471 // .arg( m_JackTransportPos.beat ) );
472 return false;
473 }
474
475 if ( abs( m_JackTransportPos.tick - nNewTick ) > 1 &&
476 abs( m_JackTransportPos.tick -
477 m_JackTransportPos.ticks_per_beat - nNewTick ) > 1 &&
478 abs( m_JackTransportPos.tick +
479 m_JackTransportPos.ticks_per_beat - nNewTick ) > 1 ) {
480 // DEBUGLOG( QString( "Change in position from tick [%1] to [%2] instead of [%3]" )
481 // .arg( m_previousJackTransportPos.tick )
482 // .arg( m_JackTransportPos.tick )
483 // .arg( nNewTick ));
484 return false;
485 }
486
487 return true;
488}
489
491{
492 if ( Preferences::get_instance()->m_bJackTransportMode !=
494 return;
495 }
496
497 auto pHydrogen = Hydrogen::get_instance();
498 auto pAudioEngine = pHydrogen->getAudioEngine();
499
500 const bool bTimebaseEnabled = Preferences::get_instance()->m_bJackTimebaseEnabled;
501
502 // jack_transport_query() (jack/transport.h) queries the
503 // current transport state and position. If called from the
504 // process thread, the second argument, which is a pointer to
505 // a structure for returning current transport, corresponds to
506 // the first frame of the current cycle and the state returned
507 // is valid for the entire cycle. #m_JackTransportPos.valid
508 // will show which fields contain valid data. If
509 // #m_JackTransportPos is NULL, do not return position
510 // information.
511 m_JackTransportState = jack_transport_query( m_pClient, &m_JackTransportPos );
512
513 switch ( m_JackTransportState ) {
514 case JackTransportStopped: // Transport is halted
515 pAudioEngine->setNextState( AudioEngine::State::Ready );
516 break;
517
518 case JackTransportRolling: // Transport is playing
519 pAudioEngine->setNextState( AudioEngine::State::Playing );
520 break;
521
522 case JackTransportStarting:
523 // Waiting for sync ready. If there are slow-sync clients,
524 // this can take more than one cycle.
525 pAudioEngine->setNextState( AudioEngine::State::Ready );
526 break;
527
528 default:
529 ERRORLOG( "Unknown jack transport state" );
530 }
531
532 if ( pHydrogen->getSong() == nullptr ) {
533 // Expected behavior if Hydrogen is exited while playback is
534 // still running.
535 // DEBUGLOG( "No song set." );
536 return;
537 }
538
539 if ( bTimebaseEnabled ) {
540 // Update the status regrading JACK timebase master.
541 if ( m_JackTransportState != JackTransportStopped ) {
542 if ( m_nTimebaseTracking > 1 ) {
544 } else if ( m_nTimebaseTracking == 1 ) {
545 // JackTimebaseCallback not called anymore -> timebase client
549 static_cast<int>(Timebase::Slave) );
550 }
551 }
552 if ( m_nTimebaseTracking == 0 &&
553 !( m_JackTransportPos.valid & JackPositionBBT ) ) {
554 // No external timebase master anymore -> regular client
558 static_cast<int>(Timebase::None) );
559 } else if ( m_nTimebaseTracking < 0 &&
560 ( m_JackTransportPos.valid & JackPositionBBT ) ) {
561 // External timebase master detected -> timebase client
565 static_cast<int>(Timebase::Slave) );
566 }
567 }
568
569 // The relocation could be either triggered by an user interaction
570 // (e.g. clicking the forward button or clicking somewhere on the
571 // timeline) or by a different JACK client.
572 if ( ( pAudioEngine->getTransportPosition()->getFrame() -
573 pAudioEngine->getTransportPosition()->getFrameOffsetTempo() ) !=
574 m_JackTransportPos.frame ) {
575
576 // DEBUGLOG( QString( "[relocation detected] frames: %1, offset: %2, Jack frames: %3" )
577 // .arg( pAudioEngine->getTransportPosition()->getFrame() )
578 // .arg( pAudioEngine->getTransportPosition()->getFrameOffsetTempo() )
579 // .arg( m_JackTransportPos.frame ) );
580
581 if ( ! bTimebaseEnabled || m_timebaseState != Timebase::Slave ) {
582 pAudioEngine->locateToFrame( m_JackTransportPos.frame );
583 } else {
585 }
586 }
587
588 if ( bTimebaseEnabled && m_timebaseState == Timebase::Slave ){
590
591 // There is a JACK timebase master and it's not us. If it
592 // provides a tempo that differs from the local one, we will
593 // use the former instead.
594 if ( pAudioEngine->getTransportPosition()->getBpm() !=
595 static_cast<float>(m_JackTransportPos.beats_per_minute ) ||
596 !compareAdjacentBBT() ) {
598 }
599 }
600}
601
603{
612 jack_default_audio_sample_t *out = static_cast<jack_default_audio_sample_t*>(jack_port_get_buffer( m_pOutputPort1, JackAudioDriver::jackServerBufferSize ));
613 return out;
614}
615
617{
618 jack_default_audio_sample_t *out = static_cast<jack_default_audio_sample_t*>(jack_port_get_buffer( m_pOutputPort2, JackAudioDriver::jackServerBufferSize ));
619 return out;
620}
621
622float* JackAudioDriver::getTrackOut_L( unsigned nTrack )
623{
624 if ( nTrack > static_cast<unsigned>(m_nTrackPortCount) ) {
625 return nullptr;
626 }
627
628 jack_port_t* pPort = m_pTrackOutputPortsL[nTrack];
629 jack_default_audio_sample_t* out = nullptr;
630 if( pPort ) {
631 out = static_cast<jack_default_audio_sample_t*>(jack_port_get_buffer( pPort, JackAudioDriver::jackServerBufferSize));
632 }
633 return out;
634}
635
636float* JackAudioDriver::getTrackOut_R( unsigned nTrack )
637{
638 if( nTrack > static_cast<unsigned>(m_nTrackPortCount) ) {
639 return nullptr;
640 }
641
642 jack_port_t* pPort = m_pTrackOutputPortsR[nTrack];
643 jack_default_audio_sample_t* out = nullptr;
644 if( pPort ) {
645 out = static_cast<jack_default_audio_sample_t*>(jack_port_get_buffer( pPort, JackAudioDriver::jackServerBufferSize));
646 }
647 return out;
648}
649
650float* JackAudioDriver::getTrackOut_L( std::shared_ptr<Instrument> instr, std::shared_ptr<InstrumentComponent> pCompo)
651{
652 return getTrackOut_L(m_trackMap[instr->get_id()][pCompo->get_drumkit_componentID()]);
653}
654
655float* JackAudioDriver::getTrackOut_R( std::shared_ptr<Instrument> instr, std::shared_ptr<InstrumentComponent> pCompo)
656{
657 return getTrackOut_R(m_trackMap[instr->get_id()][pCompo->get_drumkit_componentID()]);
658}
659
660
661#define CLIENT_FAILURE(msg) { \
662 ERRORLOG("Could not connect to JACK server (" msg ")"); \
663 if ( m_pClient != nullptr ) { \
664 ERRORLOG("...but JACK returned a non-null pointer?"); \
665 m_pClient = nullptr; \
666 } \
667 if (nTries) ERRORLOG("...trying again."); \
668}
669
670
671#define CLIENT_SUCCESS(msg) { \
672 assert(m_pClient); \
673 INFOLOG(msg); \
674 nTries = 0; \
675}
676
677int JackAudioDriver::init( unsigned bufferSize )
678{
679 auto pPreferences = Preferences::get_instance();
680
681 QString sClientName = "Hydrogen";
682
683#ifdef H2CORE_HAVE_OSC
684 QString sNsmClientId = pPreferences->getNsmClientId();
685
686 if( !sNsmClientId.isEmpty() ){
687 sClientName = sNsmClientId;
688 }
689#endif
690 // The address of the status object will be used by JACK to
691 // return information from the open operation.
692 jack_status_t status;
693 // Sometimes jackd doesn't stop and start fast enough.
694 int nTries = 2;
695 while ( nTries > 0 ) {
696 --nTries;
697
698 // Open an external client session with the JACK
699 // server. The `jack_client_open' function is defined
700 // in the jack/jack.h header. With it, clients may
701 // choose which of several servers to connect, and
702 // control whether and how to start the server
703 // automatically, if it was not already running. Its
704 // first argument _client_name_ of is at most
705 // jack_client_name_size() characters. The name scope
706 // is local to each server. Unless forbidden by the
707 // JackUseExactName option, the server will modify
708 // this name to create a unique variant, if
709 // needed. The second argument _options_ is formed by
710 // OR-ing together JackOptions bits. Only the
711 // JackOpenOptions bits are allowed. _status_ (if
712 // non-NULL) is an address for JACK to return
713 // information from the open operation. This status
714 // word is formed by OR-ing together the relevant
715 // JackStatus bits. Depending on the _status_, an
716 // optional argument _server_name_ selects from among
717 // several possible concurrent server
718 // instances. Server names are unique to each user. It
719 // returns an opaque client handle if successful. If
720 // this is NULL, the open operation failed, *status
721 // includes JackFailure and the caller is not a JACK
722 // client.
723 m_pClient = jack_client_open( sClientName.toLocal8Bit(),
724 JackNullOption,
725 &status);
726
727 // Check what did happen during the opening of the
728 // client. CLIENT_SUCCESS sets the nTries variable
729 // to 0 while CLIENT_FAILURE resets m_pClient to the
730 // nullptr.
731 switch(status) {
732 case JackFailure:
733 CLIENT_FAILURE("unknown error");
734 break;
735 case JackInvalidOption:
736 CLIENT_FAILURE("invalid option");
737 break;
738 case JackNameNotUnique:
739 if ( m_pClient != nullptr ) {
740 sClientName = jack_get_client_name(m_pClient);
741 CLIENT_SUCCESS(QString("Jack assigned the client name '%1'").arg(sClientName));
742 } else {
743 CLIENT_FAILURE("name not unique");
744 }
745 break;
746 case JackServerStarted:
747 CLIENT_SUCCESS("JACK Server started for Hydrogen.");
748 break;
749 case JackServerFailed:
750 CLIENT_FAILURE("unable to connect");
751 break;
752 case JackServerError:
753 CLIENT_FAILURE("communication error");
754 break;
755 case JackNoSuchClient:
756 CLIENT_FAILURE("unknown client type");
757 break;
758 case JackLoadFailure:
759 CLIENT_FAILURE("can't load internal client");
760 break;
761 case JackInitFailure:
762 CLIENT_FAILURE("can't initialize client");
763 break;
764 case JackShmFailure:
765 CLIENT_FAILURE("unable to access shared memory");
766 break;
767 case JackVersionError:
768 CLIENT_FAILURE("client/server protocol version mismatch");
769 break;
770 default:
771 if (status) {
772 ERRORLOG("Unknown status with JACK server.");
773 if ( m_pClient != nullptr ) {
774 CLIENT_SUCCESS("Client pointer is *not* null..."
775 " assuming we're OK");
776 }
777 } else {
778 CLIENT_SUCCESS("Connected to JACK server");
779 }
780 }
781 }
782
783 if ( m_pClient == nullptr ) {
784 return -1;
785 }
786 JackAudioDriver::jackServerSampleRate = jack_get_sample_rate( m_pClient );
787 JackAudioDriver::jackServerBufferSize = jack_get_buffer_size( m_pClient );
788
789 pPreferences->m_nSampleRate = JackAudioDriver::jackServerSampleRate;
790 pPreferences->m_nBufferSize = JackAudioDriver::jackServerBufferSize;
791
792 /* tell the JACK server to call `process()' whenever
793 there is work to be done.
794 */
795 jack_set_process_callback( m_pClient, this->m_processCallback, nullptr );
796
797 /* tell the JACK server to call `srate()' whenever
798 the sample rate of the system changes.
799 */
800 jack_set_sample_rate_callback( m_pClient, jackDriverSampleRate, this );
801
802 /* tell JACK server to update us if the buffer size
803 (frames per process cycle) changes.
804 */
805 jack_set_buffer_size_callback( m_pClient, jackDriverBufferSize, this );
806
807 /* display an XRun event in the GUI.*/
808 jack_set_xrun_callback( m_pClient, jackXRunCallback, nullptr );
809
810 /* tell the JACK server to call `jack_shutdown()' if
811 it ever shuts down, either entirely, or if it
812 just decides to stop calling us.
813 */
814 jack_on_shutdown( m_pClient, jackDriverShutdown, nullptr );
815
816 // Create two new ports for Hydrogen's client. These are
817 // objects used for moving data of any type in or out of the
818 // client. Ports may be connected in various ways. The
819 // function `jack_port_register' (jack/jack.h) is called like
820 // jack_port_register( jack_client_t *client,
821 // const char *port_name,
822 // const char *port_type,
823 // unsigned long flags,
824 // unsigned long buffer_size)
825 //
826 // All ports have a type, which may be any non-NULL and non-zero
827 // length string, passed as an argument. Some port types are built
828 // into the JACK API, currently only JACK_DEFAULT_AUDIO_TYPE.
829 // It returns a _jack_port_t_ pointer on success, otherwise NULL.
830 m_pOutputPort1 = jack_port_register( m_pClient, "out_L", JACK_DEFAULT_AUDIO_TYPE,
831 JackPortIsOutput, 0 );
832 jack_set_property( m_pClient, jack_port_uuid( m_pOutputPort1 ),
833 JACK_METADATA_PRETTY_NAME, "Main Output L", "text/plain" );
834 m_pOutputPort2 = jack_port_register( m_pClient, "out_R", JACK_DEFAULT_AUDIO_TYPE,
835 JackPortIsOutput, 0 );
836 jack_set_property( m_pClient, jack_port_uuid( m_pOutputPort2 ),
837 JACK_METADATA_PRETTY_NAME, "Main Output R", "text/plain" );
838 Hydrogen* pHydrogen = Hydrogen::get_instance();
839 if ( ( m_pOutputPort1 == nullptr ) || ( m_pOutputPort2 == nullptr ) ) {
841 return 4;
842 }
843
844#ifdef H2CORE_HAVE_LASH
845 if ( pPreferences->useLash() ){
846 LashClient* lashClient = LashClient::get_instance();
847 if ( lashClient->isConnected() ) {
848 lashClient->setJackClientName(sClientName.toLocal8Bit().constData());
849 }
850 }
851#endif
852
853 if ( pPreferences->m_bJackTransportMode == Preferences::USE_JACK_TRANSPORT &&
854 pPreferences->m_bJackMasterMode == Preferences::USE_JACK_TIME_MASTER &&
855 pPreferences->m_bJackTimebaseEnabled ){
857 }
858
859 // TODO: Apart from the makeTrackOutputs() all other calls should
860 // be redundant.
861 //
862 // Whenever there is a Song present, create per track outputs (if
863 // activated in the Preferences).
864 std::shared_ptr<Song> pSong = pHydrogen->getSong();
865 if ( pSong != nullptr ) {
866 makeTrackOutputs( pSong );
867 }
868
869 return 0;
870}
871
872void JackAudioDriver::makeTrackOutputs( std::shared_ptr<Song> pSong )
873{
874 if( Preferences::get_instance()->m_bJackTrackOuts == false ) {
875 return;
876 }
877
878 auto pInstrumentList = pSong->getInstrumentList();
879 std::shared_ptr<Instrument> pInstrument;
880 int nInstruments = static_cast<int>(pInstrumentList->size());
881
882 WARNINGLOG( QString( "Creating / renaming %1 ports" ).arg( nInstruments ) );
883
884 int nTrackCount = 0;
885
886 for( int i = 0 ; i < MAX_INSTRUMENTS ; i++ ){
887 for ( int j = 0 ; j < MAX_COMPONENTS ; j++ ){
888 m_trackMap[i][j] = 0;
889 }
890 }
891 // Creates a new output track or reassigns an existing one for
892 // each component of each instrument and stores the result in
893 // the `m_trackMap'.
894 std::shared_ptr<InstrumentComponent> pInstrumentComponent;
895 for ( int n = 0; n <= nInstruments - 1; n++ ) {
896 pInstrument = pInstrumentList->get( n );
897 for ( auto& pInstrumentComponent : *pInstrument->get_components() ) {
898 setTrackOutput( nTrackCount, pInstrument, pInstrumentComponent, pSong);
899 m_trackMap[pInstrument->get_id()][pInstrumentComponent->get_drumkit_componentID()] =
900 nTrackCount;
901 nTrackCount++;
902 }
903 }
904 // clean up unused ports
905 jack_port_t *pPortL, *pPortR;
906 for ( int n = nTrackCount; n < m_nTrackPortCount; n++ ) {
907 pPortL = m_pTrackOutputPortsL[n];
908 pPortR = m_pTrackOutputPortsR[n];
909 m_pTrackOutputPortsL[n] = nullptr;
910 jack_port_unregister( m_pClient, pPortL );
911 m_pTrackOutputPortsR[n] = nullptr;
912 jack_port_unregister( m_pClient, pPortR );
913 }
914
915 m_nTrackPortCount = nTrackCount;
916}
917
918void JackAudioDriver::setTrackOutput( int n, std::shared_ptr<Instrument> pInstrument, std::shared_ptr<InstrumentComponent> pInstrumentComponent, std::shared_ptr<Song> pSong )
919{
920 QString sComponentName;
921
922 // The function considers `m_nTrackPortCount' as the number of
923 // ports already present. If it's smaller than `n', new ports
924 // have to be created.
925 if ( m_nTrackPortCount <= n ) {
926 for ( int m = m_nTrackPortCount; m <= n; m++ ) {
927 sComponentName = QString( "Track_%1_" ).arg( m + 1 );
929 jack_port_register( m_pClient, ( sComponentName + "L" ).toLocal8Bit(),
930 JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 );
931
933 jack_port_register( m_pClient, ( sComponentName + "R" ).toLocal8Bit(),
934 JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 );
935
936 if ( ! m_pTrackOutputPortsR[m] || ! m_pTrackOutputPortsL[m] ) {
938 }
939 }
940 m_nTrackPortCount = n + 1;
941 }
942
943 // Now that we're sure there is an n'th port, rename it.
944 auto pDrumkitComponent = pSong->getComponent( pInstrumentComponent->get_drumkit_componentID() );
945 sComponentName = QString( "Track_%1_%2_%3_" ).arg( n + 1 )
946 .arg( pInstrument->get_name() ).arg( pDrumkitComponent->get_name() );
947
948#ifdef HAVE_JACK_PORT_RENAME
949 // This differs from jack_port_set_name() by triggering
950 // PortRename notifications to clients that have registered a
951 // port rename handler.
952 jack_port_rename( m_pClient, m_pTrackOutputPortsL[n], ( sComponentName + "L" ).toLocal8Bit() );
953 jack_port_rename( m_pClient, m_pTrackOutputPortsR[n], ( sComponentName + "R" ).toLocal8Bit() );
954#else
955 jack_port_set_name( m_pTrackOutputPortsL[n], ( sComponentName + "L" ).toLocal8Bit() );
956 jack_port_set_name( m_pTrackOutputPortsR[n], ( sComponentName + "R" ).toLocal8Bit() );
957#endif
958}
959
961{
962 if ( m_pClient != nullptr ) {
963 jack_transport_start( m_pClient );
964 } else {
965 ERRORLOG( "No client registered" );
966 }
967}
968
970{
971 if ( m_pClient != nullptr ) {
972 jack_transport_stop( m_pClient );
973 } else {
974 ERRORLOG( "No client registered" );
975 }
976}
977
978void JackAudioDriver::locateTransport( long long nFrame )
979{
980 auto pHydrogen = Hydrogen::get_instance();
981
982 if ( m_pClient != nullptr ) {
983 // jack_transport_locate() (jack/transport.h )
984 // re-positions the transport to a new frame number. May
985 // be called at any time by any client.
986 jack_transport_locate( m_pClient, nFrame );
987 } else {
988 ERRORLOG( "No client registered" );
989 }
990}
991
993{
994 if ( m_pClient == nullptr ) {
995 return;
996 }
997
998 if ( ! Preferences::get_instance()->m_bJackTimebaseEnabled ) {
999 ERRORLOG( "This function should not have been called with JACK timebase disabled in the Preferences" );
1000 return;
1001 }
1002
1003 Preferences* pPreferences = Preferences::get_instance();
1005 // Defined in jack/transport.h
1006 // Register as timebase master for the JACK
1007 // subsystem.
1008 //
1009 // The timebase master registers a callback that
1010 // updates extended position information such as
1011 // beats or timecode whenever necessary. Without
1012 // this extended information, there is no need for
1013 // this function.
1014 //
1015 // There is never more than one master at a time.
1016 // When a new client takes over, the former @a
1017 // timebase_callback is no longer called. Taking
1018 // over the timebase may be done conditionally, so
1019 // it fails if there was a master already.
1020 //
1021 // @param client the JACK client structure.
1022 // @param conditional non-zero for a conditional
1023 // request.
1024 // @param timebase_callback is a realtime function
1025 // that returns position information.
1026 // @param arg an argument for the @a timebase_callback
1027 // function.
1028 // @return
1029 // - 0 on success;
1030 // - EBUSY if a conditional request fails because
1031 // there was already a timebase master;
1032 // - other non-zero error code.
1033 int nReturnValue = jack_set_timebase_callback(m_pClient, 0,
1034 JackTimebaseCallback, this);
1035 if ( nReturnValue != 0 ){
1037 WARNINGLOG( QString( "Hydrogen was not able to register itself as Timebase Master: [%1]" )
1038 .arg( nReturnValue ) );
1039 }
1040 else {
1044 static_cast<int>(Timebase::Master) );
1045 }
1046 } else {
1048 }
1049}
1050
1052{
1053 if ( m_pClient == nullptr ) {
1054 ERRORLOG( QString( "Not fully initialized yet" ) );
1055 return;
1056 }
1057
1058 if ( ! Preferences::get_instance()->m_bJackTimebaseEnabled ) {
1059 ERRORLOG( "This function should not have been called with JACK timebase disabled in the Preferences" );
1060 return;
1061 }
1062
1063 jack_release_timebase( m_pClient );
1064
1065 if ( m_JackTransportPos.valid & JackPositionBBT ) {
1069 static_cast<int>(Timebase::Slave) );
1070 } else {
1074 static_cast<int>(Timebase::None) );
1075 }
1076}
1077
1078void JackAudioDriver::JackTimebaseCallback(jack_transport_state_t state,
1079 jack_nframes_t nFrames,
1080 jack_position_t* pJackPosition,
1081 int new_pos,
1082 void *arg)
1083{
1084 JackAudioDriver* pDriver = static_cast<JackAudioDriver*>(arg);
1085 if ( pDriver == nullptr ){
1086 return;
1087 }
1088
1089 Hydrogen* pHydrogen = Hydrogen::get_instance();
1090 std::shared_ptr<Song> pSong = pHydrogen->getSong();
1091 auto pPos = pHydrogen->getAudioEngine()->getTransportPosition();
1092 if ( pSong == nullptr ) {
1093 // DEBUGLOG( "No song set." );
1094 return;
1095 }
1096
1097 // Current pattern
1098 Pattern* pPattern;
1099 auto pPatternList = pHydrogen->getSong()->getPatternList();
1100 int nPatternNumber = pHydrogen->getSelectedPatternNumber();
1101 if ( nPatternNumber != -1 &&
1102 nPatternNumber < pPatternList->size() ) {
1103 pPattern = pPatternList->get( nPatternNumber );
1104 }
1105
1106 float fNumerator, fDenumerator, fTicksPerBar;
1107 if ( pPattern != nullptr ) {
1108 fNumerator = pPattern->get_length() *
1109 pPattern->get_denominator() / MAX_NOTES;
1110 fDenumerator = pPattern->get_denominator();
1111 fTicksPerBar = pPattern->get_length();
1112 } else {
1113 fNumerator = 4;
1114 fDenumerator = 4;
1115 fTicksPerBar = MAX_NOTES;
1116 }
1117
1118 pJackPosition->ticks_per_beat = fTicksPerBar;
1119 pJackPosition->valid = JackPositionBBT;
1120 // Time signature "numerator"
1121 pJackPosition->beats_per_bar = fNumerator;
1122 // Time signature "denominator"
1123 pJackPosition->beat_type = fDenumerator;
1124 pJackPosition->beats_per_minute = static_cast<double>(pPos->getBpm());
1125
1126 if ( pPos->getFrame() < 1 ) {
1127 pJackPosition->bar = 1;
1128 pJackPosition->beat = 1;
1129 pJackPosition->tick = 0;
1130 pJackPosition->bar_start_tick = 0;
1131 } else {
1132 // +1 since the counting bars starts at 1.
1133 pJackPosition->bar = pPos->getColumn() + 1;
1134
1135 // Number of ticks that have elapsed between frame 0 and the
1136 // first beat of the next measure.
1137 pJackPosition->bar_start_tick = pPos->getPatternStartTick();
1138
1139 pJackPosition->beat = pPos->getPatternTickPosition() /
1140 pJackPosition->ticks_per_beat;
1141 // +1 since the counting beats starts at 1.
1142 pJackPosition->beat++;
1143
1144 // Counting ticks starts at 0.
1145 pJackPosition->tick = pPos->getPatternTickPosition();
1146
1147 }
1148
1149 // JackAudioDriver::printJackTransportPos( pJackPosition );
1150
1151 // Tell Hydrogen it is still timebase master.
1152 pDriver->m_nTimebaseTracking = 2;
1153}
1154
1155
1157 if ( Preferences::get_instance()->m_bJackTimebaseEnabled ) {
1158 return m_timebaseState;
1159 }
1160 return Timebase::None;
1161}
1163 if ( !( m_JackTransportPos.valid & JackPositionBBT ) ||
1165 return std::nan("no tempo, no masters");
1166 }
1167
1168 return static_cast<float>(m_JackTransportPos.beats_per_minute );
1169}
1170
1171
1175
1177
1178 auto pHydrogen = Hydrogen::get_instance();
1179
1181
1182 std::cout << "\033[35m[Hydrogen] [JackAudioDriver state]"
1183 << ", m_JackTransportState: " << m_JackTransportState
1184 << ", m_timebaseState: " << static_cast<int>(m_timebaseState)
1185 << ", current pattern column: "
1186 << pHydrogen->getAudioEngine()->getTransportPosition()->getColumn()
1187 << "\33[0m" << std::endl;
1188}
1189
1190
1191void JackAudioDriver::printJackTransportPos( const jack_position_t* pPos ) {
1192 std::cout << "\033[36m[Hydrogen] [JACK transport]"
1193 << " frame: " << pPos->frame
1194 << ", frame_rate: " << pPos->frame_rate
1195 << std::hex << ", valid: 0x" << pPos->valid
1196 << std::dec << ", bar: " << pPos->bar
1197 << ", beat: " << pPos->beat
1198 << ", tick: " << pPos->tick
1199 << ", bar_start_tick: " << pPos->bar_start_tick
1200 << ", beats_per_bar: " << pPos->beats_per_bar
1201 << ", beat_type: " << pPos->beat_type
1202 << ", ticks_per_beat: " << pPos->ticks_per_beat
1203 << ", beats_per_minute: " << pPos->beats_per_minute
1204 << ", frame_time: " << pPos->frame_time
1205 << ", next_time: " << pPos->next_time
1206 << "\033[0m" << std::endl;
1207}
1208
1210
1211 bool bJackFound;
1212
1213 // Classic JACK
1214 QString sCapture = checkExecutable( "jackd", "--version" );
1215 if ( ! sCapture.isEmpty() ) {
1216 bJackFound = true;
1217 INFOLOG( QString( "'jackd' of version [%1] found." )
1218 .arg( sCapture ) );
1219 }
1220
1221 // JACK compiled with DBus support (maybe this one is packaged but
1222 // the classical one isn't).
1223 //
1224 // `jackdbus` is supposed to be run by the DBus message daemon and
1225 // does not have proper CLI options. But it does not fail by
1226 // passing a `-h` either and this will serve for checking its
1227 // presence.
1228 sCapture = checkExecutable( "jackdbus", "-h" );
1229 if ( ! sCapture.isEmpty() ) {
1230 bJackFound = true;
1231 INFOLOG( "'jackdbus' found." );
1232 }
1233
1234 // Pipewire JACK interface
1235 //
1236 // `pw-jack` has no version query CLI option (yet). But showing
1237 // the help will serve for checking its presence.
1238 sCapture = checkExecutable( "pw-jack", "-h" );
1239 if ( ! sCapture.isEmpty() ) {
1240 bJackFound = true;
1241 INFOLOG( "'pw-jack' found." );
1242 }
1243
1244 return bJackFound;
1245}
1246
1247QString JackAudioDriver::checkExecutable( const QString& sExecutable, const QString& sOption ) {
1248 QProcess process;
1249 process.start( sExecutable, QStringList( sOption ) );
1250 process.waitForFinished( -1 );
1251
1252 if ( process.exitCode() != 0 ) {
1253 return "";
1254 }
1255
1256 QString sStdout = process.readAllStandardOutput();
1257 if ( sStdout.isEmpty() ) {
1258 return "No output";
1259 }
1260
1261 return sStdout.trimmed();
1262}
1263};
1264
1265#endif // H2CORE_HAVE_JACK
#define CLIENT_FAILURE(msg)
#define CLIENT_SUCCESS(msg)
#define INFOLOG(x)
Definition Object.h:237
#define WARNINGLOG(x)
Definition Object.h:238
#define __INFOLOG(x)
Definition Object.h:249
#define ERRORLOG(x)
Definition Object.h:239
@ Playing
Transport is rolling.
@ Ready
Ready to process audio.
const std::shared_ptr< TransportPosition > getTransportPosition() const
Base abstract class for audio output classes.
Definition AudioOutput.h:39
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
long getTickForColumn(int nColumn) const
Get the total number of ticks passed up to a nColumn / pattern group.
std::shared_ptr< Song > getSong() const
Get the current song.
Definition Hydrogen.h:122
Song::Mode getMode() const
int getSelectedPatternNumber() const
Definition Hydrogen.h:660
static Hydrogen * get_instance()
Returns the current Hydrogen instance __instance.
Definition Hydrogen.h:83
AudioEngine * getAudioEngine() const
Definition Hydrogen.h:649
@ JACK_CANNOT_ACTIVATE_CLIENT
Definition Hydrogen.h:254
@ JACK_ERROR_IN_PORT_REGISTER
Unable to register output ports for the JACK client using jack_port_register() (jack/jack....
Definition Hydrogen.h:278
@ 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:271
@ JACK_CANNOT_CONNECT_OUTPUT_PORT
Unable to connect either the JackAudioDriver::output_port_1 and the JackAudioDriver::output_port_name...
Definition Hydrogen.h:265
void raiseError(unsigned nErrorCode)
Definition Hydrogen.cpp:774
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 checkExecutable(const QString &sExecutable, const QString &sOption)
Calls sExecutable in a subprocess using the sOption CLI option and reports the results.
jack_port_t * m_pTrackOutputPortsL[MAX_INSTRUMENTS]
Vector of all left audio output ports currently used by the local JACK client.
bool compareAdjacentBBT() const
Compares the BBT information stored in m_JackTransportPos and m_previousJackTransportPos with respect...
virtual float * getOut_L() override
Get content in the left stereo output port.
Timebase m_timebaseState
More user-friendly version of m_nTimebaseTracking.
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.
int m_nTimebaseTracking
Whether Hydrogen or another program is Jack timebase master.
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.
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.
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
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.
Timebase
Whether Hydrogen or another program is Jack timebase master.
@ None
Only normal clients registered.
@ Master
Hydrogen itself is timebase master.
@ Slave
An external program is timebase master and Hydrogen will disregard all tempo markers on the Timeline ...
jack_client_t * m_pClient
Object holding the external client session with the JACK server.
static void printJackTransportPos(const jack_position_t *pPos)
void deactivate()
Deactivates the JACK client of Hydrogen and disconnects all ports belonging to it.
virtual int connect() override
Connects to output ports via the JACK server.
Timebase getTimebaseState() const
jack_position_t m_JackTransportPos
Current transport position obtained using jack_transport_query() (jack/transport.h).
jack_position_t m_previousJackTransportPos
Used for detecting changes in the BBT transport information with external timebase master application...
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 void jackDriverShutdown(void *arg)
Callback function for the JACK audio server to shutting down the JACK driver.
static int jackXRunCallback(void *arg)
Report an XRun event to the GUI.
void initTimebaseMaster()
Registers Hydrogen as JACK timebase master.
static bool checkSupport()
Attempts to call several JACK executables in order to check for existing JACK support.
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 releaseTimebaseMaster()
Release Hydrogen from the JACK timebase master responsibilities.
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.
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
int get_denominator() const
get the note multimap
Definition Pattern.h:350
Manager for User Preferences File (singleton)
Definition Preferences.h:78
static Preferences * get_instance()
Returns a pointer to the current Preferences singleton stored in __instance.
int m_bJackMasterMode
Specifies if Hydrogen should run as JACK time master.
JackBBTSyncMethod m_JackBBTSync
Since Hydrogen uses both fixed pattern lengths and recalculates the tick size each time it encounters...
bool m_bJackTimebaseEnabled
External applications with a faulty JACK timebase master implementation can mess up the transport wit...
@ constMeasure
The measure - could be any - does not change during the song.
@ identicalBars
The length of each pattern must match the measure of the corresponding bar in the timebase master.
@ USE_JACK_TIME_MASTER
Specifies that Hydrogen is using in the time master mode and will thus control specific aspects of th...
Definition Preferences.h:96
@ NO_JACK_TIME_MASTER
Specifies that Hydrogen is note using in the time master mode.
@ USE_JACK_TRANSPORT
Specifies whether or not to use JACK transport capabilities.
Definition Preferences.h:89
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_XRUN
Definition EventQueue.h:85
@ EVENT_JACK_TIMEBASE_STATE_CHANGED
Toggles the button indicating the usage Jack timebase master and informs the GUI about a state change...
Definition EventQueue.h:144