-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
jack-midi support #19246
base: master
Are you sure you want to change the base?
jack-midi support #19246
Conversation
ce43ca7
to
b218652
Compare
7638762
to
22128db
Compare
999e2a8
to
7a2a941
Compare
3e582e1
to
24705bc
Compare
f56bf85
to
01f03a1
Compare
932c742
to
77c9dbe
Compare
31f02f4
to
3331ccb
Compare
@toto-polo there's some changes for mac, not sure if the jack api shows up now. If it shows up, it should be the only available audio api (hardcoded to use jack only). |
Thank you for this new version! Unfortunately, I don't see any changes compared to the previous versions. I still have a dropdown menu with all my audio devices, but Jack does not appear. I kept the same testing methodology: launching it as is the first time, then manually signing each subcomponent, and finally signing the entire app. Nothing works. I noticed in the build the reference to MacOSX14.5.sdk used by Xcode 15.4. I am on macOS 13. Could this have an influence? Could another macOS user test it as well? |
@lyrra You did it ! It works ! Audio and playback sync !
Awesome ! |
What about tempo sync with Jack ? Ardour and Musescore cannot share their tempo. Is it possible to have a Controller and a Remote ? |
@toto-polo For features like what you are asking about, you would need to have Muse Group take on a DAW project that will be synchronized with M4 as a sister application (like Dorico/Cubase). At the end of the day, meter and tempo changes are not live/dynamic attributes of a DAW or Notation program. Those elements are independently programmed into their respective projects. The passage of TIME is what is being synchronized through ReWire or Jack Transport-like connections that use timecode and midi clock. |
${CMAKE_CURRENT_LIST_DIR}/internal/platform/lin/audiodeviceslistener.cpp | ||
${CMAKE_CURRENT_LIST_DIR}/internal/platform/lin/audiodeviceslistener.h |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think these two lines shouldn't be here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's used by m_devicesListener in AudioMidiManager
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's not ideal, since the OSXAudioDriver has its own, more efficient, way of detecting changes in the devices list. Perhaps a way can be found to reuse that?
@@ -211,6 +211,8 @@ void ConsoleApp::applyCommandLineOptions(const CmdOptions& options, IApplication | |||
notationConfiguration()->setTemplateModeEnabled(options.notation.templateModeEnabled); | |||
notationConfiguration()->setTestModeEnabled(options.notation.testModeEnabled); | |||
|
|||
audioConfiguration()->setAudioDelayCompensate(options.audio.audioDelayCompensate.value_or(1024)); // FIX: equal to buffer-size |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would need to be moved to GuiApp
, otherwise it doesn't have any effect, because ConsoleApp
is only used when running in CLI mode
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll try to move this as a gui preferences option in jack section
elseif (OS_IS_WIN AND (NOT (MINGW))) | ||
set(MODULE_LINK ${MODULE_LINK} winmm mmdevapi mfplat) | ||
elseif (OS_IS_LIN) | ||
find_package(ALSA REQUIRED) | ||
set(MODULE_INCLUDE_PRIVATE ${MODULE_INCLUDE_PRIVATE} ${ALSA_INCLUDE_DIRS} ) | ||
set(MODULE_LINK ${MODULE_LINK} ${ALSA_LIBRARIES} pthread ) | ||
else () | ||
if (OS_IS_MAC) | ||
find_library(AudioToolbox NAMES AudioToolbox) | ||
find_library(CoreAudio NAMES CoreAudio) | ||
set(MODULE_LINK ${MODULE_LINK} ${AudioToolbox} ${CoreAudio}) | ||
elseif (OS_IS_WIN) | ||
set(MODULE_LINK ${MODULE_LINK} winmm mmdevapi mfplat) | ||
elseif (OS_IS_LIN) | ||
find_package(ALSA REQUIRED) | ||
set(MODULE_INCLUDE_PRIVATE ${MODULE_INCLUDE_PRIVATE} ${ALSA_INCLUDE_DIRS} ) | ||
set(MODULE_LINK ${MODULE_LINK} ${ALSA_LIBRARIES} pthread ) | ||
endif() | ||
set(MODULE_INCLUDE_PRIVATE ${MODULE_INCLUDE_PRIVATE} ${ALSA_INCLUDE_DIRS}) | ||
set(MODULE_LINK ${MODULE_LINK} ${ALSA_LIBRARIES} pthread) | ||
elseif (MINGW) | ||
set(MODULE_LINK ${MODULE_LINK} winmm mmdevapi mfplat) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The elseif (OS_IS_WIN AND (NOT (MINGW)))
and elseif (MINGW)
cases can be combined
find_package(Jack REQUIRED) | ||
set(MODULE_INCLUDE_PRIVATE ${MODULE_INCLUDE_PRIVATE} ${JACK_INCLUDE_DIRS} ) | ||
set(MODULE_LINK ${MODULE_LINK} ${JACK_LIBRARIES} pthread ) | ||
set(MODULE_INCLUDE_PRIVATE ${MODULE_INCLUDE_PRIVATE} ${JACK_INCLUDE_DIRS}) | ||
set(MODULE_LINK ${MODULE_LINK} ${JACK_LDFLAGS} pthread) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Often, a find_package
also generates an imported target, probably called something like Jack::Jack
. If that is the case, it would be enough to list(APPEND MODULE_LINK Jack::Jack)
, and that would set up include directories automatically. Might be worth trying.
#if defined(JACK_AUDIO) | ||
if (deviceId == "jack") { | ||
bool transportEnable = playbackConfiguration()->jackTransportEnable(); | ||
m_current_audioDriverState = std::make_unique<JackDriverState>(this, transportEnable); | ||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) | ||
} else if (deviceId == "alsa") { | ||
m_current_audioDriverState = std::make_unique<AlsaDriverState>(); | ||
#endif | ||
//#ifdef Q_OS_MACOS | ||
// } else if (deviceId == "osx") { | ||
// m_current_audioDriverState = std::make_unique<OSXAudioDriverState>(); | ||
//#endif | ||
#else | ||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) | ||
if (deviceId == "alsa") { | ||
m_current_audioDriverState = std::make_unique<AlsaDriverState>(); | ||
#endif | ||
//#ifdef Q_OS_MACOS | ||
// m_current_audioDriverState = std::make_unique<OSXAudioDriverState>(); | ||
//#endif | ||
#endif | ||
} else { | ||
LOGE() << "Unknown device name: " << deviceId; | ||
return false; | ||
} | ||
return true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#if defined(JACK_AUDIO) | |
if (deviceId == "jack") { | |
bool transportEnable = playbackConfiguration()->jackTransportEnable(); | |
m_current_audioDriverState = std::make_unique<JackDriverState>(this, transportEnable); | |
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) | |
} else if (deviceId == "alsa") { | |
m_current_audioDriverState = std::make_unique<AlsaDriverState>(); | |
#endif | |
//#ifdef Q_OS_MACOS | |
// } else if (deviceId == "osx") { | |
// m_current_audioDriverState = std::make_unique<OSXAudioDriverState>(); | |
//#endif | |
#else | |
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) | |
if (deviceId == "alsa") { | |
m_current_audioDriverState = std::make_unique<AlsaDriverState>(); | |
#endif | |
//#ifdef Q_OS_MACOS | |
// m_current_audioDriverState = std::make_unique<OSXAudioDriverState>(); | |
//#endif | |
#endif | |
} else { | |
LOGE() << "Unknown device name: " << deviceId; | |
return false; | |
} | |
return true; | |
#if defined(JACK_AUDIO) | |
if (deviceId == "jack") { | |
bool transportEnable = playbackConfiguration()->jackTransportEnable(); | |
m_current_audioDriverState = std::make_unique<JackDriverState>(this, transportEnable); | |
return true; | |
} | |
#endif | |
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) | |
if (deviceId == "alsa") { | |
m_current_audioDriverState = std::make_unique<AlsaDriverState>(); | |
return true; | |
} | |
#endif | |
//#ifdef Q_OS_MACOS | |
// if (deviceId == "osx") { | |
// m_current_audioDriverState = std::make_unique<OSXAudioDriverState>(); | |
// return true; | |
// } | |
//#endif | |
LOGE() << "Unknown device name: " << deviceId; | |
return false; |
|
||
bool AudioMidiManager::open(const Spec& spec, Spec* activeSpec) | ||
{ | ||
// re-initialize devide |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// re-initialize devide | |
// re-initialize device |
?
@@ -82,6 +90,7 @@ if (OS_IS_MAC) | |||
elseif (OS_IS_WIN) | |||
set(MODULE_LINK ${MODULE_LINK} winmm) | |||
elseif (OS_IS_LIN) | |||
# FIX: why does jack sources compile without a find_library here? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps this is because the include dirs of some other package are just /usr/local/include
and Jack is also installed to that location?
Thank you for your feedback! In reality, the JACK Transport engine already knows how to handle tempo and can easily share it between different applications. For example, it is easy to modify the tempo in Ardour and see this tempo change in Hydrogen or even in modules like jack_midi_clock. I think the question is rather about managing the priority of the application that will have control over tempo management. A priori, Ardour is designed to be the Time Master. Perhaps it would be wise to make Musescore the Time Master, as tempo management on a score is slightly more rigid than in a traditional DAW. The only current limitations seem to be primarily in the management of "tempo ramps," such as accelerandos and ritardandos. https://github.com/x42/jack_midi_clock A Timebase Master tempo utility example: A 15-year-old thread: |
Meter? Specifically, meter changes? |
I haven't tested it myself, but it's clearly stated in the official documentation with the functions :
|
In both Ardour and M4, meter is an element that is placed by the composer. Jack would have no ability to dynamically change the time signature and division of beats. |
Purpose
For linux users, the ability to at runtime switch between alsa and jack audio+midi driver.
Changes
class design changes
Before:
After:
Audio Tests
Midi tests
Optional cleanups / redesign
Remove LinuxAudioDriver and keep only AlsaAudioDriver and JackAudioDriver as two independent implementations. To switch between them, you need to add another class, something like IAudioDriverProvider. This is what you need to add to IoC (and remove the drivers from IoC). Access the driver through this class, like:
This greatly simplifies everything, each class becomes simple and does one thing.
Moreover, such a system is easily scalable; we can easily add other drivers, cross-platform or platform-specific.
Not in scope for this PR
Known cleanups & fixes before merge
Misc. needed for jack #22373
If possible do mutual dependency Injection between module Audio and Midi.Testing confirmed, can't inject modules.
Injection is used, but not at modul level.
No 'requires restart' added
automatic connection to audio ports upon startNot in scope, brings in too much arbitrary heuristics code from Mu3.