Skip to content

Commit b45d82c

Browse files
warmenhovenm4xw
authored andcommitted
apple: use audiotoolbox for resampling
1 parent 680e033 commit b45d82c

2 files changed

Lines changed: 179 additions & 5 deletions

File tree

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,7 @@ else ifneq (,$(findstring osx,$(platform)))
406406

407407
PLATCFLAGS += -D__MACOSX__ -DOSX -DOS_MAC_OS_X -DHAVE_UNISTD_H=1 -DHAVE_POSIX_MEMALIGN -DNO_ASM -DGL_SILENCE_DEPRECATION=1
408408
GL_LIB := -framework OpenGL
409+
LDFLAGS += -framework AudioToolbox
409410

410411
# Target Dynarec
411412
WITH_DYNAREC =
@@ -474,6 +475,7 @@ else ifneq (,$(findstring ios,$(platform)))
474475
endif
475476
LDFLAGS += -dynamiclib
476477
GL_LIB := -framework OpenGLES
478+
LDFLAGS += -framework AudioToolbox
477479
# tvOS
478480
else ifneq (,$(findstring tvos,$(platform)))
479481
ifeq ($(TVOSSDK),)
@@ -507,6 +509,7 @@ else ifneq (,$(findstring tvos,$(platform)))
507509

508510
LDFLAGS += -dynamiclib
509511
GL_LIB := -framework OpenGLES
512+
LDFLAGS += -framework AudioToolbox
510513
# Android
511514
else ifneq (,$(findstring android,$(platform)))
512515
ANDROID = 1

custom/mupen64plus-core/plugin/audio_libretro/audio_backend_libretro.c

Lines changed: 176 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,22 +36,124 @@
3636
#include <string.h>
3737
#include <stdarg.h>
3838

39+
#ifdef __APPLE__
40+
#include <AudioToolbox/AudioToolbox.h>
41+
#else
3942
#include <audio/conversion/float_to_s16.h>
4043
#include <audio/conversion/s16_to_float.h>
4144
#include <audio/audio_resampler.h>
45+
#endif
4246

4347
extern retro_audio_sample_batch_t audio_batch_cb;
4448

4549
static unsigned MAX_AUDIO_FRAMES = 2048;
4650

4751
#define VI_INTR_TIME 500000
52+
#define OUTPUT_RATE 44100
4853

4954
/* Read header for type definition */
5055
static int GameFreq = 33600;
5156
static unsigned CountsPerSecond;
5257
static unsigned BytesPerSecond;
5358
static unsigned CountsPerByte;
5459

