This commit is contained in:
Peli de Halleux 2017-04-23 22:43:51 -07:00
Родитель 52fcfbd6f8
Коммит f73a4ebb41
7 изменённых файлов: 8 добавлений и 488 удалений

Просмотреть файл

@ -33,257 +33,13 @@ const uint8_t midiServiceUuid[] = {
0xa7, 0x51, 0x6c, 0xe3, 0x4e, 0xc4, 0xc7, 0x00
};
void BluetoothMIDIService::onDataWritten(const GattWriteCallbackParams *params) {
if (midiCharacteristicHandle != params->handle)
return;
uint16_t length;
ble.readCharacteristicValue(midiCharacteristicHandle, rxBuffer, &length);
if (length > 1) {
// parse BLE message
uint8_t header = rxBuffer[0];
for (int i = 1; i < length; i++) {
uint8_t midiEvent = rxBuffer[i];
if (midiState == MIDI_STATE_TIMESTAMP) {
if ((midiEvent & 0x80) == 0) {
// running status
midiState = MIDI_STATE_WAIT;
}
if (midiEvent == 0xf7) {
// maybe error
midiState = MIDI_STATE_TIMESTAMP;
continue;
}
}
if (midiState == MIDI_STATE_TIMESTAMP) {
timestamp = ((header & 0x3f) << 7) | (midiEvent & 0x7f);
midiState = MIDI_STATE_WAIT;
} else if (midiState == MIDI_STATE_WAIT) {
switch (midiEvent & 0xf0) {
case 0xf0: {
switch (midiEvent) {
case 0xf0:
sysExBuffer[sysExBufferPos++] = midiEvent;
midiState = MIDI_STATE_SIGNAL_SYSEX;
break;
case 0xf1:
case 0xf3:
// 0xf1 MIDI Time Code Quarter Frame. : 2bytes
// 0xf3 Song Select. : 2bytes
midiEventKind = midiEvent;
midiState = MIDI_STATE_SIGNAL_2BYTES_2;
break;
case 0xf2:
// 0xf2 Song Position Pointer. : 3bytes
midiEventKind = midiEvent;
midiState = MIDI_STATE_SIGNAL_3BYTES_2;
break;
case 0xf6:
// 0xf6 Tune Request : 1byte
onTuneRequest();
midiState = MIDI_STATE_TIMESTAMP;
break;
case 0xf8:
// 0xf8 Timing Clock : 1byte
onTimingClock();
midiState = MIDI_STATE_TIMESTAMP;
break;
case 0xfa:
// 0xfa Start : 1byte
onStart();
midiState = MIDI_STATE_TIMESTAMP;
break;
case 0xfb:
// 0xfb Continue : 1byte
onContinue();
midiState = MIDI_STATE_TIMESTAMP;
break;
case 0xfc:
// 0xfc Stop : 1byte
onStop();
midiState = MIDI_STATE_TIMESTAMP;
break;
case 0xfe:
// 0xfe Active Sensing : 1byte
onActiveSensing();
midiState = MIDI_STATE_TIMESTAMP;
break;
case 0xff:
// 0xff Reset : 1byte
onReset();
midiState = MIDI_STATE_TIMESTAMP;
break;
default:
break;
}
}
break;
case 0x80:
case 0x90:
case 0xa0:
case 0xb0:
case 0xe0:
// 3bytes pattern
midiEventKind = midiEvent;
midiState = MIDI_STATE_SIGNAL_3BYTES_2;
break;
case 0xc0: // program change
case 0xd0: // channel after-touch
// 2bytes pattern
midiEventKind = midiEvent;
midiState = MIDI_STATE_SIGNAL_2BYTES_2;
break;
default:
// 0x00 - 0x70: running status
if ((midiEventKind & 0xf0) != 0xf0) {
// previous event kind is multi-bytes pattern
midiEventNote = midiEvent;
midiState = MIDI_STATE_SIGNAL_3BYTES_3;
}
break;
}
} else if (midiState == MIDI_STATE_SIGNAL_2BYTES_2) {
switch (midiEventKind & 0xf0) {
// 2bytes pattern
case 0xc0: // program change
midiEventNote = midiEvent;
onProgramChange(midiEventKind & 0xf, midiEventNote);
midiState = MIDI_STATE_TIMESTAMP;
break;
case 0xd0: // channel after-touch
midiEventNote = midiEvent;
onChannelAftertouch(midiEventKind & 0xf, midiEventNote);
midiState = MIDI_STATE_TIMESTAMP;
break;
case 0xf0: {
switch (midiEventKind) {
case 0xf1:
// 0xf1 MIDI Time Code Quarter Frame. : 2bytes
midiEventNote = midiEvent;
onTimeCodeQuarterFrame(midiEventNote);
midiState = MIDI_STATE_TIMESTAMP;
break;
case 0xf3:
// 0xf3 Song Select. : 2bytes
midiEventNote = midiEvent;
onSongSelect(midiEventNote);
midiState = MIDI_STATE_TIMESTAMP;
break;
default:
// illegal state
midiState = MIDI_STATE_TIMESTAMP;
break;
}
}
break;
default:
// illegal state
midiState = MIDI_STATE_TIMESTAMP;
break;
}
} else if (midiState == MIDI_STATE_SIGNAL_3BYTES_2) {
switch (midiEventKind & 0xf0) {
case 0x80:
case 0x90:
case 0xa0:
case 0xb0:
case 0xe0:
case 0xf0:
// 3bytes pattern
midiEventNote = midiEvent;
midiState = MIDI_STATE_SIGNAL_3BYTES_3;
break;
default:
// illegal state
midiState = MIDI_STATE_TIMESTAMP;
break;
}
} else if (midiState == MIDI_STATE_SIGNAL_3BYTES_3) {
switch (midiEventKind & 0xf0) {
// 3bytes pattern
case 0x80: // note off
midiEventVelocity = midiEvent;
onNoteOff(midiEventKind & 0xf, midiEventNote, midiEventVelocity);
midiState = MIDI_STATE_TIMESTAMP;
break;
case 0x90: // note on
midiEventVelocity = midiEvent;
if (midiEventVelocity == 0) {
onNoteOff(midiEventKind & 0xf, midiEventNote, midiEventVelocity);
} else {
onNoteOn(midiEventKind & 0xf, midiEventNote, midiEventVelocity);
}
midiState = MIDI_STATE_TIMESTAMP;
break;
case 0xa0: // control polyphonic key pressure
midiEventVelocity = midiEvent;
onPolyphonicAftertouch(midiEventKind & 0xf, midiEventNote, midiEventVelocity);
midiState = MIDI_STATE_TIMESTAMP;
break;
case 0xb0: // control change
midiEventVelocity = midiEvent;
onControlChange(midiEventKind & 0xf, midiEventNote, midiEventVelocity);
midiState = MIDI_STATE_TIMESTAMP;
break;
case 0xe0: // pitch bend
midiEventVelocity = midiEvent;
onPitchWheel(midiEventKind & 0xf, (midiEventNote & 0x7f) | ((midiEventVelocity & 0x7f) << 7));
midiState = MIDI_STATE_TIMESTAMP;
break;
case 0xf0: // Song Position Pointer.
midiEventVelocity = midiEvent;
onSongPositionPointer((midiEventNote & 0x7f) | ((midiEventVelocity & 0x7f) << 7));
midiState = MIDI_STATE_TIMESTAMP;
break;
default:
// illegal state
midiState = MIDI_STATE_TIMESTAMP;
break;
}
} else if (midiState == MIDI_STATE_SIGNAL_SYSEX) {
if (midiEvent == 0xf7) {
// the end of message
// last written uint8_t is for timestamp
if (sysExBufferPos > 0) {
sysExBuffer[sysExBufferPos - 1] = midiEvent;
onSystemExclusive(sysExBuffer, sysExBufferPos, false);
}
sysExBufferPos = 0;
midiState = MIDI_STATE_TIMESTAMP;
} else {
if (sysExBufferPos == 128) {
onSystemExclusive(sysExBuffer, sysExBufferPos, true);
sysExBufferPos = 0;
}
sysExBuffer[sysExBufferPos++] = midiEvent;
}
}
}
}
}
BluetoothMIDIService::BluetoothMIDIService(BLEDevice *dev): ble(*dev) {
sysExBufferPos = 0;
timestamp = 0;
midiEventKind = 0;
midiEventNote = 0;
midiEventVelocity = 0;
memset(midi, 0, sizeof(midi));
memset(sysExBuffer, 0, sizeof(sysExBuffer));
memset(rxBuffer, 0, sizeof(rxBuffer));
GattCharacteristic midiCharacteristic(midiCharacteristicUuid, midi, 0, sizeof(midi),
GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE
| GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE
| GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ
GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ
| GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY
);
GattCharacteristic *midiChars[] = {&midiCharacteristic};
@ -296,7 +52,6 @@ BluetoothMIDIService::BluetoothMIDIService(BLEDevice *dev): ble(*dev) {
midiCharacteristicHandle = midiCharacteristic.getValueHandle();
// ble.onDataWritten(this, &BluetoothMIDIService::onDataWritten);
tick.start();
}

Просмотреть файл

@ -38,237 +38,18 @@ public:
*/
bool connected();
/**
* Attach a callback called when the `Tune Request` event is received
*
* @param ptr function pointer
* prototype: void onTuneRequest();
*/
inline void attachTuneRequest(void (*fn)()) {
onTuneRequest = fn;
}
/**
* Attach a callback called when the `Timing Clock` event is received
*
* @param ptr function pointer
* prototype: void onTimingClock();
*/
inline void attachTimingClock(void (*fn)()) {
onTimingClock = fn;
}
/**
* Attach a callback called when the `Start` event is received
*
* @param ptr function pointer
* prototype: void onStart();
*/
inline void attachStart(void (*fn)()) {
onStart = fn;
}
/**
* Attach a callback called when the `Continue` event is received
*
* @param ptr function pointer
* prototype: void onContinue();
*/
inline void attachContinue(void (*fn)()) {
onContinue = fn;
}
/**
* Attach a callback called when the `Stop` event is received
*
* @param ptr function pointer
* prototype: void onStop();
*/
inline void attachStop(void (*fn)()) {
onStop = fn;
}
/**
* Attach a callback called when the `Active Sensing` event is received
*
* @param ptr function pointer
* prototype: void onActiveSensing();
*/
inline void attachActiveSensing(void (*fn)()) {
onActiveSensing = fn;
}
/**
* Attach a callback called when the `Reset` event is received
*
* @param ptr function pointer
* prototype: void onReset();
*/
inline void attachReset(void (*fn)()) {
onReset = fn;
}
/**
* Attach a callback called when the `Program Change` event is received
*
* @param ptr function pointer
* prototype: void onProgramChange(uint8_t channel, uint8_t program);
*/
inline void attachnProgramChange(void (*fn)(uint8_t, uint8_t)) {
onProgramChange = fn;
}
/**
* Attach a callback called when the `Channel Aftertouch` event is received
*
* @param ptr function pointer
* prototype: void onChannelAftertouch(uint8_t channel, uint8_t pressure);
*/
inline void attachChannelAftertouch(void (*fn)(uint8_t, uint8_t)) {
onChannelAftertouch = fn;
}
/**
* Attach a callback called when the `Time Code Quarter Frame` event is received
*
* @param ptr function pointer
* prototype: void onTimeCodeQuarterFrame(uint8_t timing);
*/
inline void attachTimeCodeQuarterFrame(void (*fn)(uint8_t)) {
onTimeCodeQuarterFrame = fn;
}
/**
* Attach a callback called when the `Song Select` event is received
*
* @param ptr function pointer
* prototype: void onSongSelect(uint8_t song);
*/
inline void attachSongSelect(void (*fn)(uint8_t)) {
onSongSelect = fn;
}
/**
* Attach a callback called when the `Note Off` event is received
*
* @param ptr function pointer
* prototype: void onNoteOff(uint8_t channel, uint8_t note, uint8_t velocity);
*/
inline void attachNoteOff(void (*fn)(uint8_t, uint8_t, uint8_t)) {
onNoteOff = fn;
}
/**
* Attach a callback called when the `Note On` event is received
*
* @param ptr function pointer
* prototype: void onNoteOn(uint8_t channel, uint8_t note, uint8_t velocity);
*/
inline void attachNoteOn(void (*fn)(uint8_t, uint8_t, uint8_t)) {
onNoteOn = fn;
}
/**
* Attach a callback called when the `Polyphonic Aftertouch` event is received
*
* @param ptr function pointer
* prototype: void onPolyphonicAftertouch(uint8_t channel, uint8_t note, uint8_t pressure);
*/
inline void attachPolyphonicAftertouch(void (*fn)(uint8_t, uint8_t, uint8_t)) {
onPolyphonicAftertouch = fn;
}
/**
* Attach a callback called when the `Control Change` event is received
*
* @param ptr function pointer
* prototype: void onControlChange(uint8_t channel, uint8_t function, uint8_t value);
*/
inline void attachControlChange(void (*fn)(uint8_t, uint8_t, uint8_t)) {
onControlChange = fn;
}
/**
* Attach a callback called when the `Pitch Wheel` event is received
*
* @param ptr function pointer
* prototype: void onPitchWheel(uint8_t channel, uint16_t amount);
*/
inline void attachPitchWheel(void (*fn)(uint8_t, uint16_t)) {
onPitchWheel = fn;
}
/**
* Attach a callback called when the `Song Position Pointer` event is received
*
* @param ptr function pointer
* prototype: void onSongPositionPointer(uint16_t position);
*/
inline void attachSongPositionPointer(void (*fn)(uint16_t)) {
onSongPositionPointer = fn;
}
/**
* Attach a callback called when the `System Exclusive` event is received
*
* @param ptr function pointer
* prototype: void onSystemExclusive(uint8_t *sysex, uint16_t length, bool hasNextData);
*/
inline void attachSystemExclusive(void (*fn)(uint8_t *, uint16_t, bool)) {
onSystemExclusive = fn;
}
void sendMidiMessage(uint8_t data0);
void sendMidiMessage(uint8_t data0, uint8_t data1);
void sendMidiMessage(uint8_t data0, uint8_t data1, uint8_t data2);
private:
uint16_t sysExBufferPos;
uint8_t sysExBuffer[128];
private:
uint16_t timestamp;
uint8_t midiEventKind;
uint8_t midiEventNote;
uint8_t midiEventVelocity;
uint8_t midi[20];
uint8_t rxBuffer[20];
BLEDevice &ble;
GattAttribute::Handle_t midiCharacteristicHandle;
Timer tick;
enum MIDI_STATE {
MIDI_STATE_TIMESTAMP = 0,
MIDI_STATE_WAIT,
MIDI_STATE_SIGNAL_2BYTES_2,
MIDI_STATE_SIGNAL_3BYTES_2,
MIDI_STATE_SIGNAL_3BYTES_3,
MIDI_STATE_SIGNAL_SYSEX
};
MIDI_STATE midiState;
void (*onTuneRequest)();
void (*onTimingClock)();
void (*onStart)();
void (*onContinue)();
void (*onStop)();
void (*onActiveSensing)();
void (*onReset)();
void (*onProgramChange)(uint8_t, uint8_t);
void (*onChannelAftertouch)(uint8_t, uint8_t);
void (*onTimeCodeQuarterFrame)(uint8_t);
void (*onSongSelect)(uint8_t);
void (*onNoteOff)(uint8_t, uint8_t, uint8_t);
void (*onNoteOn)(uint8_t, uint8_t, uint8_t);
void (*onPolyphonicAftertouch)(uint8_t, uint8_t, uint8_t);
void (*onControlChange)(uint8_t, uint8_t, uint8_t);
void (*onPitchWheel)(uint8_t, uint16_t);
void (*onSongPositionPointer)(uint16_t);
void (*onSystemExclusive)(uint8_t *, uint16_t, bool);
void onDataWritten(const GattWriteCallbackParams *params);
};
#endif /* __BLEMIDI_H__ */

