24#if defined(H2CORE_HAVE_JACK) || _DOXYGEN_
32#include <jack/metadata.h>
52#ifdef H2CORE_HAVE_LASH
65 __INFOLOG( QString(
"New JACK sample rate: [%1]/sec")
66 .arg( QString::number(
static_cast<int>(nframes) ) ) );
74 __INFOLOG( QString(
"new JACK buffer size: [%1]")
75 .arg( QString::number(
static_cast<int>(nframes) ) ) );
101 m_nTrackPortCount( 0 ),
102 m_pClient( nullptr ),
103 m_pOutputPort1( nullptr ),
104 m_pOutputPort2( nullptr ),
105 m_nTimebaseTracking( -1 ),
147#ifdef H2CORE_HAVE_LASH
155 bConnectDefaults =
false;
161 if ( bConnectDefaults ) {
185 WARNINGLOG(
"Could not connect to the saved output ports. Connect to the first pair of input ports instead." );
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" );
200 portnames[0] ) != 0 ||
202 portnames[1] ) != 0 ) {
203 ERRORLOG(
"Couldn't connect to first pair of Jack input ports" );
223 if ( pOldClient !=
nullptr ) {
224 int nReturnCode = jack_client_close( pOldClient );
225 if ( nReturnCode != 0 ) {
226 ERRORLOG(
"Error in jack_client_close" );
236 int nReturnCode = jack_deactivate(
m_pClient );
237 if ( nReturnCode != 0 ) {
238 ERRORLOG(
"Error in jack_deactivate" );
263 if ( pBuffer !=
nullptr ) {
264 memset( pBuffer, 0, nFrames *
sizeof(
float ) );
267 if ( pBuffer !=
nullptr ) {
268 memset( pBuffer, 0, nFrames *
sizeof(
float ) );
277 ERRORLOG(
"This function should not have been called with JACK timebase disabled in the Preferences" );
281 ERRORLOG( QString(
"Relocation using BBT information can only be used in the presence of another Jack timebase master" ) );
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" )
304 std::shared_ptr<Song> pSong = pHydrogen->
getSong();
307 if ( pSong ==
nullptr ) {
314 float fTicksPerBeat =
static_cast<float>( pSong->getResolution() /
m_JackTransportPos.beat_type * 4 );
317 float fAdditionalTicks = 0;
318 float fNumberOfBarsPassed = 0;
325 if ( barTicks < 0 ) {
332 float fBarConversion = pSong->getResolution() * 4 *
335 float fNextIncrement = 0;
337 int nLargeNumber = 100000;
338 int nMinimumPatternLength = nLargeNumber;
339 int nNumberOfPatternsPassed = 0;
343 auto pPatternGroup = pSong->getPatternGroupVector();
344 for (
const PatternList* ppPatternList : *pPatternGroup ) {
345 nMinimumPatternLength = nLargeNumber;
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();
357 if ( nMinimumPatternLength == nLargeNumber ){
361 static_cast<float>(nMinimumPatternLength) /
365 if (
static_cast<float>(nBarJack) < ( fNumberOfBarsPassed + fNextIncrement ) ) {
369 fNumberOfBarsPassed += fNextIncrement;
370 ++nNumberOfPatternsPassed;
375 if ( barTicks < 0 ) {
377 }
else if ( fNextIncrement > 1 &&
378 fNumberOfBarsPassed != nBarJack ) {
383 fAdditionalTicks = fTicksPerBeat * 4 *
384 ( fNextIncrement - 1 );
396 ERRORLOG( QString(
"Unsupported m_JackBBTSync option [%1]" )
401 float fNewTick =
static_cast<float>(barTicks) + fAdditionalTicks +
405 pAudioEngine->locate( fNewTick,
false );
411 ERRORLOG(
"This function should not have been called with JACK timebase disabled in the Preferences" );
422 double expectedTickUpdate =
429 floor( expectedTickUpdate );
498 auto pAudioEngine = pHydrogen->getAudioEngine();
514 case JackTransportStopped:
518 case JackTransportRolling:
522 case JackTransportStarting:
529 ERRORLOG(
"Unknown jack transport state" );
532 if ( pHydrogen->getSong() ==
nullptr ) {
539 if ( bTimebaseEnabled ) {
572 if ( ( pAudioEngine->getTransportPosition()->getFrame() -
573 pAudioEngine->getTransportPosition()->getFrameOffsetTempo() ) !=
594 if ( pAudioEngine->getTransportPosition()->getBpm() !=
629 jack_default_audio_sample_t* out =
nullptr;
643 jack_default_audio_sample_t* out =
nullptr;
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; \
667 if (nTries) ERRORLOG("...trying again."); \
671#define CLIENT_SUCCESS(msg) { \
681 QString sClientName =
"Hydrogen";
683#ifdef H2CORE_HAVE_OSC
684 QString sNsmClientId = pPreferences->getNsmClientId();
686 if( !sNsmClientId.isEmpty() ){
687 sClientName = sNsmClientId;
692 jack_status_t status;
695 while ( nTries > 0 ) {
723 m_pClient = jack_client_open( sClientName.toLocal8Bit(),
735 case JackInvalidOption:
738 case JackNameNotUnique:
740 sClientName = jack_get_client_name(
m_pClient);
741 CLIENT_SUCCESS(QString(
"Jack assigned the client name '%1'").arg(sClientName));
746 case JackServerStarted:
749 case JackServerFailed:
752 case JackServerError:
755 case JackNoSuchClient:
758 case JackLoadFailure:
761 case JackInitFailure:
767 case JackVersionError:
772 ERRORLOG(
"Unknown status with JACK server.");
775 " assuming we're OK");
831 JackPortIsOutput, 0 );
833 JACK_METADATA_PRETTY_NAME,
"Main Output L",
"text/plain" );
835 JackPortIsOutput, 0 );
837 JACK_METADATA_PRETTY_NAME,
"Main Output R",
"text/plain" );
844#ifdef H2CORE_HAVE_LASH
845 if ( pPreferences->useLash() ){
855 pPreferences->m_bJackTimebaseEnabled ){
864 std::shared_ptr<Song> pSong = pHydrogen->
getSong();
865 if ( pSong !=
nullptr ) {
878 auto pInstrumentList = pSong->getInstrumentList();
879 std::shared_ptr<Instrument> pInstrument;
880 int nInstruments =
static_cast<int>(pInstrumentList->size());
882 WARNINGLOG( QString(
"Creating / renaming %1 ports" ).arg( nInstruments ) );
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()] =
905 jack_port_t *pPortL, *pPortR;
910 jack_port_unregister(
m_pClient, pPortL );
912 jack_port_unregister(
m_pClient, pPortR );
918void JackAudioDriver::setTrackOutput(
int n, std::shared_ptr<Instrument> pInstrument, std::shared_ptr<InstrumentComponent> pInstrumentComponent, std::shared_ptr<Song> pSong )
920 QString sComponentName;
927 sComponentName = QString(
"Track_%1_" ).arg( m + 1 );
929 jack_port_register(
m_pClient, ( sComponentName +
"L" ).toLocal8Bit(),
930 JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 );
933 jack_port_register(
m_pClient, ( sComponentName +
"R" ).toLocal8Bit(),
934 JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 );
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() );
948#ifdef HAVE_JACK_PORT_RENAME
986 jack_transport_locate(
m_pClient, nFrame );
999 ERRORLOG(
"This function should not have been called with JACK timebase disabled in the Preferences" );
1033 int nReturnValue = jack_set_timebase_callback(
m_pClient, 0,
1035 if ( nReturnValue != 0 ){
1037 WARNINGLOG( QString(
"Hydrogen was not able to register itself as Timebase Master: [%1]" )
1038 .arg( nReturnValue ) );
1054 ERRORLOG( QString(
"Not fully initialized yet" ) );
1059 ERRORLOG(
"This function should not have been called with JACK timebase disabled in the Preferences" );
1079 jack_nframes_t nFrames,
1080 jack_position_t* pJackPosition,
1085 if ( pDriver ==
nullptr ){
1090 std::shared_ptr<Song> pSong = pHydrogen->
getSong();
1092 if ( pSong ==
nullptr ) {
1099 auto pPatternList = pHydrogen->
getSong()->getPatternList();
1101 if ( nPatternNumber != -1 &&
1102 nPatternNumber < pPatternList->size() ) {
1103 pPattern = pPatternList->get( nPatternNumber );
1106 float fNumerator, fDenumerator, fTicksPerBar;
1107 if ( pPattern !=
nullptr ) {
1118 pJackPosition->ticks_per_beat = fTicksPerBar;
1119 pJackPosition->valid = JackPositionBBT;
1121 pJackPosition->beats_per_bar = fNumerator;
1123 pJackPosition->beat_type = fDenumerator;
1124 pJackPosition->beats_per_minute =
static_cast<double>(pPos->getBpm());
1126 if ( pPos->getFrame() < 1 ) {
1127 pJackPosition->bar = 1;
1128 pJackPosition->beat = 1;
1129 pJackPosition->tick = 0;
1130 pJackPosition->bar_start_tick = 0;
1133 pJackPosition->bar = pPos->getColumn() + 1;
1137 pJackPosition->bar_start_tick = pPos->getPatternStartTick();
1139 pJackPosition->beat = pPos->getPatternTickPosition() /
1140 pJackPosition->ticks_per_beat;
1142 pJackPosition->beat++;
1145 pJackPosition->tick = pPos->getPatternTickPosition();
1165 return std::nan(
"no tempo, no masters");
1182 std::cout <<
"\033[35m[Hydrogen] [JackAudioDriver state]"
1185 <<
", current pattern column: "
1186 << pHydrogen->getAudioEngine()->getTransportPosition()->getColumn()
1187 <<
"\33[0m" << std::endl;
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;
1215 if ( ! sCapture.isEmpty() ) {
1217 INFOLOG( QString(
"'jackd' of version [%1] found." )
1229 if ( ! sCapture.isEmpty() ) {
1231 INFOLOG(
"'jackdbus' found." );
1239 if ( ! sCapture.isEmpty() ) {
1241 INFOLOG(
"'pw-jack' found." );
1249 process.start( sExecutable, QStringList( sOption ) );
1250 process.waitForFinished( -1 );
1252 if ( process.exitCode() != 0 ) {
1256 QString sStdout = process.readAllStandardOutput();
1257 if ( sStdout.isEmpty() ) {
1261 return sStdout.trimmed();
#define CLIENT_FAILURE(msg)
#define CLIENT_SUCCESS(msg)
@ Playing
Transport is rolling.
@ Ready
Ready to process audio.
const std::shared_ptr< TransportPosition > getTransportPosition() const
Base abstract class for audio output classes.
static EventQueue * get_instance()
Returns a pointer to the current EventQueue singleton stored in __instance.
void push_event(const EventType type, const int nValue)
Queues the next event into the EventQueue.
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.
Song::Mode getMode() const
int getSelectedPatternNumber() const
static Hydrogen * get_instance()
Returns the current Hydrogen instance __instance.
AudioEngine * getAudioEngine() const
@ JACK_CANNOT_ACTIVATE_CLIENT
@ JACK_ERROR_IN_PORT_REGISTER
Unable to register output ports for the JACK client using jack_port_register() (jack/jack....
@ JACK_CANNOT_CLOSE_CLIENT
The client of Hydrogen can not be disconnected from the JACK server using jack_client_close() (jack/j...
@ JACK_CANNOT_CONNECT_OUTPUT_PORT
Unable to connect either the JackAudioDriver::output_port_1 and the JackAudioDriver::output_port_name...
void raiseError(unsigned nErrorCode)
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.
float getMasterBpm() const
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.
Pattern class is a Note container.
int get_length() const
set the denominator of the pattern
int get_denominator() const
get the note multimap
Manager for User Preferences File (singleton)
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...
@ 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.
static LashClient * get_instance()
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.
#define MAX_NOTES
Maximum number of notes.
#define MAX_COMPONENTS
Maximum number of components each Instrument is allowed to have.
@ EVENT_JACK_TIMEBASE_STATE_CHANGED
Toggles the button indicating the usage Jack timebase master and informs the GUI about a state change...