6 #include <boost/variant.hpp>
8 #include <exceptions/messageexception.hpp>
9 #include <ir2midi/ir2midi.hpp>
10 #include <message/message.hpp>
11 #include <midi/limits.hpp>
13 #include "command_panpot.hpp"
14 #include "command_program.hpp"
15 #include "command_tempo.hpp"
16 #include "command_volume.hpp"
24 class EventConverter final : public boost::static_visitor<>
27 EventConverter(TrackCompilerContext& context, int relativeTime, int channel)
28 : m_Context(context), m_RelativeTime{relativeTime}, m_Channel{channel}
32 void operator()(const IR::Note& ev)
34 m_Context.PushEvent(m_RelativeTime, MIDI::NoteOn{m_Channel, ev.Number, ev.OnVelocity});
35 m_Context.PushEvent(m_RelativeTime + ev.Duration, MIDI::NoteOff{m_Channel, ev.Number, ev.OffVelocity});
38 void operator()(const IR::Rest& ev)
40 m_Context.UpdateTime(m_RelativeTime + ev.Duration);
43 void operator()(const IR::PolyphonicAftertouch& ev)
45 m_Context.PushEvent(m_RelativeTime, MIDI::PolyphonicAftertouch{m_Channel, ev.Note, ev.Pressure});
48 void operator()(const IR::ControlChange& ev)
50 m_Context.PushEvent(m_RelativeTime, MIDI::ControlChange{m_Channel, ev.Control, ev.Value});
53 void operator()(const IR::ProgramChange& ev)
55 m_Context.PushEvent(m_RelativeTime, MIDI::ProgramChange{m_Channel, ev.Program});
58 void operator()(const IR::Aftertouch& ev)
60 m_Context.PushEvent(m_RelativeTime, MIDI::Aftertouch{m_Channel, ev.Pressure});
63 void operator()(const IR::PitchBend& ev)
65 m_Context.PushEvent(m_RelativeTime, MIDI::PitchBend{m_Channel, ev.Value});
68 void operator()(const MIDI::SysExEvent& ev)
70 m_Context.PushEvent(m_RelativeTime, ev);
73 void operator()(const MIDI::MetaEvent& ev)
75 m_Context.PushEvent(m_RelativeTime, ev);
79 TrackCompilerContext& m_Context;
84 bool IR2MIDICompiler::Compile(const std::string& entryPoint)
88 if (!CompileTrackBlock(entryPoint))
96 catch (const Exceptions::MessageException& e)
101 catch (const std::exception& e)
104 Message::MessageItem{
105 Message::MessageKind::FetalError,
106 Message::MessageID::UnknownInIR2MIDI,
117 MIDI::MIDIFile& IR2MIDICompiler::GetMIDI()
122 const MIDI::MIDIFile& IR2MIDICompiler::GetMIDI() const
127 void IR2MIDICompiler::operator()(const IR::TrackList& ir)
129 CheckForUnprocessedAttributes(ir.Attributes);
131 for (auto&& i : ir.Tracks)
133 CheckForUnprocessedAttributes(i.Attributes);
134 EnsureTrackInitialized(i.Number);
137 for (auto&& i : ir.Tracks)
139 GetTrackContext(i.Number).SetLastEventTime(GetLastEventTime());
141 for (auto&& j : i.Items)
143 CheckForUnprocessedAttributes(j.Attributes);
144 GetTrackContext(i.Number).EnterBlock();
145 CompileBlock(i.Number, j.Block);
149 UpdateLastEventTime();
152 void IR2MIDICompiler::operator()(const AST::Command& ast)
154 auto itProc = m_CommandProcessors.find(ast.Name);
156 if (itProc == m_CommandProcessors.end())
159 Message::MessageItem{
160 Message::MessageKind::Error,
161 Message::MessageID::InvalidCommandName,
172 itProc->second->Process(ast);
173 UpdateLastEventTime();
175 catch (const Exceptions::MessageException& e)
182 void IR2MIDICompiler::operator()(int trackNumber, const IR::Event& ev)
184 EventConverter ec(GetTrackContext(trackNumber), ev.Time, trackNumber);
185 ev.Value.apply_visitor(ec);
188 void IR2MIDICompiler::operator()(int trackNumber, const IR::BlockReference& blockRef)
190 CompileBlock(trackNumber, blockRef);
193 void IR2MIDICompiler::InitializeCommandProcessors()
195 m_CommandProcessors["panpot"] = CreatePanpotCommandProcessor(this);
196 m_CommandProcessors["program"] = CreateProgramCommandProcessor(this);
197 m_CommandProcessors["tempo"] = CreateTempoCommandProcessor(this);
198 m_CommandProcessors["volume"] = CreateVolumeCommandProcessor(this);
201 bool IR2MIDICompiler::CompileTrackBlock(const std::string& trackBlockName)
203 auto itTrack = m_IR.TrackBlockNameMap.find(trackBlockName);
205 if (itTrack == m_IR.TrackBlockNameMap.end())
207 throw Exceptions::MessageException(
208 Message::MessageItem{
209 Message::MessageKind::Error,
210 Message::MessageID::NoSuchCompositionName,
218 // with bounds checking
219 CheckForUnprocessedAttributes(m_IR.TrackBlocks.at(itTrack->second.ID).Attributes);
221 for (auto&& i : m_IR.TrackBlocks[itTrack->second.ID].Blocks)
223 i.apply_visitor(*this);
229 void IR2MIDICompiler::CompileBlock(int trackNumber, IR::BlockReference blockRef)
231 const auto& block = m_IR.Blocks.at(blockRef.ID);
232 CheckForUnprocessedAttributes(block.Attributes);
234 boost::variant<int> varTrackNumber = trackNumber;
236 for (auto&& i : block.Events)
238 boost::apply_visitor(*this, varTrackNumber, i);
242 void IR2MIDICompiler::Finalize()
244 for (std::size_t i = 0; i < m_MIDI.Tracks.size(); i++)
246 GetTrackContext(i).SortEvents();
250 for (auto&& j : GetTrackContext(i).GetEvents())
252 m_MIDI.Tracks[i].Events.push_back(MIDI::MIDIEvent{j.AbsoluteTime - prevTime, j.Event});
253 prevTime = j.AbsoluteTime;
256 m_MIDI.Tracks[i].Events.push_back(MIDI::MIDIEvent{50, MIDI::MetaEvent{MIDI::MetaEventKind::EndOfTrack}});
260 void IR2MIDICompiler::CheckForUnprocessedAttributes(const std::vector<AST::Attribute>& attributes)
262 if (!attributes.empty())
264 throw Exceptions::MessageException(
265 Message::MessageItem{
266 Message::MessageKind::FetalError,
267 Message::MessageID::UnprocessedAttribute,
269 attributes.at(0).Location,
270 {attributes.at(0).Name}
276 void IR2MIDICompiler::EnsureTrackInitialized(int number)
278 if (!(0 <= number && number < MIDI::TrackNumberSafeLimit))
280 throw Exceptions::MessageException(
281 Message::MessageItem{
282 Message::MessageKind::FetalError,
283 Message::MessageID::TrackNumberIsOutOfSafeRange,
286 {std::to_string(number), std::to_string(MIDI::TrackNumberSafeLimit)}
291 if (static_cast<std::size_t>(number) >= m_MIDI.Tracks.size())
293 m_MIDI.Tracks.resize(number + 1);
294 m_Contexts.resize(number + 1, TrackCompilerContext(GetLastEventTime()));
298 int IR2MIDICompiler::GetLastEventTime() const
300 return m_LastEventTime;
303 void IR2MIDICompiler::UpdateLastEventTime()
305 for (auto&& i : m_Contexts)
307 m_LastEventTime = std::max(i.GetLastEventTime(), m_LastEventTime);
310 for (auto&& i : m_Contexts)
312 i.SetLastEventTime(m_LastEventTime);
316 MIDI::MIDITrack& IR2MIDICompiler::GetTrack(int trackNumber)
318 EnsureTrackInitialized(trackNumber);
319 return m_MIDI.Tracks[static_cast<std::size_t>(trackNumber)];
322 std::string IR2MIDICompiler::GetSourceName() const
327 TrackCompilerContext& IR2MIDICompiler::GetTrackContext(int trackNumber)
329 EnsureTrackInitialized(trackNumber);
330 return m_Contexts[static_cast<std::size_t>(trackNumber)];
333 } // namespace IR2MIDI