60+
#ifdef __APPLE__
61+
/* Apple AudioConverter - resamples int16 to 44100 Hz.
62+
* No float intermediates needed (unlike sinc resampler path). */
63+
static AudioConverterRef audio_converter;
64+
static unsigned converter_input_rate;
65+
static int16_t *audio_out_buffer_s16;
66+
67+
/* Context for AudioConverter input callback */
68+
typedef struct
69+
{
70+
const int16_t *data;
71+
size_t frames_left;
72+
} converter_ctx_t;
73+
74+
static OSStatus converter_input_cb(
75+
AudioConverterRef converter,
76+
UInt32 *ioNumberDataPackets,
77+
AudioBufferList *ioData,
78+
AudioStreamPacketDescription **outDataPacketDescription,
79+
void *inUserData)
80+
{
81+
converter_ctx_t *ctx = (converter_ctx_t *)inUserData;
82+
UInt32 frames_to_provide;
83+
84+
if (ctx->frames_left == 0)
85+
{
86+
*ioNumberDataPackets = 0;
87+
return noErr;
88+
}
89+
90+
frames_to_provide = *ioNumberDataPackets;
91+
if (frames_to_provide > ctx->frames_left)
92+
frames_to_provide = (UInt32)ctx->frames_left;
93+
94+
ioData->mBuffers[0].mData = (void *)ctx->data;
95+
ioData->mBuffers[0].mDataByteSize = frames_to_provide * 4; /* stereo int16 */
96+
ioData->mBuffers[0].mNumberChannels = 2;
97+
98+
ctx->data += frames_to_provide * 2; /* advance by samples */
99+
ctx->frames_left -= frames_to_provide;
100+
*ioNumberDataPackets = frames_to_provide;
101+
102+
return noErr;
103+
}
104+
105+
static int create_audio_converter(unsigned input_rate)
106+
{
107+
AudioStreamBasicDescription input_desc, output_desc;
108+
OSStatus err;
109+
110+
if (audio_converter)
111+
{
112+
AudioConverterDispose(audio_converter);
113+
audio_converter = NULL;
114+
}
115+
116+
/* Input: native-endian int16 stereo at game's sample rate
117+
* (byte swap is done before calling AudioConverter) */
118+
memset(&input_desc, 0, sizeof(input_desc));
119+
input_desc.mSampleRate = input_rate;
120+
input_desc.mFormatID = kAudioFormatLinearPCM;
121+
input_desc.mFormatFlags = kAudioFormatFlagIsSignedInteger
122+
| kAudioFormatFlagIsPacked;
123+
input_desc.mBytesPerPacket = 4;
124+
input_desc.mFramesPerPacket = 1;
125+
input_desc.mBytesPerFrame = 4;
126+
input_desc.mChannelsPerFrame = 2;
127+
input_desc.mBitsPerChannel = 16;
128+
129+
/* Output: little-endian int16 stereo at 44100 Hz */
130+
memset(&output_desc, 0, sizeof(output_desc));
131+
output_desc.mSampleRate = OUTPUT_RATE;
132+
output_desc.mFormatID = kAudioFormatLinearPCM;
133+
output_desc.mFormatFlags = kAudioFormatFlagIsSignedInteger
134+
| kAudioFormatFlagIsPacked;
135+
output_desc.mBytesPerPacket = 4;
136+
output_desc.mFramesPerPacket = 1;
137+
output_desc.mBytesPerFrame = 4;
138+
output_desc.mChannelsPerFrame = 2;
139+
output_desc.mBitsPerChannel = 16;
140+
141+
err = AudioConverterNew(&input_desc, &output_desc, &audio_converter);
142+
if (err != noErr)
143+
return -1;
144+
145+
/* Set high quality resampling */
146+
UInt32 quality = kAudioConverterQuality_High;
147+
AudioConverterSetProperty(audio_converter,
148+
kAudioConverterSampleRateConverterQuality,
149+
sizeof(quality), &quality);
150+
151+
converter_input_rate = input_rate;
152+
return 0;
153+
}
154+
155+
#else
156+
/* Non-Apple: use libretro-common sinc resampler */
55157
static const retro_resampler_t *resampler;
56158
static void *resampler_audio_data;
57159
static float *audio_in_buffer_float;
@@ -62,9 +164,19 @@ void (*audio_convert_s16_to_float_arm)(float *out,
62164
const int16_t *in, size_t samples, float gain);
63165
void (*audio_convert_float_to_s16_arm)(int16_t *out,
64166
const float *in, size_t samples);
167+
#endif
65168

66169
void deinit_audio_libretro(void)
67170
{
171+
#ifdef __APPLE__
172+
if (audio_converter)
173+
{
174+
AudioConverterDispose(audio_converter);
175+
audio_converter = NULL;
176+
}
177+
free(audio_out_buffer_s16);
178+
audio_out_buffer_s16 = NULL;
179+
#else
68180
if (resampler && resampler_audio_data)
69181
{
70182
resampler->free(resampler_audio_data);
@@ -74,20 +186,29 @@ void deinit_audio_libretro(void)
74186
free(audio_out_buffer_float);
75187
free(audio_out_buffer_s16);
76188
}
189+
#endif
77190
}
78191

