Skip to content

Commit

Permalink
MacOS Standalone Improvements (#238)
Browse files Browse the repository at this point in the history
- If the input channels dont' match 2, standalone would fail to start.
  Now we bind to min of 2 and available in default.
- Allow the NSWindow to default destroy when closed, avoiding
  a Root Leak
- UI Resizing drag works properly
- Save and Load for your session on a menu
- A hint at at audio settings menu but no implementation
- Cleanup the xib
- Add a virtual destructor to IHost
  • Loading branch information
baconpaul authored and defiantnerd committed Apr 26, 2024
1 parent c173e24 commit 01f10b1
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 33 deletions.
2 changes: 2 additions & 0 deletions src/clap_proxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ class Raise;
class IHost
{
public:
virtual ~IHost() = default;

virtual void mark_dirty() = 0;
virtual void restartPlugin() = 0;
virtual void request_callback() = 0;
Expand Down
10 changes: 9 additions & 1 deletion src/detail/standalone/macos/AppDelegate.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
#import <Cocoa/Cocoa.h>

@interface AppDelegate : NSObject <NSApplicationDelegate>
// @class AudioSettingsWindowDelegate;

@interface AppDelegate : NSObject <NSApplicationDelegate, NSWindowDelegate>
{
// AudioSettingsWindowDelegate *audioSettingsWindowDelegate;
}
@property(assign) IBOutlet NSWindow *window;

- (IBAction)openAudioSettingsWindow:(id)sender;

@end
120 changes: 119 additions & 1 deletion src/detail/standalone/macos/AppDelegate.mm
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

@interface AppDelegate ()

@property(assign) IBOutlet NSWindow *window;
@end

@implementation AppDelegate
Expand Down Expand Up @@ -76,6 +75,7 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
freeaudio::clap_wrapper::standalone::mainCreatePlugin(entry, pid, pindex, 1, (char **)argv);

[[self window] orderFrontRegardless];
[[self window] setDelegate:self];

if (plugin->_ext._gui)
{
Expand All @@ -88,6 +88,10 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification

uint32_t w, h;
ui->get_size(p, &w, &h);
if (ui->can_resize(p))
{
ui->adjust_size(p, &w, &h);
}

NSView *view = [[self window] contentView];

Expand Down Expand Up @@ -130,4 +134,118 @@ - (void)applicationWillTerminate:(NSNotification *)aNotification
freeaudio::clap_wrapper::standalone::mainFinish();
}

- (IBAction)openAudioSettingsWindow:(id)sender
{
NSLog(@"openAudioSettingsWindow: Unimplemented");
}

- (void)windowDidResize:(NSNotification *)notification
{
auto plugin = freeaudio::clap_wrapper::standalone::getMainPlugin();

if (plugin && plugin->_ext._gui)
{
auto canRS = plugin->_ext._gui->can_resize(plugin->_plugin);
if (canRS)
{
auto w = [self window];
auto f = [w frame];
auto cr = [w contentRectForFrameRect:f];
plugin->_ext._gui->set_size(plugin->_plugin, cr.size.width, cr.size.height);
}
}
}

- (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)frameSize
{
auto plugin = freeaudio::clap_wrapper::standalone::getMainPlugin();

if (plugin && plugin->_ext._gui)
{
auto w = [self window];
auto f = [w frame];
f.size = frameSize;
auto cr = [w contentRectForFrameRect:f];

auto canRS = plugin->_ext._gui->can_resize(plugin->_plugin);
if (!canRS)
{
uint32_t w, h;
plugin->_ext._gui->get_size(plugin->_plugin, &w, &h);
cr.size.width = w;
cr.size.height = h;
}
else
{
uint32_t w = frameSize.width, h = frameSize.height;
plugin->_ext._gui->adjust_size(plugin->_plugin, &w, &h);
cr.size.width = w;
cr.size.height = h;
}
auto fr = [w frameRectForContentRect:cr];
frameSize = fr.size;
}
return frameSize;
}

- (IBAction)saveDocumentAs:(id)sender
{
NSSavePanel *savePanel = [NSSavePanel savePanel];
[savePanel setNameFieldStringValue:@"Untitled"]; //

if ([savePanel runModal] == NSModalResponseOK)
{
NSURL *documentURL = [savePanel URL];
auto fsp = fs::path{[[documentURL path] UTF8String]};
auto fn = fsp.replace_extension(".cwstream");

auto standaloneHost = freeaudio::clap_wrapper::standalone::getStandaloneHost();

try
{
standaloneHost->saveStandaloneAndPluginSettings(fn.parent_path(), fn.filename());
}
catch (const fs::filesystem_error &e)
{
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:@"Unable to save file"];
[alert setInformativeText:[[NSString alloc] initWithUTF8String:e.what()]];
[alert addButtonWithTitle:@"OK"];
[alert runModal];

}
}
}

- (IBAction)openDocument:(id)sender
{
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
[openPanel setCanChooseFiles:YES];
[openPanel setCanChooseDirectories:NO];
[openPanel setAllowedFileTypes:[NSArray arrayWithObject:@"cwstream"]];
[openPanel setAllowsMultipleSelection:NO];

if ([openPanel runModal] == NSModalResponseOK)
{
NSURL *selectedUrl = [[openPanel URLs] objectAtIndex:0];

auto fn = fs::path{[[selectedUrl path] UTF8String]};

auto standaloneHost = freeaudio::clap_wrapper::standalone::getStandaloneHost();

try
{
standaloneHost->tryLoadStandaloneAndPluginSettings(fn.parent_path(), fn.filename());
}
catch (const fs::filesystem_error &e)
{
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:@"Unable to open file"];
[alert setInformativeText:[[NSString alloc] initWithUTF8String:e.what()]];
[alert addButtonWithTitle:@"OK"];
[alert runModal];
}
}
}

