-
-
Notifications
You must be signed in to change notification settings - Fork 4
/
midi.hpp
235 lines (190 loc) · 7.45 KB
/
midi.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
// SPDX-License-Identifier: GPL-3.0-or-later
//
// Copyright (c) 2024 plan44.ch / Lukas Zeller, Zurich, Switzerland
//
// Author: Lukas Zeller <[email protected]>
//
// This file is part of p44utils.
//
// p44utils is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// p44utils is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with p44utils. If not, see <http://www.gnu.org/licenses/>.
//
#ifndef __p44utils__midi__
#define __p44utils__midi__
#include "p44utils_main.hpp"
#ifndef ENABLE_MIDI
// We assume that including this file in a build usually means that modbus support is actually needed.
// Still, ENABLE_MODBUS can be set to 0 to create build variants w/o removing the file from the project/makefile
#define ENABLE_MIDI 1
#endif
#if ENABLE_MIDI
#include "serialcomm.hpp"
#include <stdio.h>
#if ENABLE_P44SCRIPT && !defined(ENABLE_MIDI_SCRIPT_FUNCS)
#define ENABLE_MIDI_SCRIPT_FUNCS 1
#endif
#if ENABLE_MIDI_SCRIPT_FUNCS && !ENABLE_P44SCRIPT
#error "ENABLE_P44SCRIPT required when ENABLE_MIDI_SCRIPT_FUNCS is set"
#endif
#if ENABLE_MIDI_SCRIPT_FUNCS
#include "p44script.hpp"
#endif
using namespace std;
namespace p44 {
typedef enum : uint8_t {
none = 0,
statusbit = 0x80,
data_mask = 0x7F,
// channel voice commands
cvcmd_mask = 0xF0,
channel_mask = 0x0F,
filter_note_on_off = 0x89, // filter only: note_on or note_off
note_off = 0x80,
note_on = 0x90,
note_mask = 0xE0,
poly_key_pressure = 0xA0,
control_change = 0xB0, // also channel mode
program_change = 0xC0, // has only 1 data byte
channel_pressure = 0xD0, // has only 1 data byte
pitch_bend = 0xE0,
channel_cmd_max = pitch_bend,
// system commands
system = 0xF0,
// - system common
system_common_mask = 0xF8,
system_common_prefix = 0xF0,
time_code_qf = 0xF1, // MIDI Time Code Quarter Frame
song_position_ptr = 0xF2, // Song Position Pointer
song_select = 0xF3, // Song Select
tune_request = 0xF6, // Tune Request
// - system exclusive
system_exclusive = 0xF0,
system_eox = 0xF7, // EOX (End of Exclusive)
// - system real time
system_real_time_mask = 0xF8,
system_real_time_prefix = 0xF8,
timing_clock = 0xF8, // Timing Clock
start = 0xFA, // Start
cont = 0xFB, // Continue
stop = 0xFC, // Stop
active_sensing = 0xFE, // Active Sensing
system_reset = 0xFF // System Reset
} MidiStatus;
typedef struct {
MidiStatus status;
uint8_t key; ///< note or controller number. 0 for messages without either
uint16_t value; ///< 14bit for pitch bend, control 0..31 (LSB 32..63), song position pointer, 7 bit for all others
} MidiMessage;
/// Callback executed when midi data arrives
/// @param aMidiData the midi data received
typedef boost::function<void (MidiMessage aMidiData)> MidiDataCB;
#if ENABLE_MIDI_SCRIPT_FUNCS
namespace P44Script {
class MidiBusObj;
typedef boost::intrusive_ptr<MidiBusObj> MidiBusObjPtr;
}
#endif
class MidiBus : public P44LoggingObj
{
typedef P44LoggingObj inherited;
#if ENABLE_MIDI_SCRIPT_FUNCS
P44Script::MidiBusObjPtr mRepresentingObj; ///< the (singleton) ScriptObj representing this midi interface
#endif
SerialCommPtr mMidiDevice;
MidiDataCB mMidiDataCB;
MidiStatus mLastReceivedStatus; ///< last status received for running status
MidiStatus mLastSentStatus; ///< last status sent for sending with running status
static const int numChannels = 16;
static const int num14BitControls = 32;
uint8_t mControlMSBCache[numChannels*num14BitControls]; ///< cache for MSBs of all 32 14bit controls in all 16 channels
uint8_t mFirstData; ///< first (maybe only) data byte
uint8_t mFinalData; ///< second (or only, in that case a copy of mFirstData) data byte
enum {
idle, // waiting for status
running, // running status, new status or data
initialData, // waiting for first or final data byte
secondData, // waiting for second and final data byte
sysex, // system exclusive, just swallow data
} mReceiveState;
public:
MidiBus();
virtual ~MidiBus();
/// @return the object type (used for context descriptions such as logging context)
virtual string contextType() const P44_OVERRIDE { return "midi bus"; };
/// open a midi interface device
/// @param aMidiConnectionSpec the connection specification
/// (usually a simple /dev/xxx, but can also be a IP socket level connection)
/// @return error, if any
ErrorPtr open(const string aMidiConnectionSpec);
/// close the midi interface
virtual void close();
/// set a midi data handler
/// @param aMidiDataCB is called whenever midi data is received
void setMidiDataHandler(MidiDataCB aMidiDataCB);
/// send midi data
/// @param aMidiMessage midi message to be sent
/// @param aRunningStatus if set, and previous command allows, running status
/// (not sending the status/command byte again) will be used
/// @param aSysExData if not null, this is system_exclusive data. Command must be actually system_exclusive to send this.
/// A system_eox will be automatically appended
ErrorPtr sendMidi(const MidiMessage& aMidiMessage, bool aRunningStatus, const string* aSysExData = nullptr);
#if ENABLE_MIDI_SCRIPT_FUNCS
/// @return a singleton script object, representing this midi bus, which can be registered as named member in a scripting domain
P44Script::MidiBusObjPtr representingScriptObj();
#endif
private:
uint8_t& controlMSBCache(MidiStatus aStatus, uint8_t aControlNumber);
void midiDataHandler(ErrorPtr aStatus);
void processMidiCommand();
};
typedef boost::intrusive_ptr<MidiBus> MidiBusPtr;
#if ENABLE_MIDI_SCRIPT_FUNCS
namespace P44Script {
/// represents a midi message
class MidiMessageObj : public ScriptObj
{
typedef ScriptObj inherited;
MidiMessage mMessage;
public:
MidiMessageObj(const MidiMessage& aMessage) : mMessage(aMessage) {};
virtual string getAnnotation() const P44_OVERRIDE { return "midi message"; };
virtual ScriptObjPtr actualValue() const P44_OVERRIDE;
const MidiMessage& message() { return mMessage; };
};
/// represents a midi bus
class MidiBusObj : public StructuredLookupObject, public EventSource
{
typedef StructuredLookupObject inherited;
friend class p44::MidiBus;
MidiBusPtr mMidiBus;
public:
MidiBusObj(MidiBusPtr aMidiBus);
virtual ~MidiBusObj();
virtual void deactivate() P44_OVERRIDE;
virtual string getAnnotation() const P44_OVERRIDE { return "midi bus"; };
MidiBusPtr midibus() { return mMidiBus; }
private:
void gotMessage(const MidiMessage &aMessage);
};
/// represents the global objects related to midi
class MidiLookup : public BuiltInMemberLookup
{
typedef BuiltInMemberLookup inherited;
public:
MidiLookup();
};
} // namespace P44Script
#endif // ENABLE_MIDI_SCRIPT_FUNCS
} // namespace p44
#endif // ENABLE_MIDI
#endif // __p44utils__midi__