Convert CoreAudio output to use AUGraph in preparation for effects

This commit is contained in:
Vas Crabb 2015-04-07 19:24:47 +10:00
parent e3df530786
commit caf4face0d
3 changed files with 200 additions and 60 deletions

View File

@ -220,6 +220,7 @@ function osdmodulestargetconf()
elseif _OPTIONS["targetos"]=="macosx" then
links {
"AudioUnit.framework",
"AudioToolbox.framework",
"CoreAudio.framework",
"CoreServices.framework",
}

View File

@ -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,

View File

@ -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;