Standalone MIDI Clock Project

Status
Not open for further replies.

jama

Member
Hello!

I'm new to using teensy/arduino, and I’m really excited about starting a project that is a teensy-controlled MIDI standalone clock (not USB MIDI). Essentially, I want to be able to “turn it on” and begin looping, sending out a MIDI clock at a rate set by a potentiometer. It will include a pause button that acts as a momentary killswitch. I’ve gotten to a point in the process where I’m totally stumped. Attached is my code, any help anyone can provide would be awesome.

Note: I'm brand new to this so it could be riddled with mistakes/incorrect language/unnecessary lines!

View attachment MIDI_ctrl_ard.ino
 
edit - nevermind... just not sure why contstrain instead of map for valx.
 
Last edited:
I see you're using the MIDI library, but nowhere in your code actually seems to make use of it after MIDI.begin(). Is that intentional? You're observing all those Serial.print() outputs in the Arduino Serial Monitor, right?

One of the most common mistakes when adapting code from other Arduino boards like Uno is use of Serial. On Teensy, "Serial" sends to the USB port where you see the output in the Arduino Serial Monitor. To send to the first hardware serial port (pins 0 & 1), you use Serial1. The MIDI library should do that automatically. But if you want to send bytes manually as MIDI, use Serial1, not Serial.

Also, for reading pushbuttons, use the Bounce library. Then you won't need delays which just slow your program's response to other stuff. Look at File > Examples > Teensy > USB_MIDI > Buttons for a quick example.

