Cog/Plugins/MIDI/MIDI/AUPlayer.mm
Christopher Snowhill bcd5ab0dd2 Amend MIDI Audio Unit player a bit
This should fix some potential initialization errors it may have had
before, but this doesn't fix the broken Sound Canvas VA plugin. Roland
says it's supposed to be broken on macOS 12+ and/or Apple silicon
anyway, so I guess there's no dodging that.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-10-21 16:37:14 -07:00

385 lines
11 KiB
Text

#include "AUPlayer.h"
#include <stdlib.h>
#define SF2PACK
// #define AUPLAYERVIEW
#ifdef AUPLAYERVIEW
#import "AUPlayerView.h"
#endif
#define _countof(arr) (sizeof(arr) / sizeof((arr)[0]))
#define BLOCK_SIZE (512)
AUPlayer::AUPlayer()
: MIDIPlayer() {
samplerUnit[0] = NULL;
samplerUnit[1] = NULL;
samplerUnit[2] = NULL;
#ifdef AUPLAYERVIEW
samplerUI[0] = NULL;
samplerUI[1] = NULL;
samplerUI[2] = NULL;
samplerUIinitialized[0] = false;
samplerUIinitialized[1] = false;
samplerUIinitialized[2] = false;
#endif
bufferList = NULL;
audioBuffer = NULL;
componentSubType = kAudioUnitSubType_DLSSynth;
componentManufacturer = kAudioUnitManufacturer_Apple;
}
AUPlayer::~AUPlayer() {
shutdown();
}
void AUPlayer::send_event(uint32_t b) {
send_event_time(b, 0);
}
void AUPlayer::send_sysex(const uint8_t *data, size_t size, size_t port) {
send_sysex_time(data, size, port, 0);
}
void AUPlayer::send_event_time(uint32_t b, unsigned int time) {
unsigned char event[3];
event[0] = (unsigned char)b;
event[1] = (unsigned char)(b >> 8);
event[2] = (unsigned char)(b >> 16);
unsigned port = (b >> 24) & 0x7F;
if(port > 2) port = 2;
MusicDeviceMIDIEvent(samplerUnit[port], event[0], event[1], event[2], time);
#ifdef AUPLAYERVIEW
if(port >= 0 && !samplerUIinitialized[port]) {
samplerUIinitialized[port] = true;
dispatch_async(dispatch_get_main_queue(), ^{
samplerUI[port] = new AUPluginUI(samplerUnit[port]);
});
}
#endif
}
void AUPlayer::send_sysex_time(const uint8_t *data, size_t size, size_t port, unsigned int time) {
if(port > 2) port = 0;
MusicDeviceSysEx(samplerUnit[port], data, (UInt32)size);
if(port == 0) {
MusicDeviceSysEx(samplerUnit[1], data, (UInt32)size);
MusicDeviceSysEx(samplerUnit[2], data, (UInt32)size);
}
#ifdef AUPLAYERVIEW
if(port >= 0 && !samplerUIinitialized[port]) {
samplerUIinitialized[port] = true;
dispatch_async(dispatch_get_main_queue(), ^{
samplerUI[port] = new AUPluginUI(samplerUnit[port]);
});
}
#endif
}
void AUPlayer::render(float *out, unsigned long count) {
float *ptrL, *ptrR;
memset(out, 0, count * sizeof(float) * 2);
while(count) {
UInt32 numberFrames = count > BLOCK_SIZE ? BLOCK_SIZE : (UInt32)count;
for(unsigned long i = 0; i < 3; ++i) {
AudioUnitRenderActionFlags ioActionFlags = 0;
for(unsigned long j = 0; j < 2; j++) {
bufferList->mBuffers[j].mNumberChannels = 1;
bufferList->mBuffers[j].mDataByteSize = (UInt32)(numberFrames * sizeof(float));
bufferList->mBuffers[j].mData = audioBuffer + j * BLOCK_SIZE;
memset(bufferList->mBuffers[j].mData, 0, numberFrames * sizeof(float));
}
AudioUnitRender(samplerUnit[i], &ioActionFlags, &mTimeStamp, 0, numberFrames, bufferList);
ptrL = (float *)bufferList->mBuffers[0].mData;
ptrR = (float *)bufferList->mBuffers[1].mData;
for(unsigned long j = 0; j < numberFrames; ++j) {
out[j * 2 + 0] += ptrL[j];
out[j * 2 + 1] += ptrR[j];
}
}
out += numberFrames * 2;
count -= numberFrames;
mTimeStamp.mSampleTime += (double)numberFrames;
}
}
void AUPlayer::shutdown() {
if(samplerUnit[2]) {
#ifdef AUPLAYERVIEW
if(samplerUI[2]) {
delete samplerUI[2];
samplerUI[2] = 0;
samplerUIinitialized[2] = false;
}
#endif
AudioUnitUninitialize(samplerUnit[2]);
AudioComponentInstanceDispose(samplerUnit[2]);
samplerUnit[2] = NULL;
}
if(samplerUnit[1]) {
#ifdef AUPLAYERVIEW
if(samplerUI[1]) {
delete samplerUI[1];
samplerUI[1] = 0;
samplerUIinitialized[1] = false;
}
#endif
AudioUnitUninitialize(samplerUnit[1]);
AudioComponentInstanceDispose(samplerUnit[1]);
samplerUnit[1] = NULL;
}
if(samplerUnit[0]) {
#ifdef AUPLAYERVIEW
if(samplerUI[0]) {
delete samplerUI[0];
samplerUI[0] = 0;
samplerUIinitialized[0] = false;
}
#endif
AudioUnitUninitialize(samplerUnit[0]);
AudioComponentInstanceDispose(samplerUnit[0]);
samplerUnit[0] = NULL;
}
if(audioBuffer) {
free(audioBuffer);
audioBuffer = NULL;
}
if(bufferList) {
free(bufferList);
bufferList = NULL;
}
initialized = false;
}
void AUPlayer::enumComponents(callback cbEnum) {
AudioComponentDescription cd = { 0 };
cd.componentType = kAudioUnitType_MusicDevice;
AudioComponent comp = NULL;
const char *bytes;
char bytesBuffer[512];
comp = AudioComponentFindNext(comp, &cd);
while(comp != NULL) {
CFStringRef cfName;
AudioComponentCopyName(comp, &cfName);
bytes = CFStringGetCStringPtr(cfName, kCFStringEncodingUTF8);
if(!bytes) {
CFStringGetCString(cfName, bytesBuffer, sizeof(bytesBuffer) - 1, kCFStringEncodingUTF8);
bytes = bytesBuffer;
}
AudioComponentGetDescription(comp, &cd);
cbEnum(cd.componentSubType, cd.componentManufacturer, bytes);
CFRelease(cfName);
comp = AudioComponentFindNext(comp, &cd);
}
}
void AUPlayer::setComponent(OSType uSubType, OSType uManufacturer) {
componentSubType = uSubType;
componentManufacturer = uManufacturer;
shutdown();
}
void AUPlayer::setSoundFont(const char *in) {
const char *ext = strrchr(in, '.');
if(ext && *ext && ((strncasecmp(ext + 1, "sf2", 3) == 0) || (strncasecmp(ext + 1, "dls", 3) == 0))) {
sSoundFontName = in;
shutdown();
}
}
/*void AUPlayer::setFileSoundFont( const char * in )
{
sFileSoundFontName = in;
shutdown();
}*/
static OSStatus renderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) {
if(inNumberFrames && ioData) {
for(int i = 0, j = ioData->mNumberBuffers; i < j; ++i) {
int k = inNumberFrames * sizeof(float);
if(k > ioData->mBuffers[i].mDataByteSize)
k = ioData->mBuffers[i].mDataByteSize;
memset(ioData->mBuffers[i].mData, 0, k);
}
}
return noErr;
}
bool AUPlayer::startup() {
if(bufferList) return true;
AudioComponentDescription cd = { 0 };
cd.componentType = kAudioUnitType_MusicDevice;
cd.componentSubType = componentSubType;
cd.componentManufacturer = componentManufacturer;
AudioComponent comp = NULL;
comp = AudioComponentFindNext(comp, &cd);
if(!comp)
return false;
OSStatus error;
for(int i = 0; i < 3; i++) {
UInt32 value = 1;
UInt32 size = sizeof(value);
error = AudioComponentInstanceNew(comp, &samplerUnit[i]);
if(error != noErr)
return false;
needsInput = NO;
{
AudioStreamBasicDescription stream = { 0 };
stream.mSampleRate = uSampleRate;
stream.mFormatID = kAudioFormatLinearPCM;
stream.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked | kAudioFormatFlagIsNonInterleaved;
stream.mBytesPerPacket = 4;
stream.mFramesPerPacket = 1;
stream.mBytesPerFrame = 4;
stream.mChannelsPerFrame = 2;
stream.mBitsPerChannel = 32;
AUChannelInfo channelInfo = { 0 };
Boolean *isWritable = 0;
size = 0;
error = AudioUnitGetPropertyInfo(samplerUnit[i], kAudioUnitProperty_SupportedNumChannels, kAudioUnitScope_Global, 0, &size, isWritable);
if(error == noErr) {
size = sizeof(channelInfo);
error = AudioUnitGetProperty(samplerUnit[i], kAudioUnitProperty_SupportedNumChannels, kAudioUnitScope_Global, 0, &channelInfo, &size);
if(error == noErr && channelInfo.inChannels == -1 || channelInfo.inChannels <= -2 || channelInfo.inChannels >= 2) {
needsInput = YES;
}
} else {
UInt32 channelCount = 0;
size = sizeof(channelCount);
error = AudioUnitGetProperty(samplerUnit[i], kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &channelCount, &size);
if(error == noErr && channelCount >= 2) {
needsInput = YES;
}
}
if(needsInput) {
AudioUnitSetProperty(samplerUnit[i], kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input, 0, &stream, sizeof(stream));
}
AudioUnitSetProperty(samplerUnit[i], kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output, 0, &stream, sizeof(stream));
}
value = BLOCK_SIZE;
AudioUnitSetProperty(samplerUnit[i], kAudioUnitProperty_MaximumFramesPerSlice,
kAudioUnitScope_Global, 0, &value, size);
value = 127;
AudioUnitSetProperty(samplerUnit[i], kAudioUnitProperty_RenderQuality,
kAudioUnitScope_Global, 0, &value, size);
if(needsInput) {
AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc = renderCallback;
callbackStruct.inputProcRefCon = 0;
AudioUnitSetProperty(samplerUnit[i], kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input, 0, &callbackStruct, sizeof(callbackStruct));
}
/*Float64 sampleRateIn = 0, sampleRateOut = 0;
UInt32 sampleRateSize = sizeof (sampleRateIn);
const Float64 sr = uSampleRate;
AudioUnitGetProperty(samplerUnit[i], kAudioUnitProperty_SampleRate, kAudioUnitScope_Input, 0, &sampleRateIn, &sampleRateSize);
if (sampleRateIn != sr)
AudioUnitSetProperty(samplerUnit[i], kAudioUnitProperty_SampleRate, kAudioUnitScope_Input, 0, &sr, sizeof (sr));
AudioUnitGetProperty (samplerUnit[i], kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, 0, &sampleRateOut, &sampleRateSize);
if (sampleRateOut != sr)
AudioUnitSetProperty (samplerUnit[i], kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, i, &sr, sizeof (sr));*/
if(needsInput) {
AudioUnitReset(samplerUnit[i], kAudioUnitScope_Input, 0);
}
AudioUnitReset(samplerUnit[i], kAudioUnitScope_Output, 0);
AudioUnitReset(samplerUnit[i], kAudioUnitScope_Global, 0);
value = 1;
AudioUnitSetProperty(samplerUnit[i], kMusicDeviceProperty_StreamFromDisk, kAudioUnitScope_Global, 0, &value, size);
error = AudioUnitInitialize(samplerUnit[i]);
if(error != noErr)
return false;
}
// Now load instruments
if(sSoundFontName.length()) {
loadSoundFont(sSoundFontName.c_str());
}
/*if ( sFileSoundFontName.length() )
{
loadSoundFont( sFileSoundFontName.c_str() );
}*/
bufferList = (AudioBufferList *)calloc(1, sizeof(AudioBufferList) + sizeof(AudioBuffer));
if(!bufferList)
return false;
audioBuffer = (float *)malloc(BLOCK_SIZE * 2 * sizeof(float));
if(!audioBuffer)
return false;
bufferList->mNumberBuffers = 2;
memset(&mTimeStamp, 0, sizeof(mTimeStamp));
mTimeStamp.mFlags = kAudioTimeStampSampleTimeValid;
initialized = true;
setFilterMode(mode, reverb_chorus_disabled);
return true;
}
void AUPlayer::loadSoundFont(const char *name) {
// kMusicDeviceProperty_SoundBankURL was added in 10.5 as a replacement
// In addition, the File Manager API became deprecated starting in 10.8
CFURLRef url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)name, strlen(name), false);
if(url) {
for(int i = 0; i < 3; i++)
AudioUnitSetProperty(samplerUnit[i],
kMusicDeviceProperty_SoundBankURL, kAudioUnitScope_Global,
0,
&url, sizeof(url));
CFRelease(url);
}
}
unsigned int AUPlayer::send_event_needs_time() {
return BLOCK_SIZE;
}