<commit_before>#import "AppController.h"

#include "AudioThruEngine.h"

@implementation AppController

AudioThruEngine  *gThruEngine2 = NULL;
Boolean startOnAwake = false;

void  CheckErr(OSStatus err)
{
  if (err) {
    printf("error %-4.4s %i\n", (char *)&err, (int)err);
    throw 1;
  }
}

OSStatus  HardwareListenerProc (  AudioHardwarePropertyID  inPropertyID,
                                    void*          inClientData)
{
  AppController *app = (AppController *)inClientData;
printf("HardwareListenerProc\n");
    switch(inPropertyID)
    {
        case kAudioHardwarePropertyDevices:
//      printf("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:
//    printf("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:
//    printf("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:
//    printf("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) {
            //printf("non-soundflower 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>

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:YES];

  [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
{
  mOutputDeviceList = NULL;

  mSoundflower2Device = 0;
  mNchnls2 = 0;
  mSuspended2chDevice = NULL;

  return self;
}

- (void)dealloc
{
  [ self RemoveListeners];
  delete mOutputDeviceList;

  [super dealloc];
}

- (void)buildRoutingMenu:(BOOL)is2ch
{
  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];
  }
}

- (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 {
    item = [mMenu addItemWithTitle:@"Soundflower Is Not Installed!!" action:NULL keyEquivalent:@""];
    [item setTarget:self];
  }

  [mMenu addItem:[NSMenuItem separatorItem]];

  item = [mMenu addItemWithTitle:@"Audio Setup..." action:@selector(doAudioSetup) keyEquivalent:@""];<commit_msg>change error message when kext isnt installed<commit_after>#import "AppController.h"

#include "AudioThruEngine.h"

@implementation AppController

AudioThruEngine  *gThruEngine2 = NULL;
Boolean startOnAwake = false;

void  CheckErr(OSStatus err)
{
  if (err) {
    printf("error %-4.4s %i\n", (char *)&err, (int)err);
    throw 1;
  }
}

OSStatus  HardwareListenerProc (  AudioHardwarePropertyID  inPropertyID,
                                    void*          inClientData)
{
  AppController *app = (AppController *)inClientData;
printf("HardwareListenerProc\n");
    switch(inPropertyID)
    {
        case kAudioHardwarePropertyDevices:
//      printf("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:
//    printf("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:
//    printf("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:
//    printf("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) {
            //printf("non-soundflower 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>

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:YES];

  [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
{
  mOutputDeviceList = NULL;

  mSoundflower2Device = 0;
  mNchnls2 = 0;
  mSuspended2chDevice = NULL;

  return self;
}

- (void)dealloc
{
  [ self RemoveListeners];
  delete mOutputDeviceList;

  [super dealloc];
}

- (void)buildRoutingMenu:(BOOL)is2ch
{
  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];
  }
}

- (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 {
    item = [mMenu addItemWithTitle:@"Kernel Extension Not Installed" action:NULL keyEquivalent:@""];
    [item setTarget:self];
  }

  [mMenu addItem:[NSMenuItem separatorItem]];

  item = [mMenu addItemWithTitle:@"Audio Setup..." action:@selector(doAudioSetup) keyEquivalent:@""];
{'fraction_non_alphanumeric': 0.11347473094593744, 'fraction_numerical': 0.010447479388256025, 'mean_word_length': 3.791044776119403, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 59, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4511973', 'n_tokens_mistral': 10070, 'n_tokens_neox': 9536, 'n_words': 2009}