OpenShot Audio Library | OpenShotAudio  0.6.0
juce_UMPMidi1ToBytestreamTranslator.h
1 /*
2  ==============================================================================
3 
4  This file is part of the JUCE library.
5  Copyright (c) 2022 - Raw Material Software Limited
6 
7  JUCE is an open source library subject to commercial or open-source
8  licensing.
9 
10  The code included in this file is provided under the terms of the ISC license
11  http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12  To use, copy, modify, and/or distribute this software for any purpose with or
13  without fee is hereby granted provided that the above copyright notice and
14  this permission notice appear in all copies.
15 
16  JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17  EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18  DISCLAIMED.
19 
20  ==============================================================================
21 */
22 
23 #ifndef DOXYGEN
24 
25 namespace juce::universal_midi_packets
26 {
27 
35 {
36 public:
40  explicit Midi1ToBytestreamTranslator (int initialBufferSize)
41  {
42  pendingSysExData.reserve (size_t (initialBufferSize));
43  }
44 
46  void reset()
47  {
48  pendingSysExData.clear();
49  pendingSysExTime = 0.0;
50  }
51 
60  template <typename MessageCallback>
61  void dispatch (const View& packet, double time, MessageCallback&& callback)
62  {
63  const auto firstWord = *packet.data();
64 
65  if (! pendingSysExData.empty() && shouldPacketTerminateSysExEarly (firstWord))
66  pendingSysExData.clear();
67 
68  switch (packet.size())
69  {
70  case 1:
71  {
72  // Utility messages don't translate to bytestream format
73  if (Utils::getMessageType (firstWord) != 0x00)
74  {
75  const auto message = fromUmp (PacketX1 { firstWord }, time);
76  callback (BytestreamMidiView (&message));
77  }
78 
79  break;
80  }
81 
82  case 2:
83  {
84  if (Utils::getMessageType (firstWord) == 0x3)
85  processSysEx (PacketX2 { packet[0], packet[1] }, time, callback);
86 
87  break;
88  }
89 
90  case 3: // no 3-word packets in the current spec
91  case 4: // no 4-word packets translate to bytestream format
92  default:
93  break;
94  }
95  }
96 
107  static MidiMessage fromUmp (const PacketX1& m, double time = 0)
108  {
109  const auto word = m.front();
110  jassert (Utils::getNumWordsForMessageType (word) == 1);
111 
112  const std::array<uint8_t, 3> bytes { { uint8_t ((word >> 0x10) & 0xff),
113  uint8_t ((word >> 0x08) & 0xff),
114  uint8_t ((word >> 0x00) & 0xff) } };
115  const auto numBytes = MidiMessage::getMessageLengthFromFirstByte (bytes.front());
116  return MidiMessage (bytes.data(), numBytes, time);
117  }
118 
119 private:
120  template <typename MessageCallback>
121  void processSysEx (const PacketX2& packet,
122  double time,
123  MessageCallback&& callback)
124  {
125  switch (getSysEx7Kind (packet[0]))
126  {
128  startSysExMessage (time);
129  pushBytes (packet);
130  terminateSysExMessage (callback);
131  break;
132 
133  case SysEx7::Kind::begin:
134  startSysExMessage (time);
135  pushBytes (packet);
136  break;
137 
139  if (pendingSysExData.empty())
140  break;
141 
142  pushBytes (packet);
143  break;
144 
145  case SysEx7::Kind::end:
146  if (pendingSysExData.empty())
147  break;
148 
149  pushBytes (packet);
150  terminateSysExMessage (callback);
151  break;
152  }
153  }
154 
155  void pushBytes (const PacketX2& packet)
156  {
157  const auto bytes = SysEx7::getDataBytes (packet);
158  pendingSysExData.insert (pendingSysExData.end(),
159  bytes.data.begin(),
160  bytes.data.begin() + bytes.size);
161  }
162 
163  void startSysExMessage (double time)
164  {
165  pendingSysExTime = time;
166  pendingSysExData.push_back (std::byte { 0xf0 });
167  }
168 
169  template <typename MessageCallback>
170  void terminateSysExMessage (MessageCallback&& callback)
171  {
172  pendingSysExData.push_back (std::byte { 0xf7 });
173  callback (BytestreamMidiView (pendingSysExData, pendingSysExTime));
174  pendingSysExData.clear();
175  }
176 
177  static bool shouldPacketTerminateSysExEarly (uint32_t firstWord)
178  {
179  return ! (isSysExContinuation (firstWord)
180  || isSystemRealTime (firstWord)
181  || isJROrNOP (firstWord));
182  }
183 
184  static SysEx7::Kind getSysEx7Kind (uint32_t word)
185  {
186  return SysEx7::Kind ((word >> 0x14) & 0xf);
187  }
188 
189  static bool isJROrNOP (uint32_t word)
190  {
191  return Utils::getMessageType (word) == 0x0;
192  }
193 
194  static bool isSysExContinuation (uint32_t word)
195  {
196  if (Utils::getMessageType (word) != 0x3)
197  return false;
198 
199  const auto kind = getSysEx7Kind (word);
200  return kind == SysEx7::Kind::continuation || kind == SysEx7::Kind::end;
201  }
202 
203  static bool isSystemRealTime (uint32_t word)
204  {
205  return Utils::getMessageType (word) == 0x1 && ((word >> 0x10) & 0xff) >= 0xf8;
206  }
207 
208  std::vector<std::byte> pendingSysExData;
209 
210  double pendingSysExTime = 0.0;
211 };
212 
213 } // namespace juce::universal_midi_packets
214 
215 #endif
static int getMessageLengthFromFirstByte(uint8 firstByte) noexcept
void dispatch(const View &packet, double time, MessageCallback &&callback)
static MidiMessage fromUmp(const PacketX1 &m, double time=0)
uint32_t size() const noexcept
const uint32_t * data() const noexcept
Definition: juce_UMPView.h:55
static PacketBytes getDataBytes(const PacketX2 &packet)
static uint32_t getNumWordsForMessageType(uint32_t)