mirror of
https://github.com/videolan/vlc.git
synced 2024-12-03 23:04:35 +08:00
* Mac OS X audio device discovery and selection, patch courtesy of
Heiko Panther <heiko.panther@web.de>.
This commit is contained in:
parent
415af2aded
commit
4f2beec27e
5
AUTHORS
5
AUTHORS
@ -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
|
||||
|
||||
|
26
modules/gui/macosx/adev_discovery.h
Executable file
26
modules/gui/macosx/adev_discovery.h
Executable file
@ -0,0 +1,26 @@
|
||||
//
|
||||
// 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;
|
||||
|
||||
/* 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');
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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
|
||||
{
|
||||
|
58
modules/gui/macosx/asystm.h
Executable file
58
modules/gui/macosx/asystm.h
Executable file
@ -0,0 +1,58 @@
|
||||
//
|
||||
// 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
|
449
modules/gui/macosx/asystm.m
Executable file
449
modules/gui/macosx/asystm.m
Executable file
@ -0,0 +1,449 @@
|
||||
//
|
||||
// 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
|
@ -2,7 +2,7 @@
|
||||
* intf.h: MacOS X interface plugin
|
||||
*****************************************************************************
|
||||
* Copyright (C) 2002 VideoLAN
|
||||
* $Id: intf.h,v 1.1 2002/08/04 17:23:43 sam Exp $
|
||||
* $Id: intf.h,v 1.2 2002/10/02 22:56:53 massiot Exp $
|
||||
*
|
||||
* Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
|
||||
* Christophe Massiot <massiot@via.ecp.fr>
|
||||
@ -22,6 +22,12 @@
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
|
||||
*****************************************************************************/
|
||||
|
||||
#include <vlc/vlc.h>
|
||||
#include <vlc/intf.h>
|
||||
#include <vlc/vout.h>
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
|
||||
/*****************************************************************************
|
||||
* VLCApplication interface
|
||||
*****************************************************************************/
|
||||
@ -140,6 +146,8 @@ struct intf_sys_t
|
||||
IBOutlet id o_dmi_play;
|
||||
IBOutlet id o_dmi_pause;
|
||||
IBOutlet id o_dmi_stop;
|
||||
|
||||
id asystm; // MacOSXAudioSystem
|
||||
}
|
||||
|
||||
- (void)terminate;
|
||||
@ -156,6 +164,8 @@ struct intf_sys_t
|
||||
- (IBAction)clearRecentItems:(id)sender;
|
||||
- (void)openRecentItem:(id)sender;
|
||||
|
||||
//- (void)selectAction:(id)sender;
|
||||
|
||||
@end
|
||||
|
||||
@interface VLCMain (Internal)
|
||||
|
@ -2,7 +2,7 @@
|
||||
* intf.m: MacOS X interface plugin
|
||||
*****************************************************************************
|
||||
* Copyright (C) 2002 VideoLAN
|
||||
* $Id: intf.m,v 1.2 2002/08/12 09:34:15 sam Exp $
|
||||
* $Id: intf.m,v 1.3 2002/10/02 22:56:53 massiot Exp $
|
||||
*
|
||||
* Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
|
||||
* Christophe Massiot <massiot@via.ecp.fr>
|
||||
@ -29,16 +29,12 @@
|
||||
#include <sys/param.h> /* for MAXPATHLEN */
|
||||
#include <string.h>
|
||||
|
||||
#include <vlc/vlc.h>
|
||||
#include <vlc/intf.h>
|
||||
#include <vlc/vout.h>
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#include <QuickTime/QuickTime.h>
|
||||
|
||||
#include "intf.h"
|
||||
#include "vout.h"
|
||||
#include "playlist.h"
|
||||
#include "asystm.h"
|
||||
|
||||
/*****************************************************************************
|
||||
* Local prototypes.
|
||||
@ -246,6 +242,15 @@ static void Run( intf_thread_t *p_intf )
|
||||
[[NSRunLoop currentRunLoop]
|
||||
addPort: p_intf->p_sys->o_sendport
|
||||
forMode: NSDefaultRunLoopMode];
|
||||
|
||||
// Since we need the sound menu now, it's a good time to create the sound system class
|
||||
asystm=[[MacOSXAudioSystem alloc] initWithGUI:self];
|
||||
[asystm retain];
|
||||
|
||||
}
|
||||
|
||||
- (void)noopAction:(id)sender {
|
||||
// nothing
|
||||
}
|
||||
|
||||
- (BOOL)application:(NSApplication *)o_app openFile:(NSString *)o_filename
|
||||
@ -431,6 +436,8 @@ static void Run( intf_thread_t *p_intf )
|
||||
context: [NSGraphicsContext currentContext] eventNumber: 1
|
||||
clickCount: 1 pressure: 0.0];
|
||||
[NSApp postEvent: pEvent atStart: YES];
|
||||
|
||||
[asystm release];
|
||||
}
|
||||
|
||||
- (void)manageMode
|
||||
|
Loading…
Reference in New Issue
Block a user