@end
2 changes: 1 addition & 1 deletion src/detail/standalone/macos/Info.plist.in
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>org.cmake.CocoaExample</string>
<string>${MACOSX_BUNDLE_BUNDLE_NAME}.standalone</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
Expand Down
27 changes: 11 additions & 16 deletions src/detail/standalone/macos/MainMenu.xib
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22689"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
Expand Down Expand Up @@ -31,6 +31,12 @@
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
<menuItem title="Audio/MIDI Settings" id="6kV-Vb-QxS">
<connections>
<action selector="openAudioSettingsWindow:" target="Voe-Tx-rLC" id="3a0-x6-H3R"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kCx-OF-vgT"/>
<menuItem title="Hide ${SA_OUTPUT_NAME}" keyEquivalent="h" id="Olw-nP-bQN">
<connections>
<action selector="hide:" target="-1" id="PnN-Uc-m68"/>
Expand Down Expand Up @@ -66,25 +72,14 @@
<action selector="openDocument:" target="-1" id="bVn-NM-KNZ"/>
</connections>
</menuItem>
<menuItem title="Open Recent" id="tXI-mr-wws">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="oas-Oc-fiZ">
<items>
<menuItem title="Clear Menu" id="vNY-rz-j42">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="clearRecentDocuments:" target="-1" id="Daa-9d-B3U"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<!--
<menuItem isSeparatorItem="YES" id="m54-Is-iLE"/>
<menuItem title="Save…" keyEquivalent="s" id="pxx-59-PXV">
<connections>
<action selector="saveDocument:" target="-1" id="teZ-XB-qJY"/>
</connections>
</menuItem>
-->
<menuItem title="Save As…" keyEquivalent="S" id="Bw7-FT-i3A">
<connections>
<action selector="saveDocumentAs:" target="-1" id="mDf-zr-I0C"/>
Expand Down Expand Up @@ -121,7 +116,7 @@
</items>
<point key="canvasLocation" x="-69" y="65"/>
</menu>
<window title="${SA_OUTPUT_NAME}" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g">
<window title="${SA_OUTPUT_NAME}" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" animationBehavior="default" id="QvC-M9-y7g">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="335" y="390" width="480" height="360"/>
Expand Down
37 changes: 23 additions & 14 deletions src/detail/standalone/standalone_host_audio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,16 @@ void StandaloneHost::startAudioThread()
auto dnms = rtaDac->getDeviceNames();

RtAudio::StreamParameters oParams;
oParams.nChannels = 2;
oParams.firstChannel = 0;
oParams.deviceId = rtaDac->getDefaultOutputDevice();
auto outInfo = rtaDac->getDeviceInfo(oParams.deviceId);
oParams.nChannels = std::min(2U, outInfo.outputChannels);
oParams.firstChannel = 0;

RtAudio::StreamParameters iParams;
iParams.nChannels = 2;
iParams.firstChannel = 0;
iParams.deviceId = rtaDac->getDefaultInputDevice();
auto inInfo = rtaDac->getDeviceInfo(iParams.deviceId);
iParams.nChannels = std::min(2U, inInfo.inputChannels);
iParams.firstChannel = 0;

LOG << "RtAudio Attached Devices" << std::endl;
for (auto i = 0U; i < dids.size(); ++i)
Expand Down Expand Up @@ -89,19 +91,26 @@ void StandaloneHost::startAudioThread()
void StandaloneHost::stopAudioThread()
{
LOG << "Shutting down audio" << std::endl;
running = false;

// bit of a hack. Wait until we get an ack from audio callback
for (auto i = 0; i < 10000 && !finishedRunning; ++i)
if (!rtaDac->isStreamRunning())
{
using namespace std::chrono_literals;
std::this_thread::sleep_for(1ms);
// todo put a sleep here
LOG << "Stream not running" << std::endl;
}
LOG << "Audio Thread acknowledges shutdown" << std::endl;
else
{
running = false;

if (rtaDac && rtaDac->isStreamRunning()) rtaDac->stopStream();
LOG << "RtAudio stream stopped" << std::endl;
// bit of a hack. Wait until we get an ack from audio callback
for (auto i = 0; i < 10000 && !finishedRunning; ++i)
{
using namespace std::chrono_literals;
std::this_thread::sleep_for(1ms);
// todo put a sleep here
}
LOG << "Audio Thread acknowledges shutdown" << std::endl;

if (rtaDac && rtaDac->isStreamRunning()) rtaDac->stopStream();
LOG << "RtAudio stream stopped" << std::endl;
}
return;
}
} // namespace freeaudio::clap_wrapper::standalone

0 comments on commit 01f10b1

Please sign in to comment.