From 6ecada7f87f2ffcceec49970d69b8190f9d3907b Mon Sep 17 00:00:00 2001 From: AronVietti Date: Thu, 28 May 2020 00:30:02 -0700 Subject: [PATCH] Fixes issue with AppNap causing stuttering/silence in background Adds a mac only Activity class that controls the NSProccessInfo Activity options. This exposes a C++ interface for the Objective C calls and handles the Activity Id lifecycle. Currently sets the Activity to background, latency critical, sleep disabled. Added the Activity calls to main.cpp, starting it in the same location that Windows priority is set, and ending it right before the app exits. Updated Jamulus.pro to add the new files and required lib. --- Jamulus.pro | 5 ++++- mac/activity.h | 46 ++++++++++++++++++++++++++++++++++++++++++ mac/activity.mm | 53 +++++++++++++++++++++++++++++++++++++++++++++++++ src/main.cpp | 18 +++++++++++++++-- 4 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 mac/activity.h create mode 100644 mac/activity.mm diff --git a/Jamulus.pro b/Jamulus.pro index 60da17c2..18867a87 100755 --- a/Jamulus.pro +++ b/Jamulus.pro @@ -91,6 +91,8 @@ win32 { QT += macextras HEADERS += mac/sound.h SOURCES += mac/sound.cpp + HEADERS += mac/activity.h + OBJECTIVE_SOURCES += mac/activity.mm RC_FILE = mac/mainicon.icns CONFIG += x86 QMAKE_TARGET_BUNDLE_PREFIX = net.sourceforge.llcon @@ -107,7 +109,8 @@ win32 { -framework CoreAudio \ -framework CoreMIDI \ -framework AudioToolbox \ - -framework AudioUnit + -framework AudioUnit \ + -framework Foundation # replace coreaudio with jack if requested contains(CONFIG, "jackonmac") { diff --git a/mac/activity.h b/mac/activity.h new file mode 100644 index 00000000..8f4fa5cb --- /dev/null +++ b/mac/activity.h @@ -0,0 +1,46 @@ +/******************************************************************************\ + * Copyright (c) 2004-2020 + * + * Author(s): + * Volker Fischer + * + ****************************************************************************** + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * +\******************************************************************************/ +#pragma once + +// Forward Delcaration to pointer that holds the activity id +class CActivityId; + +// Reperesents an OSX specific activity. See Managing Activities +// https://developer.apple.com/documentation/foundation/nsprocessinfo?language=objc +// This essentially lets us start and stop an Activity frame where we tell the OS +// that we are Latency Critical and are not performing background tasks. +class CActivity +{ +private: + CActivityId *pActivity; + +public: + CActivity(); + + ~CActivity(); + + void BeginActivity(); + + void EndActivity(); +}; diff --git a/mac/activity.mm b/mac/activity.mm new file mode 100644 index 00000000..9fdd2e9d --- /dev/null +++ b/mac/activity.mm @@ -0,0 +1,53 @@ +/******************************************************************************\ + * Copyright (c) 2004-2020 + * + * Author(s): + * Volker Fischer + * + ****************************************************************************** + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * +\******************************************************************************/ + +#include "activity.h" +#include + +class CActivityId +{ +public: + id activityId; +}; + +CActivity::CActivity() : pActivity(new CActivityId()) {} + +CActivity::~CActivity() +{ + delete pActivity; +} + +void CActivity::BeginActivity() +{ + NSActivityOptions options = NSActivityBackground | NSActivityIdleSystemSleepDisabled | NSActivityLatencyCritical; + + pActivity->activityId = [[NSProcessInfo processInfo] beginActivityWithOptions: options reason:@"Jamulus provides low latency audio processing and should not be inturrupted by system throttling."]; +} + +void CActivity::EndActivity() +{ + [[NSProcessInfo processInfo] endActivity: pActivity->activityId]; + + pActivity->activityId = nil; +} diff --git a/src/main.cpp b/src/main.cpp index 6dda5852..42557d28 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -37,6 +37,9 @@ #ifdef ANDROID # include #endif +#if defined ( __APPLE__ ) || defined ( __MACOSX ) +# include "mac/activity.h" +#endif // Implementation ************************************************************** @@ -517,8 +520,7 @@ int main ( int argc, char** argv ) { tsConsole << "Qt5 requires a windowing system to paint a JPEG image; image will use SVG" << endl; } - - + // Application/GUI setup --------------------------------------------------- // Application object QCoreApplication* pApp = bUseGUI @@ -550,6 +552,14 @@ int main ( int argc, char** argv ) QDir ApplDir ( QApplication::applicationDirPath() ); pApp->addLibraryPath ( QString ( ApplDir.absolutePath() ) ); #endif + +#if defined ( __APPLE__ ) || defined ( __MACOSX ) + // On OSX we need to declare an activity to ensure the process doesn't get + // throttled by OS level Nap, Sleep, and Thread Priority systems. + CActivity activity; + + activity.BeginActivity(); +#endif // init resources Q_INIT_RESOURCE(resources); @@ -688,6 +698,10 @@ int main ( int argc, char** argv ) tsConsole << generr.GetErrorText() << endl; } } + + #if defined ( __APPLE__ ) || defined ( __MACOSX ) + activity.EndActivity(); + #endif return 0; }