Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

22354 fix midi out velocity and midi mac #24837

Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ tuning_t FluidSequencer::noteTuning(const mpe::NoteEvent& noteEvent, const int n

velocity_t FluidSequencer::noteVelocity(const mpe::NoteEvent& noteEvent) const
{
static constexpr midi::velocity_t MAX_SUPPORTED_VELOCITY = 127;
static constexpr midi::velocity_t MAX_SUPPORTED_VELOCITY = std::numeric_limits<midi::velocity_t>::max(); // jg: changed

const mpe::ExpressionContext& expressionCtx = noteEvent.expressionCtx();

Expand All @@ -300,7 +300,7 @@ velocity_t FluidSequencer::noteVelocity(const mpe::NoteEvent& noteEvent) const
}

dynamic_level_t dynamicLevel = expressionCtx.expressionCurve.maxAmplitudeLevel();
return expressionLevel(dynamicLevel);
return expressionLevel(dynamicLevel) << 9; // midi::Event::scaleUp(7,16)
}

int FluidSequencer::expressionLevel(const mpe::dynamic_level_t dynamicLevel) const
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,14 +160,44 @@ void FluidSynth::allNotesOff()
setControllerValue(i, midi::SUSTAIN_PEDAL_CONTROLLER, 0);
setPitchBend(i, 8192);
}

auto port = midiOutPort();
if (port->isConnected()) {
// Send all notes off to connected midi ports.
// Room for improvement:
// - We could record which groups/channels we sent something or which channels were scheduled in the sequencer.
// - We could send all events at once.
// Does i directly relate to midi channels? -> lastChannelIndex, otherwise use 16.
int upperBound = min(lastChannelIdx, 16);
for (int i = 0; i < upperBound; i++) {
muse::midi::Event e(muse::midi::Event::Opcode::ControlChange, muse::midi::Event::MessageType::ChannelVoice20);
e.setChannel(i);
e.setIndex(123); // CC#123 = All notes off
port->sendEvent(e);
}
for (int i = 0; i < upperBound; i++) {
muse::midi::Event e(muse::midi::Event::Opcode::ControlChange, muse::midi::Event::MessageType::ChannelVoice20);
e.setChannel(i);
e.setIndex(midi::SUSTAIN_PEDAL_CONTROLLER);
e.setData(0);
port->sendEvent(e);
}
for (int i = 0; i < upperBound; i++) {
muse::midi::Event e(muse::midi::Event::Opcode::PitchBend, muse::midi::Event::MessageType::ChannelVoice20);
e.setChannel(i);
e.setData(0x80000000);
port->sendEvent(e);
}
}
}

