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>
385 lines
11 KiB
Text
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;
|
|
}
|