Просмотреть файл

@ -24,8 +24,9 @@ Please refer to that project documentation for further details.
## Apps
Any app that supports a MIDI keyboard or instrument should work.
* iPhone/iPad: [Apple GarageBand](https://itunes.apple.com/us/app/garageband/id408709785?mt=8)
* iPhone/iPad: [KORG Module Le](https://itunes.apple.com/us/app/korg-module-le/id1048875111)
## Supported targets

Просмотреть файл

@ -8,7 +8,7 @@ namespace bluetooth {
function send(buffer: Buffer) {
bluetooth.midiSendMessage(buffer);
}
midi.setInputTransport(send);
midi.setTransport(send);
bluetooth.midiSendMessage(pins.createBuffer(0));
}

17
enums.d.ts поставляемый
Просмотреть файл

@ -1,21 +1,4 @@
// Auto-generated. Do not edit.
/**
* Attach a callback called when the `System Exclusive` event is received
*
* @param ptr function pointer
* prototype: void onSystemExclusive(uint8_t *sysex, uint16_t length, bool hasNextData);
*/
declare enum MIDI_STATE {
MIDI_STATE_TIMESTAMP = 0,
MIDI_STATE_WAIT = 1,
MIDI_STATE_SIGNAL_2BYTES_2 = 2,
MIDI_STATE_SIGNAL_3BYTES_2 = 3,
MIDI_STATE_SIGNAL_3BYTES_3 = 4,
MIDI_STATE_SIGNAL_SYSEX = 5,
}
declare namespace bluetooth {
}

Просмотреть файл

@ -1,12 +1,12 @@
{
"name": "bluetooth-midi",
"version": "1.1.13",
"version": "2.0.0",
"description": "A Bluetooth MIDI service",
"license": "MIT",
"dependencies": {
"core": "*",
"bluetooth": "*",
"midi": "github:Microsoft/pxt-midi#v1.1.6"
"midi": "github:Microsoft/pxt-midi#v2.0.1"
},
"files": [
"README.md",

Просмотреть файл

@ -8,7 +8,7 @@ bluetooth.startMidiService();
basic.showString("S")
basic.forever(() => {
midi.inputChannel(1).pitchBend(Math.abs(input.acceleration(Dimension.X)));
midi.pitchBend(Math.abs(input.acceleration(Dimension.X)));
basic.pause(50);
})
input.onButtonPressed(Button.A, () => {