bool FluidSynth::handleEvent(const midi::Event& event)
{
int ret = FLUID_OK;
switch (event.opcode()) {
case Event::Opcode::NoteOn: {
ret = fluid_synth_noteon(m_fluid->synth, event.channel(), event.note(), event.velocity());
// fluid_synth_noteon expects 0...127
ret = fluid_synth_noteon(m_fluid->synth, event.channel(), event.note(), event.velocity7());
m_tuning.add(event.note(), event.pitchTuningCents());
} break;
case Event::Opcode::NoteOff: {
Expand All @@ -176,16 +206,16 @@ bool FluidSynth::handleEvent(const midi::Event& event)
} break;
case Event::Opcode::ControlChange: {
if (event.index() == muse::midi::EXPRESSION_CONTROLLER) {
ret = setExpressionLevel(event.data());
ret = setExpressionLevel(event.data7());
} else {
ret = setControllerValue(event.channel(), event.index(), event.data());
ret = setControllerValue(event.channel(), event.index(), event.data7());
}
} break;
case Event::Opcode::ProgramChange: {
fluid_synth_program_change(m_fluid->synth, event.channel(), event.program());
} break;
case Event::Opcode::PitchBend: {
ret = setPitchBend(event.channel(), event.data());
ret = setPitchBend(event.channel(), event.pitchBend14());
} break;
default: {
LOGD() << "not supported event type: " << event.opcodeString();
Expand Down
87 changes: 60 additions & 27 deletions src/framework/midi/internal/platform/osx/coremidiinport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ void CoreMidiInPort::initCore()
}

QString portName = "MuseScore MIDI input port";
static bool doLog = false; // kors::logger::Logger::instance()->isLevel(kors::logger::Level::Debug);
if (__builtin_available(macOS 11.0, *)) {
MIDIReceiveBlock receiveBlock = ^ (const MIDIEventList* eventList, void* /*srcConnRefCon*/) {
// For reference have a look at Table 4 on page 22f in
Expand All @@ -198,34 +199,46 @@ void CoreMidiInPort::initCore()
// Document Version 1.1.2
// Draft Date 2023-10-27
// Published 2023-11-10
const uint32_t message_type_to_size_in_byte[] = { 1, // 0x0
1, // 0x1
1, // 0x2
2, // 0x3
2, // 0x4
4, // 0x5
1, // 0x6
1, // 0x7
2, // 0x8
2, // 0x9
2, // 0xA
3, // 0xB
3, // 0xC
4, // 0xD
4, // 0xE
4 };// 0xF
// Section 2.1.4
const uint32_t message_type_to_size_in_words[] = {
1, // 0x0 Utility
1, // 0x1 SystemRealTime
1, // 0x2 ChannelVoice10
2, // 0x3 SystemExclusiveData
2, // 0x4 ChannelVoice20
4, // 0x5 Data
1, // 0x6 Reserved
1, // 0x7 Reserved
2, // 0x8 Reserved
2, // 0x9 Reserved
2, // 0xA Reserved
3, // 0xB Reserved
3, // 0xC Reserved
4, // 0xD Reserved
4, // 0xE Reserved
4 // 0xF Reserved
};

const MIDIEventPacket* packet = eventList->packet;
for (UInt32 index = 0; index < eventList->numPackets; index++) {
LOGD() << "midi packet size " << packet->wordCount << " bytes";
if (doLog) {
LOGW() << "Receiving MIDIEventPacket with " << packet->wordCount << " words";
}
// Handle packet
uint32_t pos = 0;
while (pos < packet->wordCount) {
uint32_t most_significant_4_bit = packet->words[pos] >> 28;
uint32_t message_size = message_type_to_size_in_byte[most_significant_4_bit];
assert(most_significant_4_bit < 6);

LOGD() << "midi message size " << message_size << " bytes";
uint32_t message_size = message_type_to_size_in_words[most_significant_4_bit];
if (doLog) {
LOGW() << "Receiving midi message with " << message_size << " words";
}
Event e = Event::fromRawData(&packet->words[pos], message_size);
if (e) {
if (doLog) {
LOGW() << "Received midi message:" << e.to_string();
}
m_eventReceived.send((tick_t)packet->timeStamp, e);
}
pos += message_size;
Expand All @@ -241,18 +254,38 @@ void CoreMidiInPort::initCore()
{
const MIDIPacket* packet = packetList->packet;
for (UInt32 index = 0; index < packetList->numPackets; index++) {
if (packet->length != 0 && packet->length <= 4) {
uint32_t message(0);
memcpy(&message, packet->data, std::min(sizeof(message), sizeof(char) * packet->length));

auto e = Event::fromMIDI10Package(message).toMIDI20();
auto len = packet->length;
int pos = 0;
const Byte* pointer = static_cast<const Byte*>(&(packet->data[0]));
while (pos < len) {
Byte status = pointer[pos] >> 4;
if (status < 8 || status >= 15) {
if (doLog) {
LOGW() << "Unhandled status byte:" << status;
}
return;
}
Event::Opcode opcode = static_cast<Event::Opcode>(status);
int msgLen = Event::midi10ByteCountForOpcode(opcode);
if (msgLen == 0) {
if (doLog) {
LOGW() << "Unhandled opcode:" << status;
}
return;
}
Event e = Event::fromMIDI10BytePackage(pointer + pos, msgLen);
if (doLog) {
LOGW() << "Received midi 1.0 message:" << e.to_string();
}
e = e.toMIDI20();
if (e) {
if (doLog) {
LOGW() << "Converted to midi 2.0 midi message:" << e.to_string();
}
m_eventReceived.send((tick_t)packet->timeStamp, e);
}
} else if (packet->length > 4) {
LOGW() << "unsupported midi message size " << packet->length << " bytes";
pos += msgLen;
}

packet = MIDIPacketNext(packet);
}
};
Expand Down
59 changes: 36 additions & 23 deletions src/framework/midi/internal/platform/osx/coremidioutport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -289,18 +289,6 @@ bool CoreMidiOutPort::supportsMIDI20Output() const
return false;
}

static ByteCount packetListSize(const std::vector<Event>& events)
{
if (events.empty()) {
return 0;
}

// TODO: should be dynamic per type of event
constexpr size_t eventSize = sizeof(Event().to_MIDI10Package());

return offsetof(MIDIPacketList, packet) + events.size() * (offsetof(MIDIPacket, data) + eventSize);
}

Ret CoreMidiOutPort::sendEvent(const Event& e)
{
if (!isConnected()) {
Expand All @@ -309,35 +297,60 @@ Ret CoreMidiOutPort::sendEvent(const Event& e)

OSStatus result;
MIDITimeStamp timeStamp = AudioGetCurrentHostTime();
static bool doLog = false; // kors::logger::Logger::instance()->isLevel(kors::logger::Level::Debug);

if (__builtin_available(macOS 11.0, *)) {
MIDIProtocolID protocolId = configuration()->useMIDI20Output() ? m_core->destinationProtocolId : kMIDIProtocol_1_0;
MIDIProtocolID protocolId = kMIDIProtocol_2_0; // configuration()->useMIDI20Output() ? m_core->destinationProtocolId : kMIDIProtocol_1_0;

ByteCount wordCount = e.midi20WordCount();
if (wordCount == 0) {
LOGW() << "Failed to send message for event: " << e.to_string();
return make_ret(Err::MidiSendError, "failed send message. unknown word count");
}
if (doLog) {
LOGW() << "Sending MIDIEventList event: " << e.to_string();
}

MIDIEventList eventList;
MIDIEventPacket* packet = MIDIEventListInit(&eventList, protocolId);
// TODO: Replace '4' with something specific for the type of element?
MIDIEventListAdd(&eventList, sizeof(eventList), packet, timeStamp, 4, e.rawData());

if (doLog) {
bool isChannelVoiceMessage20 = e.messageType() == Event::MessageType::ChannelVoice20;
bool isNoteOnMessage = isChannelVoiceMessage20 && e.opcode() == muse::midi::Event::Opcode::NoteOn;
if (isNoteOnMessage && e.velocity() < 128) {
LOGW() << "Detected low MIDI 2.0 ChannelVoiceMessage velocity.";
}
}

MIDIEventListAdd(&eventList, sizeof(eventList), packet, timeStamp, wordCount, e.rawData());

result = MIDISendEventList(m_core->outputPort, m_core->destinationId, &eventList);
} else {
const std::vector<Event> events = e.toMIDI10();

ByteCount packetListSize = ::packetListSize(events);
if (packetListSize == 0) {
if (events.empty()) {
return make_ret(Err::MidiSendError, "midi 1.0 messages list was empty");
}

MIDIPacketList packetList;
// packetList has one packet of 256 bytes by default, which is more than enough for one midi 2.0 event.
MIDIPacket* packet = MIDIPacketListInit(&packetList);
ByteCount packetListSize = sizeof(packetList);
Byte bytesPackage[4];

for (const Event& event : events) {
uint32_t msg = event.to_MIDI10Package();

if (!msg) {
return make_ret(Err::MidiSendError, "message wasn't converted");
int length = event.to_MIDI10BytesPackage(bytesPackage);
assert(length <= 3);
if (doLog) {
if (length == 0) {
LOGW() << "Failed Sending MIDIPacketList event: " << event.to_string();
} else {
LOGW() << "Sending MIDIPacketList event: " << event.to_string();
}
}
if (length > 0) {
packet = MIDIPacketListAdd(&packetList, packetListSize, packet, timeStamp, length, bytesPackage);
}

packet = MIDIPacketListAdd(&packetList, packetListSize, packet, timeStamp, sizeof(msg), reinterpret_cast<Byte*>(&msg));
}

result = MIDISend(m_core->outputPort, m_core->destinationId, &packetList);
Expand Down
Loading
Loading