Permalink
Browse files

Merge pull request #18 from pje/new

0.1.0
  • Loading branch information...
2 parents e714307 + 2f76b6a commit e4e0257ad4456276c626a63896c5b6525c99856f @pje committed Sep 2, 2012
Showing with 840 additions and 1,924 deletions.
  1. +14 −34 App/AppController.h
  2. +254 −706 App/AppController.mm
  3. +94 −54 App/AudioDevice.cpp
  4. +4 −9 App/AudioDevice.h
  5. +15 −17 App/AudioDeviceList.cpp
  6. +1 −2 App/AudioDeviceList.h
  7. +0 −118 App/AudioRingBuffer.cpp
  8. +0 −29 App/AudioRingBuffer.h
  9. +67 −238 App/AudioThruEngine.cpp
  10. +23 −37 App/AudioThruEngine.h
  11. +14 −184 App/English.lproj/MainMenu.nib/designable.nib
  12. BIN App/English.lproj/MainMenu.nib/keyedobjects.nib
  13. +0 −10 App/HelpWindowController.h
  14. +0 −14 App/HelpWindowController.m
  15. +3 −3 App/Info.plist
  16. +33 −35 App/WavTap.xcodeproj/project.pbxproj
  17. BIN App/appIcon.icns
  18. BIN App/appIcon.psd
  19. +5 −0 App/kill_recorders.sh
  20. BIN App/menuIcon.psd
  21. BIN App/menuIcon.tiff
  22. BIN App/menuIconInverse.psd
  23. BIN App/menuIconInverse.tiff
  24. BIN App/menuIconRecording.psd
  25. BIN App/menuIconRecording.tiff
  26. +6 −0 App/quit.applescript
  27. +17 −0 App/record.sh
  28. +0 −28 App/record_start
  29. +0 −10 App/record_stop
  30. +5 −5 Extension/Info.plist
  31. +0 −70 Extension/SoundflowerClip.cpp
  32. 0 Extension/{Soundflower.xcconfig → WavTap.xcconfig}
  33. +30 −30 Extension/{Soundflower.xcodeproj → WavTap.xcodeproj}/project.pbxproj
  34. +49 −0 Extension/WavTapClip.cpp
  35. +31 −52 Extension/{SoundflowerDevice.cpp → WavTapDevice.cpp}
  36. +15 −36 Extension/{SoundflowerDevice.h → WavTapDevice.h}
  37. +133 −154 Extension/{SoundflowerEngine.cpp → WavTapEngine.cpp}
  38. +20 −42 Extension/{SoundflowerEngine.h → WavTapEngine.h}
  39. +1 −1 Installer/WavTap.pmdoc/01wavtap.xml
  40. +1 −1 Installer/WavTap.pmdoc/02wavtap-contents.xml
  41. +1 −1 Installer/WavTap.pmdoc/02wavtap.xml
  42. +1 −1 Installer/WavTap.pmdoc/index.xml
  43. +3 −3 Makefile
