mirror of
https://github.com/holub/mame
synced 2025-04-20 15:32:45 +03:00
Convert CoreAudio output to use AUGraph in preparation for effects
This commit is contained in:
parent
e3df530786
commit
caf4face0d
@ -220,6 +220,7 @@ function osdmodulestargetconf()
|
||||
elseif _OPTIONS["targetos"]=="macosx" then
|
||||
links {
|
||||
"AudioUnit.framework",
|
||||
"AudioToolbox.framework",
|
||||
"CoreAudio.framework",
|
||||
"CoreServices.framework",
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
#ifdef SDLMAME_MACOSX
|
||||
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
#include <AudioUnit/AudioUnit.h>
|
||||
#include <CoreAudio/CoreAudio.h>
|
||||
#include <CoreServices/CoreServices.h>
|
||||
@ -26,7 +27,8 @@ public:
|
||||
sound_coreaudio() :
|
||||
osd_module(OSD_SOUND_PROVIDER, "coreaudio"),
|
||||
sound_module(),
|
||||
m_open(false),
|
||||
m_graph(NULL),
|
||||
m_node_count(0),
|
||||
m_sample_bytes(0),
|
||||
m_headroom(0),
|
||||
m_buffer_size(0),
|
||||
@ -43,7 +45,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
virtual int init(const osd_options &options);
|
||||
virtual int init(osd_options const &options);
|
||||
virtual void exit();
|
||||
|
||||
// sound_module
|
||||
@ -52,10 +54,19 @@ public:
|
||||
virtual void set_mastervolume(int attenuation);
|
||||
|
||||
private:
|
||||
struct node_detail
|
||||
{
|
||||
node_detail() : m_node(0), m_unit(NULL) { }
|
||||
|
||||
AUNode m_node;
|
||||
AudioUnit m_unit;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
LATENCY_MIN = 1,
|
||||
LATENCY_MAX = 5
|
||||
LATENCY_MAX = 5,
|
||||
EFFECT_COUNT_MAX = 10
|
||||
};
|
||||
|
||||
UINT32 clamped_latency() const { return MAX(MIN(m_audio_latency, LATENCY_MAX), LATENCY_MIN); }
|
||||
@ -70,6 +81,8 @@ private:
|
||||
*d = (*s * m_scale) >> 7;
|
||||
}
|
||||
|
||||
int create_graph(osd_options const &options);
|
||||
|
||||
OSStatus render(
|
||||
AudioUnitRenderActionFlags *action_flags,
|
||||
const AudioTimeStamp *timestamp,
|
||||
@ -85,8 +98,10 @@ private:
|
||||
UInt32 number_frames,
|
||||
AudioBufferList *data);
|
||||
|
||||
bool m_open;
|
||||
AudioUnit m_output;
|
||||
AUGraph m_graph;
|
||||
unsigned m_node_count;
|
||||
node_detail m_node_details[EFFECT_COUNT_MAX + 2];
|
||||
|
||||
UINT32 m_sample_bytes;
|
||||
UINT32 m_headroom;
|
||||
UINT32 m_buffer_size;
|
||||
@ -108,37 +123,9 @@ int sound_coreaudio::init(const osd_options &options)
|
||||
if (sample_rate() == 0)
|
||||
return 0;
|
||||
|
||||
// Get the Default Output AudioUnit component and open an instance
|
||||
ComponentDescription output_desc;
|
||||
output_desc.componentType = kAudioUnitType_Output;
|
||||
output_desc.componentSubType = kAudioUnitSubType_DefaultOutput;
|
||||
output_desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
output_desc.componentFlags = 0;
|
||||
output_desc.componentFlagsMask = 0;
|
||||
Component output_comp = FindNextComponent(NULL, &output_desc);
|
||||
if (!output_comp)
|
||||
{
|
||||
osd_printf_error("Could not find Default Output AudioUnit component\n");
|
||||
// Create the output graph
|
||||
if (0 != create_graph(options))
|
||||
return -1;
|
||||
}
|
||||
err = OpenAComponent(output_comp, &m_output);
|
||||
if (noErr != err)
|
||||
{
|
||||
osd_printf_error("Could not open Default Output AudioUnit component (%ld)\n", (long)err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Set render callback
|
||||
AURenderCallbackStruct renderer;
|
||||
renderer.inputProc = sound_coreaudio::render_callback;
|
||||
renderer.inputProcRefCon = this;
|
||||
err = AudioUnitSetProperty(m_output, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &renderer, sizeof(renderer));
|
||||
if (noErr != err)
|
||||
{
|
||||
CloseComponent(m_output);
|
||||
osd_printf_error("Could not set audio output render callback (%ld)\n", (long)err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Set audio stream format for two-channel native-endian 16-bit packed linear PCM
|
||||
AudioStreamBasicDescription format;
|
||||
@ -152,12 +139,17 @@ int sound_coreaudio::init(const osd_options &options)
|
||||
format.mBitsPerChannel = 16;
|
||||
format.mBytesPerFrame = format.mChannelsPerFrame * format.mBitsPerChannel / 8;
|
||||
format.mBytesPerPacket = format.mFramesPerPacket * format.mBytesPerFrame;
|
||||
err = AudioUnitSetProperty(m_output, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &format, sizeof(format));
|
||||
err = AudioUnitSetProperty(
|
||||
m_node_details[m_node_count - 1].m_unit,
|
||||
kAudioUnitProperty_StreamFormat,
|
||||
kAudioUnitScope_Input,
|
||||
0,
|
||||
&format,
|
||||
sizeof(format));
|
||||
if (noErr != err)
|
||||
{
|
||||
CloseComponent(m_output);
|
||||
osd_printf_error("Could not set audio output stream format (%ld)\n", (long)err);
|
||||
return -1;
|
||||
goto close_graph_and_return_error;
|
||||
}
|
||||
m_sample_bytes = format.mBytesPerFrame;
|
||||
|
||||
@ -167,9 +159,8 @@ int sound_coreaudio::init(const osd_options &options)
|
||||
m_buffer = global_alloc_array_clear(INT8, m_buffer_size);
|
||||
if (!m_buffer)
|
||||
{
|
||||
CloseComponent(m_output);
|
||||
osd_printf_error("Could not allocate stream buffer\n");
|
||||
return -1;
|
||||
goto close_graph_and_return_error;
|
||||
}
|
||||
m_playpos = 0;
|
||||
m_writepos = m_headroom;
|
||||
@ -178,40 +169,45 @@ int sound_coreaudio::init(const osd_options &options)
|
||||
m_overflows = m_underflows = 0;
|
||||
|
||||
// Initialise and start
|
||||
err = AudioUnitInitialize(m_output);
|
||||
err = AUGraphInitialize(m_graph);
|
||||
if (noErr != err)
|
||||
{
|
||||
CloseComponent(m_output);
|
||||
global_free_array(m_buffer);
|
||||
m_buffer = NULL;
|
||||
osd_printf_error("Could not initialize audio output (%ld)\n", (long)err);
|
||||
return -1;
|
||||
osd_printf_error("Could not initialize AudioUnit graph (%ld)\n", (long)err);
|
||||
goto free_buffer_and_return_error;
|
||||
}
|
||||
err = AudioOutputUnitStart(m_output);
|
||||
err = AUGraphStart(m_graph);
|
||||
if (noErr != err)
|
||||
{
|
||||
AudioUnitUninitialize(m_output);
|
||||
CloseComponent(m_output);
|
||||
global_free_array(m_buffer);
|
||||
m_buffer = NULL;
|
||||
osd_printf_error("Could not start audio output (%ld)\n", (long)err);
|
||||
return -1;
|
||||
osd_printf_error("Could not start AudioUnit graph (%ld)\n", (long)err);
|
||||
AUGraphUninitialize(m_graph);
|
||||
goto free_buffer_and_return_error;
|
||||
}
|
||||
m_open = true;
|
||||
osd_printf_verbose("Audio: End initialization\n");
|
||||
return 0;
|
||||
|
||||
free_buffer_and_return_error:
|
||||
global_free_array(m_buffer);
|
||||
m_buffer_size = 0;
|
||||
m_buffer = NULL;
|
||||
close_graph_and_return_error:
|
||||
AUGraphClose(m_graph);
|
||||
DisposeAUGraph(m_graph);
|
||||
m_graph = NULL;
|
||||
m_node_count = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
void sound_coreaudio::exit()
|
||||
{
|
||||
if (m_open)
|
||||
if (m_graph)
|
||||
{
|
||||
osd_printf_verbose("Closing output AudioUnit component\n");
|
||||
AudioOutputUnitStop(m_output);
|
||||
AudioUnitUninitialize(m_output);
|
||||
CloseComponent(m_output);
|
||||
m_open = false;
|
||||
osd_printf_verbose("Stopping CoreAudio output\n");
|
||||
AUGraphStop(m_graph);
|
||||
AUGraphUninitialize(m_graph);
|
||||
DisposeAUGraph(m_graph);
|
||||
m_graph = 0;
|
||||
m_node_count = 0;
|
||||
}
|
||||
if (m_buffer)
|
||||
{
|
||||
@ -258,6 +254,136 @@ void sound_coreaudio::set_mastervolume(int attenuation)
|
||||
}
|
||||
|
||||
|
||||
int sound_coreaudio::create_graph(osd_options const &options)
|
||||
{
|
||||
OSStatus err;
|
||||
AudioComponentDescription node_desc;
|
||||
|
||||
err = NewAUGraph(&m_graph);
|
||||
if (noErr != err)
|
||||
{
|
||||
osd_printf_error("Failed to create AudioUnit graph (%ld)\n", (long)err);
|
||||
goto return_error;
|
||||
}
|
||||
|
||||
err = AUGraphOpen(m_graph);
|
||||
if (noErr != err)
|
||||
{
|
||||
osd_printf_error("Failed to open AudioUnit graph (%ld)\n", (long)err);
|
||||
goto dispose_graph_and_return_error;
|
||||
}
|
||||
|
||||
node_desc.componentType = kAudioUnitType_Output;
|
||||
node_desc.componentSubType = kAudioUnitSubType_DefaultOutput;
|
||||
node_desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
node_desc.componentFlags = 0;
|
||||
node_desc.componentFlagsMask = 0;
|
||||
err = AUGraphAddNode(m_graph, &node_desc, &m_node_details[m_node_count].m_node);
|
||||
if (noErr != err)
|
||||
{
|
||||
osd_printf_error(
|
||||
"Failed to add default sound output to AudioUnit graph (%ld)\n",
|
||||
(long)err);
|
||||
goto close_graph_and_return_error;
|
||||
}
|
||||
err = AUGraphNodeInfo(
|
||||
m_graph,
|
||||
m_node_details[m_node_count].m_node,
|
||||
NULL,
|
||||
&m_node_details[m_node_count].m_unit);
|
||||
if (noErr != err)
|
||||
{
|
||||
osd_printf_error(
|
||||
"Failed to obtain AudioUnit for default sound output (%ld)\n",
|
||||
(long)err);
|
||||
}
|
||||
m_node_count++;
|
||||
|
||||
for (unsigned i = EFFECT_COUNT_MAX; 0U < i; i--)
|
||||
{
|
||||
}
|
||||
|
||||
if (1U < m_node_count)
|
||||
{
|
||||
node_desc.componentType = kAudioUnitType_FormatConverter;
|
||||
node_desc.componentSubType = kAudioUnitSubType_AUConverter;
|
||||
node_desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
node_desc.componentFlags = 0;
|
||||
node_desc.componentFlagsMask = 0;
|
||||
err = AUGraphAddNode(m_graph, &node_desc, &m_node_details[m_node_count].m_node);
|
||||
if (noErr != err)
|
||||
{
|
||||
osd_printf_error(
|
||||
"Failed to add sound format converter to AudioUnit graph (%ld)\n",
|
||||
(long)err);
|
||||
goto close_graph_and_return_error;
|
||||
}
|
||||
err = AUGraphNodeInfo(
|
||||
m_graph,
|
||||
m_node_details[m_node_count].m_node,
|
||||
NULL,
|
||||
&m_node_details[m_node_count].m_unit);
|
||||
if (noErr != err)
|
||||
{
|
||||
osd_printf_error(
|
||||
"Failed to obtain AudioUnit for sound format converter (%ld)\n",
|
||||
(long)err);
|
||||
goto close_graph_and_return_error;
|
||||
}
|
||||
err = AUGraphConnectNodeInput(
|
||||
m_graph,
|
||||
m_node_details[m_node_count].m_node,
|
||||
0,
|
||||
m_node_details[m_node_count - 1].m_node,
|
||||
0);
|
||||
if (noErr != err)
|
||||
{
|
||||
osd_printf_error(
|
||||
"Failed to connect sound format converter in AudioUnit graph (%ld)\n",
|
||||
(long)err);
|
||||
goto close_graph_and_return_error;
|
||||
}
|
||||
m_node_count++;
|
||||
}
|
||||
|
||||
{
|
||||
AURenderCallbackStruct const renderer = { sound_coreaudio::render_callback, this };
|
||||
err = AUGraphSetNodeInputCallback(
|
||||
m_graph,
|
||||
m_node_details[m_node_count - 1].m_node,
|
||||
0,
|
||||
&renderer);
|
||||
if (noErr != err)
|
||||
{
|
||||
osd_printf_error(
|
||||
"Failed to set audio render callback for AudioUnit graph (%ld)\n",
|
||||
(long)err);
|
||||
goto close_graph_and_return_error;
|
||||
}
|
||||
}
|
||||
|
||||
err = AUGraphUpdate(m_graph, NULL);
|
||||
if (noErr != err)
|
||||
{
|
||||
osd_printf_error(
|
||||
"Failed to update AudioUnit graph (%ld)\n",
|
||||
(long)err);
|
||||
goto close_graph_and_return_error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
close_graph_and_return_error:
|
||||
AUGraphClose(m_graph);
|
||||
dispose_graph_and_return_error:
|
||||
DisposeAUGraph(m_graph);
|
||||
return_error:
|
||||
m_graph = NULL;
|
||||
m_node_count = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
OSStatus sound_coreaudio::render(
|
||||
AudioUnitRenderActionFlags *action_flags,
|
||||
const AudioTimeStamp *timestamp,
|
||||
|
@ -1,3 +1,4 @@
|
||||
#import <AvailabilityMacros.h>
|
||||
#import <AudioUnit/AudioUnit.h>
|
||||
#import <AudioUnit/AUCocoaUIView.h>
|
||||
#import <AudioToolbox/AudioToolbox.h>
|
||||
@ -13,6 +14,18 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
#ifdef MAC_OS_X_VERSION_MAX_ALLOWED
|
||||
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED < 1060
|
||||
|
||||
@protocol NSWindowDelegate <NSObject>
|
||||
@end
|
||||
|
||||
#endif // MAC_OS_X_VERSION_MAX_ALLOWED < 1060
|
||||
|
||||
#endif // MAC_OS_X_VERSION_MAX_ALLOWED
|
||||
|
||||
|
||||
struct EffectInfo
|
||||
{
|
||||
Component component;
|
||||
|
Loading…
Reference in New Issue
Block a user