368 lines
10 KiB
Plaintext
368 lines
10 KiB
Plaintext
#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) {
|
|
#ifdef AUPLAYERVIEW
|
|
int _port = -1;
|
|
#endif
|
|
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;
|
|
#ifdef AUPLAYERVIEW
|
|
_port = (int)port;
|
|
#endif
|
|
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;
|
|
#ifdef AUPLAYERVIEW
|
|
_port = (int)port;
|
|
#endif
|
|
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 && ((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;
|
|
|
|
{
|
|
AudioStreamBasicDescription stream = { 0 };
|
|
stream.mSampleRate = uSampleRate;
|
|
stream.mFormatID = kAudioFormatLinearPCM;
|
|
stream.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved | kAudioFormatFlagsNativeEndian;
|
|
stream.mFramesPerPacket = 1;
|
|
stream.mBytesPerPacket = 4;
|
|
stream.mBytesPerFrame = 4;
|
|
stream.mBitsPerChannel = 32;
|
|
stream.mChannelsPerFrame = 2;
|
|
|
|
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);
|
|
|
|
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));*/
|
|
|
|
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;
|
|
}
|