View
48 App/AppController.h
@@ -1,43 +1,23 @@
#import <Cocoa/Cocoa.h>
-#import "HelpWindowController.h"
#include "AudioDeviceList.h"
+#include "AudioThruEngine.h"
@interface AppController : NSObject
{
NSStatusItem *mSbItem;
NSMenu *mMenu;
- NSMenuItem *m2chMenu;
- NSMenu *m2chBuffer;
- BOOL menuItemVisible;
- BOOL mIsRecording;
- NSMenuItem *mCur2chDevice;
- NSMenuItem *mCur2chBufferSize;
- NSMenuItem *mSuspended2chDevice;
- AudioDeviceID mSoundflower2Device;
- AudioDeviceList *mOutputDeviceList;
- UInt32 mNchnls2;
- UInt32 mMenuID2[64];
- IBOutlet HelpWindowController *mAboutController;
+ AudioThruEngine *mEngine;
}
-- (IBAction)suspend;
-- (IBAction)resume;
-- (IBAction)srChanged2ch;
-- (IBAction)srChanged2chOutput;
-- (IBAction)checkNchnls;
-- (IBAction)refreshDevices;
-- (IBAction)outputDeviceSelected:(id)sender;
-- (IBAction)bufferSizeChanged2ch:(id)sender;
-- (IBAction)cloningChanged:(id)sender;
-- (IBAction)cloningChanged:(id)sender cloneChannels:(bool)clone;
-- (IBAction)routingChanged2ch:(id)sender;
-- (void)buildRoutingMenu:(BOOL)is2ch;
-- (void)buildDeviceList;
-- (void)buildMenu;
-- (void)bindHotKeys;
-- (void)InstallListeners;
-- (void)RemoveListeners;
-- (void)readGlobalPrefs;
-- (void)writeGlobalPrefs;
-- (void)readDevicePrefs:(BOOL)is2ch;
-- (void)writeDevicePrefs:(BOOL)is2ch;
+- (void)toggleRecord;
@end
+
+NSDictionary *mMenuItemTags;
+EventHandlerUPP hotKeyFunction;
+BOOL menuItemVisible;
+BOOL mIsRecording;
+Float32 mStashedVolume;
+Float32 mStashedVolume2;
+AudioDeviceID mStashedAudioDeviceID;
+AudioDeviceID mWavTapDeviceID;
+AudioDeviceID mOutputDeviceID;
+AudioDeviceList *mOutputDeviceList;
View
960 App/AppController.mm
@@ -1,820 +1,368 @@
-#import "AppController.h"
#import <Carbon/Carbon.h>
-
+#import <AudioUnit/AudioUnit.h>
+#include "AppController.h"
#include "AudioThruEngine.h"
-
-OSStatus myHotKeyHandler(EventHandlerCallRef nextHandler, EventRef anEvent, void *userData)
-{
- [userData doToggleRecord];
- return noErr;
-}
+#import <QuartzCore/QuartzCore.h>
@implementation AppController
-EventHandlerUPP hotKeyFunction;
-AudioThruEngine *gThruEngine2 = NULL;
-Boolean startOnAwake = false;
-
-UInt32 MENU_ITEM_TOGGLE_RECORD_TAG=1;
-
-void CheckErr(OSStatus err)
-{
- if (err) {
- NSLog(@"error %-4.4s %i\n", (char *)&err, (int)err);
- throw 1;
- }
-}
-
-OSStatus HardwareListenerProc (AudioHardwarePropertyID inPropertyID,
- void* inClientData)
-{
- AppController *app = (AppController *)inClientData;
- NSLog(@"HardwareListenerProc\n");
- switch(inPropertyID)
- {
- case kAudioHardwarePropertyDevices:
-// NSLog(@"kAudioHardwarePropertyDevices\n");
-
- // An audio device has been added or removed to the system, so lets just start over
- [NSThread detachNewThreadSelector:@selector(refreshDevices) toTarget:app withObject:nil];
- break;
-
- case kAudioHardwarePropertyIsInitingOrExiting:
-// NSLog(@"kAudioHardwarePropertyIsInitingOrExiting\n");
- // A UInt32 whose value will be non-zero if the HAL is either in the midst of
- //initializing or in the midst of exiting the process.
- break;
-
- case kAudioHardwarePropertySleepingIsAllowed:
-// NSLog(@"kAudioHardwarePropertySleepingIsAllowed\n");
- // A UInt32 where 1 means that the process will allow the CPU to idle sleep
- // even if there is audio IO in progress. A 0 means that the CPU will not be
- // allowed to idle sleep. Note that this property won't affect when the CPU is
- // forced to sleep.
- break;
-
- case kAudioHardwarePropertyUnloadingIsAllowed:
-// NSLog(@"kAudioHardwarePropertyUnloadingIsAllowed\n");
- // A UInt32 where 1 means that this process wants the HAL to unload itself
- // after a period of inactivity where there are no IOProcs and no listeners
- // registered with any AudioObject.
- break;
-
- }
-
- return (noErr);
-}
-
-OSStatus DeviceListenerProc (AudioDeviceID inDevice,
- UInt32 inChannel,
- Boolean isInput,
- AudioDevicePropertyID inPropertyID,
- void* inClientData)
-{
- AppController *app = (AppController *)inClientData;
-
- switch(inPropertyID)
- {
- case kAudioDevicePropertyNominalSampleRate:
- if (isInput) {
- if (gThruEngine2->IsRunning() && gThruEngine2->GetInputDevice() == inDevice)
- [NSThread detachNewThreadSelector:@selector(srChanged2ch) toTarget:app withObject:nil];
- }
- else {
- if (inChannel == 0) {
- if (gThruEngine2->IsRunning() && gThruEngine2->GetOutputDevice() == inDevice)
- [NSThread detachNewThreadSelector:@selector(srChanged2chOutput) toTarget:app withObject:nil];
- }
- }
- break;
-
- case kAudioDevicePropertyDeviceIsAlive:
- break;
-
- case kAudioDevicePropertyDeviceHasChanged:
- break;
-
- case kAudioDevicePropertyDataSource:
- if (gThruEngine2->IsRunning() && gThruEngine2->GetOutputDevice() == inDevice)
- [NSThread detachNewThreadSelector:@selector(srChanged2chOutput) toTarget:app withObject:nil];
- break;
-
- case kAudioDevicePropertyDeviceIsRunning:
- break;
-
- case kAudioDeviceProcessorOverload:
- break;
-
- case kAudioDevicePropertyAvailableNominalSampleRates:
- break;
-
- case kAudioStreamPropertyPhysicalFormat:
- break;
- case kAudioDevicePropertyStreamFormat:
- break;
-
- case kAudioDevicePropertyStreams:
- case kAudioDevicePropertyStreamConfiguration:
- if (!isInput) {
- if (inChannel == 0) {
- if (gThruEngine2->GetOutputDevice() == inDevice) {
- //NSLog(@"non-wavtap device potential # of chnls change\n");
- [NSThread detachNewThreadSelector:@selector(checkNchnls) toTarget:app withObject:nil];
- }
- else // this could be an aggregate device in the middle of constructing, going from/to 0 chans & we need to add/remove to menu
- [NSThread detachNewThreadSelector:@selector(refreshDevices) toTarget:app withObject:nil];
- }
- }
- break;
-
- default:
- break;
- }
-
- return noErr;
-}
-
-#include <mach/mach_port.h>
-#include <mach/mach_interface.h>
-#include <mach/mach_init.h>
-
-#include <IOKit/pwr_mgt/IOPMLib.h>
-#include <IOKit/IOMessage.h>
-
+AudioThruEngine *mEngine = NULL;
io_connect_t root_port;
-void
-MySleepCallBack(void * x, io_service_t y, natural_t messageType, void * messageArgument)
-{
- AppController *app = (AppController *)x;
-
- switch ( messageType ) {
- case kIOMessageSystemWillSleep:
-
- [NSThread detachNewThreadSelector:@selector(suspend) toTarget:app withObject:nil];
-
- IOAllowPowerChange(root_port, (long)messageArgument);
- break;
-
- case kIOMessageSystemWillNotSleep:
- break;
-
- case kIOMessageCanSystemSleep:
-
- IOAllowPowerChange(root_port, (long)messageArgument);
- break;
-
- case kIOMessageSystemHasPoweredOn:
-
- [NSTimer scheduledTimerWithTimeInterval:0.0 target:app selector:@selector(resume) userInfo:nil repeats:NO];
-
- break;
-
- default:
- break;
- }
-}
-
-- (IBAction)suspend
-{
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
- mSuspended2chDevice = mCur2chDevice;
-
- [self outputDeviceSelected:[mMenu itemAtIndex:1]];
-
- [pool release];
-}
-
-- (IBAction)resume
-{
- //NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- if (mSuspended2chDevice) {
- [self outputDeviceSelected:mSuspended2chDevice];
- mCur2chDevice = mSuspended2chDevice;
- mSuspended2chDevice = NULL;
- }
-
- //[pool release];
-}
-
-- (IBAction)srChanged2ch
-{
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
- gThruEngine2->Mute();
- OSStatus err = gThruEngine2->MatchSampleRate(true);
-
- NSMenuItem *curdev = mCur2chDevice;
- [self outputDeviceSelected:[mMenu itemAtIndex:1]];
- if (err == kAudioHardwareNoError) {
- //usleep(1000);
- [self outputDeviceSelected:curdev];
- }
-
- gThruEngine2->Mute(false);
-
- [pool release];
-}
-
-- (IBAction)srChanged2chOutput
-{
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
- gThruEngine2->Mute();
- OSStatus err = gThruEngine2->MatchSampleRate(false);
-
- // restart devices
- NSMenuItem *curdev = mCur2chDevice;
- [self outputDeviceSelected:[mMenu itemAtIndex:1]];
- if (err == kAudioHardwareNoError) {
- //usleep(1000);
- [self outputDeviceSelected:curdev];
- }
- gThruEngine2->Mute(false);
-
- [pool release];
-}
-
-- (IBAction)checkNchnls
-{
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
- if (mNchnls2 != gThruEngine2->GetOutputNchnls())
- {
- NSMenuItem *curdev = mCur2chDevice;
- [self outputDeviceSelected:[mMenu itemAtIndex:1]];
- //usleep(1000);
- [self outputDeviceSelected:curdev];
- }
-
- [pool release];
-}
-
-- (IBAction)refreshDevices
-{
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
- [self buildDeviceList];
-
- [mSbItem setMenu:nil];
- [mMenu dealloc];
-
- [self buildMenu];
-
- // make sure that one of our current device's was not removed!
- AudioDeviceID dev = gThruEngine2->GetOutputDevice();
- AudioDeviceList::DeviceList &thelist = mOutputDeviceList->GetList();
- AudioDeviceList::DeviceList::iterator i;
- for (i = thelist.begin(); i != thelist.end(); ++i)
- if ((*i).mID == dev)
- break;
- if (i == thelist.end()) // we didn't find it, turn selection to none
- [self outputDeviceSelected:[mMenu itemAtIndex:1]];
- else
- [self buildRoutingMenu];
-
- [pool release];
-}
-
-- (void)InstallListeners;
-{
- // add listeners for all devices, including soundflowers
- AudioDeviceList::DeviceList &thelist = mOutputDeviceList->GetList();
- int index = 0;
- for (AudioDeviceList::DeviceList::iterator i = thelist.begin(); i != thelist.end(); ++i, ++index) {
- if (0 == strncmp("Soundflower", (*i).mName, strlen("Soundflower"))) {
- verify_noerr (AudioDeviceAddPropertyListener((*i).mID, 0, true, kAudioDevicePropertyNominalSampleRate, DeviceListenerProc, self));
- verify_noerr (AudioDeviceAddPropertyListener((*i).mID, 0, true, kAudioDevicePropertyStreamConfiguration, DeviceListenerProc, self));
-
- verify_noerr (AudioDeviceAddPropertyListener((*i).mID, 0, true, kAudioDevicePropertyDeviceIsAlive, DeviceListenerProc, self));
- verify_noerr (AudioDeviceAddPropertyListener((*i).mID, 0, true, kAudioDevicePropertyDeviceHasChanged, DeviceListenerProc, self));
- verify_noerr (AudioDeviceAddPropertyListener((*i).mID, 0, true, kAudioDevicePropertyDeviceIsRunning, DeviceListenerProc, self));
- verify_noerr (AudioDeviceAddPropertyListener((*i).mID, 0, true, kAudioDeviceProcessorOverload, DeviceListenerProc, self));
- }
- else {
- verify_noerr (AudioDeviceAddPropertyListener((*i).mID, 0, false, kAudioDevicePropertyNominalSampleRate, DeviceListenerProc, self));
- verify_noerr (AudioDeviceAddPropertyListener((*i).mID, 0, false, kAudioDevicePropertyStreamConfiguration, DeviceListenerProc, self));
- verify_noerr (AudioDeviceAddPropertyListener((*i).mID, 0, false, kAudioDevicePropertyStreams, DeviceListenerProc, self));
- verify_noerr (AudioDeviceAddPropertyListener((*i).mID, 0, false, kAudioDevicePropertyDataSource, DeviceListenerProc, self));
- }
- }
-
- // check for added/removed devices
- verify_noerr (AudioHardwareAddPropertyListener(kAudioHardwarePropertyDevices, HardwareListenerProc, self));
-
- verify_noerr (AudioHardwareAddPropertyListener(kAudioHardwarePropertyIsInitingOrExiting, HardwareListenerProc, self));
- verify_noerr (AudioHardwareAddPropertyListener(kAudioHardwarePropertySleepingIsAllowed, HardwareListenerProc, self));
- verify_noerr (AudioHardwareAddPropertyListener(kAudioHardwarePropertyUnloadingIsAllowed, HardwareListenerProc, self));
-}
-
-- (void)RemoveListeners
-{
- AudioDeviceList::DeviceList &thelist = mOutputDeviceList->GetList();
- int index = 0;
- for (AudioDeviceList::DeviceList::iterator i = thelist.begin(); i != thelist.end(); ++i, ++index) {
- if (0 == strncmp("Soundflower", (*i).mName, strlen("Soundflower"))) {
- verify_noerr (AudioDeviceRemovePropertyListener((*i).mID, 0, true, kAudioDevicePropertyNominalSampleRate, DeviceListenerProc));
- verify_noerr (AudioDeviceRemovePropertyListener((*i).mID, 0, true, kAudioDevicePropertyStreamConfiguration, DeviceListenerProc));
- }
- else {
- verify_noerr (AudioDeviceRemovePropertyListener((*i).mID, 0, false, kAudioDevicePropertyNominalSampleRate, DeviceListenerProc));
- verify_noerr (AudioDeviceRemovePropertyListener((*i).mID, 0, false, kAudioDevicePropertyStreamConfiguration, DeviceListenerProc));
- verify_noerr (AudioDeviceRemovePropertyListener((*i).mID, 0, false, kAudioDevicePropertyStreams, DeviceListenerProc));
- verify_noerr (AudioDeviceRemovePropertyListener((*i).mID, 0, false, kAudioDevicePropertyDataSource, DeviceListenerProc));
- }
- }
-
- verify_noerr (AudioHardwareRemovePropertyListener(kAudioHardwarePropertyDevices, HardwareListenerProc));
-}
-
- (id)init
{
+ mMenuItemTags = [[NSDictionary alloc] initWithObjectsAndKeys:
+ @"toggleRecord", @1,
+ @"preferences", @2,
+ @"quit", @3,
+ nil];
mIsRecording = NO;
mOutputDeviceList = NULL;
-
- mSoundflower2Device = 0;
- mNchnls2 = 0;
- mSuspended2chDevice = NULL;
-
- [ self bindHotKeys];
-
+ mOutputDeviceID = 0;
+ mWavTapDeviceID = 0;
return self;
}
- (void)dealloc
{
- [ self RemoveListeners];
delete mOutputDeviceList;
-
- [super dealloc];
}
-- (void)buildRoutingMenu
+- (void)awakeFromNib
{
- NSMenuItem *hostMenu = m2chMenu;
- UInt32 nchnls = (mNchnls2 = gThruEngine2->GetOutputNchnls());
- AudioDeviceID outDev = (gThruEngine2->GetOutputDevice());
- SEL menuAction = (@selector(routingChanged2ch:));
-
- for (UInt32 menucount = 0; menucount < 2; menucount++) {
- NSMenuItem *superMenu = [[hostMenu submenu] itemAtIndex:(menucount+3)];
-
- NSMenu *menu = [[NSMenu alloc] initWithTitle:@"Output Device Channel"];
- NSMenuItem *item;
-
- AudioDeviceList::DeviceList &thelist = mOutputDeviceList->GetList();
- char *name = 0;
- for (AudioDeviceList::DeviceList::iterator i = thelist.begin(); i != thelist.end(); ++i) {
- if ((*i).mID == outDev)
- name = (*i).mName;
- }
-
- item = [menu addItemWithTitle:@"None" action:menuAction keyEquivalent:@""];
- [item setState:NSOnState];
-
- char text[128];
- for (UInt32 c = 1; c <= nchnls; ++c) {
- sprintf(text, "%s [%d]", name, (int)c);
- item = [menu addItemWithTitle:[NSString stringWithUTF8String:text] action:menuAction keyEquivalent:@""];
- [item setTarget:self];
-
- // set check marks according to route map
- if (c == 1 + ((UInt32)gThruEngine2->GetChannelMap(menucount))) {
- [[menu itemAtIndex:0] setState:NSOffState];
- [item setState:NSOnState];
- }
- }
-
- [superMenu setSubmenu:menu];
+ [[NSApplication sharedApplication] setDelegate:(id)self];
+ [self rebuildDeviceList];
+ AudioDeviceList::DeviceList &list = mOutputDeviceList->GetList();
+ for (AudioDeviceList::DeviceList::iterator i = list.begin(); i != list.end(); ++i) {
+ if (0 == strcmp("WavTap", (*i).mName)) mWavTapDeviceID = (*i).mID;
}
+ [self initConnections];
+ [self registerPropertyListeners];
+ [self bindHotKeys];
+ [self initStatusBar];
+ [self buildMenu];
}
-- (void)setToggleRecordHotKey:(NSString*)keyEquivalent
+- (void)initStatusBar
{
- NSMenuItem *item = [mMenu itemWithTag:MENU_ITEM_TOGGLE_RECORD_TAG];
+ mSbItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
- [item setKeyEquivalentModifierMask: NSControlKeyMask | NSCommandKeyMask];
- [item setKeyEquivalent:keyEquivalent];
+ NSImage *image = [NSImage imageNamed:@"menuIcon"];
+ [image setTemplate:YES];
+ [mSbItem setImage:image];
+
+ NSImage *alternateImage = [NSImage imageNamed:@"menuIconInverse"];
+ [alternateImage setTemplate:YES];
+ [mSbItem setAlternateImage:alternateImage];
+
+ [mSbItem setToolTip: @"WavTap"];
+ [mSbItem setHighlightMode:YES];
}
- (void)buildMenu
{
NSMenuItem *item;
-
mMenu = [[NSMenu alloc] initWithTitle:@"Main Menu"];
- if (mSoundflower2Device) {
- m2chMenu = [mMenu addItemWithTitle:@"WavTap (2ch)" action:@selector(doNothing) keyEquivalent:@""];
- [m2chMenu setTarget:self];
- NSMenu *submenu = [[NSMenu alloc] initWithTitle:@"2ch submenu"];
- NSMenuItem *bufItem = [submenu addItemWithTitle:@"Buffer Size" action:@selector(doNothing) keyEquivalent:@""];
- m2chBuffer = [[NSMenu alloc] initWithTitle:@"2ch Buffer"];
- item = [m2chBuffer addItemWithTitle:@"64" action:@selector(bufferSizeChanged2ch:) keyEquivalent:@""];
- [item setTarget:self];
- item = [m2chBuffer addItemWithTitle:@"128" action:@selector(bufferSizeChanged2ch:) keyEquivalent:@""];
- [item setTarget:self];
- item = [m2chBuffer addItemWithTitle:@"256" action:@selector(bufferSizeChanged2ch:) keyEquivalent:@""];
- [item setTarget:self];
- item = [m2chBuffer addItemWithTitle:@"512" action:@selector(bufferSizeChanged2ch:) keyEquivalent:@""];
- [item setTarget:self];
- [item setState:NSOnState];
- mCur2chBufferSize = item;
- item = [m2chBuffer addItemWithTitle:@"1024" action:@selector(bufferSizeChanged2ch:) keyEquivalent:@""];
- [item setTarget:self];
- item = [m2chBuffer addItemWithTitle:@"2048" action:@selector(bufferSizeChanged2ch:) keyEquivalent:@""];
- [item setTarget:self];
- [bufItem setSubmenu:m2chBuffer];
-
- [submenu addItem:[NSMenuItem separatorItem]];
-
- item = [submenu addItemWithTitle:@"Routing" action:NULL keyEquivalent:@""];
- item = [submenu addItemWithTitle:@"Channel 1" action:@selector(doNothing) keyEquivalent:@""];
- [item setTarget:self];
- item = [submenu addItemWithTitle:@"Channel 2" action:@selector(doNothing) keyEquivalent:@""];
- [item setTarget:self];
-
- [submenu addItem:[NSMenuItem separatorItem]];
-
- [[submenu addItemWithTitle:@"Clone to all channels" action:@selector(cloningChanged:) keyEquivalent:@""] setTarget:self];
-
- [m2chMenu setSubmenu:submenu];
-
- item = [mMenu addItemWithTitle:@"None" action:@selector(outputDeviceSelected:) keyEquivalent:@""];
- [item setTarget:self];
- [item setState:NSOnState];
- mCur2chDevice = item;
-
- AudioDeviceList::DeviceList &thelist = mOutputDeviceList->GetList();
- int index = 0;
- for (AudioDeviceList::DeviceList::iterator i = thelist.begin(); i != thelist.end(); ++i) {
- AudioDevice ad((*i).mID, false);
- if (ad.CountChannels())
- {
- item = [mMenu addItemWithTitle:[NSString stringWithUTF8String: (*i).mName] action:@selector(outputDeviceSelected:) keyEquivalent:@""];
- [item setTarget:self];
- mMenuID2[index++] = (*i).mID;
- }
- }
- }
- else {
+ if (mWavTapDeviceID){
+ item = [mMenu addItemWithTitle:@"Record" action:@selector(toggleRecord) keyEquivalent:@""];
+ [item setTarget:self];
+ [item setTag:(NSInteger)[mMenuItemTags objectForKey:@"toggleRecord"]];
+ [self setToggleRecordHotKey:@" "];
+ } else {
item = [mMenu addItemWithTitle:@"Kernel Extension Not Installed" action:NULL keyEquivalent:@""];
[item setTarget:self];
}
[mMenu addItem:[NSMenuItem separatorItem]];
+ [mSbItem setMenu:mMenu];
-
- item = [mMenu addItemWithTitle:@"Record" action:@selector(doToggleRecord) keyEquivalent:@""];
- [item setTarget:self];
- [item setTag:MENU_ITEM_TOGGLE_RECORD_TAG];
- [self setToggleRecordHotKey:@" "];
- [mMenu addItem:[NSMenuItem separatorItem]];
-
-
- item = [mMenu addItemWithTitle:@"About..." action:@selector(doAbout) keyEquivalent:@""];
- [item setTarget:self];
+ // item = [mMenu addItemWithTitle:@"Preferences..." action:@selector(showPreferencesWindow) keyEquivalent:@","];
+ // [item setKeyEquivalentModifierMask:NSCommandKeyMask];
+ // [item setTag:(NSInteger)[mMenuItemTags objectForKey:@"preferences"]];
+ // [item setTarget:self];
item = [mMenu addItemWithTitle:@"Quit" action:@selector(doQuit) keyEquivalent:@""];
+ [item setTag:(NSInteger)[mMenuItemTags objectForKey:@"quit"]];
[item setTarget:self];
- [mSbItem setMenu:mMenu];
-}
-
-- (void)buildDeviceList
-{
- if (mOutputDeviceList) {
- [ self RemoveListeners];
- delete mOutputDeviceList;
- }
-
- mOutputDeviceList = new AudioDeviceList(false);
- [ self InstallListeners];
-
- AudioDeviceList::DeviceList &thelist = mOutputDeviceList->GetList();
- int index = 0;
- for (AudioDeviceList::DeviceList::iterator i = thelist.begin(); i != thelist.end(); ++i, ++index) {
- if (0 == strcmp("WavTap (2ch)", (*i).mName)) {
- mSoundflower2Device = (*i).mID;
- AudioDeviceList::DeviceList::iterator toerase = i;
- i--;
- thelist.erase(toerase);
+ [mMenu setDelegate:(id)self];
+}
+
+OSStatus DeviceListenerProc (AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void*inClientData)
+{
+ OSStatus err = noErr;
+ AppController *app = (AppController *)CFBridgingRelease(inClientData);
+ AudioObjectPropertyAddress addr;
+
+ for(int i = 0; i < inNumberAddresses; i++){
+ addr = inAddresses[i];
+ switch(addr.mSelector) {
+ case kAudioDevicePropertyAvailableNominalSampleRates: { break; }
+ case kAudioDevicePropertyClockDomain: { break; }
+ case kAudioDevicePropertyConfigurationApplication: { break; }
+ case kAudioDevicePropertyDeviceCanBeDefaultDevice: { break; }
+ case kAudioDevicePropertyDeviceCanBeDefaultSystemDevice: { break; }
+ case kAudioDevicePropertyDeviceIsAlive: { break; }
+ case kAudioDevicePropertyDeviceIsRunning: { break; }
+ case kAudioDevicePropertyDeviceUID: { break; }
+ case kAudioDevicePropertyIcon: { break; }
+ case kAudioDevicePropertyIsHidden: { break; }
+ case kAudioDevicePropertyLatency: { break; }
+ case kAudioDevicePropertyModelUID: { break; }
+ case kAudioDevicePropertyNominalSampleRate:{
+ app->mEngine->Stop();
+ err = app->mEngine->MatchSampleRates(inObjectID);
+ printf("(DeviceListenerProc) MatchSampleRates returned status code: %u \n", err);
+ app->mEngine->Start();
+ break;
+ }
+ case kAudioDevicePropertyPreferredChannelLayout: { break; }
+ case kAudioDevicePropertyPreferredChannelsForStereo: { break; }
+ case kAudioDevicePropertyRelatedDevices: { break; }
+ case kAudioDevicePropertySafetyOffset: { break; }
+ case kAudioDevicePropertyStreams: { break; }
+ case kAudioDevicePropertyTransportType: { break; }
+ case kAudioObjectPropertyControlList: { break; }
}
}
+ return err;
}
-- (void)awakeFromNib
+- (void)registerPropertyListeners
{
- [[NSApplication sharedApplication] setDelegate:self];
-
- [self buildDeviceList];
-
- mSbItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
- [mSbItem retain];
-
- [mSbItem setImage:[NSImage imageNamed:@"menuIcon"]];
- [mSbItem setHighlightMode:YES];
-
- [self buildMenu];
+ OSStatus err = noErr;
- if (mSoundflower2Device) {
- gThruEngine2 = new AudioThruEngine;
- gThruEngine2->SetInputDevice(mSoundflower2Device);
+ AudioObjectPropertyAddress addr = {
+ kAudioDevicePropertyNominalSampleRate,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster
+ };
+ err = AudioObjectAddPropertyListener(mEngine->GetOutputDeviceID(), &addr, DeviceListenerProc, (__bridge void *)self);
- gThruEngine2->Start();
-
- [self buildRoutingMenu];
-
- [self readGlobalPrefs];
-
- [self bindHotKeys];
- }
-
- // ask to be notified on system sleep to avoid a crash
- IONotificationPortRef notify;
- io_object_t anIterator;
-
- root_port = IORegisterForSystemPower(self, &notify, MySleepCallBack, &anIterator);
- if (!root_port) {
- NSLog(@"IORegisterForSystemPower failed\n");
- } else {
- CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(notify), kCFRunLoopCommonModes);
- }
+// addr.mElement = kAudioObjectPropertyScopeWildcard;
+// err = AudioObjectAddPropertyListener(mEngine->GetOutputDeviceID(), &addr, DeviceListenerProc, (__bridge void *)self);
+//
+// addr.mElement = kAudioObjectPropertyScopePlayThrough;
+// err = AudioObjectAddPropertyListener(mEngine->GetOutputDeviceID(), &addr, DeviceListenerProc, (__bridge void *)self);
+//
+// addr.mElement = kAudioObjectPropertyScopeInput;
+// err = AudioObjectAddPropertyListener(mEngine->GetOutputDeviceID(), &addr, DeviceListenerProc, (__bridge void *)self);
+//
+// addr.mElement = kAudioObjectPropertyScopeOutput;
+// err = AudioObjectAddPropertyListener(mEngine->GetOutputDeviceID(), &addr, DeviceListenerProc, (__bridge void *)self);
}
-- (void)bindHotKeys
+- (void)setToggleRecordHotKey:(NSString*)keyEquivalent
{
- hotKeyFunction = NewEventHandlerUPP(myHotKeyHandler);
- EventTypeSpec eventType;
- eventType.eventClass = kEventClassKeyboard;
- eventType.eventKind = kEventHotKeyReleased;
- InstallApplicationEventHandler(hotKeyFunction,1,&eventType,self,NULL);
-
- UInt32 keyCode = 49;
- EventHotKeyRef theRef = NULL;
- EventHotKeyID keyID;
- keyID.signature = 'FOO ';
- keyID.id = 1;
- RegisterEventHotKey(keyCode, cmdKey+controlKey, keyID, GetApplicationEventTarget(), 0, &theRef);
+ NSMenuItem *item = [mMenu itemWithTag:(NSInteger)[mMenuItemTags objectForKey:@"toggleRecord"]];
+ [item setKeyEquivalentModifierMask: NSControlKeyMask | NSCommandKeyMask];
+ [item setKeyEquivalent:keyEquivalent];
}
-- (void)applicationWillTerminate:(NSNotification *)aNotification
+- (void)rebuildDeviceList
{
- [self recordStop];
-
- if (gThruEngine2) {
- gThruEngine2->Stop();
- }
-
- if (mSoundflower2Device) {
- [self writeGlobalPrefs];
- }
+ if (mOutputDeviceList) delete mOutputDeviceList;
+ mOutputDeviceList = new AudioDeviceList;
}
-- (IBAction)bufferSizeChanged2ch:(id)sender
+- (void)initConnections
{
- UInt32 val = [m2chBuffer indexOfItem:sender];
- UInt32 size = 64 << val;
- gThruEngine2->SetBufferSize(size);
+ OSStatus err = noErr;
+ Float32 maxVolume = 1.0;
+ UInt32 size;
- [mCur2chBufferSize setState:NSOffState];
- [sender setState:NSOnState];
- mCur2chBufferSize = sender;
-}
+ AudioObjectPropertyAddress devCurrDefAddress = {
+ kAudioHardwarePropertyDefaultOutputDevice,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster
+ };
+ size = sizeof(AudioDeviceID);
+ err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &devCurrDefAddress, 0, NULL, &size, &mStashedAudioDeviceID);
-- (IBAction)cloningChanged:(id)sender
-{
- [sender setState:([sender state]==NSOnState) ? NSOffState : NSOnState];
- gThruEngine2->SetCloneChannels([sender state]==NSOnState);
- [self writeDevicePrefs:YES];
-}
+ mOutputDeviceID = mStashedAudioDeviceID;
-- (IBAction)cloningChanged:(id)sender cloneChannels:(bool)clone
-{
- gThruEngine2->SetCloneChannels(clone);
- [sender setState:(clone ? NSOnState : NSOffState)];
-}
+ AudioObjectPropertyAddress volCurrDef1Address = {
+ kAudioDevicePropertyVolumeScalar,
+ kAudioObjectPropertyScopeOutput,
+ 1
+ };
+ size = sizeof(Float32);
+ err = AudioObjectGetPropertyData(mStashedAudioDeviceID, &volCurrDef1Address, 0, NULL, &size, &mStashedVolume);
-- (IBAction)routingChanged2ch:(id)outDevChanItem
-{
- NSMenu *outDevMenu = [outDevChanItem menu];
- NSMenu *superMenu = [outDevMenu supermenu];
- int sfChan = [superMenu indexOfItemWithSubmenu:outDevMenu] - 3;
- int outDevChan = [outDevMenu indexOfItem:outDevChanItem];
+ AudioObjectPropertyAddress volCurrDef2Address = {
+ kAudioDevicePropertyVolumeScalar,
+ kAudioObjectPropertyScopeOutput,
+ 2
+ };
+ size = sizeof(Float32);
+ err = AudioObjectGetPropertyData(mStashedAudioDeviceID, &volCurrDef2Address, 0, NULL, &size, &mStashedVolume2);
- // set the new channel map
- gThruEngine2->SetChannelMap(sfChan, outDevChan-1);
+ mEngine = new AudioThruEngine(mWavTapDeviceID, mOutputDeviceID);
- // turn off all check marks
- for (int i = 0; i < [outDevMenu numberOfItems]; i++)
- [[outDevMenu itemAtIndex:i] setState:NSOffState];
+ AudioObjectPropertyAddress volSwapWav0Address = {
+ kAudioDevicePropertyVolumeScalar,
+ kAudioObjectPropertyScopeOutput,
+ 0
+ };
+ err = AudioObjectSetPropertyData(mWavTapDeviceID, &volSwapWav0Address, 0, NULL, sizeof(Float32), &maxVolume);
- // set this one
- [outDevChanItem setState:NSOnState];
+ AudioObjectPropertyAddress volSwapWav1Address = {
+ kAudioDevicePropertyVolumeScalar,
+ kAudioObjectPropertyScopeOutput,
+ 1
+ };
+ err = AudioObjectSetPropertyData(mWavTapDeviceID, &volSwapWav1Address, 0, NULL, sizeof(Float32), &maxVolume);
- // write to prefs
- [self writeDevicePrefs:YES];
-}
+ AudioObjectPropertyAddress volSwapWav2Address = {
+ kAudioDevicePropertyVolumeScalar,
+ kAudioObjectPropertyScopeOutput,
+ 2
+ };
+ err = AudioObjectSetPropertyData(mWavTapDeviceID, &volSwapWav2Address, 0, NULL, sizeof(Float32), &maxVolume);
-- (IBAction)outputDeviceSelected:(id)sender
-{
- int val = [mMenu indexOfItem:sender];
- val -= 2;
-
- // if 'None' was selected, our val will be == -1, which will return a NULL
- // device from the list, which is what we want anyway, and seems to work
- // here -- probably should check to see if there are any potential problems
- // and handle this more properly
- gThruEngine2->SetOutputDevice( (val < 0 ? kAudioDeviceUnknown : mMenuID2[val]) );
- [mCur2chDevice setState:NSOffState];
- [sender setState:NSOnState];
- mCur2chDevice = sender;
- [self readDevicePrefs:YES];
- [self buildRoutingMenu];
-}
+// AudioObjectPropertyAddress volSwapDefAddress = {
+// kAudioDevicePropertyVolumeScalar,
+// kAudioObjectPropertyScopeOutput,
+// 1
+// };
+//
+// err = AudioObjectSetPropertyData(mStashedAudioDeviceID, &volSwapDefAddress, 0, NULL, sizeof(Float32), &maxVolume);
+//
+// AudioObjectPropertyAddress volSwapDef2Address = {
+// kAudioDevicePropertyVolumeScalar,
+// kAudioObjectPropertyScopeOutput,
+// 2
+// };
+//
+// err = AudioObjectSetPropertyData(mStashedAudioDeviceID, &volSwapDef2Address, 0, NULL, sizeof(Float32), &maxVolume);
-- (void)doNothing { }
+ mEngine->Start();
-- (void)readGlobalPrefs
-{
- CFStringRef strng = (CFStringRef) CFPreferencesCopyAppValue(CFSTR("2ch Output Device"), kCFPreferencesCurrentApplication);
- if (strng) {
- char name[64];
- CFStringGetCString(strng, name, 64, kCFStringEncodingMacRoman);
- NSMenuItem *item = [mMenu itemWithTitle:[NSString stringWithUTF8String:name]];
- if (item)
- [self outputDeviceSelected:item];
- }
+ err = AudioObjectSetPropertyData(kAudioObjectSystemObject, &devCurrDefAddress, 0, NULL, sizeof(AudioDeviceID), &mWavTapDeviceID);
+}
- CFNumberRef num = (CFNumberRef) CFPreferencesCopyAppValue(CFSTR("2ch Buffer Size"), kCFPreferencesCurrentApplication);
- if (num) {
- UInt32 val;
- CFNumberGetValue(num, kCFNumberLongType, &val);
-// CFRelease(num);
+- (OSStatus)restoreSystemOutputDevice
+{
+ OSStatus err = noErr;
- switch (val) {
- case 64:
- [self bufferSizeChanged2ch:[m2chBuffer itemAtIndex:0]];
- break;
- case 128:
- [self bufferSizeChanged2ch:[m2chBuffer itemAtIndex:1]];
- break;
- case 256:
- [self bufferSizeChanged2ch:[m2chBuffer itemAtIndex:2]];
- break;
- case 1024:
- [self bufferSizeChanged2ch:[m2chBuffer itemAtIndex:4]];
- break;
- case 2048:
- [self bufferSizeChanged2ch:[m2chBuffer itemAtIndex:5]];
- break;
+ AudioObjectPropertyAddress devAddress = {
+ kAudioHardwarePropertyDefaultOutputDevice,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster
+ };
+ err = AudioObjectSetPropertyData(kAudioObjectSystemObject, &devAddress, 0, NULL, sizeof(AudioDeviceID), &mStashedAudioDeviceID);
- case 512:
- default:
- [self bufferSizeChanged2ch:[m2chBuffer itemAtIndex:3]];
- break;
- }
- }
+ return err;
}
-- (void)writeGlobalPrefs
+- (OSStatus)restoreSystemOutputDeviceVolume
{
- CFStringRef cfstr = CFStringCreateWithCString(kCFAllocatorSystemDefault, [[mCur2chDevice title] UTF8String], kCFStringEncodingMacRoman);
- CFPreferencesSetAppValue(CFSTR("2ch Output Device"), cfstr, kCFPreferencesCurrentApplication);
- CFRelease(cfstr);
-
- UInt32 val = 64 << [m2chBuffer indexOfItem:mCur2chBufferSize];
- CFNumberRef number = CFNumberCreate(kCFAllocatorSystemDefault, kCFNumberIntType, &val);
- CFPreferencesSetAppValue(CFSTR("2ch Buffer Size"), number, kCFPreferencesCurrentApplication);
- CFRelease(number);
+ OSStatus err = noErr;
- CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication);
+ AudioObjectPropertyAddress volAddress = {
+ kAudioDevicePropertyVolumeScalar,
+ kAudioObjectPropertyScopeOutput,
+ 1
+ };
+
+ err = AudioObjectSetPropertyData(kAudioObjectSystemObject, &volAddress, 0, NULL, sizeof(Float32), &mStashedVolume);
+
+ AudioObjectPropertyAddress vol2Address = {
+ kAudioDevicePropertyVolumeScalar,
+ kAudioObjectPropertyScopeOutput,
+ 2
+ };
+ err = AudioObjectSetPropertyData(kAudioObjectSystemObject, &vol2Address, 0, NULL, sizeof(Float32), &mStashedVolume2);
+
+ return err;
}
-- (CFStringRef)formDevicePrefName:(BOOL)is2ch
+OSStatus myHotKeyHandler(EventHandlerCallRef nextHandler, EventRef anEvent, void *userData)
{
- NSString *routingTag = @" [2ch Routing]";
- NSString *deviceName = [mCur2chDevice title];
- return CFStringCreateWithCString(kCFAllocatorSystemDefault, [[deviceName stringByAppendingString:routingTag] UTF8String], kCFStringEncodingMacRoman);
+ AppController* inUserData = (__bridge AppController*)userData;
+ [inUserData toggleRecord];
+ return noErr;
}
-- (void)readDevicePrefs:(BOOL)is2ch
+- (void)bindHotKeys
{
- AudioThruEngine *thruEng = gThruEngine2;
- int numChans = 2;
- CFStringRef arrayName = [self formDevicePrefName:is2ch];
- CFArrayRef mapArray = (CFArrayRef) CFPreferencesCopyAppValue(arrayName, kCFPreferencesCurrentApplication);
-
- if (mapArray) {
- for (int i = 0; i < numChans; i++) {
- CFNumberRef num = (CFNumberRef)CFArrayGetValueAtIndex(mapArray, i);
- if (num) {
- UInt32 val;
- CFNumberGetValue(num, kCFNumberLongType, &val);
- thruEng->SetChannelMap(i, val-1);
- //CFRelease(num);
- }
- }
- //CFRelease(mapArray);
- }
- else { // set to default
- for (int i = 0; i < numChans; i++)
- thruEng->SetChannelMap(i, i);
- }
-
- //CFRelease(arrayName);
+ hotKeyFunction = NewEventHandlerUPP(myHotKeyHandler);
+ EventTypeSpec eventType;
+ eventType.eventClass = kEventClassKeyboard;
+ eventType.eventKind = kEventHotKeyReleased;
+ InstallApplicationEventHandler(hotKeyFunction, 1, &eventType, (void *) CFBridgingRetain(self), NULL);
- if (is2ch) {
- CFBooleanRef clone = (CFBooleanRef)CFPreferencesCopyAppValue(CFSTR("Clone channels"), kCFPreferencesCurrentApplication);
- NSMenuItem* item = [[m2chMenu submenu] itemWithTitle:@"Clone to all channels"];
- if (clone && item) {
- [self cloningChanged:item cloneChannels:CFBooleanGetValue(clone)];
- CFRelease(clone);
- }
- else {
- thruEng->SetCloneChannels(false);
- }
- }
+ UInt32 keyCode = 49;
+ EventHotKeyRef theRef = NULL;
+ EventHotKeyID keyID;
+ keyID.signature = 'FOO ';
+ keyID.id = 1;
+ RegisterEventHotKey(keyCode, cmdKey+controlKey, keyID, GetApplicationEventTarget(), 0, &theRef);
}
-- (void)writeDevicePrefs:(BOOL)is2ch
+-(void)launchRecordProcess
{
- AudioThruEngine *thruEng = gThruEngine2;
- int numChans = 2;
- CFNumberRef map[64];
-
- CFStringRef arrayName = [self formDevicePrefName:is2ch];
-
- for (int i = 0; i < numChans; i++)
- {
- UInt32 val = thruEng->GetChannelMap(i) + 1;
- map[i] = CFNumberCreate(kCFAllocatorSystemDefault, kCFNumberIntType, &val);
- }
-
- CFArrayRef mapArray = CFArrayCreate(kCFAllocatorSystemDefault, (const void**)&map, numChans, NULL);
- CFPreferencesSetAppValue(arrayName, mapArray, kCFPreferencesCurrentApplication);
- //CFRelease(mapArray);
-
- //for (int i = 0; i < numChans; i++)
- // CFRelease(map[i]);
-
- //CFRelease(arrayName);
-
- if(is2ch){
- char cloneValue = thruEng->CloneChannels();
- CFNumberRef clone = (CFNumberRef)CFNumberCreate(kCFAllocatorSystemDefault, kCFNumberCharType, &cloneValue);
- CFPreferencesSetAppValue(CFSTR("Clone channels"),
- clone,
- kCFPreferencesCurrentApplication);
- CFRelease(clone);
- }
-
- CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication);
+ mEngine->mOutputDevice.ReloadStreamFormat();
+ NSString *bits = @"16"; // TODO: get physical format of output device's stream
+// bits = [NSString stringWithFormat:@"%d", mEngine->mOutputDevice.mFormat.mBitsPerChannel];
+ NSTask *task=[[NSTask alloc] init];
+ NSArray *argv=[NSArray arrayWithObject:bits];
+ [task setArguments: argv];
+ [task setLaunchPath:@"/Applications/WavTap.app/Contents/SharedSupport/record.sh"];
+ [task launch];
}
--(void)recordStart
+-(void)killRecordProcesses
{
- NSMenuItem *item = [mMenu itemWithTag:MENU_ITEM_TOGGLE_RECORD_TAG];
-
NSArray *argv=[NSArray arrayWithObjects:nil];
NSTask *task=[[NSTask alloc] init];
[task setArguments: argv];
- [task setLaunchPath:@"/Applications/WavTap.app/Contents/SharedSupport/record_start"];
+ [task setLaunchPath:@"/Applications/WavTap.app/Contents/SharedSupport/kill_recorders.sh"];
[task launch];
+}
+
+-(void)recordStart
+{
+ NSMenuItem *item = [mMenu itemWithTag:(NSInteger)[mMenuItemTags objectForKey:@"toggleRecord"]];
+ [self launchRecordProcess];
[item setTitle:@"Stop Recording"];
+ [mSbItem setImage:[NSImage imageNamed:@"menuIconRecording"]];
mIsRecording = YES;
}
-(void)recordStop
{
- NSMenuItem *item = [mMenu itemWithTag:MENU_ITEM_TOGGLE_RECORD_TAG];
-
- NSArray *argv=[NSArray arrayWithObjects:nil];
- NSTask *task=[[NSTask alloc] init];
- [task setArguments: argv];
- [task setLaunchPath:@"/Applications/WavTap.app/Contents/SharedSupport/record_stop"];
- [task launch];
+ NSMenuItem *item = [mMenu itemWithTag:(NSInteger)[mMenuItemTags objectForKey:@"toggleRecord"]];
+ [self killRecordProcesses];
[item setTitle:@"Record"];
+ NSImage *image = [NSImage imageNamed:@"menuIcon"];
+ [image setTemplate:YES];
+ [mSbItem setImage:image];
mIsRecording = NO;
}
--(void)doToggleRecord
+-(void)toggleRecord
{
- if(mIsRecording){
- [self recordStop];
- } else {
- [self recordStart];
- }
+ (mIsRecording) ? [self recordStop] : [self recordStart];
+}
+
+- (void)cleanupOnBeforeQuit
+{
+ if(mIsRecording) [self recordStop];
+ if(mEngine) mEngine->Stop();
+ [self restoreSystemOutputDevice];
+// [self restoreSystemOutputDeviceVolume]; // TODO: preserve our eardrums
}
--(void)doAbout
+- (void)applicationWillTerminate:(NSNotification *)notification
{
- [mAboutController doAbout];
+ [self cleanupOnBeforeQuit];
}
- (void)doQuit
{
- [self recordStop];
+ [self cleanupOnBeforeQuit];
[NSApp terminate:nil];
}
View
148 App/AudioDevice.cpp
@@ -1,72 +1,112 @@
#include "AudioDevice.h"
-void AudioDevice::Init(AudioDeviceID devid, bool isInput)
-{
- mID = devid;
- mIsInput = isInput;
- if (mID == kAudioDeviceUnknown) return;
-
- UInt32 propsize;
-
- propsize = sizeof(UInt32);
- verify_noerr(AudioDeviceGetProperty(mID, 0, mIsInput, kAudioDevicePropertySafetyOffset, &propsize, &mSafetyOffset));
-
- propsize = sizeof(UInt32);
- verify_noerr(AudioDeviceGetProperty(mID, 0, mIsInput, kAudioDevicePropertyBufferFrameSize, &propsize, &mBufferSizeFrames));
-
- UpdateFormat();
+AudioDevice::AudioDevice(AudioDeviceID id, bool isInput) {
+ OSStatus err = noErr;
+ mID = id;
+ mIsInput = isInput;
+ if (mID == kAudioDeviceUnknown) return;
+ UInt32 size = sizeof(Float32);
+
+ AudioObjectPropertyAddress addr = {
+ kAudioDevicePropertySafetyOffset,
+ (mIsInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput),
+ 0
+ };
+ err = AudioObjectGetPropertyData(mID, &addr, 0, NULL, &size, &mSafetyOffset);
+
+ size = sizeof(UInt32);
+ addr.mSelector = kAudioDevicePropertyBufferFrameSize;
+ err = AudioObjectGetPropertyData(mID, &addr, 0, NULL, &size, &mBufferSizeFrames);
+
+ size = sizeof(AudioStreamBasicDescription);
+ addr.mSelector = kAudioDevicePropertyStreamFormat;
+ err = AudioObjectGetPropertyData(mID, &addr, 0, NULL, &size, &mFormat);
}
-void AudioDevice::UpdateFormat()
-{
- UInt32 propsize = sizeof(AudioStreamBasicDescription);
- verify_noerr(AudioDeviceGetProperty(mID, 0, mIsInput, kAudioDevicePropertyStreamFormat, &propsize, &mFormat));
-}
+OSStatus AudioDevice::ReloadStreamFormat() {
+ OSStatus err = noErr;
+ UInt32 size = sizeof(mFormat);
-void AudioDevice::SetBufferSize(UInt32 size)
-{
- UInt32 propsize = sizeof(UInt32);
- verify_noerr(AudioDeviceSetProperty(mID, NULL, 0, mIsInput, kAudioDevicePropertyBufferFrameSize, propsize, &size));
+ AudioObjectPropertyAddress addr = {
+ kAudioDevicePropertyStreamFormat,
+ (mIsInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput),
+ 0
+ };
- propsize = sizeof(UInt32);
- verify_noerr(AudioDeviceGetProperty(mID, 0, mIsInput, kAudioDevicePropertyBufferFrameSize, &propsize, &mBufferSizeFrames));
+ err = AudioObjectGetPropertyData(mID, &addr, 0, NULL, &size, &mFormat);
+ return err;
}
-OSStatus AudioDevice::SetSampleRate(Float64 sr)
-{
- UInt32 propsize = sizeof(AudioStreamBasicDescription);
+OSStatus AudioDevice::SetSampleRate(Float64 sr) {
+ OSStatus err = noErr;
mFormat.mSampleRate = sr;
- OSStatus err = AudioDeviceSetProperty(mID, NULL, 0, mIsInput, kAudioDevicePropertyStreamFormat, propsize, &mFormat);
+ UInt32 size = sizeof(mFormat);
- // now re-read to see what actual value is
- UpdateFormat();
+ AudioObjectPropertyAddress addr = {
+ kAudioDevicePropertyStreamFormat,
+ (mIsInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput),
+ 0
+ };
+ err = AudioObjectSetPropertyData(mID, &addr, 0, NULL, size, &mFormat);
+
+ if(mFormat.mSampleRate != sr) printf("Error in AudioDevice::SetSampleRate - sample rate mismatch!");
return err;
}
+OSStatus AudioDevice::SetBufferSize(UInt32 buffersize) {
+ OSStatus err = noErr;
+ UInt32 size = sizeof(UInt32);
+
+ AudioObjectPropertyAddress addr = {
+ kAudioDevicePropertyBufferFrameSize,
+ (mIsInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput),
+ 0
+ };
+
+ err = AudioObjectSetPropertyData(mID, &addr, 0, NULL, size, &buffersize);
+ err = AudioObjectGetPropertyData(mID, &addr, 0, NULL, &size, &mBufferSizeFrames);
+
+ if(mBufferSizeFrames != buffersize) printf("buffer size mismatch!");
+
+ return err;
+}
-int AudioDevice::CountChannels()
-{
- OSStatus err;
- UInt32 propSize;
- int result = 0;
-
- err = AudioDeviceGetPropertyInfo(mID, 0, mIsInput, kAudioDevicePropertyStreamConfiguration, &propSize, NULL);
- if (err) return 0;
-
- AudioBufferList *buflist = (AudioBufferList *)malloc(propSize);
- err = AudioDeviceGetProperty(mID, 0, mIsInput, kAudioDevicePropertyStreamConfiguration, &propSize, buflist);
- if (!err) {
- for (UInt32 i = 0; i < buflist->mNumberBuffers; ++i) {
- result += buflist->mBuffers[i].mNumberChannels;
- }
- }
- free(buflist);
- return result;
+int AudioDevice::CountChannels() {
+ OSStatus err = noErr;
+ UInt32 size;
+ int result = 0;
+
+ AudioObjectPropertyAddress addr = {
+ kAudioDevicePropertyStreamConfiguration,
+ (mIsInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput),
+ 0
+ };
+
+ err = AudioObjectGetPropertyDataSize(mID, &addr, 0, NULL, &size);
+ if (err) return 0;
+
+ AudioBufferList *buflist = (AudioBufferList *)malloc(size);
+ err = AudioObjectGetPropertyData(mID, &addr, 0, NULL, &size, buflist);
+ if (!err) {
+ for (UInt32 i = 0; i < buflist->mNumberBuffers; ++i) {
+ result += buflist->mBuffers[i].mNumberChannels;
+ }
+ }
+ free(buflist);
+ return result;
}
-char * AudioDevice::GetName(char *buf, UInt32 maxlen)
-{
- verify_noerr(AudioDeviceGetProperty(mID, 0, mIsInput, kAudioDevicePropertyDeviceName, &maxlen, buf));
- return buf;
+char *AudioDevice::GetName(char *buf, UInt32 maxlen) {
+ OSStatus err = noErr;
+
+ AudioObjectPropertyAddress addr = {
+ kAudioDevicePropertyDeviceName,
+ (mIsInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput),
+ 0
+ };
+
+ err = AudioObjectGetPropertyData(mID, &addr, 0, NULL, &maxlen, buf);
+
+ return buf;
}
View
13 App/AudioDevice.h
@@ -5,19 +5,14 @@
#include <CoreAudio/CoreAudio.h>
class AudioDevice {
-
public:
- AudioDevice() : mID(kAudioDeviceUnknown) { }
- AudioDevice(AudioDeviceID devid, bool isInput) { Init(devid, isInput); }
- void Init(AudioDeviceID devid, bool isInput);
- bool Valid() { return mID != kAudioDeviceUnknown; }
- void SetBufferSize(UInt32 size);
+ AudioDevice(AudioDeviceID devid, bool isInput);
+ OSStatus SetBufferSize(UInt32 size);
OSStatus SetSampleRate(Float64 sr);
- void UpdateFormat();
+ OSStatus ReloadStreamFormat();
+ bool Valid() { return mID != kAudioDeviceUnknown; }
int CountChannels();
char *GetName(char *buf, UInt32 maxlen);
-
-public:
AudioDeviceID mID;
bool mIsInput;
UInt32 mSafetyOffset;
View
32 App/AudioDeviceList.cpp
@@ -1,33 +1,31 @@
#include "AudioDeviceList.h"
#include "AudioDevice.h"
-AudioDeviceList::AudioDeviceList(bool inputs) :
- mInputs(inputs)
-{
- BuildList();
-}
-
-AudioDeviceList::~AudioDeviceList()
-{
-}
-
+AudioDeviceList::AudioDeviceList() { BuildList(); }
+AudioDeviceList::~AudioDeviceList() {}
void AudioDeviceList::BuildList()
{
+ OSStatus err = noErr;
mDevices.clear();
-
UInt32 propsize;
+ AudioObjectPropertyAddress theAddress = {
+ kAudioHardwarePropertyDevices,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster
+ };
- verify_noerr(AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &propsize, NULL));
- int nDevices = propsize / sizeof(AudioDeviceID);
- AudioDeviceID *devids = new AudioDeviceID[nDevices];
- verify_noerr(AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &propsize, devids));
+ err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &theAddress, 0, NULL,&propsize);
+ int audioDeviceIDSize = sizeof(AudioDeviceID);
+ int nDevices = propsize / audioDeviceIDSize;
+
+ AudioDeviceID *devids = (AudioDeviceID *)calloc(nDevices, sizeof(AudioDeviceID));
+ err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &theAddress, 0, NULL, &propsize, devids);
for (int i = 0; i < nDevices; ++i) {
+ int mInputs = 2;
AudioDevice dev(devids[i], mInputs);
- //if (dev.CountChannels() > 0)
{
Device d;
-
d.mID = devids[i];
dev.GetName(d.mName, sizeof(d.mName));
mDevices.push_back(d);
View
3 App/AudioDeviceList.h
@@ -13,14 +13,13 @@ class AudioDeviceList {
AudioDeviceID mID;
};
typedef std::vector<Device> DeviceList;
- AudioDeviceList(bool inputs);
+ AudioDeviceList();
~AudioDeviceList();
DeviceList &GetList() { return mDevices; }
protected:
void BuildList();
void EraseList();
- bool mInputs;
DeviceList mDevices;
};
View
118 App/AudioRingBuffer.cpp
@@ -1,118 +0,0 @@
-#include "AudioRingBuffer.h"
-
-AudioRingBuffer::AudioRingBuffer(UInt32 bytesPerFrame, UInt32 capacityFrames) :
- mBuffer(NULL)
-{
- Allocate(bytesPerFrame, capacityFrames);
-}
-
-AudioRingBuffer::~AudioRingBuffer()
-{
- if (mBuffer)
- free(mBuffer);
-}
-
-void AudioRingBuffer::Allocate(UInt32 bytesPerFrame, UInt32 capacityFrames)
-{
- if (mBuffer)
- free(mBuffer);
-
- mBytesPerFrame = bytesPerFrame;
- mCapacityFrames = capacityFrames;
- mCapacityBytes = bytesPerFrame * capacityFrames;
- mBuffer = (Byte *)malloc(mCapacityBytes);
- Clear();
-}
-
-void AudioRingBuffer::Clear()
-{
- memset(mBuffer, 0, mCapacityBytes);
- mStartOffset = 0;
- mStartFrame = 0;
- mEndFrame = 0;
-}
-
-bool AudioRingBuffer::Store(const Byte *data, UInt32 nFrames, SInt64 startFrame)
-{
- if (nFrames > mCapacityFrames) return false;
-
- // $$$ we have an unaddressed critical region here
- // reading and writing could well be in separate threads
-
- SInt64 endFrame = startFrame + nFrames;
- if (startFrame >= mEndFrame + mCapacityFrames)
- // writing more than one buffer ahead -- fine but that means that everything we have is now too far in the past
- Clear();
-
- if (mStartFrame == 0) {
- // empty buffer
- mStartOffset = 0;
- mStartFrame = startFrame;
- mEndFrame = endFrame;
- memcpy(mBuffer, data, nFrames * mBytesPerFrame);
- } else {
- UInt32 offset0, offset1, nBytes;
- if (endFrame > mEndFrame) {
- // advancing (as will be usual with sequential stores)
-
- if (startFrame > mEndFrame) {
- // we are skipping some samples, so zero the range we are skipping
- offset0 = FrameOffset(mEndFrame);
- offset1 = FrameOffset(startFrame);
- if (offset0 < offset1)
- memset(mBuffer + offset0, 0, offset1 - offset0);
- else {
- nBytes = mCapacityBytes - offset0;
- memset(mBuffer + offset0, 0, nBytes);
- memset(mBuffer, 0, offset1);
- }
- }
- mEndFrame = endFrame;
-
- // except for the case of not having wrapped yet, we will normally
- // have to advance the start
- SInt64 newStart = mEndFrame - mCapacityFrames;
- if (newStart > mStartFrame) {
- mStartOffset = (mStartOffset + (newStart - mStartFrame) * mBytesPerFrame) % mCapacityBytes;
- mStartFrame = newStart;
- }
- }
- // now everything is lined up and we can just write the new data
- offset0 = FrameOffset(startFrame);
- offset1 = FrameOffset(endFrame);
- if (offset0 < offset1)
- memcpy(mBuffer + offset0, data, offset1 - offset0);
- else {
- nBytes = mCapacityBytes - offset0;
- memcpy(mBuffer + offset0, data, nBytes);
- memcpy(mBuffer, data + nBytes, offset1);
- }
- }
- //printf("Store - buffer times: %.0f - %.0f, writing %.0f - %.0f\n", double(mStartFrame), double(mEndFrame), double(startFrame), double(endFrame));
-
- return true;
-}
-
-double AudioRingBuffer::Fetch(Byte *data, UInt32 nFrames, SInt64 startFrame)
-{
- SInt64 endFrame = startFrame + nFrames;
- if (startFrame < mStartFrame || endFrame > mEndFrame) {
- //printf("error - buffer times: %.0f - %.0f, reading for %.0f - %.0f\n", double(mStartFrame), double(mEndFrame), double(startFrame), double(endFrame));
- if (startFrame < mStartFrame)
- return double(startFrame - mStartFrame);
- else
- return double(endFrame - mEndFrame);
- }
-
- UInt32 offset0 = FrameOffset(startFrame);
- UInt32 offset1 = FrameOffset(endFrame);
-
- if (offset0 < offset1)
- memcpy(data, mBuffer + offset0, offset1 - offset0);
- else {
- UInt32 nBytes = mCapacityBytes - offset0;
- memcpy(data, mBuffer + offset0, nBytes);
- memcpy(data + nBytes, mBuffer, offset1);
- }
- return double((mEndFrame - nFrames) - startFrame);
-}
View
29 App/AudioRingBuffer.h
@@ -1,29 +0,0 @@
-#ifndef __AudioRingBuffer_h__
-#define __AudioRingBuffer_h__
-
-#include <CoreServices/CoreServices.h>
-
-class AudioRingBuffer {
-
-public:
- AudioRingBuffer(UInt32 bytesPerFrame, UInt32 capacityFrames);
- ~AudioRingBuffer();
- void Allocate(UInt32 bytesPerFrame, UInt32 capacityFrames);
- void Clear();
- bool Store(const Byte *data, UInt32 nFrames, SInt64 frameNumber);
- double Fetch(Byte *data, UInt32 nFrames, SInt64 frameNumber);
-
-protected:
- UInt32 FrameOffset(SInt64 frameNumber) { return (mStartOffset + UInt32(frameNumber - mStartFrame) * mBytesPerFrame) % mCapacityBytes; }
-
-protected:
- UInt32 mBytesPerFrame;
- UInt32 mCapacityFrames;
- UInt32 mCapacityBytes;
- Byte *mBuffer;
- UInt32 mStartOffset;
- SInt64 mStartFrame;
- SInt64 mEndFrame;
-};
-
-#endif // __AudioRingBuffer_h__
View
305 App/AudioThruEngine.cpp
@@ -1,180 +1,107 @@
#include "AudioThruEngine.h"
-#include "AudioRingBuffer.h"
+#include "AudioDevice.h"
#include <unistd.h>
-#define USE_AUDIODEVICEREAD 0
-#if USE_AUDIODEVICEREAD
-AudioBufferList *gInputIOBuffer = NULL;
-#endif
-
-#define kSecondsInRingBuffer 2.
-
-AudioThruEngine::AudioThruEngine() :
- mWorkBuf(NULL),
- mRunning(false),
- mMuting(false),
- mThruing(true),
- mBufferSize(512),
- mExtraLatencyFrames(0),
- mInputLoad(0.),
- mOutputLoad(0.)
-{
- mInputBuffer = new AudioRingBuffer(4, 88200);
-
- // init routing map to default chan->chan
- for (int i = 0; i < 64; i++)
- mChannelMap[i] = i;
-}
-
-AudioThruEngine::~AudioThruEngine()
-{
- SetDevices(kAudioDeviceUnknown, kAudioDeviceUnknown);
- delete mInputBuffer;
+AudioThruEngine::AudioThruEngine(AudioDeviceID inputDeviceID, AudioDeviceID outputDeviceID) : mWorkBuf(NULL), mBufferSize(512), mExtraLatencyFrames(0), mInputDevice(inputDeviceID, true), mOutputDevice(outputDeviceID, false), mRunning(false), mMuting(false), mThruing(true) {
+ mInputDevice.SetBufferSize(mBufferSize);
+ mOutputDevice.SetBufferSize(mBufferSize);
}
-void AudioThruEngine::SetDevices(AudioDeviceID input, AudioDeviceID output)
-{
- Stop();
-
- if (input != kAudioDeviceUnknown)
- mInputDevice.Init(input, true);
- if (output != kAudioDeviceUnknown)
- mOutputDevice.Init(output, false);
-}
-
-
-void AudioThruEngine::SetInputDevice(AudioDeviceID input)
-{
- Stop();
- mInputDevice.Init(input, true);
- SetBufferSize(mBufferSize);
- mInputBuffer->Clear();
- Start();
-}
-
-void AudioThruEngine::SetOutputDevice(AudioDeviceID output)
-{
- Stop();
- mOutputDevice.Init(output, false);
- SetBufferSize(mBufferSize);
- Start();
-}
-
-void AudioThruEngine::SetBufferSize(UInt32 size)
-{
- bool wasRunning = Stop();
- mBufferSize = size;
- mInputDevice.SetBufferSize(size);
- mOutputDevice.SetBufferSize(size);
- if (wasRunning) Start();
-}
+void AudioThruEngine::ComputeThruOffset() {
+ if (!mRunning) {
+ mActualThruLatency = 0;
+ mInToOutSampleOffset = 0;
+ return;
+ }
-void AudioThruEngine::SetExtraLatency(SInt32 frames)
-{
- mExtraLatencyFrames = frames;
- if (mRunning)
- ComputeThruOffset();
+ mActualThruLatency = SInt32(mInputDevice.mSafetyOffset + mInputDevice.mBufferSizeFrames + mOutputDevice.mSafetyOffset + mOutputDevice.mBufferSizeFrames) + mExtraLatencyFrames;
+ mInToOutSampleOffset = mActualThruLatency + mIODeltaSampleCount;
}
-// change sample rate of one device to match another - returns if successful or not
-OSStatus AudioThruEngine::MatchSampleRate(bool useInputDevice)
-{
+OSStatus AudioThruEngine::MatchSampleRates(AudioObjectID changedDeviceID) {
OSStatus status = kAudioHardwareNoError;
-
- mInputDevice.UpdateFormat();
- mOutputDevice.UpdateFormat();
-
+ mInputDevice.ReloadStreamFormat();
+ mOutputDevice.ReloadStreamFormat();
if (mInputDevice.mFormat.mSampleRate != mOutputDevice.mFormat.mSampleRate)
{
- if (useInputDevice)
+ if (mInputDevice.mID == changedDeviceID) {
status = mOutputDevice.SetSampleRate(mInputDevice.mFormat.mSampleRate);
- else
+ } else if (mOutputDevice.mID == changedDeviceID) {
status = mInputDevice.SetSampleRate(mOutputDevice.mFormat.mSampleRate);
-
- printf("reset sample rate\n");
+ }
+ else {
+ printf("Error in AudioThruEngine::MatchSampleRates() - unrelated device ID: %u \n", changedDeviceID);
+ }
}
return status;
}
-void AudioThruEngine::Start()
+inline void MakeBufferSilent(AudioBufferList * ioData)
{
- if (mRunning) return;
- if (!mInputDevice.Valid() || !mOutputDevice.Valid()) {
- return;
+ for(UInt32 i=0; i<ioData->mNumberBuffers; i++){
+ memset(ioData->mBuffers[i].mData, 0, ioData->mBuffers[i].mDataByteSize);
}
+}
+
+void AudioThruEngine::Start() {
+ OSStatus err = noErr;
+ if (mRunning) return;
+ if (!mInputDevice.Valid() || !mOutputDevice.Valid()) return;
+
+ MatchSampleRates(mOutputDevice.mID);
if (mInputDevice.mFormat.mSampleRate != mOutputDevice.mFormat.mSampleRate) {
- if (MatchSampleRate(false)) {
- printf("Error - sample rate mismatch: %f / %f\n", mInputDevice.mFormat.mSampleRate, mOutputDevice.mFormat.mSampleRate);
- return;
- }
+ printf("Error in AudioThruEngine::Start() - sample rate mismatch: %f / %f\n", mInputDevice.mFormat.mSampleRate, mOutputDevice.mFormat.mSampleRate);
+ return;
}
- mInputBuffer->Allocate(mInputDevice.mFormat.mBytesPerFrame, UInt32(kSecondsInRingBuffer * mInputDevice.mFormat.mSampleRate));
mSampleRate = mInputDevice.mFormat.mSampleRate;
mWorkBuf = new Byte[mInputDevice.mBufferSizeFrames * mInputDevice.mFormat.mBytesPerFrame];
memset(mWorkBuf, 0, mInputDevice.mBufferSizeFrames * mInputDevice.mFormat.mBytesPerFrame);
- mRunning = true;
-
-#if USE_AUDIODEVICEREAD
- UInt32 streamListSize;
- verify_noerr (AudioDeviceGetPropertyInfo(gInputDevice, 0, true, kAudioDevicePropertyStreams, &streamListSize, NULL));
- UInt32 nInputStreams = streamListSize / sizeof(AudioStreamID);
+ printf("Initializing mWorkBuf with mBufferSizeFrames:%u and mBytesPerFrame %u\n", mInputDevice.mBufferSizeFrames, mInputDevice.mFormat.mBytesPerFrame);
- propsize = offsetof(AudioBufferList, mBuffers[nInputStreams]);
- gInputIOBuffer = (AudioBufferList *)malloc(propsize);
- verify_noerr (AudioDeviceGetProperty(gInputDevice, 0, true, kAudioDevicePropertyStreamConfiguration, &propsize, gInputIOBuffer));
- gInputIOBuffer->mBuffers[0].mData = malloc(gInputIOBuffer->mBuffers[0].mDataByteSize);
-
- verify_noerr (AudioDeviceSetProperty(gInputDevice, NULL, 0, true, kAudioDevicePropertyRegisterBufferList, propsize, gInputIOBuffer));
-#endif
+ mRunning = true;
mInputProcState = kStarting;
mOutputProcState = kStarting;
- verify_noerr (AudioDeviceAddIOProc(mInputDevice.mID, InputIOProc, this));
- verify_noerr (AudioDeviceStart(mInputDevice.mID, InputIOProc));
+ mInputIOProcID = NULL;
+ err = AudioDeviceCreateIOProcID(mInputDevice.mID, InputIOProc, this, &mInputIOProcID);
+ err = AudioDeviceStart(mInputDevice.mID, mInputIOProcID);
mOutputIOProc = OutputIOProc;
- verify_noerr (AudioDeviceAddIOProc(mOutputDevice.mID, mOutputIOProc, this));
- verify_noerr (AudioDeviceStart(mOutputDevice.mID, mOutputIOProc));
+ mOutputIOProcID = NULL;
+ err = AudioDeviceCreateIOProcID(mOutputDevice.mID, mOutputIOProc, this, &mOutputIOProcID);
+ err = AudioDeviceStart(mOutputDevice.mID, mOutputIOProcID);
- while (mInputProcState != kRunning || mOutputProcState != kRunning)
+ while (mInputProcState != kRunning || mOutputProcState != kRunning) {
usleep(1000);
-
- ComputeThruOffset();
-}
-
-void AudioThruEngine::ComputeThruOffset()
-{
- if (!mRunning) {
- mActualThruLatency = 0;
- mInToOutSampleOffset = 0;
- return;
}
- mActualThruLatency = SInt32(mInputDevice.mSafetyOffset + /*2 * */ mInputDevice.mBufferSizeFrames +
- mOutputDevice.mSafetyOffset + mOutputDevice.mBufferSizeFrames) + mExtraLatencyFrames;
- mInToOutSampleOffset = mActualThruLatency + mIODeltaSampleCount;
+ ComputeThruOffset();
}
-bool AudioThruEngine::Stop()
-{
+bool AudioThruEngine::Stop() {
+ OSStatus err = noErr;
if (!mRunning) return false;
mRunning = false;
mInputProcState = kStopRequested;
mOutputProcState = kStopRequested;
- while (mInputProcState != kOff || mOutputProcState != kOff)
+ while (mInputProcState != kOff || mOutputProcState != kOff){
usleep(5000);
+ }
- AudioDeviceRemoveIOProc(mInputDevice.mID, InputIOProc);
- AudioDeviceRemoveIOProc(mOutputDevice.mID, mOutputIOProc);
+ err = AudioDeviceStop(mInputDevice.mID, mInputIOProcID);
+ err = AudioDeviceDestroyIOProcID(mInputDevice.mID, mInputIOProcID);
+
+ err = AudioDeviceStop(mOutputDevice.mID, mOutputIOProcID);
+ err = AudioDeviceDestroyIOProcID(mOutputDevice.mID, mOutputIOProcID);
if (mWorkBuf) {
delete[] mWorkBuf;
@@ -184,18 +111,7 @@ bool AudioThruEngine::Stop()
return true;
}
-
-
-// Input IO Proc
-// Receiving input for 1 buffer + safety offset into the past
-OSStatus AudioThruEngine::InputIOProc ( AudioDeviceID inDevice,
- const AudioTimeStamp* inNow,
- const AudioBufferList* inInputData,
- const AudioTimeStamp* inInputTime,
- AudioBufferList* outOutputData,
- const AudioTimeStamp* inOutputTime,
- void* inClientData)
-{
+OSStatus AudioThruEngine::InputIOProc(AudioDeviceID inDevice, const AudioTimeStamp *inNow, const AudioBufferList *inInputData, const AudioTimeStamp *inInputTime, AudioBufferList *outOutputData, const AudioTimeStamp *inOutputTime, void *inClientData) {
AudioThruEngine *This = (AudioThruEngine *)inClientData;
switch (This->mInputProcState) {
@@ -211,24 +127,15 @@ OSStatus AudioThruEngine::InputIOProc ( AudioDeviceID inDevice,
}
This->mLastInputSampleCount = inInputTime->mSampleTime;
- This->mInputBuffer->Store((const Byte *)inInputData->mBuffers[0].mData,
- This->mInputDevice.mBufferSizeFrames,
- UInt64(inInputTime->mSampleTime));
-// This->ApplyLoad(This->mInputLoad);
+ for(UInt32 i=0; i<outOutputData->mNumberBuffers; i++){
+ memcpy(This->mWorkBuf, inInputData->mBuffers[i].mData, inInputData->mBuffers[i].mDataByteSize);
+ }
+
return noErr;
}
-// Output IO Proc
-// Rendering output for 1 buffer + safety offset into the future
-OSStatus AudioThruEngine::OutputIOProc ( AudioDeviceID inDevice,
- const AudioTimeStamp* inNow,
- const AudioBufferList* inInputData,
- const AudioTimeStamp* inInputTime,
- AudioBufferList* outOutputData,
- const AudioTimeStamp* inOutputTime,
- void* inClientData)
-{
+OSStatus AudioThruEngine::OutputIOProc(AudioDeviceID inDevice, const AudioTimeStamp *inNow, const AudioBufferList *inInputData, const AudioTimeStamp *inInputTime, AudioBufferList *outOutputData, const AudioTimeStamp *inOutputTime, void *inClientData) {
AudioThruEngine *This = (AudioThruEngine *)inClientData;
switch (This->mOutputProcState) {
@@ -247,96 +154,18 @@ OSStatus AudioThruEngine::OutputIOProc ( AudioDeviceID inDevice,
}
if (!This->mMuting && This->mThruing) {
- //double delta = This->mInputBuffer->Fetch((Byte *)outOutputData->mBuffers[0].mData,
- // This->mOutputDevice.mBufferSizeFrames,
- // UInt64(inOutputTime->mSampleTime - This->mInToOutSampleOffset));
- double delta = This->mInputBuffer->Fetch(This->mWorkBuf,
- This->mInputDevice.mBufferSizeFrames,UInt64(inOutputTime->mSampleTime - This->mInToOutSampleOffset));
-
-
- // not the most efficient, but this should handle devices with multiple streams [i think]
- // with identitical formats [we know soundflower input channels are always one stream]
- UInt32 innchnls = This->mInputDevice.mFormat.mChannelsPerFrame;
-
- // iSchemy's edit
- //
- // this solution will probably be a little bit less efficient
- // but I wanted to retain the functionality of previous solution
- // and only add new function
- // Activity Monitor says it's not bad. 14.8MB and 3% CPU for me
- // is IMHO insignificant
- UInt32* chanstart = new UInt32[64];
-
- for (UInt32 buf = 0; buf < outOutputData->mNumberBuffers; buf++)
- {
- for (int i = 0; i < 64; i++)
- chanstart[i] = 0;
- UInt32 outnchnls = outOutputData->mBuffers[buf].mNumberChannels;
- for (UInt32 chan = 0; chan <
- ((This->CloneChannels() && innchnls==2) ? outnchnls : innchnls);
- chan++)
- {
- UInt32 outChan = This->GetChannelMap(chan) - chanstart[chan];
- if (outChan >= 0 && outChan < outnchnls)
- {
- // odd-even
- float *in = (float *)This->mWorkBuf + (chan % innchnls);
- float *out = (float *)outOutputData->mBuffers[buf].mData + outChan;
- long framesize = outnchnls * sizeof(float);
-
- for (UInt32 frame = 0; frame < outOutputData->mBuffers[buf].mDataByteSize; frame += framesize )
- {
- *out += *in;
- in += innchnls;
- out += outnchnls;
- }
- }
- chanstart[chan] += outnchnls;
- }
+ for(UInt32 i=0; i<outOutputData->mNumberBuffers; i++){
+ memcpy(outOutputData->mBuffers[i].mData, This->mWorkBuf, outOutputData->mBuffers[i].mDataByteSize);
}
-
- delete [] chanstart;
-
- //
- // end
-
- This->mThruTime = delta;
-
- //This->ApplyLoad(This->mOutputLoad);
-
-#if USE_AUDIODEVICEREAD
- AudioTimeStamp readTime;
-
- readTime.mFlags = kAudioTimeStampSampleTimeValid;
- readTime.mSampleTime = inNow->mSampleTime - gInputSafetyOffset - gOutputSampleCount;
-
- verify_noerr(AudioDeviceRead(gInputDevice.mID, &readTime, gInputIOBuffer));
- memcpy(outOutputData->mBuffers[0].mData, gInputIOBuffer->mBuffers[0].mData, outOutputData->mBuffers[0].mDataByteSize);
-#endif
- } else
+ } else {
This->mThruTime = 0.;
-
+ }
return noErr;
}
-UInt32 AudioThruEngine::GetOutputNchnls()
-{
- if (mOutputDevice.mID != kAudioDeviceUnknown)
- return mOutputDevice.CountChannels();//mFormat.mChannelsPerFrame;
-
- return 0;
-}
-
-#if 0
-void AudioThruEngine::ApplyLoad(double load)
-{
- double loadNanos = (load * mBufferSize / mSampleRate) /* seconds */ * 1000000000.;
-
- UInt64 now = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
- UInt64 waitUntil = UInt64(now + loadNanos);
-
- while (now < waitUntil) {
- now = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
+UInt32 AudioThruEngine::GetOutputNchnls() {
+ if (mOutputDevice.mID != kAudioDeviceUnknown) {
+ return mOutputDevice.CountChannels();
}
+ return 0;
}
-#endif
View
60 App/AudioThruEngine.h
@@ -3,37 +3,27 @@
#include "AudioDevice.h"
-class AudioRingBuffer;
-
class AudioThruEngine {
public:
- AudioThruEngine();
- ~AudioThruEngine();
-
- void SetDevices(AudioDeviceID input, AudioDeviceID output);
- void SetInputDevice(AudioDeviceID input);
- void SetOutputDevice(AudioDeviceID output);
+ AudioThruEngine(AudioDeviceID inputDeviceID, AudioDeviceID outputDeviceID);
+ ~AudioThruEngine() {}
void Start();
bool Stop();
+ void SetDevices(AudioDeviceID input, AudioDeviceID output);
void Mute(bool mute = true) { mMuting = mute; }
bool IsRunning() { return mRunning; }
void EnableThru(bool enable) { mThruing = enable; }
void SetBufferSize(UInt32 size);
- void SetInputLoad(double load) { mInputLoad = load; }
- void SetOutputLoad(double load) { mOutputLoad = load; }
- void SetExtraLatency(SInt32 frames);
double GetThruTime() { return mThruTime; }
SInt32 GetThruLatency() { return mActualThruLatency; }
UInt32 GetOutputNchnls();
- AudioDeviceID GetOutputDevice() { return mOutputDevice.mID; }
- AudioDeviceID GetInputDevice() { return mInputDevice.mID; }
- OSStatus MatchSampleRate(bool useInputDevice);
-
- void SetChannelMap(int ch, int val) { mChannelMap[ch] = val; } // valid values are 0 to nchnls-1; -1 = off
- int GetChannelMap(int ch) { return mChannelMap[ch]; }
+ AudioDeviceID GetOutputDeviceID() { return mOutputDevice.mID; }
+ AudioDeviceID GetInputDeviceID() { return mInputDevice.mID; }
+ OSStatus MatchSampleRates(AudioObjectID changedDeviceID);
Byte *mWorkBuf;
- void SetCloneChannels(bool clone) { mCloneChannels = clone; }
- bool CloneChannels() { return mCloneChannels; }
+ UInt32 mBufferSize;
+ SInt32 mExtraLatencyFrames;
+ AudioDevice mInputDevice, mOutputDevice;
protected:
enum IOProcState {
@@ -44,39 +34,35 @@ class AudioThruEngine {
};
static OSStatus InputIOProc (AudioDeviceID inDevice,
- const AudioTimeStamp *inNow,
- const AudioBufferList *inInputData,
- const AudioTimeStamp *inInputTime,
- AudioBufferList *outOutputData,
- const AudioTimeStamp *inOutputTime,
- void *inClientData);
+ const AudioTimeStamp *inNow,
+ const AudioBufferList *inInputData,
+ const AudioTimeStamp *inInputTime,
+ AudioBufferList *outOutputData,
+ const AudioTimeStamp *inOutputTime,
+ void *inClientData);
static OSStatus OutputIOProc (AudioDeviceID inDevice,
- const AudioTimeStamp *inNow,
- const AudioBufferList *inInputData,
- const AudioTimeStamp *inInputTime,
- AudioBufferList *outOutputData,
- const AudioTimeStamp *inOutputTime,
- void *inClientData);
+ const AudioTimeStamp *inNow,
+ const AudioBufferList *inInputData,
+ const AudioTimeStamp *inInputTime,
+ AudioBufferList *outOutputData,
+ const AudioTimeStamp *inOutputTime,
+ void *inClientData);
void ComputeThruOffset();
- AudioDevice mInputDevice, mOutputDevice;
bool mRunning;
bool mMuting;
bool mThruing;
IOProcState mInputProcState, mOutputProcState;
Float64 mLastInputSampleCount, mIODeltaSampleCount;
- UInt32 mBufferSize;
- SInt32 mExtraLatencyFrames;
SInt32 mActualThruLatency;
Float64 mSampleRate;
Float64 mInToOutSampleOffset; // subtract from the output time to obtain input time
- AudioRingBuffer *mInputBuffer;
double mInputLoad, mOutputLoad;
double mThruTime;
- int mChannelMap[64];
+ AudioDeviceIOProcID mInputIOProcID;
+ AudioDeviceIOProcID mOutputIOProcID;
AudioDeviceIOProc mOutputIOProc;
- bool mCloneChannels;
};
#endif // __AudioThruEngine_h__
View
198 App/English.lproj/MainMenu.nib/designable.nib
@@ -2,7 +2,7 @@
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="8.00">
<data>
<int key="IBDocument.SystemTarget">1050</int>
- <string key="IBDocument.SystemVersion">12A269</string>
+ <string key="IBDocument.SystemVersion">12B19</string>
<string key="IBDocument.InterfaceBuilderVersion">2549</string>
<string key="IBDocument.AppKitVersion">1187</string>
<string key="IBDocument.HIToolboxVersion">624.00</string>
@@ -12,10 +12,6 @@
</object>
<array key="IBDocument.IntegratedClassDependencies">
<string>NSCustomObject</string>
- <string>NSTextField</string>
- <string>NSTextFieldCell</string>
- <string>NSView</string>
- <string>NSWindowTemplate</string>
</array>
<array key="IBDocument.PluginDependencies">
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
@@ -37,112 +33,9 @@
<object class="NSCustomObject" id="543572546">
<string key="NSClassName">AppController</string>
</object>
- <object class="NSWindowTemplate" id="759315484">
- <int key="NSWindowStyleMask">3</int>
- <int key="NSWindowBacking">2</int>
- <string key="NSWindowRect">{{638, 856}, {377, 106}}</string>
- <int key="NSWTFlags">813171712</int>
- <string key="NSWindowTitle">WavTap</string>
- <string key="NSWindowClass">NSWindow</string>
- <object class="NSMutableString" key="NSViewClass">
- <characters key="NS.bytes">View</characters>
- </object>
- <nil key="NSUserInterfaceItemIdentifier"/>
- <object class="NSView" key="NSWindowView" id="845083901">
- <reference key="NSNextResponder"/>
- <int key="NSvFlags">256</int>
- <array class="NSMutableArray" key="NSSubviews">
- <object class="NSTextField" id="587231349">
- <reference key="NSNextResponder" ref="845083901"/>
- <int key="NSvFlags">256</int>
- <string key="NSFrame">{{80, 67}, {216, 17}}</string>
- <reference key="NSSuperview" ref="845083901"/>
- <reference key="NSNextKeyView" ref="375902303"/>
- <bool key="NSEnabled">YES</bool>
- <object class="NSTextFieldCell" key="NSCell" id="979784372">
- <int key="NSCellFlags">67108864</int>
- <int key="NSCellFlags2">138412032</int>
- <string key="NSContents">0.0.3</string>
- <object class="NSFont" key="NSSupport">
- <string key="NSName">LucidaGrande-Bold</string>
- <double key="NSSize">13</double>
- <int key="NSfFlags">2072</int>
- </object>
- <reference key="NSControlView" ref="587231349"/>
- <object class="NSColor" key="NSBackgroundColor" id="515670787">
- <int key="NSColorSpace">6</int>
- <string key="NSCatalogName">System</string>
- <string key="NSColorName">controlColor</string>
- <object class="NSColor" key="NSColor">
- <int key="NSColorSpace">3</int>
- <bytes key="NSWhite">MC42NjY2NjY2NjY3AA</bytes>
- </object>
- </object>
- <object class="NSColor" key="NSTextColor" id="2825295">
- <int key="NSColorSpace">6</int>
- <string key="NSCatalogName">System</string>
- <string key="NSColorName">controlTextColor</string>
- <object class="NSColor" key="NSColor">
- <int key="NSColorSpace">3</int>
- <bytes key="NSWhite">MAA</bytes>
- </object>
- </object>
- </object>
- <bool key="NSAllowsLogicalLayoutDirection">NO</bool>
- </object>
- <object class="NSTextField" id="375902303">
- <reference key="NSNextResponder" ref="845083901"/>
- <int key="NSvFlags">256</int>
- <string key="NSFrame">{{89, 48}, {199, 14}}</string>
- <reference key="NSSuperview" ref="845083901"/>
- <bool key="NSEnabled">YES</bool>
- <object class="NSTextFieldCell" key="NSCell" id="227147509">
- <int key="NSCellFlags">69206017</int>
- <int key="NSCellFlags2">138412032</int>
- <string key="NSContents">http://github.com/pje/WavTap</string>
- <object class="NSFont" key="NSSupport">
- <string key="NSName">LucidaGrande</string>
- <double key="NSSize">11</double>
- <int key="NSfFlags">3100</int>
- </object>
- <reference key="NSControlView" ref="375902303"/>
- <reference key="NSBackgroundColor" ref="515670787"/>
- <reference key="NSTextColor" ref="2825295"/>
- </object>
- <bool key="NSAllowsLogicalLayoutDirection">NO</bool>
- </object>
- </array>
- <string key="NSFrameSize">{377, 106}</string>
- <reference key="NSSuperview"/>
- <reference key="NSNextKeyView" ref="587231349"/>
- </object>
- <string key="NSScreenRect">{{0, 0}, {1440, 878}}</string>
- <string key="NSMaxSize">{10000000000000, 10000000000000}</string>
- <bool key="NSWindowIsRestorable">YES</bool>
- </object>
- <object class="NSCustomObject" id="412716826">
- <string key="NSClassName">HelpWindowController</string>
- </object>
</array>
<object class="IBObjectContainer" key="IBDocument.Objects">
- <array class="NSMutableArray" key="connectionRecords">
- <object class="IBConnectionRecord">
- <object class="IBOutletConnection" key="connection">
- <string key="label">mAboutController</string>
- <reference key="source" ref="543572546"/>
- <reference key="destination" ref="412716826"/>
- </object>
- <int key="connectionID">315</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBOutletConnection" key="connection">
- <string key="label">window</string>
- <reference key="source" ref="412716826"/>
- <reference key="destination" ref="759315484"/>
- </object>
- <int key="connectionID">314</int>
- </object>
- </array>
+ <array class="NSMutableArray" key="connectionRecords"/>
<object class="IBMutableOrderedSet" key="objectRecords">
<array key="orderedObjects">
<object class="IBObjectRecord">
@@ -175,101 +68,38 @@
<reference key="parent" ref="0"/>
<string key="objectName">AppController</string>
</object>
- <object class="IBObjectRecord">
- <int key="objectID">305</int>
- <reference key="object" ref="759315484"/>
- <array class="NSMutableArray" key="children">
- <reference ref="845083901"/>
- </array>
- <reference key="parent" ref="0"/>
- <string key="objectName">Window</string>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">306</int>
- <reference key="object" ref="845083901"/>
- <array class="NSMutableArray" key="children">
- <reference ref="375902303"/>
- <reference ref="587231349"/>
- </array>
- <reference key="parent" ref="759315484"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">308</int>
- <reference key="object" ref="587231349"/>
- <array class="NSMutableArray" key="children">
- <reference ref="979784372"/>
- </array>
- <reference key="parent" ref="845083901"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">309</int>
- <reference key="object" ref="375902303"/>
- <array class="NSMutableArray" key="children">
- <reference ref="227147509"/>
- </array>
- <reference key="parent" ref="845083901"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">313</int>
- <reference key="object" ref="412716826"/>
- <reference key="parent" ref="0"/>
- <string key="objectName">HelpWindowController</string>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">319</int>
- <reference key="object" ref="979784372"/>
- <reference key="parent" ref="587231349"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">320</int>
- <reference key="object" ref="227147509"/>
- <reference key="parent" ref="375902303"/>
- </object>
</array>
</object>
<dictionary class="NSMutableDictionary" key="flattenedProperties">
<string key="-1.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="-2.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="-3.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="304.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <boolean value="YES" key="305.IBNSWindowAutoPositionCentersHorizontal"/>
- <boolean value="YES" key="305.IBNSWindowAutoPositionCentersVertical"/>
- <string key="305.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="305.IBWindowTemplateEditedContentRect">{{14, 732}, {377, 113}}</string>
- <string key="306.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="308.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <object class="NSMutableAttributedString" key="308.notes">
- <object class="NSMutableString" key="NSString">
- <characters key="NS.bytes"/>
- </object>
- </object>
- <string key="309.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="313.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="319.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <object class="NSMutableAttributedString" key="319.notes">
- <object class="NSMutableString" key="NSString">
- <characters key="NS.bytes"/>
- </object>
- </object>
- <string key="320.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
</dictionary>
<dictionary class="NSMutableDictionary" key="unlocalizedProperties"/>
<nil key="activeLocalization"/>
<dictionary class="NSMutableDictionary" key="localizations"/>
<nil key="sourceID"/>
<int key="maxID">325</int>
</object>
- <object class="IBClassDescriber" key="IBDocument.Classes"/>
+ <object class="IBClassDescriber" key="IBDocument.Classes">
+ <array class="NSMutableArray" key="referencedPartialClassDescriptions">
+ <object class="IBPartialClassDescription">
+ <string key="className">AppController</string>
+ <string key="superclassName">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/AppController.h</string>
+ </object>
+ </object>
+ </array>
+ </object>
<int key="IBDocument.localizationMode">0</int>
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencies">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
<integer value="1050" key="NS.object.0"/>
</object>
- <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
- <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx<