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.
This commit is contained in:
AronVietti 2020-05-28 00:30:02 -07:00
parent 43b709d6c2
commit 6ecada7f87
4 changed files with 119 additions and 3 deletions

View file

@ -91,6 +91,8 @@ win32 {
QT += macextras QT += macextras
HEADERS += mac/sound.h HEADERS += mac/sound.h
SOURCES += mac/sound.cpp SOURCES += mac/sound.cpp
HEADERS += mac/activity.h
OBJECTIVE_SOURCES += mac/activity.mm
RC_FILE = mac/mainicon.icns RC_FILE = mac/mainicon.icns
CONFIG += x86 CONFIG += x86
QMAKE_TARGET_BUNDLE_PREFIX = net.sourceforge.llcon QMAKE_TARGET_BUNDLE_PREFIX = net.sourceforge.llcon
@ -107,7 +109,8 @@ win32 {
-framework CoreAudio \ -framework CoreAudio \
-framework CoreMIDI \ -framework CoreMIDI \
-framework AudioToolbox \ -framework AudioToolbox \
-framework AudioUnit -framework AudioUnit \
-framework Foundation
# replace coreaudio with jack if requested # replace coreaudio with jack if requested
contains(CONFIG, "jackonmac") { contains(CONFIG, "jackonmac") {

46
mac/activity.h Normal file
View file

@ -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();
};

53
mac/activity.mm Normal file
View file

@ -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 <Foundation/Foundation.h>
class CActivityId
{
public:
id<NSObject> 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;
}

View file

@ -37,6 +37,9 @@
#ifdef ANDROID #ifdef ANDROID
# include <QtAndroidExtras/QtAndroid> # include <QtAndroidExtras/QtAndroid>
#endif #endif
#if defined ( __APPLE__ ) || defined ( __MACOSX )
# include "mac/activity.h"
#endif
// Implementation ************************************************************** // 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; tsConsole << "Qt5 requires a windowing system to paint a JPEG image; image will use SVG" << endl;
} }
// Application/GUI setup --------------------------------------------------- // Application/GUI setup ---------------------------------------------------
// Application object // Application object
QCoreApplication* pApp = bUseGUI QCoreApplication* pApp = bUseGUI
@ -550,6 +552,14 @@ int main ( int argc, char** argv )
QDir ApplDir ( QApplication::applicationDirPath() ); QDir ApplDir ( QApplication::applicationDirPath() );
pApp->addLibraryPath ( QString ( ApplDir.absolutePath() ) ); pApp->addLibraryPath ( QString ( ApplDir.absolutePath() ) );
#endif #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 // init resources
Q_INIT_RESOURCE(resources); Q_INIT_RESOURCE(resources);
@ -688,6 +698,10 @@ int main ( int argc, char** argv )
tsConsole << generr.GetErrorText() << endl; tsConsole << generr.GetErrorText() << endl;
} }
} }
#if defined ( __APPLE__ ) || defined ( __MACOSX )
activity.EndActivity();
#endif
return 0; return 0;
} }