thestk / rtmidi

A set of C++ classes that provide a common API for realtime MIDI input/output across Linux (ALSA & JACK), Macintosh OS X (CoreMIDI) and Windows (Multimedia)
Other
993 stars 275 forks source link

Send Note OFF in closePort #109

Closed rualark closed 7 years ago

rualark commented 7 years ago

rtMidi is not sending Note OFF while closing OUT port:

void MidiOutWinMM :: closePort( void ) { if ( connected_ ) { WinMidiData data = static_cast<WinMidiData > (apiData); midiOutReset( data->outHandle ); midiOutClose( data->outHandle ); connected = false; } }

Do you think it is a good idea to add this function? What is the best way to send all notes off? A single command (status=123) or track notes that were playing and send note off for all of these notes?

SpotlightKid commented 7 years ago

I don't think that RtMidi should handle this. RtMidi is a MIDI I/O library and so only concerned with the transport layer, so to speak, not with generating or parsing MIDI data.

rualark commented 7 years ago

What about creating a library that will use RtMidi for sending timestamped events in a separate thread and clean up on close sending note offs?

Today I created a prototype for this inside an MGen project: https://github.com/rualark/MGen/blob/master/MGen/MidiOut.cpp https://github.com/rualark/MGen/blob/master/MGen/MidiOut.h

P.S. One important thing to note: I use PmTimestamp and PmEvent structures from portmidi library here. I think these can easily be exctacted from portmidi so that this library does not depend on portmidi.

SpotlightKid commented 7 years ago

I do something similar (the separate thread thing) in Python with python-rtmidi, e.g. here.

radarsat1 commented 7 years ago

Hi, I agree with @SpotlightKid that this goes beyond RtMidi, and should be implemented on another layer. However, it could be pretty trivial, something like:

#include <stdio.h>
#include <RtMidi.h>

class NoteTracker
{
public:
  RtMidiOut midiOut;
  char notesOn[127] = {};
  void noteOff(int note) { printf("turned off %d\n", note); notesOn[note] = 0; /*midiOut.sendMessage(...);*/ }
  void note(int note, int volume) { printf("turned on %d\n", note); notesOn[note] = 1; /*midiOut.sendMessage(...);*/ }
  ~NoteTracker() { printf("cleaning up notes..\n"); for (int i=0; i<127; i++) if (notesOn[i]) noteOff(i); }
};

int main()
{
  NoteTracker t;
  t.note(4, 0x7F);
  t.note(10, 0x7F);
  t.noteOff(4);
  t.note(20, 0x7F);
}

prints,

turned on 4
turned on 10
turned off 4
turned on 20
cleaning up notes..
turned off 10
turned off 20

Does this answer the issue?

rualark commented 7 years ago

Yes, it is close. But not exactly, because it mutes only one channel.

My solution mutes all channels: https://github.com/rualark/MGen/blob/master/MGen/MidiOut.cpp https://github.com/rualark/MGen/blob/master/MGen/MidiOut.h

radarsat1 commented 7 years ago

Yes, I was just giving a brief example of an approach, in fact the difference in these choices (all channels, one channel, etc) confirms to me that this is outside the scope of RtMidi. I'll close, thanks.