For pushbuttons, usually they connect between a pin and ground, and usually you configure the pin with INPUT_PULLUP. I see you have only INPUT mode. That's fine if you've got real resistors connected (we can't see your circuit without a photo or diagram). If you don't have resistors, you'll almost certainly need to use INPUT_PULLUP for the buttons to work properly.
 
I'm simply unfamiliar with the library, I have some studying to do. Thanks for the insights! I need to debounce my buttons it seems, also I am using real resistors.
 
Use the Bounce library. Debouncing seems simple at first, but consider that library had many revisions years ago before it really worked well. Many people's projects have wasted a lot of time fiddling with almost-but-not-quite perfect debouncing code. I highly recommend using the well tested Bounce library. It's easy to learn and in the long run saves a lot of work with troubleshooting.
 
I see you're using the MIDI library, but nowhere in your code actually seems to make use of it after MIDI.begin(). Is that intentional? You're observing all those Serial.print() outputs in the Arduino Serial Monitor, right?

One of the most common mistakes when adapting code from other Arduino boards like Uno is use of Serial. On Teensy, "Serial" sends to the USB port where you see the output in the Arduino Serial Monitor. To send to the first hardware serial port (pins 0 & 1), you use Serial1. The MIDI library should do that automatically. But if you want to send bytes manually as MIDI, use Serial1, not Serial.

//

Is there any protocol for MIDI clock in the MIDI library? Is there a way I can replicate it, if not? I'm trying to take the past of least resistance due to my level (or lack thereof) of knowledge
 
I think the library can be used to access the 'system common' and 'system real-time' MIDI messages needed to implement a MIDI clock but the documentation on Paul's page is not complete.

It looks like you can use: MIDI.getType();

Once you know the types not enumerated on PJRC page. For these messages the Type is the message so you don`t need the data getters.
Edit - sorry... you do need them for position info...
MIDI.getData1(); // LSB
MIDI.getData2(); //MSB



Code:
/*! Enumeration of MIDI types */
enum kMIDIType {
	NoteOff	              = 0x80,	///< Note Off
	NoteOn                = 0x90,	///< Note On
	AfterTouchPoly        = 0xA0,	///< Polyphonic AfterTouch
	ControlChange         = 0xB0,	///< Control Change / Channel Mode
	ProgramChange         = 0xC0,	///< Program Change
	AfterTouchChannel     = 0xD0,	///< Channel (monophonic) AfterTouch
	PitchBend             = 0xE0,	///< Pitch Bend
	SystemExclusive       = 0xF0,	///< System Exclusive
	TimeCodeQuarterFrame  = 0xF1,	///< System Common - MIDI Time Code Quarter Frame
	SongPosition          = 0xF2,	///< System Common - Song Position Pointer
	SongSelect            = 0xF3,	///< System Common - Song Select
	TuneRequest           = 0xF6,	///< System Common - Tune Request
	Clock                 = 0xF8,	///< System Real Time - Timing Clock
	Start                 = 0xFA,	///< System Real Time - Start
	Continue              = 0xFB,	///< System Real Time - Continue
	Stop                  = 0xFC,	///< System Real Time - Stop
	ActiveSensing         = 0xFE,	///< System Real Time - Active Sensing
	SystemReset           = 0xFF,	///< System Real Time - System Reset
	InvalidType           = 0x00    ///< For notifying errors
};
 
Last edited:
I keep forgetting whether you were reading or sending clock... (despite the thread title!)
...so given you are writing you need one of the send messages (which I had enumerated on a earlier attempt at this message but then deleted when I convinced myself you were making a display clock to show your DAWs clock value... )

For real-time and system-common messages there are routines equivalent to the basic ones shown on Paul's info-page.

Code:
	void sendNoteOn(byte NoteNumber,byte Velocity,byte Channel);
	void sendNoteOff(byte NoteNumber,byte Velocity,byte Channel);
	void sendProgramChange(byte ProgramNumber,byte Channel);
	void sendControlChange(byte ControlNumber, byte ControlValue,byte Channel);
	void sendPitchBend(int PitchValue,byte Channel);
	void sendPitchBend(unsigned int PitchValue,byte Channel);
	void sendPitchBend(double PitchValue,byte Channel);
	void sendPolyPressure(byte NoteNumber,byte Pressure,byte Channel);
	void sendAfterTouch(byte Pressure,byte Channel);
	void sendSysEx(int length, const byte *const array,bool ArrayContainsBoundaries = false);	
	void sendTimeCodeQuarterFrame(byte TypeNibble, byte ValuesNibble);
	void sendTimeCodeQuarterFrame(byte data);
	void sendSongPosition(unsigned int Beats);
	void sendSongSelect(byte SongNumber);
	void sendTuneRequest();
	void sendRealTime(kMIDIType Type);
	
	void send(kMIDIType type, byte param1, byte param2, byte channel);



Code:
/*! Enumeration of MIDI types */
enum kMIDIType {
	NoteOff	              = 0x80,	///< Note Off
	NoteOn                = 0x90,	///< Note On
	AfterTouchPoly        = 0xA0,	///< Polyphonic AfterTouch
	ControlChange         = 0xB0,	///< Control Change / Channel Mode
	ProgramChange         = 0xC0,	///< Program Change
	AfterTouchChannel     = 0xD0,	///< Channel (monophonic) AfterTouch
	PitchBend             = 0xE0,	///< Pitch Bend
	SystemExclusive       = 0xF0,	///< System Exclusive
	TimeCodeQuarterFrame  = 0xF1,	///< System Common - MIDI Time Code Quarter Frame
	SongPosition          = 0xF2,	///< System Common - Song Position Pointer
	SongSelect            = 0xF3,	///< System Common - Song Select
	TuneRequest           = 0xF6,	///< System Common - Tune Request
	Clock                 = 0xF8,	///< System Real Time - Timing Clock
	Start                 = 0xFA,	///< System Real Time - Start
	Continue              = 0xFB,	///< System Real Time - Continue
	Stop                  = 0xFC,	///< System Real Time - Stop
	ActiveSensing         = 0xFE,	///< System Real Time - Active Sensing
	SystemReset           = 0xFF,	///< System Real Time - System Reset
	InvalidType           = 0x00    ///< For notifying errors
};
 
Last edited:
Status
Not open for further replies.
Back
Top