2007-04-27 06:41:34 +00:00
|
|
|
//
|
|
|
|
// File: VLSoundOut.cpp - Sound output and file playing functionality
|
|
|
|
//
|
|
|
|
// Author(s):
|
|
|
|
//
|
|
|
|
// (MN) Matthias Neeracher
|
|
|
|
//
|
2017-11-24 21:56:38 +00:00
|
|
|
// Copyright © 2005-2017 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();
|
|
|
|
|
2006-11-17 04:03:14 +00:00
|
|
|
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);
|
2007-12-03 17:45:56 +00:00
|
|
|
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);
|
|
|
|
|
2006-11-17 04:03:14 +00:00
|
|
|
virtual ~VLAUSoundOut();
|
2011-09-04 19:48:57 +00:00
|
|
|
void PollMusic();
|
2017-11-24 21:56:38 +00:00
|
|
|
void PropagateProperty(AudioUnitPropertyID inID,
|
|
|
|
AudioUnitScope inScope,
|
|
|
|
AudioUnitElement inElement);
|
2006-11-17 04:03:14 +00:00
|
|
|
protected:
|
|
|
|
VLAUSoundOut(bool fileOutput);
|
|
|
|
|
2006-11-27 07:01:38 +00:00
|
|
|
void InitSoundOutput(bool fileOutput);
|
2017-11-24 21:56:38 +00:00
|
|
|
void TeardownSoundOutput();
|
2006-11-17 04:03:14 +00:00
|
|
|
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;
|
2017-11-24 21:56:38 +00:00
|
|
|
AudioUnit fOutputUnit;
|
2018-02-17 21:56:38 +00:00
|
|
|
AudioUnit fLimiterUnit;
|
2017-11-24 21:56:38 +00:00
|
|
|
AudioUnit fSynthUnit;
|
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;
|
2006-10-08 05:56:25 +00:00
|
|
|
|
2011-09-04 19:48:57 +00:00
|
|
|
void Play(const int8_t * note, size_t numNotes = 1);
|
2006-11-17 04:03:14 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class VLAUFileSoundOut : public VLAUSoundOut {
|
|
|
|
public:
|
2006-11-27 07:01:38 +00:00
|
|
|
VLAUFileSoundOut(CFURLRef file, OSType dataFormat);
|
2006-11-17 04:03:14 +00:00
|
|
|
~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) {}
|
2006-11-17 04:03:14 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2006-10-08 05:56:25 +00:00
|
|
|
VLSoundEvent::~VLSoundEvent()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void VLSoundScheduler::Schedule(VLSoundEvent * what, float when)
|
|
|
|
{
|
|
|
|
usleep((int)(1000000.0f*when));
|
2006-10-16 09:00:02 +00:00
|
|
|
what->Perform();
|
2006-10-08 05:56:25 +00:00
|
|
|
}
|
|
|
|
|
2018-02-19 00:59:23 +00:00
|
|
|
static std::unique_ptr<VLSoundOut> sSoundOut;
|
|
|
|
static std::unique_ptr<VLSoundScheduler> sSoundScheduler;
|
2006-09-11 02:49:56 +00:00
|
|
|
|
|
|
|
VLSoundOut * VLSoundOut::Instance()
|
|
|
|
{
|
2006-10-08 05:56:25 +00:00
|
|
|
if (!sSoundOut.get()) {
|
2006-09-11 02:49:56 +00:00
|
|
|
sSoundOut.reset(new VLAUSoundOut);
|
2006-10-08 05:56:25 +00:00
|
|
|
if (!sSoundScheduler.get())
|
|
|
|
sSoundScheduler.reset(new VLSoundScheduler);
|
|
|
|
}
|
2006-09-11 02:49:56 +00:00
|
|
|
return sSoundOut.get();
|
|
|
|
}
|
|
|
|
|
2006-10-08 05:56:25 +00:00
|
|
|
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-17 04:03:14 +00:00
|
|
|
{
|
2006-11-27 07:01:38 +00:00
|
|
|
return new VLAUFileSoundOut(file, dataFormat);
|
2006-11-17 04:03:14 +00:00
|
|
|
}
|
|
|
|
|
2008-05-29 18:54:30 +00:00
|
|
|
void VLSoundOut::PlayFile(CFDataRef file)
|
|
|
|
{
|
|
|
|
MusicSequence music;
|
|
|
|
|
|
|
|
NewMusicSequence(&music);
|
2018-02-19 00:59:23 +00:00
|
|
|
MusicSequenceFileLoadData(music, file, kMusicSequenceFile_MIDIType, 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)
|
2006-11-17 04:03:14 +00:00
|
|
|
{
|
|
|
|
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-17 04:03:14 +00:00
|
|
|
}
|
|
|
|
|
2006-11-27 07:01:38 +00:00
|
|
|
VLAUSoundOut::VLAUSoundOut(bool)
|
2006-11-17 04:03:14 +00:00
|
|
|
: 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);
|
2017-11-24 21:56:38 +00:00
|
|
|
TeardownSoundOutput();
|
|
|
|
}
|
|
|
|
|
|
|
|
extern "C" void
|
|
|
|
VLAUSoundOutPropagateProperty(void * inRefCon,
|
|
|
|
AudioUnit, AudioUnitPropertyID inID,
|
|
|
|
AudioUnitScope inScope,
|
|
|
|
AudioUnitElement inElement)
|
|
|
|
{
|
|
|
|
VLAUSoundOut * liveAudioObject = reinterpret_cast<VLAUSoundOut *>(inRefCon);
|
|
|
|
|
|
|
|
liveAudioObject->PropagateProperty(inID, inScope, inElement);
|
2006-11-17 04:03:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2006-11-17 04:03:14 +00:00
|
|
|
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-17 04:03:14 +00:00
|
|
|
|
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);
|
|
|
|
|
2017-11-24 21:56:38 +00:00
|
|
|
R(AUGraphNodeInfo(fGraph, outNode, NULL, &fOutputUnit));
|
2018-02-17 21:56:38 +00:00
|
|
|
R(AUGraphNodeInfo(fGraph, limiterNode, NULL, &fLimiterUnit));
|
2017-11-24 21:56:38 +00:00
|
|
|
R(AUGraphNodeInfo(fGraph, synthNode, NULL, &fSynthUnit));
|
|
|
|
|
2006-11-17 04:03:14 +00:00
|
|
|
if (fileOutput) {
|
|
|
|
UInt32 value = 1;
|
2017-11-24 21:56:38 +00:00
|
|
|
R(AudioUnitSetProperty(fSynthUnit,
|
2006-11-27 07:01:38 +00:00
|
|
|
kAudioUnitProperty_OfflineRender,
|
|
|
|
kAudioUnitScope_Global, 0,
|
|
|
|
&value, sizeof(value)));
|
|
|
|
value = 512;
|
2017-11-24 21:56:38 +00:00
|
|
|
R(AudioUnitSetProperty(fSynthUnit,
|
2006-11-27 07:01:38 +00:00
|
|
|
kAudioUnitProperty_OfflineRender,
|
|
|
|
kAudioUnitScope_Global, 0,
|
|
|
|
&value, sizeof(value)));
|
2017-11-24 21:56:38 +00:00
|
|
|
} else {
|
2018-02-17 21:56:38 +00:00
|
|
|
PropagateProperty(kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0);
|
2017-11-24 21:56:38 +00:00
|
|
|
AudioUnitAddPropertyListener(fOutputUnit,
|
|
|
|
kAudioUnitProperty_MaximumFramesPerSlice,
|
|
|
|
VLAUSoundOutPropagateProperty,
|
|
|
|
this);
|
|
|
|
}
|
|
|
|
|
2006-11-17 04:03:14 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2017-11-24 21:56:38 +00:00
|
|
|
void VLAUSoundOut::PropagateProperty(AudioUnitPropertyID inID,
|
|
|
|
AudioUnitScope inScope,
|
|
|
|
AudioUnitElement inElement)
|
|
|
|
{
|
|
|
|
Boolean running = false;
|
|
|
|
if (MusicPlayerIsPlaying(fPlayer, &running))
|
|
|
|
running = false;
|
|
|
|
else if (running)
|
|
|
|
MusicPlayerStop(fPlayer);
|
|
|
|
|
|
|
|
std::vector<char> data;
|
|
|
|
UInt32 sz = 0;
|
|
|
|
Boolean wr;
|
|
|
|
if (AudioUnitGetPropertyInfo(fOutputUnit, inID, inScope, inElement, &sz, &wr))
|
|
|
|
goto reinitialize;
|
|
|
|
data.resize(sz);
|
|
|
|
if (!AudioUnitGetProperty(fOutputUnit, inID, inScope, inElement, &data[0], &sz)) {
|
2018-02-17 21:56:38 +00:00
|
|
|
if (OSStatus status = AudioUnitSetProperty(fSynthUnit, inID, kAudioUnitScope_Global, inElement,
|
2017-11-24 21:56:38 +00:00
|
|
|
&data[0], sz)) {
|
|
|
|
if (status == kAudioUnitErr_Initialized) {
|
|
|
|
AudioUnitUninitialize(fSynthUnit);
|
2018-02-17 21:56:38 +00:00
|
|
|
status = AudioUnitSetProperty(fSynthUnit, inID, kAudioUnitScope_Global, inElement,
|
2017-11-24 21:56:38 +00:00
|
|
|
&data[0], sz);
|
|
|
|
AudioUnitInitialize(fSynthUnit);
|
|
|
|
}
|
|
|
|
}
|
2018-02-17 21:56:38 +00:00
|
|
|
if (OSStatus status = AudioUnitSetProperty(fLimiterUnit, inID, kAudioUnitScope_Global, inElement,
|
|
|
|
&data[0], sz)) {
|
|
|
|
if (status == kAudioUnitErr_Initialized) {
|
|
|
|
AudioUnitUninitialize(fLimiterUnit);
|
|
|
|
status = AudioUnitSetProperty(fLimiterUnit, inID, kAudioUnitScope_Global, inElement,
|
|
|
|
&data[0], sz);
|
|
|
|
AudioUnitInitialize(fLimiterUnit);
|
|
|
|
}
|
|
|
|
}
|
2017-11-24 21:56:38 +00:00
|
|
|
}
|
|
|
|
reinitialize:
|
|
|
|
if (running)
|
|
|
|
MusicPlayerStart(fPlayer);
|
|
|
|
}
|
|
|
|
|
|
|
|
void VLAUSoundOut::TeardownSoundOutput()
|
|
|
|
{
|
|
|
|
AudioUnitRemovePropertyListenerWithUserData(fOutputUnit,
|
|
|
|
kAudioUnitProperty_MaximumFramesPerSlice,
|
|
|
|
VLAUSoundOutPropagateProperty,
|
|
|
|
this);
|
|
|
|
DisposeMusicPlayer(fPlayer);
|
|
|
|
DisposeAUGraph(fGraph);
|
|
|
|
}
|
|
|
|
|
2006-11-17 04:03:14 +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
|
|
|
}
|
|
|
|
|
2007-12-03 17:45: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(¬e.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(¬es[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) {
|
2018-02-19 00:59:23 +00:00
|
|
|
MIDINoteMessage n = {0, static_cast<UInt8>(note[i]), kNoteVelocity, 0, 1.0};
|
2006-10-16 09:00:02 +00:00
|
|
|
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-17 04:03:14 +00:00
|
|
|
{
|
2006-11-27 07:01:38 +00:00
|
|
|
InitSoundOutput(true);
|
|
|
|
|
2006-11-17 04:03:14 +00:00
|
|
|
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));
|
2006-11-17 04:03:14 +00:00
|
|
|
Float64 sampleRate = 22050.0;
|
2006-11-27 07:01:38 +00:00
|
|
|
R(AudioUnitSetProperty(fOutput,
|
|
|
|
kAudioUnitProperty_SampleRate,
|
|
|
|
kAudioUnitScope_Output, 0,
|
|
|
|
&sampleRate, sizeof(sampleRate)));
|
|
|
|
}
|
2006-11-17 04:03:14 +00:00
|
|
|
|
|
|
|
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;
|
2006-11-17 04:03:14 +00:00
|
|
|
CFStringRef name =
|
|
|
|
CFURLCopyLastPathComponent(fFile);
|
2006-11-27 07:01:38 +00:00
|
|
|
CAAudioFileFormats * formats = CAAudioFileFormats::Instance();
|
2006-11-17 04:03:14 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2006-11-17 04:03:14 +00:00
|
|
|
outputFormat.mChannelsPerFrame = 2;
|
2006-11-27 07:01:38 +00:00
|
|
|
outputFormat.mSampleRate = 22050.0;
|
2006-11-17 04:03:14 +00:00
|
|
|
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));
|
2006-11-17 04:03:14 +00:00
|
|
|
}
|
2011-09-03 20:34:53 +00:00
|
|
|
CFRelease(name);
|
2006-11-17 04:03:14 +00:00
|
|
|
|
|
|
|
ExtAudioFileRef outfile;
|
2011-09-03 20:34:53 +00:00
|
|
|
R(ExtAudioFileCreateWithURL(fFile, fileType, &outputFormat, NULL,
|
|
|
|
kAudioFileFlags_EraseFile, &outfile));
|
2006-11-17 04:03:14 +00:00
|
|
|
|
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-17 04:03:14 +00:00
|
|
|
|
2006-11-27 07:01:38 +00:00
|
|
|
R(ExtAudioFileWrite(outfile, numFrames, outputBuffer.ABL()));
|
2006-11-17 04:03:14 +00:00
|
|
|
|
2006-11-27 07:01:38 +00:00
|
|
|
MusicPlayerGetTime (fPlayer, ¤tTime);
|
|
|
|
} while (currentTime < musicLen);
|
2006-11-17 04:03:14 +00:00
|
|
|
|
|
|
|
ExtAudioFileDispose(outfile);
|
|
|
|
}
|
|
|
|
|
|
|
|
|