* aout.m: CoreAudio output plugin
*****************************************************************************
* Copyright (C) 2002 VideoLAN
- * $Id: aout.m,v 1.11 2002/09/30 21:32:33 massiot Exp $
+ * $Id: aout.m,v 1.12 2002/10/02 22:56:53 massiot Exp $
*
* Authors: Colin Delacroix <colin@zoy.org>
* Jon Lech Johansen <jon-vl@nanocrew.net>
#include <vlc/vlc.h>
#include <vlc/aout.h>
#include "aout_internal.h"
+#include "asystm.h"
#include <Carbon/Carbon.h>
#include <CoreAudio/AudioHardware.h>
#include <CoreAudio/HostTime.h>
#include <AudioToolbox/AudioConverter.h>
+#define A52_FRAME_NB 1536
+
/*****************************************************************************
* aout_sys_t: private audio output method descriptor
*****************************************************************************
/*****************************************************************************
* Open: open a CoreAudio HAL device
*****************************************************************************/
+extern MacOSXAudioSystem *gTheMacOSXAudioSystem; // Remove this global, access audio system froma aout some other way
+
int E_(OpenAudio)( vlc_object_t * p_this )
{
OSStatus err;
UInt32 i_param_size;
aout_instance_t * p_aout = (aout_instance_t *)p_this;
struct aout_sys_t * p_sys;
-
+ msg_Dbg(p_aout, "************************* ENTER OpenAudio ****************************");
+
/* Allocate instance */
p_sys = p_aout->output.p_sys = malloc( sizeof( struct aout_sys_t ) );
memset( p_sys, 0, sizeof( struct aout_sys_t ) );
}
/* Get the default output device */
- /* FIXME : be more clever in choosing from several devices */
- i_param_size = sizeof( p_sys->device );
- err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
- &i_param_size,
- (void *)&p_sys->device );
- if( err != noErr )
+ // We now ask the GUI for the selected device
+ p_sys->device=[gTheMacOSXAudioSystem getSelectedDeviceSetToRate:p_aout->output.output.i_rate];
+ if(p_sys->device==0)
{
- msg_Err( p_aout, "failed to get the device: %d", err );
+ msg_Err( p_aout, "couldn't get output device");
return( -1 );
}
+ msg_Dbg(p_aout, "device returned: %ld", p_sys->device);
p_aout->output.pf_play = Play;
aout_VolumeSoftInit( p_aout );
/* Get a description of the data format used by the device */
- i_param_size = sizeof( p_sys->stream_format );
- err = AudioDeviceGetProperty( p_sys->device, 0, false,
- kAudioDevicePropertyStreamFormat,
- &i_param_size,
- &p_sys->stream_format );
+ i_param_size = sizeof(AudioStreamBasicDescription);
+ err = AudioDeviceGetProperty(p_sys->device, 0, false, kAudioDevicePropertyStreamFormat, &i_param_size, &p_sys->stream_format );
if( err != noErr )
{
- msg_Err( p_aout, "failed to get stream format: %d", err );
+ msg_Err( p_aout, "failed to get stream format: %4.4s", &err );
return -1 ;
}
- if( p_sys->stream_format.mFormatID != kAudioFormatLinearPCM )
+ msg_Dbg( p_aout, "mSampleRate %ld, mFormatID %4.4s, mFormatFlags %ld, mBytesPerPacket %ld, mFramesPerPacket %ld, mBytesPerFrame %ld, mChannelsPerFrame %ld, mBitsPerChannel %ld",
+ (UInt32)p_sys->stream_format.mSampleRate, &p_sys->stream_format.mFormatID,
+ p_sys->stream_format.mFormatFlags, p_sys->stream_format.mBytesPerPacket,
+ p_sys->stream_format.mFramesPerPacket, p_sys->stream_format.mBytesPerFrame,
+ p_sys->stream_format.mChannelsPerFrame, p_sys->stream_format.mBitsPerChannel );
+
+ msg_Dbg( p_aout, "vlc format %4.4s, mac output format '%4.4s'",
+ (char *)&p_aout->output.output.i_format, &p_sys->stream_format.mFormatID );
+
+ switch(p_sys->stream_format.mFormatID)
{
- msg_Err( p_aout, "kAudioFormatLinearPCM required" );
- return -1 ;
+ case 0:
+ case kAudioFormatLinearPCM:
+ p_aout->output.output.i_format = VLC_FOURCC('f','l','3','2');
+ if ( p_sys->stream_format.mChannelsPerFrame < 6 )
+ p_aout->output.output.i_channels = AOUT_CHAN_STEREO;
+ else
+ p_aout->output.output.i_channels = AOUT_CHAN_3F2R | AOUT_CHAN_LFE;
+ break;
+
+ case kAudioFormat60958AC3:
+ case 'IAC3':
+ p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');
+ //not necessary, use the input's format by default --Meuuh
+ //p_aout->output.output.i_channels = AOUT_CHAN_DOLBY | AOUT_CHAN_LFE;
+ p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE; //p_sys->stream_format.mBytesPerFrame;
+ p_aout->output.output.i_frame_length = A52_FRAME_NB; //p_sys->stream_format.mFramesPerPacket;
+ break;
+
+ default:
+ msg_Err( p_aout, "Unknown hardware format '%4.4s'. Go ask Heiko.", &p_sys->stream_format.mFormatID );
+ return -1;
}
- /* We only deal with floats. FIXME : this is where we should do S/PDIF. */
- p_aout->output.output.i_format = VLC_FOURCC('f','l','3','2');
-
/* Set sample rate and channels per frame */
p_aout->output.output.i_rate = p_sys->stream_format.mSampleRate;
- /* FIXME : this is where we should ask for downmixing. */
- p_aout->output.output.i_channels = 2; //p_sys->stream_format.mChannelsPerFrame;
/* Get the buffer size that the device uses for IO */
i_param_size = sizeof( p_sys->i_buffer_size );
-#if 0
- err = AudioDeviceGetProperty( p_sys->device, 0, false,
+#if 1 // i have a feeling we should use the buffer size imposed by the AC3 device (usually about 6144)
+ err = AudioDeviceGetProperty( p_sys->device, 1, false,
kAudioDevicePropertyBufferSize,
&i_param_size, &p_sys->i_buffer_size );
-msg_Dbg( p_aout, "toto : %d", p_sys->i_buffer_size );
+ if(err) {
+ msg_Err(p_aout, "failed to get buffer size - err %4.4s, device %ld", &err, p_sys->device);
+ return -1;
+ }
+ else msg_Dbg( p_aout, "native buffer Size: %d", p_sys->i_buffer_size );
#else
- p_sys->i_buffer_size = sizeof(float) * p_aout->output.output.i_channels
- * 4096;
- err = AudioDeviceSetProperty( p_sys->device, 0, 0, false,
+ p_sys->i_buffer_size = p_aout->output.output.i_bytes_per_frame;
+ err = AudioDeviceSetProperty( p_sys->device, 0, 1, false,
kAudioDevicePropertyBufferSize,
i_param_size, &p_sys->i_buffer_size );
-#endif
if( err != noErr )
{
- msg_Err( p_aout, "failed to set device buffer size: %d", err );
+ msg_Err( p_aout, "failed to set device buffer size: %4.4s", err );
return( -1 );
}
+ else msg_Dbg(p_aout, "bufferSize set to %d", p_sys->i_buffer_size);
+#endif
- p_aout->output.i_nb_samples = p_sys->i_buffer_size / sizeof(float)
- / p_aout->output.output.i_channels;
+ p_aout->output.i_nb_samples = p_sys->i_buffer_size / p_sys->stream_format.mBytesPerFrame;
/* Add callback */
err = AudioDeviceAddIOProc( p_sys->device,
current_date = p_sys->clock_diff
+ AudioConvertHostTimeToNanos(host_time.mHostTime) / 1000;
- p_buffer = aout_OutputNextBuffer( p_aout, current_date, VLC_FALSE );
+// msg_Dbg(p_aout, "Now fetching audio data");
+ p_buffer = aout_OutputNextBuffer( p_aout, current_date, (p_aout->output.output.i_format == VLC_FOURCC('s','p','d','i')) );
/* move data into output data buffer */
if ( p_buffer != NULL )
{
- BlockMoveData( p_buffer->p_buffer,
+ BlockMoveData( p_buffer->p_buffer,
outOutputData->mBuffers[ 0 ].mData,
p_sys->i_buffer_size );
- aout_BufferFree( p_buffer );
+
+// msg_Dbg(p_aout, "This buffer has %d bytes, i take %d", p_buffer->i_nb_bytes, p_sys->i_buffer_size);
+
+ aout_BufferFree( p_buffer );
}
else
{
--- /dev/null
+//
+// asystm.m
+//
+//
+// Created by Heiko Panther on Tue Sep 10 2002.
+// Copyright (c) 2002 __MyCompanyName__. All rights reserved.
+//
+
+#import "asystm.h"
+#define MAXINT 0x7fffffff
+
+#define DEBUG_ASYSTM 1
+
+// this is a basic set of rules
+#define gNumClassificationRules 4
+
+const struct classificationRule gClassificationRules[gNumClassificationRules]=
+{
+{ // The old AC3 format type
+ 'IAC3',
+ 0,
+ audiodevice_class_ac3,
+ "Digital AC3"
+},
+{ // The new AC3 format type
+ kAudioFormat60958AC3,
+ 0,
+ audiodevice_class_ac3,
+ "Digital AC3"
+},
+{
+ kAudioFormatLinearPCM,
+ 2,
+ audiodevice_class_pcm2,
+ "Stereo PCM"
+},
+{
+ kAudioFormatLinearPCM,
+ 6,
+ audiodevice_class_pcm6,
+ "6 Channel PCM"
+}
+};
+
+MacOSXAudioSystem *gTheMacOSXAudioSystem; // Remove this global, access audio system froma aout some other way
+
+@implementation MacOSXSoundOption
+
+- (id)initWithName:(NSString*)_name deviceID:(AudioDeviceID)devID streamIndex:(UInt32)strInd formatID:(UInt32)formID chans:(UInt32)chans;
+{
+ id me=0;
+ if((me=[super init]))
+ {
+ name = _name;
+ [name retain];
+ deviceID=devID;
+ streamIndex=strInd;
+ mFormatID=formID;
+ mChannels=chans;
+ }
+ return(me);
+}
+
+- (NSString*)name {return name;};
+
+- (AudioDeviceID)deviceID {return deviceID;};
+
+- (UInt32)streamIndex {return streamIndex;};
+
+- (UInt32)mFormatID {return mFormatID;};
+
+- (UInt32)mChannels {return mChannels;};
+
+- (void)dealloc {[name release];};
+
+@end
+
+
+@implementation MacOSXAudioSystem
+
+OSStatus listenerProc (AudioDeviceID inDevice,
+ UInt32 inChannel,
+ Boolean isInput,
+ AudioDevicePropertyID inPropertyID,
+ void* inClientData)
+{
+ intf_thread_t * p_intf = [NSApp getIntf];
+ msg_Dbg(p_intf, "********** Property Listener called! device %d, channel %d, isInput %d, propertyID %4.4s",
+ inDevice, inChannel, isInput, &inPropertyID);
+ return 0;
+};
+
+OSStatus streamListenerProc (AudioStreamID inStream,
+ UInt32 inChannel,
+ AudioDevicePropertyID inPropertyID,
+ void* inClientData)
+{
+ intf_thread_t * p_intf = [NSApp getIntf];
+ msg_Dbg(p_intf, "********** Property Listener called! stream %d, channel %d, propertyID %4.4s",
+ inStream, inChannel, &inPropertyID);
+ return 0;
+};
+
+- (id)initWithGUI:(VLCMain*)_main
+{
+ id me=0;
+ if((me=[super init]))
+ {
+ gTheMacOSXAudioSystem=self;
+ main=_main;
+ [main retain];
+ intf_thread_t * p_intf = [NSApp getIntf];
+ selectedOutput=0;
+
+ // find audio devices
+ // find out how many audio devices there are, if any
+ OSStatus status = noErr;
+ UInt32 theSize;
+ Boolean outWritable;
+ AudioDeviceID *deviceList = NULL;
+ UInt32 i;
+
+ status = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &theSize, &outWritable);
+ if(status != noErr)
+ {
+ msg_Err(p_intf, "AudioHardwareGetPropertyInfo failed");
+ };
+
+ // calculate the number of device available
+ UInt32 devicesAvailable = theSize / sizeof(AudioDeviceID);
+ // Bail if there aren't any devices
+ if(devicesAvailable < 1)
+ {
+ msg_Err(p_intf, "no devices found");
+ }
+ if(DEBUG_ASYSTM) msg_Dbg(p_intf, "Have %i devices!", devicesAvailable);
+
+ // make space for the devices we are about to get
+ deviceList = (AudioDeviceID*)malloc(theSize);
+ // get an array of AudioDeviceIDs
+ status = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &theSize, (void *) deviceList);
+ if(status != noErr)
+ {
+ msg_Err(p_intf, "could not get Device list");
+ };
+
+ // Build a menu
+ NSMenuItem *newItem;
+ newItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Sound output" action:NULL keyEquivalent:@""];
+ newMenu = [[NSMenu allocWithZone:[NSMenu menuZone]] initWithTitle:@"Sound output"];
+ [newItem setSubmenu:newMenu];
+ [[NSApp mainMenu] addItem:newItem];
+ [newItem release];
+
+ // check which devices can do what class of audio
+ // struct mosx_AudioDeviceData deviceData;
+ for(i=0; i<devicesAvailable; i++)
+ [self CheckDevice:deviceList[i] isInput:false]; // only check the output part
+
+ [newMenu release];
+ free(deviceList);
+ };
+ return me;
+};
+
+- (AudioStreamID) getStreamIDForIndex:(UInt32)streamIndex device:(AudioDeviceID)deviceID
+{
+ // Does not currently use the stream index, but just returns the stream ID of the first stream.
+ // Get the stream ID
+ Boolean isInput=false, outWritable;
+ UInt32 theSize;
+ OSStatus err = AudioDeviceGetPropertyInfo(deviceID, 0, isInput, kAudioDevicePropertyStreams, &theSize, &outWritable);
+ AudioStreamID *streamList = (AudioStreamID*)malloc(theSize);
+ err = AudioDeviceGetProperty(deviceID, 0, isInput, kAudioDevicePropertyStreams, &theSize, streamList);
+ AudioStreamID streamID = streamList[streamIndex - 1];
+ free(streamList);
+ return streamID;
+}
+
+- (void)CheckDevice:(AudioDeviceID)deviceID isInput:(bool)isInput
+{
+ OSStatus err;
+ UInt32 theSize;
+ Boolean outWritable;
+ AudioBufferList *bufferList = 0;
+ UInt32 i, j;
+ intf_thread_t * p_intf = [NSApp getIntf];
+ char deviceName[32]; // Make this a CFString!
+
+ // Add property listener
+ err=AudioDeviceAddPropertyListener(deviceID, 1, isInput, kAudioDevicePropertyStreams, listenerProc, 0);
+ if(err) msg_Err(p_intf, "Add Property Listener failed, err=%4.4s", &err);
+ err=AudioDeviceAddPropertyListener(deviceID, 1, isInput, kAudioDevicePropertyStreamConfiguration, listenerProc, 0);
+ if(err) msg_Err(p_intf, "Add Property Listener failed, err=%4.4s", &err);
+ err=AudioDeviceAddPropertyListener(deviceID, 1, isInput, kAudioDevicePropertyStreamFormat, listenerProc, 0);
+ if(err) msg_Err(p_intf, "Add Property Listener failed, err=%4.4s", &err);
+
+ // Get the device name
+ err = AudioDeviceGetPropertyInfo(deviceID, 0, isInput, kAudioDevicePropertyDeviceName, &theSize, &outWritable);
+ theSize=sizeof(deviceName);
+ err = AudioDeviceGetProperty(deviceID, 0, isInput, kAudioDevicePropertyDeviceName, &theSize, deviceName);
+
+ // Get the stream configuration
+ err = AudioDeviceGetPropertyInfo(deviceID, 0, isInput, kAudioDevicePropertyStreamConfiguration, &theSize, &outWritable);
+ bufferList = (AudioBufferList*)malloc(theSize);
+ err = AudioDeviceGetProperty(deviceID, 0, isInput, kAudioDevicePropertyStreamConfiguration, &theSize, bufferList);
+
+ if(DEBUG_ASYSTM) msg_Dbg(p_intf, "\nFound a %s, examing its %s, it has %i streams.", deviceName, (isInput?"Input":"Output"), bufferList->mNumberBuffers);
+
+ // find details of each stream
+ for (i=0; i < bufferList->mNumberBuffers; i++)
+ {
+ short streamIndex=i+1;
+ UInt32 nActFormats;
+ AudioStreamBasicDescription *formatsAvailable;
+
+ AudioStreamID streamID=[self getStreamIDForIndex:streamIndex device:deviceID];
+
+ // Add property listener
+ err=AudioStreamAddPropertyListener(streamID, 0, kAudioDevicePropertyStreams, streamListenerProc, 0);
+ if(err) msg_Err(p_intf, "Add Property Listener failed, err=%4.4s", &err);
+ err=AudioStreamAddPropertyListener(streamID, 0, kAudioDevicePropertyStreamConfiguration, streamListenerProc, 0);
+ if(err) msg_Err(p_intf, "Add Property Listener failed, err=%4.4s", &err);
+ err=AudioStreamAddPropertyListener(streamID, 0, kAudioDevicePropertyStreamFormat, streamListenerProc, 0);
+ if(err) msg_Err(p_intf, "Add Property Listener failed, err=%4.4s", &err);
+ err=AudioStreamAddPropertyListener(streamID, 0, kAudioStreamPropertyPhysicalFormat, streamListenerProc, 0);
+ if(err) msg_Err(p_intf, "Add Property Listener failed, err=%4.4s", &err);
+
+ // Get the # of actual formats in the current stream
+ err = AudioStreamGetPropertyInfo(streamID, 0, kAudioStreamPropertyPhysicalFormats, &theSize, &outWritable);
+ nActFormats = theSize / sizeof(AudioStreamBasicDescription);
+ if(DEBUG_ASYSTM) msg_Dbg(p_intf, "stream index %i, streamID %i, nActFormats %d", streamIndex, streamID, nActFormats);
+
+ // Get the format specifications
+ formatsAvailable=(AudioStreamBasicDescription*) malloc(theSize);
+ err = AudioStreamGetProperty(streamID, 0, kAudioStreamPropertyPhysicalFormats, &theSize, formatsAvailable);
+ if(err) msg_Err(p_intf, "AudioDeviceGetProperty err %d", err);
+
+ // now classify the device and add a menu entry for each device class it matches
+ for(j=0; j<gNumClassificationRules; j++)
+ {
+ UInt32 numChans=MAXINT, format=0;
+ for(i=0; i<theSize/sizeof(AudioStreamBasicDescription); i++)
+ {
+ if(DEBUG_ASYSTM) msg_Dbg(p_intf, "Finding formats: %4.4s - %d chans, %d Hz, %d bits/sample, %d bytes/frame",
+ &formatsAvailable[i].mFormatID, formatsAvailable[i].mChannelsPerFrame,
+ (UInt32)formatsAvailable[i].mSampleRate,
+ formatsAvailable[i].mBitsPerChannel, formatsAvailable[i].mBytesPerFrame);
+
+ if(formatsAvailable[i].mFormatID != gClassificationRules[j].mFormatID && gClassificationRules[j].mFormatID!=0) continue;
+ if(formatsAvailable[i].mChannelsPerFrame < gClassificationRules[j].mChannelsPerFrame && gClassificationRules[j].mChannelsPerFrame!=0) continue;
+ // we want to choose the format with the smallest allowable channel number for this class
+ if(formatsAvailable[i].mChannelsPerFrame < numChans)
+ {
+ numChans=formatsAvailable[i].mChannelsPerFrame;
+ format=i;
+ };
+ };
+ if(numChans!=MAXINT) // we found a good setting
+ {
+ if(DEBUG_ASYSTM) msg_Dbg(p_intf, "classified into %d", gClassificationRules[j].characteristic);
+ // make a sound option object
+ char menuentry[48];
+ snprintf(menuentry, 48, "%.32s: %.16s", deviceName, gClassificationRules[j].qualifierString);
+ MacOSXSoundOption *device=[[MacOSXSoundOption alloc] initWithName:[NSString stringWithCString:menuentry] deviceID:deviceID streamIndex:streamIndex formatID:formatsAvailable[format].mFormatID chans:formatsAvailable[format].mChannelsPerFrame];
+ [self registerSoundOption:device];
+ };
+ };
+ free(formatsAvailable);
+ }
+
+ free(bufferList);
+};
+
+- (void)registerSoundOption:(MacOSXSoundOption*)option {
+ NSMenuItem *newItem;
+ newItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:[option name] action:NULL keyEquivalent:@""];
+ [newItem setImage:[NSImage imageNamed:@"eomt_browsedata"]];
+ [newItem setTarget:self];
+ [newItem setAction:@selector(selectAction:)];
+ [newItem setRepresentedObject:option];
+ [newMenu addItem:newItem];
+ if(selectedOutput==0) [self selectAction:newItem];
+ [newItem release];
+};
+
+- (void)selectAction:(id)sender {
+ [selectedOutput setState:NSOffState];
+ selectedOutput=sender;
+ [sender setState:NSOnState];
+};
+
+static void printStreamDescription(char *description, AudioStreamBasicDescription *format)
+{
+ intf_thread_t * p_intf = [NSApp getIntf];
+ if(DEBUG_ASYSTM) msg_Dbg(p_intf, "%s: mSampleRate %ld, mFormatID %4.4s, mFormatFlags %ld, mBytesPerPacket %ld, mFramesPerPacket %ld, mBytesPerFrame %ld, mChannelsPerFrame %ld, mBitsPerChannel %ld",
+ description,
+ (UInt32)format->mSampleRate, &format->mFormatID,
+ format->mFormatFlags, format->mBytesPerPacket,
+ format->mFramesPerPacket, format->mBytesPerFrame,
+ format->mChannelsPerFrame, format->mBitsPerChannel);
+};
+
+
+- (AudioDeviceID)getSelectedDeviceSetToRate:(int)preferredSampleRate{
+ // I know the selected device, stream, and the required format ID. Now find a format
+ // that comes closest to the preferred rate
+ // For sample size, it is assumed that 16 bits will always be enough.
+ // Note that the caller is not guranteed to get the rate she preferred.
+ AudioStreamBasicDescription *formatsAvailable;
+ MacOSXSoundOption *selectedOption=[selectedOutput representedObject];
+ bool foundFormat=false;
+ UInt32 theSize;
+ Boolean outWritable;
+ OSStatus err;
+ UInt32 i;
+ intf_thread_t * p_intf = [NSApp getIntf];
+ AudioDeviceID deviceID=[selectedOption deviceID];
+
+ // get the streamID (it might have changed)
+ AudioStreamID streamID=[self getStreamIDForIndex:[selectedOption streamIndex] device:deviceID];
+
+ // Find the actual formats
+ err = AudioStreamGetPropertyInfo(streamID, 0, kAudioStreamPropertyPhysicalFormats, &theSize, &outWritable);
+ formatsAvailable=(AudioStreamBasicDescription*) malloc(theSize);
+ err = AudioStreamGetProperty(streamID, 0, kAudioStreamPropertyPhysicalFormats, &theSize, formatsAvailable);
+ if(err)
+ {
+ msg_Err(p_intf, "Error %4.4s getting the stream formats", &err);
+ return 0;
+ };
+
+ UInt32 formtmp=[selectedOption mFormatID];
+ if(DEBUG_ASYSTM) msg_Dbg(p_intf, "looking for: %4.4s - %d chans, %d Hz", &formtmp,
+ [selectedOption mChannels], preferredSampleRate);
+
+ // Check if there's a "best match" which has our required rate
+ for(i=0; i<theSize/sizeof(AudioStreamBasicDescription); i++)
+ {
+ if(DEBUG_ASYSTM) msg_Dbg(p_intf, "actual: %4.4s - %d chans, %d Hz, %d bits/sample, %d bytes/frame",
+ &formatsAvailable[i].mFormatID, formatsAvailable[i].mChannelsPerFrame,
+ (int)formatsAvailable[i].mSampleRate,
+ formatsAvailable[i].mBitsPerChannel, formatsAvailable[i].mBytesPerFrame);
+
+ if(formatsAvailable[i].mChannelsPerFrame<0 || formatsAvailable[i].mChannelsPerFrame>100) {
+ msg_Err(p_intf, "bogus format! index %i", i);
+ return 0;
+ };
+
+ if( formatsAvailable[i].mFormatID == [selectedOption mFormatID]
+ && formatsAvailable[i].mChannelsPerFrame == [selectedOption mChannels]
+ && (int)formatsAvailable[i].mSampleRate == preferredSampleRate)
+ {
+ if(DEBUG_ASYSTM) msg_Dbg(p_intf, "Found the perfect format!!");
+ foundFormat=true;
+ break;
+ };
+ };
+
+ int rate=MAXINT, format=0;
+ if(!foundFormat)
+ {
+ for(i=0; i<theSize/sizeof(AudioStreamBasicDescription); i++)
+ {
+ // We don't have one... check if there's one with a higher sample rate.
+ // Upsampling should be better than downsampling.
+ // Select the smallest of the higher sample rates, to save resources.
+ int actrate=(int)formatsAvailable[i].mSampleRate;
+ if( formatsAvailable[i].mFormatID == [selectedOption mFormatID]
+ && formatsAvailable[i].mChannelsPerFrame == [selectedOption mChannels]
+ && actrate > preferredSampleRate)
+ {
+ if(actrate < rate)
+ {
+ rate=actrate;
+ format=i;
+ }
+ };
+ };
+ if(rate!=MAXINT) // This means we have found a rate!! Yes!
+ {
+ if(DEBUG_ASYSTM) msg_Dbg(p_intf, "Only got a format with higher sample rate");
+ foundFormat=true;
+ i=format;
+ };
+ };
+
+ if(!foundFormat)
+ {
+ rate=0;
+ for(i=0; i<theSize/sizeof(AudioStreamBasicDescription); i++)
+ {
+ // Our last chance: select the highest lower sample rate.
+ int actrate=(int)formatsAvailable[i].mSampleRate;
+ if( actrate >= preferredSampleRate) // We must have done something wrong
+ {
+ if(DEBUG_ASYSTM) msg_Err(p_intf, "Found a rate that should have been selected previously.");
+ free(formatsAvailable);
+ return 0;
+ };
+
+ if(actrate > rate)
+ {
+ rate=actrate;
+ format=i;
+ }
+ };
+
+ if(rate!=0) // This means we have found a rate!! Yes!
+ {
+ if(DEBUG_ASYSTM) msg_Dbg(p_intf, "Only got a format with lower sample rate");
+ foundFormat=true;
+ i=format;
+ }
+ else // We must have done something wrong
+ {
+ msg_Err(p_intf, "Found no rate which is equal, higher or lower to requested rate. That means our device either: a) didn't exist in the first place or b) has been removed since.");
+ free(formatsAvailable);
+ return 0;
+ };
+ };
+ AudioStreamBasicDescription desc=formatsAvailable[i];
+ free(formatsAvailable);
+
+ // Set the device stream format
+ Boolean isWriteable;
+
+ err = AudioStreamGetPropertyInfo(streamID, 0, kAudioStreamPropertyPhysicalFormat, &theSize, &isWriteable);
+ if(err) msg_Err(p_intf, "GetPropertyInfo (stream format) error %4.4s - theSize %d", &err, theSize);
+ if(DEBUG_ASYSTM) msg_Dbg(p_intf, "size %d, writable %d", theSize, isWriteable);
+
+ if(DEBUG_ASYSTM) printStreamDescription("want to set", &desc);
+
+ err = AudioStreamSetProperty(streamID, 0, 0, kAudioStreamPropertyPhysicalFormat, theSize, &desc);
+ if(err) msg_Err(p_intf, "SetProperty (stream format) error %4.4s - theSize %d", &err, theSize);
+
+ // Because of the format change, the streamID has changed!
+ // That's why we return the deviceID.
+ return deviceID;
+};
+
+
+- (void)dealloc
+{
+ [main release];
+};
+
+
+@end