1
0

MidiInOut.h 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. /*
  2. * MidiInOut.h
  3. * -----------
  4. * Purpose: A plugin for sending and receiving MIDI data.
  5. * Notes : (currently none)
  6. * Authors: Johannes Schultz (OpenMPT Devs)
  7. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  8. */
  9. #pragma once
  10. #include "openmpt/all/BuildSettings.hpp"
  11. #include "mpt/mutex/mutex.hpp"
  12. #include "../../common/mptTime.h"
  13. #include "../../soundlib/plugins/PlugInterface.h"
  14. #include <rtmidi/RtMidi.h>
  15. #include <array>
  16. #include <deque>
  17. OPENMPT_NAMESPACE_BEGIN
  18. class MidiDevice
  19. {
  20. public:
  21. using ID = decltype(RtMidiIn().getPortCount());
  22. static constexpr ID NO_MIDI_DEVICE = ID(-1);
  23. RtMidi &stream;
  24. std::string name; // Charset::UTF8
  25. ID index = NO_MIDI_DEVICE;
  26. public:
  27. MidiDevice(RtMidi &stream)
  28. : stream(stream)
  29. , name("<none>")
  30. { }
  31. std::string GetPortName(ID port); // Charset::UTF8
  32. };
  33. class MidiInOut final : public IMidiPlugin
  34. {
  35. friend class MidiInOutEditor;
  36. protected:
  37. enum
  38. {
  39. kInputParameter = 0,
  40. kOutputParameter = 1,
  41. kNumPrograms = 1,
  42. kNumParams = 2,
  43. kNoDevice = MidiDevice::NO_MIDI_DEVICE,
  44. kMaxDevices = 65536, // Should be a power of 2 to avoid rounding errors.
  45. };
  46. // MIDI queue entry with small storage optimiziation.
  47. // This optimiziation is going to be used for all messages that OpenMPT can send internally,
  48. // but SysEx messages received from other plugins may be longer.
  49. class Message
  50. {
  51. public:
  52. double m_time;
  53. size_t m_size;
  54. unsigned char *m_message = nullptr;
  55. protected:
  56. std::array<unsigned char, 32> m_msgSmall;
  57. public:
  58. Message(double time, const void *data, size_t size)
  59. : m_time(time)
  60. , m_size(size)
  61. {
  62. if(size > m_msgSmall.size())
  63. m_message = new unsigned char[size];
  64. else
  65. m_message = m_msgSmall.data();
  66. std::memcpy(m_message, data, size);
  67. }
  68. Message(const Message &) = delete;
  69. Message & operator=(const Message &) = delete;
  70. Message(double time, unsigned char msg) noexcept : Message(time, &msg, 1) { }
  71. Message(Message &&other) noexcept
  72. : m_time(other.m_time)
  73. , m_size(other.m_size)
  74. , m_message(other.m_message)
  75. , m_msgSmall(other.m_msgSmall)
  76. {
  77. other.m_message = nullptr;
  78. if(m_size <= m_msgSmall.size())
  79. m_message = m_msgSmall.data();
  80. }
  81. ~Message()
  82. {
  83. if(m_size > m_msgSmall.size())
  84. delete[] m_message;
  85. }
  86. Message& operator= (Message &&other) noexcept
  87. {
  88. m_time = other.m_time;
  89. m_size = other.m_size;
  90. m_message = (m_size <= m_msgSmall.size()) ? m_msgSmall.data() : other.m_message;
  91. m_msgSmall = other.m_msgSmall;
  92. other.m_message = nullptr;
  93. return *this;
  94. }
  95. };
  96. std::string m_chunkData; // Storage for GetChunk
  97. std::deque<Message> m_outQueue; // Latency-compensated output
  98. std::vector<unsigned char> m_bufferedInput; // For receiving long SysEx messages
  99. mpt::mutex m_mutex;
  100. double m_nextClock = 0.0; // Remaining samples until next MIDI clock tick should be sent
  101. double m_latency = 0.0; // User-adjusted latency in seconds
  102. // I/O device settings
  103. Util::MultimediaClock m_clock;
  104. RtMidiIn m_midiIn;
  105. RtMidiOut m_midiOut;
  106. MidiDevice m_inputDevice;
  107. MidiDevice m_outputDevice;
  108. bool m_sendTimingInfo = true;
  109. #ifdef MODPLUG_TRACKER
  110. CString m_programName;
  111. #endif
  112. public:
  113. static IMixPlugin* Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct);
  114. MidiInOut(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct);
  115. ~MidiInOut();
  116. // Translate a VST parameter to an RtMidi device ID
  117. static MidiDevice::ID ParameterToDeviceID(float value)
  118. {
  119. return static_cast<MidiDevice::ID>(value * static_cast<float>(kMaxDevices)) - 1;
  120. }
  121. // Translate a RtMidi device ID to a VST parameter
  122. static float DeviceIDToParameter(MidiDevice::ID index)
  123. {
  124. return static_cast<float>(index + 1) / static_cast<float>(kMaxDevices);
  125. }
  126. /////////////////////////////////////////////////
  127. // Destroy the plugin
  128. void Release() final { delete this; }
  129. int32 GetUID() const final { return 'MMID'; }
  130. int32 GetVersion() const final { return 2; }
  131. void Idle() final { }
  132. uint32 GetLatency() const final;
  133. int32 GetNumPrograms() const final { return kNumPrograms; }
  134. int32 GetCurrentProgram() final { return 0; }
  135. void SetCurrentProgram(int32) final { }
  136. PlugParamIndex GetNumParameters() const final { return kNumParams; }
  137. void SetParameter(PlugParamIndex paramindex, PlugParamValue paramvalue) final;
  138. PlugParamValue GetParameter(PlugParamIndex nIndex) final;
  139. // Save parameters for storing them in a module file
  140. void SaveAllParameters() final;
  141. // Restore parameters from module file
  142. void RestoreAllParameters(int32 program) final;
  143. void Process(float *pOutL, float *pOutR, uint32 numFrames) final;
  144. // Render silence and return the highest resulting output level
  145. float RenderSilence(uint32) final{ return 0; }
  146. bool MidiSend(uint32 midiCode) final;
  147. bool MidiSysexSend(mpt::const_byte_span sysex) final;
  148. void HardAllNotesOff() final;
  149. // Modify parameter by given amount. Only needs to be re-implemented if plugin architecture allows this to be performed atomically.
  150. void Resume() final;
  151. void Suspend() final;
  152. // Tell the plugin that there is a discontinuity between the previous and next render call (e.g. aftert jumping around in the module)
  153. void PositionChanged() final;
  154. void Bypass(bool bypass = true) final;
  155. bool IsInstrument() const final { return true; }
  156. bool CanRecieveMidiEvents() final { return true; }
  157. // If false is returned, mixing this plugin can be skipped if its input are currently completely silent.
  158. bool ShouldProcessSilence() final { return true; }
  159. #ifdef MODPLUG_TRACKER
  160. CString GetDefaultEffectName() final { return _T("MIDI Input / Output"); }
  161. CString GetParamName(PlugParamIndex param) final;
  162. CString GetParamLabel(PlugParamIndex) final{ return CString(); }
  163. CString GetParamDisplay(PlugParamIndex param) final;
  164. CString GetCurrentProgramName() final { return m_programName; }
  165. void SetCurrentProgramName(const CString &name) final { m_programName = name; }
  166. CString GetProgramName(int32) final { return m_programName; }
  167. virtual CString GetPluginVendor() { return _T("OpenMPT Project"); }
  168. bool HasEditor() const final { return true; }
  169. protected:
  170. CAbstractVstEditor *OpenEditor() final;
  171. #endif
  172. public:
  173. int GetNumInputChannels() const final { return 0; }
  174. int GetNumOutputChannels() const final { return 0; }
  175. bool ProgramsAreChunks() const final { return true; }
  176. ChunkData GetChunk(bool isBank) final;
  177. void SetChunk(const ChunkData &chunk, bool isBank) final;
  178. protected:
  179. // Open a device for input or output.
  180. void OpenDevice(MidiDevice::ID newDevice, bool asInputDevice);
  181. // Close an active device.
  182. void CloseDevice(MidiDevice &device);
  183. static void InputCallback(double deltatime, std::vector<unsigned char> *message, void *userData) { static_cast<MidiInOut *>(userData)->InputCallback(deltatime, *message); }
  184. void InputCallback(double deltatime, std::vector<unsigned char> &message);
  185. // Calculate the current output timestamp
  186. double GetOutputTimestamp() const;
  187. };
  188. OPENMPT_NAMESPACE_END