Created
December 9, 2017 21:12
-
-
Save adamski/b42eb8f91910fe28a017458e2edad5d3 to your computer and use it in GitHub Desktop.
JUCE MTC Classes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// MTCEmitter.cpp | |
// XDAT | |
// | |
// Created by Arvid Rosén on 2012-02-09. | |
// Copyright (c) 2012 Arvid Rosén. All rights reserved. | |
// | |
#include <iostream> | |
#include "MTCEmitter.h" | |
#include "SMPTE.h" | |
AbstractFifo SMPTE::fifo(1024); | |
double SMPTE::positionInSeconds(0.0); | |
MidiMessage::SmpteTimecodeType SMPTE::type(MidiMessage::fps30); | |
double SMPTE::fps(30); | |
int SMPTE::buffer[] = {}; | |
MTCEmitter::MTCEmitter() | |
:Thread("SMPTE Updater") | |
{ | |
smpteTimeCodeType = MidiMessage::fps30; | |
framesPerSecond = 30; | |
interval = (1000.0/(4.0*framesPerSecond)); | |
delta = 0.0; | |
currentQuarterFrame = 0; | |
backwards = false; | |
SMPTE::init(smpteTimeCodeType); | |
} | |
MTCEmitter::~MTCEmitter() | |
{ | |
stopThread(2000); | |
} | |
bool MTCEmitter::open(const int devNum=0) | |
{ | |
midiOutput = MidiOutput::openDevice(devNum); | |
DBG("Opening MTC device: " << MidiOutput::getDevices()[devNum]); | |
if(!midiOutput) | |
return false; | |
reset(); | |
return true; | |
} | |
bool MTCEmitter::start() | |
{ | |
startThread(6); | |
if(!midiOutput) | |
return false; | |
return true; | |
} | |
void MTCEmitter::stop() | |
{ | |
if(!midiOutput) | |
return; | |
stopThread(500); | |
} | |
void MTCEmitter::run() | |
{ | |
while (! threadShouldExit()) | |
{ | |
// sleep a bit so the threads don't all grind the CPU to a halt.. | |
const int timeToWait = roundToInt(interval + delta); | |
delta = timeToWait - interval; | |
wait (timeToWait); | |
midiOutput->sendMessageNow(nextMidiMsg); | |
postDisplayUpdate(); | |
prepareNextMessage(false); | |
} | |
} | |
// Post message to display component, to update its SMPTE display. | |
void MTCEmitter::postDisplayUpdate() | |
{ | |
int hour,min,sec,frame; | |
SMPTE::getSMPTEValue(hour, min, sec, frame); | |
SMPTEMessage *msg = new SMPTEMessage(); | |
msg->intParameter1 = hour; | |
msg->intParameter2 = (min<<8) + sec; | |
msg->intParameter3 = frame; | |
displayOwner->postMessage(msg); | |
} | |
// Send full MTC goto message | |
void MTCEmitter::sendGoto() | |
{ | |
prepareNextMessage(true); | |
midiOutput->sendMessageNow(nextMidiMsg); | |
postDisplayUpdate(); | |
} | |
// Re-init SMPTE counter and update possition | |
void MTCEmitter::reset() | |
{ | |
SMPTE::init(smpteTimeCodeType); | |
// Send full message | |
sendGoto(); | |
} | |
void MTCEmitter::prepareNextMessage(const bool fullFrame=false) | |
{ | |
int val; | |
int hour,min,sec,frame; | |
if(fullFrame) { | |
SMPTE::update(samplerate); | |
SMPTE::getSMPTEValue(hour, min, sec, frame); | |
nextMidiMsg = MidiMessage::fullFrame(hour, min, sec, frame, smpteTimeCodeType); | |
} else { | |
SMPTE::getSMPTEValue(hour, min, sec, frame); | |
switch(currentQuarterFrame) { | |
case 0: val = frame & 0x0F; break; | |
case 1: val = (frame & 0x10) >> 4; break; | |
case 2: val = sec & 0x0F; break; | |
case 3: val = (sec & 0x30) >> 4; break; | |
case 4: val = min & 0x0F; break; | |
case 5: val = (min & 0x30) >> 4; break; | |
case 6: val = hour & 0x0F; break; | |
case 7: val = ((hour & 0x10) >> 4) | ((SMPTE::type & 0x03) << 1); break; | |
} | |
nextMidiMsg = MidiMessage::quarterFrame(currentQuarterFrame, val); | |
if(backwards) | |
currentQuarterFrame--; | |
else | |
currentQuarterFrame++; | |
if(currentQuarterFrame < 0) { | |
currentQuarterFrame = 7; | |
} | |
if(currentQuarterFrame > 7) { | |
currentQuarterFrame = 0; | |
SMPTE::update(samplerate); | |
} | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// MTCEmitter.h | |
// XDAT | |
// | |
// Created by Arvid Rosén on 2012-02-09. | |
// Copyright (c) 2012 Arvid Rosén. All rights reserved. | |
// | |
#ifndef XDAT_MTCEmitter_h | |
#define XDAT_MTCEmitter_h | |
#include "../JuceLibraryCode/JuceHeader.h" | |
class MTCEmitter: public Thread | |
{ | |
public: | |
MTCEmitter(); | |
~MTCEmitter(); | |
bool start(); | |
void stop(); | |
bool open(const int devNum); | |
void reset(); | |
void sendGoto(); | |
void setSamplerate(const double samplerate_) { samplerate = samplerate_; } | |
void prepareNextMessage(const bool fullFrame); | |
void run(); | |
void postDisplayUpdate(); | |
MessageListener *displayOwner; | |
private: | |
MidiMessage nextMidiMsg; | |
ScopedPointer<MidiOutput> midiOutput; | |
int currentQuarterFrame; | |
float interval; | |
float delta; | |
bool backwards; | |
double samplerate; | |
double framesPerSecond; | |
MidiMessage::SmpteTimecodeType smpteTimeCodeType; | |
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MTCEmitter); | |
}; | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// SMPTE.h | |
// XDAT | |
// | |
// Created by Arvid Rosén on 2012-02-28. | |
// Copyright (c) 2012 Arvid Rosén. All rights reserved. | |
// | |
#ifndef XDAT_SMPTE_h | |
#define XDAT_SMPTE_h | |
#include "../JuceLibraryCode/JuceHeader.h" | |
class SMPTEMessage: public Message | |
{ | |
public: | |
int intParameter1; | |
int intParameter2; | |
int intParameter3; | |
}; | |
class SMPTE | |
{ | |
public: | |
static MidiMessage::SmpteTimecodeType type; | |
void static init(const MidiMessage::SmpteTimecodeType type_) | |
{ | |
positionInSeconds = 0.0; | |
fifo.reset(); | |
if(type==MidiMessage::fps30) | |
fps = 30; | |
if(type==MidiMessage::fps25) | |
fps = 25; | |
if(type==MidiMessage::fps24) | |
fps = 24; | |
} | |
// Add samples that are not yet used to update the current time | |
static void addSamples (const int samples) | |
{ | |
int start1, size1, start2, size2; | |
fifo.prepareToWrite (1, start1, size1, start2, size2); | |
if (size1 > 0) | |
buffer[start1]=samples; | |
if (size2 > 0) | |
buffer[start2]=samples; | |
fifo.finishedWrite (size1 + size2); | |
} | |
// Update time using the added samples | |
static void update (const double samplerate) | |
{ | |
if(samplerate <= 0) | |
return; | |
int start1, size1, start2, size2; | |
int newSamples=0; | |
fifo.prepareToRead (fifo.getNumReady(), start1, size1, start2, size2); | |
for(int i=0; i<size1; i++) | |
newSamples += buffer[start1+i]; | |
for(int i=0; i<size2; i++) | |
newSamples += buffer[start2+i]; | |
fifo.finishedRead (size1 + size2); | |
positionInSeconds += double(newSamples)/samplerate; | |
} | |
static void getSMPTEValue(int &hour, int &min, int &sec, int &frames) | |
{ | |
double tmp = positionInSeconds; | |
int q; | |
q = floor(tmp/(3600.0)); | |
tmp -= q*3600.0; | |
hour = q; | |
q = floor(tmp/(60.0)); | |
tmp -= q*60.0; | |
min = q; | |
q = floor(tmp); | |
tmp -= q; | |
sec = q; | |
frames = floor(tmp*fps); | |
} | |
static void setSMPTEValue(const int hour, const int min, const int sec, const int frames) | |
{ | |
positionInSeconds = hour*3600 + min*60 + sec + double(frames)/fps; | |
} | |
static int getNumReady() | |
{ | |
return fifo.getNumReady(); | |
} | |
static double getPosition() | |
{ | |
return positionInSeconds; | |
} | |
private: | |
static double positionInSeconds; | |
static double fps; | |
static AbstractFifo fifo; | |
static int buffer[1024]; | |
}; | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Cool stuff! Do you have an updated version of this for JUCE7? A usage example in modern JUCE would also be great. In particular I'm curious with regard to
MTCEmitter::open(...)
and its call toMidiOutput::openDevice(devNum)
, which is now deprecated in JUCE7.