Commit 4f2beec2 authored by Christophe Massiot's avatar Christophe Massiot

* Mac OS X audio device discovery and selection, patch courtesy of

Heiko Panther <heiko.panther@web.de>.
parent 415af2ad
......@@ -346,6 +346,11 @@ C: fgp
D: MacOS X port
S: Austria
N: Heiko Panther
E: heiko.panther@web.de
D: Mac OS X audio device selection framework
S: Germany
N: Olivier Pomel
E: pomel@via.ecp.fr
C: pomel
......
......@@ -106,8 +106,8 @@
productName = vlc;
productReference = 014CEA410018CDE011CA2923;
productSettingsXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<!DOCTYPE plist SYSTEM \"file://localhost/System/Library/DTDs/PropertyList.dtd\">
<plist version=\"0.9\">
<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
<plist version=\"1.0\">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
......@@ -215,6 +215,7 @@
files = (
);
isa = PBXHeadersBuildPhase;
runOnlyForDeploymentPostprocessing = 0;
};
089C1675FE841209C02AAC07 = {
buildActionMask = 2147483647;
......@@ -230,12 +231,14 @@
F69B0CA802E24F6401A80112,
);
isa = PBXResourcesBuildPhase;
runOnlyForDeploymentPostprocessing = 0;
};
089C1676FE841209C02AAC07 = {
buildActionMask = 2147483647;
files = (
);
isa = PBXSourcesBuildPhase;
runOnlyForDeploymentPostprocessing = 0;
};
089C1677FE841209C02AAC07 = {
buildActionMask = 2147483647;
......@@ -243,12 +246,14 @@
1058C7AFFEA557BF11CA2CBB,
);
isa = PBXFrameworksBuildPhase;
runOnlyForDeploymentPostprocessing = 0;
};
089C1679FE841209C02AAC07 = {
buildActionMask = 2147483647;
files = (
);
isa = PBXRezBuildPhase;
runOnlyForDeploymentPostprocessing = 0;
};
089C167CFE841241C02AAC07 = {
children = (
......
......@@ -5,11 +5,14 @@ SOURCES_macosx = \
modules/gui/macosx/intf.m \
modules/gui/macosx/open.m \
modules/gui/macosx/playlist.m \
modules/gui/macosx/controls.m
modules/gui/macosx/controls.m \
modules/gui/macosx/asystm.m
noinst_HEADERS += \
modules/gui/macosx/intf.h \
modules/gui/macosx/open.h \
modules/gui/macosx/playlist.h \
modules/gui/macosx/vout.h
modules/gui/macosx/vout.h \
modules/gui/macosx/adev_discovery.h \
modules/gui/macosx/asystm.h
//
// main.h
// FindHW
//
// Created by Heiko Panther on Sun Sep 08 2002.
//
#import <Foundation/Foundation.h>
#import <CoreAudio/CoreAudio.h>
enum audiodeviceClasses
{
audiodevice_class_ac3 =1<<0, // compressed AC3
audiodevice_class_pcm2 =1<<1, // stereo PCM (uncompressed)
audiodevice_class_pcm6 =1<<2 // 6-channel PCM (uncompressed)
};
// specifies a rule for finding if a Device belongs to a class from above.
// if value==0, the value is ignored when matching. All other values must match.
struct classificationRule
{
UInt32 mFormatID;
UInt32 mChannelsPerFrame;
enum audiodeviceClasses characteristic;
char qualifierString[16];
};
......@@ -2,7 +2,7 @@
* 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>
......@@ -32,12 +32,15 @@
#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
*****************************************************************************
......@@ -70,13 +73,16 @@ static OSStatus IOCallback ( AudioDeviceID inDevice,
/*****************************************************************************
* 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 ) );
......@@ -87,68 +93,89 @@ int E_(OpenAudio)( vlc_object_t * p_this )
}
/* 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,
......@@ -220,15 +247,19 @@ static OSStatus IOCallback( AudioDeviceID inDevice,
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
{
......
//
// asystm.h
//
//
// Created by Heiko Panther on Tue Sep 10 2002.
// Copyright (c) 2002 __MyCompanyName__. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <CoreAudio/CoreAudio.h>
#import "adev_discovery.h"
#import "intf.h"
/*****************************************************************************
* MacOSXSoundOption
* Each audio device can give several sound options: there might be several
* streams on one device, each can have different formats which might qualify
* as an option.
* We record format and channels, since these attributes are requirements
* from the user and the aout should deliver what the user wants. This
* selection is basically done when the user chooses the output option.
* We do not record sample rate and bit depth, since these attributes are
* tied to the media source, and the device format that matches these media
* formats best should be selected. This selection is done when the aout
* module is created with a certain stream, and asks the asystm for a device.
*****************************************************************************/
@interface MacOSXSoundOption:NSObject
{
NSString *name;
AudioDeviceID deviceID;
UInt32 streamIndex;
UInt32 mFormatID;
UInt32 mChannels;
}
- (id)initWithName:(NSString*)_name deviceID:(AudioDeviceID)devID streamIndex:(UInt32)strInd formatID:(UInt32)formID chans:(UInt32)chans;
- (AudioDeviceID)deviceID;
- (UInt32)streamIndex;
- (UInt32)mFormatID;
- (UInt32)mChannels;
- (void)dealloc;
- (NSString*)name;
@end
@interface MacOSXAudioSystem : NSObject {
VLCMain *main;
/* selected output device */
NSMenuItem *selectedOutput;
NSMenu *newMenu;
}
- (id)initWithGUI:(VLCMain*)main;
- (AudioStreamID) getStreamIDForIndex:(UInt32)streamIndex device:(AudioDeviceID)deviceID;
- (void)CheckDevice:(AudioDeviceID)deviceID isInput:(bool)isInput;
- (void)registerSoundOption:(MacOSXSoundOption*)option;
- (void)selectAction:(id)sender;
- (AudioStreamID)getSelectedDeviceSetToRate:(int)preferredSampleRate;
- (void)dealloc;
@end
//
// 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]