- removed JK Midi clock
git-svn-id: http://moon:8086/svn/software/trunk/projects/JaySynth@733 b431acfa-c32f-4a4a-93f1-934dc6c82436
This commit is contained in:
@@ -1,189 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
//#######################################################################################
|
|
||||||
//Class to insert Midiclock messages into a given MidiBuffer, suitable for block
|
|
||||||
//based audio callbacks. The class can act on position jumps e.g. "Loops" by sending a
|
|
||||||
//positioning message.
|
|
||||||
//NOTE: No ballistik came in to play so I wouldn't recommend using it to drive a mechanical
|
|
||||||
//tape deck!!!
|
|
||||||
//
|
|
||||||
//solar3d-software, April- 10- 2012
|
|
||||||
//#######################################################################################
|
|
||||||
|
|
||||||
*/
|
|
||||||
#include "JK_MidiClock.h"
|
|
||||||
|
|
||||||
JK_MidiClock::JK_MidiClock()
|
|
||||||
:wasPlaying(false),
|
|
||||||
syncPpqPosition(-999.0),
|
|
||||||
posChangeThreshold(0.001),
|
|
||||||
ppqToStartSyncAt(0.0),
|
|
||||||
followSongPosition(true),
|
|
||||||
syncFlag(0),
|
|
||||||
ppqOffset(0)
|
|
||||||
{
|
|
||||||
//Prepare Midi messages to avoid blocking the caller of generateMidiclock()!
|
|
||||||
continueMessage = new MidiMessage(MidiMessage::midiContinue());
|
|
||||||
stopMessage = new MidiMessage(MidiMessage::midiStop());
|
|
||||||
clockMessage = new MidiMessage(MidiMessage::midiClock());
|
|
||||||
songPositionMessage = new MidiMessage(MidiMessage::songPositionPointer(0));
|
|
||||||
}
|
|
||||||
//=================================================================================================
|
|
||||||
void JK_MidiClock::generateMidiclock(AudioPlayHead::CurrentPositionInfo &lastPosInfo,
|
|
||||||
MidiBuffer* midiBuffer, const int bufferSize, const double sampleRate)
|
|
||||||
{
|
|
||||||
//###################################Some explanation about musical tempo #################
|
|
||||||
//A Time Signature, is two numbers, one on top of the other. The numerator describes the #
|
|
||||||
//number of Beats in a Bar, while the denominator describes of what note value a Beat is. #
|
|
||||||
//So 4/4 would be four quarter-notes per Bar, while 4/2 would be four half-notes per Bar, #
|
|
||||||
//4/8 would be four eighth-notes per Bar, and 2/4 would be two quarter-notes per Bar. #
|
|
||||||
//#########################################################################################
|
|
||||||
|
|
||||||
|
|
||||||
if (midiBuffer != nullptr)
|
|
||||||
{
|
|
||||||
//PPQ value of one sample
|
|
||||||
const double ppqPerSample = (lastPosInfo.bpm / 60.0) / sampleRate;
|
|
||||||
|
|
||||||
//PPQ offset to compensate Midi interface latency
|
|
||||||
double hostPpqPosition = lastPosInfo.ppqPosition + ppqOffset * ppqPerSample;;
|
|
||||||
|
|
||||||
if (lastPosInfo.isPlaying || lastPosInfo.isRecording)
|
|
||||||
{
|
|
||||||
if (! wasPlaying)
|
|
||||||
{
|
|
||||||
//set the point where to start the slave
|
|
||||||
ppqToStartSyncAt = getNearestSixteenthInPPQ(hostPpqPosition);
|
|
||||||
|
|
||||||
//Special case: Master is set to always start playback from the previous start position...
|
|
||||||
if (positionJumped(syncPpqPosition, hostPpqPosition, sampleRate, ppqPerSample))
|
|
||||||
{
|
|
||||||
//Cue Midiclock slave to the nearest sixteenth note to new start position
|
|
||||||
//because the one calculated in stop mode isn't valid anymore.
|
|
||||||
sendSongPositionPointerMessage(ppqToStartSyncAt, 0, midiBuffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//Position jump (loop or manually position change while playing)
|
|
||||||
if (positionJumped(syncPpqPosition, hostPpqPosition, sampleRate, ppqPerSample))
|
|
||||||
{
|
|
||||||
//set the point where to start the slave
|
|
||||||
ppqToStartSyncAt = getNearestSixteenthInPPQ(hostPpqPosition);
|
|
||||||
|
|
||||||
//User has changed position manually while playing
|
|
||||||
if (syncFlag == 0)
|
|
||||||
{
|
|
||||||
midiBuffer->addEvent(*stopMessage, 0);
|
|
||||||
sendSongPositionPointerMessage(hostPpqPosition, 0, midiBuffer);
|
|
||||||
|
|
||||||
syncFlag = startSlave_;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (followSongPosition)
|
|
||||||
{
|
|
||||||
sendSongPositionPointerMessage(hostPpqPosition, 0, midiBuffer);
|
|
||||||
|
|
||||||
syncFlag = startSlave_;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
syncFlag = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int posInBuffer = 0; posInBuffer < bufferSize; ++posInBuffer)
|
|
||||||
{
|
|
||||||
syncPpqPosition = hostPpqPosition + (posInBuffer * ppqPerSample);
|
|
||||||
|
|
||||||
const int clockDistanceInSamples = roundToInt((60.0 * sampleRate) / (lastPosInfo.bpm * 24.0));
|
|
||||||
const int64 hostSamplePos = roundToInt64((hostPpqPosition * (60.0 / lastPosInfo.bpm)) * sampleRate);
|
|
||||||
const int64 syncSamplePos = hostSamplePos + posInBuffer;
|
|
||||||
|
|
||||||
//Some hosts like Cubase come up with a wacky ppqPosition
|
|
||||||
//that could break the timing! Best is to "wait"
|
|
||||||
//here for the right ppqPosition to jump on.
|
|
||||||
if (syncPpqPosition >= ppqToStartSyncAt)
|
|
||||||
{
|
|
||||||
if ((syncFlag & startSlave_) == startSlave_)
|
|
||||||
{
|
|
||||||
midiBuffer->addEvent(*continueMessage, posInBuffer);
|
|
||||||
|
|
||||||
syncFlag &= cycleEnd_;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Cycle mode on
|
|
||||||
if (lastPosInfo.isLooping && lastPosInfo.ppqLoopStart != lastPosInfo.ppqLoopEnd)
|
|
||||||
{
|
|
||||||
const double ppqToCycleEnd = fabs(lastPosInfo.ppqLoopEnd - syncPpqPosition);
|
|
||||||
const int64 samplesToCycleEnd = roundToInt64(ppqToCycleEnd * (60.0 / lastPosInfo.bpm) * sampleRate);
|
|
||||||
|
|
||||||
if ((syncFlag & cycleEnd_) == 0)
|
|
||||||
{
|
|
||||||
if (samplesToCycleEnd <= clockDistanceInSamples) //For fine tuning tweak here
|
|
||||||
{
|
|
||||||
//We have reached the cycle- end position
|
|
||||||
//and must stop the Midiclock slave here
|
|
||||||
if (followSongPosition)
|
|
||||||
midiBuffer->addEvent(*stopMessage, posInBuffer);
|
|
||||||
|
|
||||||
syncFlag |= cycleEnd_;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//For best timing we should never interupt Midiclock messages!
|
|
||||||
//Seems that some slaves constantly adjusting their internal clock
|
|
||||||
//to Midiclock even if they are in stop mode.
|
|
||||||
if (syncSamplePos % clockDistanceInSamples == 0)
|
|
||||||
midiBuffer->addEvent(*clockMessage, posInBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
wasPlaying = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//Send positioning message if the user has stopped or if he changed the playhead position
|
|
||||||
//manually in stop mode! This will also initially cue slave after loading plugin instance.
|
|
||||||
if (wasPlaying || positionJumped(syncPpqPosition, hostPpqPosition, sampleRate, ppqPerSample))
|
|
||||||
{
|
|
||||||
midiBuffer->addEvent(*stopMessage, 0);
|
|
||||||
sendSongPositionPointerMessage(hostPpqPosition, 0, midiBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
syncPpqPosition = hostPpqPosition;
|
|
||||||
|
|
||||||
syncFlag = startSlave_;
|
|
||||||
|
|
||||||
wasPlaying = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//=================================================================================================
|
|
||||||
bool JK_MidiClock::positionJumped(const double lastPosInPPQ, const double currentPosInPPQ,
|
|
||||||
const double sampleRate, const double ppqPerSample)
|
|
||||||
{
|
|
||||||
//This returns true if the user has changed the playhead position manually or if
|
|
||||||
//a jump has occured! The comperator's default threshold is lastPosInPPQ +- 10ms.
|
|
||||||
if (currentPosInPPQ < lastPosInPPQ - ((posChangeThreshold * sampleRate) * ppqPerSample) ||
|
|
||||||
currentPosInPPQ > lastPosInPPQ + ((posChangeThreshold * sampleRate) * ppqPerSample))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
//=================================================================================================
|
|
||||||
void JK_MidiClock::sendSongPositionPointerMessage(const double ppqPosition, const int posInBuffer, MidiBuffer* buffer)
|
|
||||||
{
|
|
||||||
//This will cue the slave to the NEAREST
|
|
||||||
//16th note to the given ppqPosition.
|
|
||||||
int intBeat = int(ceil(ppqPosition * 4));
|
|
||||||
|
|
||||||
uint8* pSongPositionTime((uint8*)(songPositionMessage->getRawData()));
|
|
||||||
|
|
||||||
*(pSongPositionTime + 1) = (uint8)(intBeat & 0x7f);
|
|
||||||
*(pSongPositionTime + 2) = (uint8)((intBeat & 0x3f80)>>7);
|
|
||||||
|
|
||||||
buffer->addEvent(*songPositionMessage, posInBuffer);
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
//#######################################################################################
|
|
||||||
//Class to insert Midiclock messages into a given MidiBuffer, suitable for block
|
|
||||||
//based audio callbacks. The class can act on position jumps e.g. "Loops" by sending a
|
|
||||||
//positioning message.
|
|
||||||
//NOTE: No ballistik came in to play so I wouldn't recommend using it to drive a mechanical
|
|
||||||
//tape deck!!!
|
|
||||||
//
|
|
||||||
//solar3d-software, April- 10- 2012
|
|
||||||
//#######################################################################################
|
|
||||||
|
|
||||||
*/
|
|
||||||
#ifndef __JK_MIDICLOCK
|
|
||||||
#define __JK_MIDICLOCK
|
|
||||||
|
|
||||||
#include "../JuceLibraryCode/JuceHeader.h"
|
|
||||||
|
|
||||||
//#include "Includes.h"
|
|
||||||
|
|
||||||
class JK_MidiClock
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
JK_MidiClock();
|
|
||||||
|
|
||||||
void setPositionJumpThreshold(const double ms) {posChangeThreshold = ms / 1000.0;}
|
|
||||||
double getPositionJumpThreshold() {return posChangeThreshold;}
|
|
||||||
void setFollowSongPosition(const bool shouldFollow) {followSongPosition = shouldFollow;}
|
|
||||||
bool getFollowSongPosition() {return followSongPosition;}
|
|
||||||
void setOffset(const int offset) {ppqOffset = offset;}
|
|
||||||
int getOffset() {return ppqOffset;}
|
|
||||||
|
|
||||||
void generateMidiclock(AudioPlayHead::CurrentPositionInfo &lastPosInfo,
|
|
||||||
MidiBuffer* midiBuffer,
|
|
||||||
const int bufferSize,
|
|
||||||
const double sampleRate);
|
|
||||||
|
|
||||||
//=================================================================================================
|
|
||||||
juce_UseDebuggingNewOperator
|
|
||||||
|
|
||||||
private:
|
|
||||||
inline int64 roundToInt64 (const double val) noexcept
|
|
||||||
{
|
|
||||||
return (val - floor(val) >= 0.5) ? (int64)(ceil(val)) : (int64)(floor(val));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool wasPlaying;
|
|
||||||
double syncPpqPosition;
|
|
||||||
double posChangeThreshold;
|
|
||||||
double ppqToStartSyncAt;
|
|
||||||
bool followSongPosition;
|
|
||||||
uint8 syncFlag;
|
|
||||||
int ppqOffset;
|
|
||||||
|
|
||||||
static const int cycleEnd_ = 1;
|
|
||||||
static const int startSlave_ = 2;
|
|
||||||
|
|
||||||
ScopedPointer <MidiMessage> clockMessage;
|
|
||||||
ScopedPointer <MidiMessage> continueMessage;
|
|
||||||
ScopedPointer <MidiMessage> stopMessage;
|
|
||||||
ScopedPointer <MidiMessage> songPositionMessage;
|
|
||||||
|
|
||||||
void sendSongPositionPointerMessage(const double ppqPosition, const int posInBuffer, MidiBuffer* buffer);
|
|
||||||
|
|
||||||
bool positionJumped(const double lastPosInPPQ, const double currentPosInPPQ,
|
|
||||||
const double sampleRate, const double ppqPerSample);
|
|
||||||
|
|
||||||
double getNearestSixteenthInPPQ(const double ppqPosition) {return ceil(ppqPosition * 4.0) / 4.0;}
|
|
||||||
|
|
||||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(JK_MidiClock);
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
@@ -528,7 +528,7 @@ void JaySynthAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer
|
|||||||
AudioPlayHead::CurrentPositionInfo pos;
|
AudioPlayHead::CurrentPositionInfo pos;
|
||||||
getPlayHead()->getCurrentPosition(pos);
|
getPlayHead()->getCurrentPosition(pos);
|
||||||
|
|
||||||
m_JK_MidiClock.generateMidiclock(pos, &midiMessages, numSamples, getSampleRate());
|
// m_JK_MidiClock.generateMidiclock(pos, &midiMessages, numSamples, getSampleRate());
|
||||||
|
|
||||||
// Now pass any incoming midi messages to our keyboard state object, and let it
|
// Now pass any incoming midi messages to our keyboard state object, and let it
|
||||||
// add messages to the buffer if the user is clicking on the on-screen keys
|
// add messages to the buffer if the user is clicking on the on-screen keys
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
#include "synth/synth_defs.h"
|
#include "synth/synth_defs.h"
|
||||||
#include "JaySynthSound.h"
|
#include "JaySynthSound.h"
|
||||||
#include "JaySynth.h"
|
#include "JaySynth.h"
|
||||||
#include "JK_MidiClock.h"
|
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
/**
|
/**
|
||||||
@@ -354,7 +353,6 @@ private:
|
|||||||
JaySynthMidiCC midCC_editBuffer;
|
JaySynthMidiCC midCC_editBuffer;
|
||||||
int m_limiter_active;
|
int m_limiter_active;
|
||||||
synth_float_t m_limiter_env;
|
synth_float_t m_limiter_env;
|
||||||
JK_MidiClock m_JK_MidiClock;
|
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JaySynthAudioProcessor);
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JaySynthAudioProcessor);
|
||||||
|
|||||||
Reference in New Issue
Block a user