79192
void init_audio_libretro(unsigned max_audio_frames)
80193
{
81-
retro_resampler_realloc(&resampler_audio_data, &resampler, "sinc", RESAMPLER_QUALITY_DONTCARE, 1.0);
82-
83194
MAX_AUDIO_FRAMES = max_audio_frames;
84195

196+
#ifdef __APPLE__
197+
/* Allocate output buffer - sized for maximum expansion ratio */
198+
audio_out_buffer_s16 = malloc(2 * MAX_AUDIO_FRAMES * 2 * sizeof(int16_t));
199+
/* Converter will be created on first use when we know the input rate */
200+
audio_converter = NULL;
201+
converter_input_rate = 0;
202+
#else
203+
retro_resampler_realloc(&resampler_audio_data, &resampler, "sinc", RESAMPLER_QUALITY_DONTCARE, 1.0);
204+
85205
audio_in_buffer_float = malloc(2 * MAX_AUDIO_FRAMES * sizeof(float));
86206
audio_out_buffer_float = malloc(2 * MAX_AUDIO_FRAMES * sizeof(float));
87207
audio_out_buffer_s16 = malloc(2 * MAX_AUDIO_FRAMES * sizeof(int16_t));
88208

89209
convert_s16_to_float_init_simd();
90210
convert_float_to_s16_init_simd();
211+
#endif
91212
}
92213

93214
static void aiDacrateChanged(void *user_data, unsigned int frequency)
@@ -122,15 +243,13 @@ void set_audio_format_via_libretro(void* user_data,
122243

123244
static void aiLenChanged(void* user_data, const void* buffer, size_t size)
124245
{
125-
size_t max_frames, remain_frames;
126246
uint32_t i;
127-
double ratio;
128-
struct resampler_data data = {0};
129247
int16_t *out = NULL;
130248
int16_t *raw_data = (int16_t*)buffer;
131249
size_t frames = size / 4;
132250
uint8_t *p = (uint8_t*)buffer;
133251

252+
/* Byte swap - handles endianness and channel order from N64 format */
134253
for (i = 0; i < size; i += 4)
135254
{
136255
p[i ] ^= p[i + 2];
@@ -141,6 +260,57 @@ static void aiLenChanged(void* user_data, const void* buffer, size_t size)
141260
p[i + 1] ^= p[i + 3];
142261
}
143262

263+
#ifdef __APPLE__
264+
/* Apple path: AudioConverter resamples, no float conversion needed */
265+
converter_ctx_t ctx;
266+
AudioBufferList output_buffer;
267+
UInt32 output_frames;
268+
OSStatus err;
269+
270+
/* Create or recreate converter if sample rate changed */
271+
if (!audio_converter || converter_input_rate != (unsigned)GameFreq)
272+
{
273+
if (create_audio_converter(GameFreq) != 0)
274+
return;
275+
}
276+
277+
/* Reset converter to clear end-of-stream state from previous call */
278+
AudioConverterReset(audio_converter);
279+
280+
ctx.data = raw_data;
281+
ctx.frames_left = frames;
282+
283+
/* Calculate expected output frames */
284+
output_frames = (UInt32)((frames * OUTPUT_RATE) / GameFreq + 1);
285+
if (output_frames > MAX_AUDIO_FRAMES * 2)
286+
output_frames = MAX_AUDIO_FRAMES * 2;
287+
288+
output_buffer.mNumberBuffers = 1;
289+
output_buffer.mBuffers[0].mNumberChannels = 2;
290+
output_buffer.mBuffers[0].mDataByteSize = output_frames * 4;
291+
output_buffer.mBuffers[0].mData = audio_out_buffer_s16;
292+
293+
err = AudioConverterFillComplexBuffer(audio_converter,
294+
converter_input_cb, &ctx,
295+
&output_frames, &output_buffer, NULL);
296+
297+
if (err != noErr && err != 1)
298+
return;
299+
300+
out = audio_out_buffer_s16;
301+
while (output_frames)
302+
{
303+
size_t ret = audio_batch_cb(out, output_frames);
304+
output_frames -= ret;
305+
out += ret * 2;
306+
}
307+
308+
#else
309+
/* Non-Apple path: sinc resampler with float conversion */
310+
size_t max_frames, remain_frames;
311+
double ratio;
312+
struct resampler_data data = {0};
313+
144314
audio_batch:
145315
out = NULL;
146316
ratio = 44100.0 / GameFreq;
@@ -176,6 +346,7 @@ static void aiLenChanged(void* user_data, const void* buffer, size_t size)
176346
frames = remain_frames;
177347
goto audio_batch;
178348
}
349+
#endif
179350
}
180351

181352
/* Abuse core & audio plugin implementation details to obtain the desired effect. */

0 commit comments

Comments
 (0)