VocalEasel/Sources/VLSoundOut.cpp

560 lines
14 KiB
C++
Raw Normal View History

2007-04-27 06:41:34 +00:00
//
// File: VLSoundOut.cpp - Sound output and file playing functionality
//
// Author(s):
//
// (MN) Matthias Neeracher
//
2011-09-04 19:48:57 +00:00
// Copyright <20> 2005-2011 Matthias Neeracher
2007-04-27 06:41:34 +00:00
//
2006-09-11 02:49:56 +00:00
#include "VLSoundOut.h"
2011-09-11 21:27:53 +00:00
#include "VLMIDIWriter.h"
2006-09-11 02:49:56 +00:00
#include <AudioUnit/AudioUnit.h>
2006-11-27 07:01:38 +00:00
#include "CAAudioFileFormats.h"
#include "AUOutputBL.h"
2006-09-11 02:49:56 +00:00
#include <memory>
#include <vector>
2011-08-29 00:41:04 +00:00
#include <dispatch/dispatch.h>
2006-09-11 02:49:56 +00:00
2011-08-29 00:41:04 +00:00
#define R(x) if (OSStatus r = (x)) fprintf(stderr, "%s -> %d\n", #x, r);
2006-11-27 07:01:38 +00:00
2011-09-04 19:48:57 +00:00
CFStringRef kVLSoundStartedNotification = CFSTR("VLSoundStarted");
CFStringRef kVLSoundStoppedNotification = CFSTR("VLSoundStopped");
2006-09-11 02:49:56 +00:00
class VLAUSoundOut : public VLSoundOut {
public:
VLAUSoundOut();
virtual void PlayNote(const VLNote & note);
virtual void PlayChord(const VLChord & chord);
2008-05-29 18:54:30 +00:00
virtual void PlaySequence(MusicSequence music);
2011-09-11 21:27:53 +00:00
virtual void SetStart(MusicTimeStamp start);
virtual void SetEnd(MusicTimeStamp end);
2008-07-05 13:56:51 +00:00
virtual void Stop(bool pause);
virtual bool Playing();
2008-07-06 11:07:57 +00:00
virtual bool AtEnd();
2011-09-11 21:27:53 +00:00
virtual bool AtBeginning();
2011-09-11 21:43:13 +00:00
virtual void ResetSelection();
2008-05-29 18:54:30 +00:00
virtual void SetPlayRate(float rate);
2011-08-29 00:41:04 +00:00
virtual void Fwd();
virtual void Bck();
2011-09-11 17:20:34 +00:00
virtual void Slow(float rate);
2011-09-04 23:38:01 +00:00
virtual void SetMelodyState(MelodyState state);
virtual ~VLAUSoundOut();
2011-09-04 19:48:57 +00:00
void PollMusic();
protected:
VLAUSoundOut(bool fileOutput);
2006-11-27 07:01:38 +00:00
void InitSoundOutput(bool fileOutput);
virtual void SetupOutput(AUNode outputNode);
2011-08-29 00:41:04 +00:00
void SkipTimeInterval();
2006-09-11 02:49:56 +00:00
2011-09-04 19:48:57 +00:00
AUGraph fGraph;
MusicPlayer fPlayer;
2006-11-27 07:01:38 +00:00
private:
2011-09-04 19:48:57 +00:00
MusicSequence fMusic;
2011-09-11 21:27:53 +00:00
MusicTimeStamp fMusicEnd;
2011-09-04 19:48:57 +00:00
bool fRunning;
bool fForward;
2011-09-11 21:27:53 +00:00
bool fWasAtEnd;
2011-09-11 17:20:34 +00:00
float fPlayRate;
2011-09-04 19:48:57 +00:00
dispatch_source_t fMusicPoll;
2011-09-04 19:48:57 +00:00
void Play(const int8_t * note, size_t numNotes = 1);
};
class VLAUFileSoundOut : public VLAUSoundOut {
public:
2006-11-27 07:01:38 +00:00
VLAUFileSoundOut(CFURLRef file, OSType dataFormat);
~VLAUFileSoundOut();
protected:
virtual void SetupOutput(AUNode outputNode);
virtual void PlaySequence(MusicSequence music);
2011-09-04 23:38:01 +00:00
virtual void SetMelodyState(MelodyState state) {}
private:
AudioUnit fOutput;
CFURLRef fFile;
2006-11-27 07:01:38 +00:00
OSType fDataFormat;
2006-09-11 02:49:56 +00:00
};
2011-09-11 17:20:34 +00:00
class VLResetTimer {
public:
VLResetTimer(int64_t interval, void (^block)());
~VLResetTimer();
void Prime();
private:
dispatch_source_t fTimer;
int64_t fInterval;
void (^fBlock)();
};
VLResetTimer::VLResetTimer(int64_t interval, void (^block)())
: fInterval(interval), fBlock(Block_copy(block))
{
fTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
dispatch_source_set_event_handler(fTimer, fBlock);
dispatch_source_set_timer(fTimer, DISPATCH_TIME_FOREVER, INT64_MAX, 1000*NSEC_PER_USEC);
dispatch_resume(fTimer);
}
VLResetTimer::~VLResetTimer()
{
Block_release(fBlock);
}
void VLResetTimer::Prime()
{
dispatch_source_set_timer(fTimer, dispatch_time(DISPATCH_TIME_NOW, fInterval),
INT64_MAX, 10*NSEC_PER_MSEC);
}
VLSoundEvent::~VLSoundEvent()
{
}
void VLSoundScheduler::Schedule(VLSoundEvent * what, float when)
{
usleep((int)(1000000.0f*when));
2006-10-16 09:00:02 +00:00
what->Perform();
}
static std::auto_ptr<VLSoundOut> sSoundOut;
2006-10-16 09:00:02 +00:00
static std::auto_ptr<VLSoundScheduler> sSoundScheduler;
2006-09-11 02:49:56 +00:00
VLSoundOut * VLSoundOut::Instance()
{
if (!sSoundOut.get()) {
2006-09-11 02:49:56 +00:00
sSoundOut.reset(new VLAUSoundOut);
if (!sSoundScheduler.get())
sSoundScheduler.reset(new VLSoundScheduler);
}
2006-09-11 02:49:56 +00:00
return sSoundOut.get();
}
void VLSoundOut::SetScheduler(VLSoundScheduler * scheduler)
{
sSoundScheduler.reset(scheduler);
}
2006-11-27 07:01:38 +00:00
VLSoundOut * VLSoundOut::FileWriter(CFURLRef file, OSType dataFormat)
{
2006-11-27 07:01:38 +00:00
return new VLAUFileSoundOut(file, dataFormat);
}
2008-05-29 18:54:30 +00:00
void VLSoundOut::PlayFile(CFDataRef file)
{
MusicSequence music;
NewMusicSequence(&music);
2011-09-04 23:38:01 +00:00
MusicSequenceFileLoadData(music, file, 0, 0);
2008-05-29 18:54:30 +00:00
PlaySequence(music);
}
2006-09-11 02:49:56 +00:00
VLSoundOut::~VLSoundOut()
{
}
VLAUSoundOut::VLAUSoundOut()
2011-09-11 21:27:53 +00:00
: fMusic(0), fRunning(false), fForward(true), fWasAtEnd(true)
{
InitSoundOutput(false);
2011-09-04 19:48:57 +00:00
fMusicPoll = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
dispatch_source_set_event_handler(fMusicPoll, ^{
this->PollMusic();
});
dispatch_source_set_timer(fMusicPoll, DISPATCH_TIME_FOREVER, INT64_MAX, 1000*NSEC_PER_USEC);
dispatch_resume(fMusicPoll);
}
2006-11-27 07:01:38 +00:00
VLAUSoundOut::VLAUSoundOut(bool)
: fRunning(false), fMusic(0)
{
}
VLAUSoundOut::~VLAUSoundOut()
{
2008-07-05 13:56:51 +00:00
Stop(false);
2011-09-04 19:48:57 +00:00
dispatch_release(fMusicPoll);
DisposeMusicPlayer(fPlayer);
DisposeAUGraph(fGraph);
}
void VLAUSoundOut::InitSoundOutput(bool fileOutput)
2006-09-11 02:49:56 +00:00
{
AUNode synthNode, limiterNode, outNode;
2011-09-03 20:34:53 +00:00
AudioComponentDescription cd;
2006-09-11 02:49:56 +00:00
cd.componentManufacturer = kAudioUnitManufacturer_Apple;
cd.componentFlags = 0;
cd.componentFlagsMask = 0;
NewAUGraph(&fGraph);
cd.componentType = kAudioUnitType_MusicDevice;
cd.componentSubType = kAudioUnitSubType_DLSSynth;
2011-09-03 20:34:53 +00:00
AUGraphAddNode(fGraph, &cd, &synthNode);
2006-09-11 02:49:56 +00:00
cd.componentType = kAudioUnitType_Effect;
cd.componentSubType = kAudioUnitSubType_PeakLimiter;
2011-09-03 20:34:53 +00:00
AUGraphAddNode (fGraph, &cd, &limiterNode);
2006-09-11 02:49:56 +00:00
cd.componentType = kAudioUnitType_Output;
if (fileOutput)
cd.componentSubType = kAudioUnitSubType_GenericOutput;
else
cd.componentSubType = kAudioUnitSubType_DefaultOutput;
2006-09-11 02:49:56 +00:00
2011-09-03 20:34:53 +00:00
AUGraphAddNode(fGraph, &cd, &outNode);
2006-11-27 07:01:38 +00:00
R(AUGraphOpen(fGraph));
2006-09-11 02:49:56 +00:00
AUGraphConnectNodeInput(fGraph, synthNode, 0, limiterNode, 0);
AUGraphConnectNodeInput(fGraph, limiterNode, 0, outNode, 0);
if (fileOutput) {
UInt32 value = 1;
AudioUnit synth;
2011-09-03 20:34:53 +00:00
R(AUGraphNodeInfo(fGraph, synthNode, NULL, &synth));
2006-11-27 07:01:38 +00:00
R(AudioUnitSetProperty(synth,
kAudioUnitProperty_OfflineRender,
kAudioUnitScope_Global, 0,
&value, sizeof(value)));
value = 512;
R(AudioUnitSetProperty(synth,
kAudioUnitProperty_OfflineRender,
kAudioUnitScope_Global, 0,
&value, sizeof(value)));
}
SetupOutput(outNode);
2006-11-27 07:01:38 +00:00
R(AUGraphInitialize(fGraph));
2006-10-16 09:00:02 +00:00
NewMusicPlayer(&fPlayer);
2006-09-11 02:49:56 +00:00
}
void VLAUSoundOut::SetupOutput(AUNode)
2006-09-11 02:49:56 +00:00
{
}
2006-10-16 09:00:02 +00:00
void VLAUSoundOut::PlaySequence(MusicSequence music)
2006-09-11 02:49:56 +00:00
{
2008-07-05 13:56:51 +00:00
if (music) {
Stop(false);
fMusic = music;
2011-09-11 21:27:53 +00:00
fMusicEnd = VLMIDIUtilities(music).Length();
2011-09-11 17:20:34 +00:00
fPlayRate = 1.0;
2011-09-11 21:27:53 +00:00
fWasAtEnd = true;
2006-10-16 09:00:02 +00:00
2008-07-05 13:56:51 +00:00
R(MusicSequenceSetAUGraph(fMusic, fGraph));
R(MusicPlayerSetSequence(fPlayer, fMusic));
}
2006-11-27 07:01:38 +00:00
R(MusicPlayerStart(fPlayer));
2006-10-16 09:00:02 +00:00
fRunning = true;
2011-09-04 19:48:57 +00:00
CFNotificationCenterPostNotification(CFNotificationCenterGetLocalCenter(), kVLSoundStartedNotification,
NULL, NULL, false);
2011-09-11 21:27:53 +00:00
dispatch_source_set_timer(fMusicPoll, DISPATCH_TIME_NOW, 10*NSEC_PER_MSEC, 200*NSEC_PER_MSEC);
}
void VLAUSoundOut::SetStart(MusicTimeStamp start)
{
if (fWasAtEnd)
MusicPlayerSetTime(fPlayer, start);
}
void VLAUSoundOut::SetEnd(MusicTimeStamp end)
{
if (fWasAtEnd)
fMusicEnd = end;
2006-09-11 02:49:56 +00:00
}
2011-09-04 23:38:01 +00:00
void VLAUSoundOut::SetMelodyState(VLSoundOut::MelodyState state)
{
if (fMusic) {
UInt32 numTracks;
MusicTrack curTrack;
MusicSequenceGetTrackCount(fMusic, &numTracks);
MusicSequenceGetIndTrack(fMusic, numTracks-2, &curTrack);
Boolean mute = state==kMelodyMute;
Boolean solo = state==kMelodySolo;
MusicTrackSetProperty(curTrack, kSequenceTrackProperty_MuteStatus, &mute, sizeof(mute));
MusicTrackSetProperty(curTrack, kSequenceTrackProperty_SoloStatus, &solo, sizeof(solo));
}
}
2008-05-29 18:54:30 +00:00
void VLAUSoundOut::SetPlayRate(float rate)
{
if ((rate < 0) != fForward) {
fForward = !fForward;
MusicTimeStamp rightNow;
MusicPlayerGetTime(fPlayer, &rightNow);
MusicSequenceReverse(fMusic);
2011-09-11 21:27:53 +00:00
MusicPlayerSetTime(fPlayer, fMusicEnd - rightNow);
2008-05-29 18:54:30 +00:00
}
2011-09-11 17:20:34 +00:00
fPlayRate = fabsf(rate);
MusicPlayerSetPlayRateScalar(fPlayer, fPlayRate);
2008-05-29 18:54:30 +00:00
}
2011-09-11 18:17:02 +00:00
const MusicTimeStamp kInitialSkip= 0.15;
const MusicTimeStamp kMaxSkip = 1.0;
const MusicTimeStamp kSkipFactor = 0.5;
static MusicTimeStamp sSkipSign = 0;
static int sSkipSteps = 0;
2011-09-11 17:20:34 +00:00
static VLResetTimer * sSkipResetTimer;
2011-08-29 00:41:04 +00:00
void VLAUSoundOut::SkipTimeInterval()
{
MusicTimeStamp time;
MusicPlayerGetTime(fPlayer, &time);
2011-09-11 18:17:02 +00:00
++sSkipSteps;
MusicTimeStamp delta = kInitialSkip+(kMaxSkip-kInitialSkip)
*sSkipSign*(1.0-exp(-sSkipSteps*kSkipFactor));
time += delta;
2011-09-11 17:20:34 +00:00
if (!sSkipResetTimer)
sSkipResetTimer = new VLResetTimer(500*NSEC_PER_MSEC, ^{
2011-09-11 18:17:02 +00:00
sSkipSteps = 0;
2011-08-29 00:41:04 +00:00
});
2011-09-11 17:20:34 +00:00
sSkipResetTimer->Prime();
2011-08-29 00:41:04 +00:00
MusicPlayerSetTime(fPlayer, time);
}
void VLAUSoundOut::Fwd()
{
2011-09-11 18:17:02 +00:00
if (sSkipSign <= 0.0) {
sSkipSign = 1.0;
sSkipSteps = 0;
}
2011-08-29 00:41:04 +00:00
SkipTimeInterval();
}
void VLAUSoundOut::Bck()
2008-05-29 18:54:30 +00:00
{
2011-09-11 18:17:02 +00:00
if (sSkipSign >= 0.0) {
sSkipSign = -1.0;
sSkipSteps = 0;
}
2011-08-29 00:41:04 +00:00
SkipTimeInterval();
2008-05-29 18:54:30 +00:00
}
2011-09-11 17:20:34 +00:00
static VLResetTimer * sSlowResetTimer;
void VLAUSoundOut::Slow(float rate)
{
if (!sSlowResetTimer)
sSlowResetTimer = new VLResetTimer(500*NSEC_PER_MSEC, ^{
MusicPlayerSetPlayRateScalar(fPlayer, fPlayRate);
});
sSlowResetTimer->Prime();
MusicPlayerSetPlayRateScalar(fPlayer, fPlayRate*rate);
}
2008-07-05 13:56:51 +00:00
void VLAUSoundOut::Stop(bool pause)
2006-09-11 02:49:56 +00:00
{
2011-09-11 21:27:53 +00:00
if (!fRunning)
return;
2006-10-16 09:00:02 +00:00
MusicPlayerStop(fPlayer);
2008-07-05 13:56:51 +00:00
fRunning = false;
2011-09-11 21:27:53 +00:00
fWasAtEnd = false;
2008-07-05 13:56:51 +00:00
if (!pause && fMusic) {
MusicPlayerSetSequence(fPlayer, NULL);
DisposeMusicSequence(fMusic);
fMusic = 0;
2006-09-11 02:49:56 +00:00
}
2011-09-04 19:48:57 +00:00
CFNotificationCenterPostNotification(CFNotificationCenterGetLocalCenter(), kVLSoundStoppedNotification,
NULL, NULL, false);
dispatch_source_set_timer(fMusicPoll, DISPATCH_TIME_FOREVER, INT64_MAX, 200*NSEC_PER_MSEC);
2006-09-11 02:49:56 +00:00
}
bool VLAUSoundOut::Playing()
{
return fRunning;
}
2008-07-06 11:07:57 +00:00
bool VLAUSoundOut::AtEnd()
{
MusicTimeStamp time;
2011-09-11 21:27:53 +00:00
return !MusicPlayerGetTime(fPlayer, &time) && time >= fMusicEnd;
}
bool VLAUSoundOut::AtBeginning()
{
MusicTimeStamp time;
return MusicPlayerGetTime(fPlayer, &time) || !time;
2008-07-06 11:07:57 +00:00
}
2011-09-11 21:43:13 +00:00
void VLAUSoundOut::ResetSelection()
{
fWasAtEnd = true;
}
2011-09-04 19:48:57 +00:00
void VLAUSoundOut::PollMusic()
{
if (fRunning && AtEnd()) {
MusicPlayerSetTime(fPlayer, 0);
Stop(true);
2011-09-11 21:27:53 +00:00
fWasAtEnd = true;
2011-09-04 19:48:57 +00:00
}
}
2006-09-11 02:49:56 +00:00
void VLAUSoundOut::PlayNote(const VLNote & note)
{
Play(&note.fPitch);
}
void VLAUSoundOut::PlayChord(const VLChord & chord)
{
2011-08-28 21:38:36 +00:00
//
// TODO: The voicings here are not very realistic
//
2006-09-11 02:49:56 +00:00
std::vector<int8_t> notes;
for (int i = 0; i < 32; ++i)
if (chord.fSteps & (1 << i))
2011-08-28 21:38:36 +00:00
notes.push_back(chord.fPitch+i%12);
2006-09-11 02:49:56 +00:00
if (chord.fRootPitch != VLNote::kNoPitch)
notes.push_back(chord.fRootPitch);
Play(&notes[0], notes.size());
}
void VLAUSoundOut::Play(const int8_t * note, size_t numNotes)
{
2006-10-16 09:00:02 +00:00
MusicSequence music;
MusicTrack track;
NewMusicSequence(&music);
MusicSequenceNewTrack(music, &track);
const int8_t kNoteVelocity = 127;
for (int i=0; i<numNotes; ++i) {
MIDINoteMessage n = {0, note[i], kNoteVelocity, 0, 1.0};
MusicTrackNewMIDINoteEvent(track, 0.0, &n);
}
PlaySequence(music);
2006-09-11 02:49:56 +00:00
}
2006-11-13 04:26:09 +00:00
2006-11-27 07:01:38 +00:00
VLAUFileSoundOut::VLAUFileSoundOut(CFURLRef file, OSType dataFormat)
: VLAUSoundOut(true), fFile(file), fDataFormat(dataFormat)
{
2006-11-27 07:01:38 +00:00
InitSoundOutput(true);
CFRetain(fFile);
}
VLAUFileSoundOut::~VLAUFileSoundOut()
{
CFRelease(fFile);
}
void VLAUFileSoundOut::SetupOutput(AUNode outputNode)
{
2011-09-03 20:34:53 +00:00
R(AUGraphNodeInfo(fGraph, outputNode, NULL, &fOutput));
Float64 sampleRate = 22050.0;
2006-11-27 07:01:38 +00:00
R(AudioUnitSetProperty(fOutput,
kAudioUnitProperty_SampleRate,
kAudioUnitScope_Output, 0,
&sampleRate, sizeof(sampleRate)));
}
void VLAUFileSoundOut::PlaySequence(MusicSequence music)
{
SInt32 urlErr;
CFURLDestroyResource(fFile, &urlErr);
UInt32 size;
2006-11-27 07:01:38 +00:00
UInt32 numFrames = 512;
2011-09-11 21:27:53 +00:00
MusicTimeStamp musicLen = VLMIDIUtilities(music).Length()+8;
CFStringRef name =
CFURLCopyLastPathComponent(fFile);
2006-11-27 07:01:38 +00:00
CAAudioFileFormats * formats = CAAudioFileFormats::Instance();
AudioFileTypeID fileType;
formats->InferFileFormatFromFilename(name, fileType);
CAStreamBasicDescription outputFormat;
2006-11-27 07:01:38 +00:00
if (fDataFormat)
outputFormat.mFormatID = fDataFormat;
else if (!formats->InferDataFormatFromFileFormat(fileType, outputFormat))
switch (fileType) {
case kAudioFileM4AType:
outputFormat.mFormatID = kAudioFormatMPEG4AAC;
break;
default:
outputFormat.mFormatID = kAudioFormatLinearPCM;
break;
}
outputFormat.mChannelsPerFrame = 2;
2006-11-27 07:01:38 +00:00
outputFormat.mSampleRate = 22050.0;
if (outputFormat.mFormatID == kAudioFormatLinearPCM) {
outputFormat.mBytesPerPacket = outputFormat.mChannelsPerFrame * 2;
outputFormat.mFramesPerPacket = 1;
outputFormat.mBytesPerFrame = outputFormat.mBytesPerPacket;
outputFormat.mBitsPerChannel = 16;
if (fileType == kAudioFileWAVEType)
outputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger
| kLinearPCMFormatFlagIsPacked;
else
outputFormat.mFormatFlags = kLinearPCMFormatFlagIsBigEndian
| kLinearPCMFormatFlagIsSignedInteger
| kLinearPCMFormatFlagIsPacked;
} else {
// use AudioFormat API to fill out the rest.
size = sizeof(outputFormat);
2006-11-27 07:01:38 +00:00
R(AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL,
&size, &outputFormat));
}
2011-09-03 20:34:53 +00:00
CFRelease(name);
ExtAudioFileRef outfile;
2011-09-03 20:34:53 +00:00
R(ExtAudioFileCreateWithURL(fFile, fileType, &outputFormat, NULL,
kAudioFileFlags_EraseFile, &outfile));
2006-11-27 07:01:38 +00:00
CAStreamBasicDescription clientFormat;
size = sizeof(clientFormat);
R(AudioUnitGetProperty(fOutput, kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output, 0, &clientFormat, &size));
clientFormat.Print(stderr);
2006-12-30 09:55:27 +00:00
outputFormat.Print(stderr);
2006-11-27 07:01:38 +00:00
R(ExtAudioFileSetProperty(outfile, kExtAudioFileProperty_ClientDataFormat,
size, &clientFormat));
VLAUSoundOut::PlaySequence(music);
MusicTimeStamp currentTime;
AUOutputBL outputBuffer (clientFormat, numFrames);
AudioTimeStamp tStamp;
memset (&tStamp, 0, sizeof(AudioTimeStamp));
tStamp.mFlags = kAudioTimeStampSampleTimeValid;
do {
outputBuffer.Prepare();
AudioUnitRenderActionFlags actionFlags = 0;
R(AudioUnitRender(fOutput, &actionFlags, &tStamp, 0, numFrames,
outputBuffer.ABL()));
tStamp.mSampleTime += numFrames;
2006-11-27 07:01:38 +00:00
R(ExtAudioFileWrite(outfile, numFrames, outputBuffer.ABL()));
2006-11-27 07:01:38 +00:00
MusicPlayerGetTime (fPlayer, &currentTime);
} while (currentTime < musicLen);
ExtAudioFileDispose(outfile);
}