OpenShot Audio Library | OpenShotAudio  0.6.0
juce_UMPMidi1ToMidi2DefaultTranslator.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:
38 
48  template <typename PacketCallback>
49  void dispatch (const View& v, PacketCallback&& callback)
50  {
51  const auto firstWord = v[0];
52  const auto messageType = Utils::getMessageType (firstWord);
53 
54  if (messageType != 0x2)
55  {
56  callback (v);
57  return;
58  }
59 
60  const HelperValues helperValues
61  {
62  std::byte ((0x4 << 0x4) | Utils::getGroup (firstWord)),
63  std::byte ((firstWord >> 0x10) & 0xff),
64  std::byte ((firstWord >> 0x08) & 0x7f),
65  std::byte ((firstWord >> 0x00) & 0x7f),
66  };
67 
68  switch (Utils::getStatus (firstWord))
69  {
70  case 0x8:
71  case 0x9:
72  {
73  const auto packet = processNoteOnOrOff (helperValues);
74  callback (View (packet.data()));
75  return;
76  }
77 
78  case 0xa:
79  {
80  const auto packet = processPolyPressure (helperValues);
81  callback (View (packet.data()));
82  return;
83  }
84 
85  case 0xb:
86  {
87  PacketX2 packet;
88 
89  if (processControlChange (helperValues, packet))
90  callback (View (packet.data()));
91 
92  return;
93  }
94 
95  case 0xc:
96  {
97  const auto packet = processProgramChange (helperValues);
98  callback (View (packet.data()));
99  return;
100  }
101 
102  case 0xd:
103  {
104  const auto packet = processChannelPressure (helperValues);
105  callback (View (packet.data()));
106  return;
107  }
108 
109  case 0xe:
110  {
111  const auto packet = processPitchBend (helperValues);
112  callback (View (packet.data()));
113  return;
114  }
115  }
116  }
117 
118  void reset()
119  {
120  groupAccumulators = {};
121  groupBanks = {};
122  }
123 
124 private:
125  enum class PnKind { nrpn, rpn };
126 
127  struct HelperValues
128  {
129  std::byte typeAndGroup;
130  std::byte byte0;
131  std::byte byte1;
132  std::byte byte2;
133  };
134 
135  static PacketX2 processNoteOnOrOff (HelperValues helpers);
136  static PacketX2 processPolyPressure (HelperValues helpers);
137 
138  bool processControlChange (HelperValues helpers, PacketX2& packet);
139 
140  PacketX2 processProgramChange (HelperValues helpers) const;
141 
142  static PacketX2 processChannelPressure (HelperValues helpers);
143  static PacketX2 processPitchBend (HelperValues helpers);
144 
145  class PnAccumulator
146  {
147  public:
148  bool addByte (uint8_t cc, std::byte byte);
149 
150  const std::array<std::byte, 4>& getBytes() const noexcept { return bytes; }
151  PnKind getKind() const noexcept { return kind; }
152 
153  private:
154  std::array<std::byte, 4> bytes;
155  uint8_t index = 0;
156  PnKind kind = PnKind::nrpn;
157  };
158 
159  class Bank
160  {
161  public:
162  bool isValid() const noexcept { return ! (msb & 0x80); }
163 
164  uint8_t getMsb() const noexcept { return msb & 0x7f; }
165  uint8_t getLsb() const noexcept { return lsb & 0x7f; }
166 
167  void setMsb (uint8_t i) noexcept { msb = i & 0x7f; }
168  void setLsb (uint8_t i) noexcept { msb &= 0x7f; lsb = i & 0x7f; }
169 
170  private:
171  // We use the top bit to indicate whether this bank is valid.
172  // After reading the spec, it's not clear how we should determine whether
173  // there are valid values, so we'll just assume that the bank is valid
174  // once either the lsb or msb have been written.
175  uint8_t msb = 0x80;
176  uint8_t lsb = 0x00;
177  };
178 
179  using ChannelAccumulators = std::array<PnAccumulator, 16>;
180  std::array<ChannelAccumulators, 16> groupAccumulators;
181 
182  using ChannelBanks = std::array<Bank, 16>;
183  std::array<ChannelBanks, 16> groupBanks;
184 };
185 
186 } // namespace juce::universal_midi_packets
187 
188 #endif