OSDN Git Service

panpot コマンドを追加
[yamml/yamml-git.git] / src / ir2midi / ir2midi.cpp
1
2 #include <algorithm>
3 #include <exception>
4 #include <vector>
5
6 #include <boost/variant.hpp>
7
8 #include <exceptions/messageexception.hpp>
9 #include <ir2midi/ir2midi.hpp>
10 #include <message/message.hpp>
11 #include <midi/limits.hpp>
12
13 #include "command_panpot.hpp"
14 #include "command_program.hpp"
15 #include "command_tempo.hpp"
16 #include "command_volume.hpp"
17
18 namespace YAMML
19 {
20
21 namespace IR2MIDI
22 {
23
24 class EventConverter final : public boost::static_visitor<>
25 {
26 public:
27     EventConverter(TrackCompilerContext& context, int relativeTime, int channel)
28         : m_Context(context), m_RelativeTime{relativeTime}, m_Channel{channel}
29     {
30     }
31
32     void operator()(const IR::Note& ev)
33     {
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});
36     }
37
38     void operator()(const IR::Rest& ev)
39     {
40         m_Context.UpdateTime(m_RelativeTime + ev.Duration);
41     }
42
43     void operator()(const IR::PolyphonicAftertouch& ev)
44     {
45         m_Context.PushEvent(m_RelativeTime, MIDI::PolyphonicAftertouch{m_Channel, ev.Note, ev.Pressure});
46     }
47
48     void operator()(const IR::ControlChange& ev)
49     {
50         m_Context.PushEvent(m_RelativeTime, MIDI::ControlChange{m_Channel, ev.Control, ev.Value});
51     }
52
53     void operator()(const IR::ProgramChange& ev)
54     {
55         m_Context.PushEvent(m_RelativeTime, MIDI::ProgramChange{m_Channel, ev.Program});
56     }
57
58     void operator()(const IR::Aftertouch& ev)
59     {
60         m_Context.PushEvent(m_RelativeTime, MIDI::Aftertouch{m_Channel, ev.Pressure});
61     }
62
63     void operator()(const IR::PitchBend& ev)
64     {
65         m_Context.PushEvent(m_RelativeTime, MIDI::PitchBend{m_Channel, ev.Value});
66     }
67
68     void operator()(const MIDI::SysExEvent& ev)
69     {
70         m_Context.PushEvent(m_RelativeTime, ev);
71     }
72
73     void operator()(const MIDI::MetaEvent& ev)
74     {
75         m_Context.PushEvent(m_RelativeTime, ev);
76     }
77
78 private:
79     TrackCompilerContext& m_Context;
80     int m_RelativeTime;
81     int m_Channel;
82 };
83
84 bool IR2MIDICompiler::Compile(const std::string& entryPoint)
85 {
86     try
87     {
88         if (!CompileTrackBlock(entryPoint))
89         {
90             return false;
91         }
92
93         Finalize();
94         return !HasErrors();
95     }
96     catch (const Exceptions::MessageException& e)
97     {
98         AddMessage(e.Item);
99         return false;
100     }
101     catch (const std::exception& e)
102     {
103         AddMessage(
104             Message::MessageItem{
105                 Message::MessageKind::FetalError,
106                 Message::MessageID::UnknownInIR2MIDI,
107                 m_IR.Name,
108                 {0, 0},
109                 {e.what()}
110             }
111         );
112
113         return false;
114     }
115 }
116
117 MIDI::MIDIFile& IR2MIDICompiler::GetMIDI()
118 {
119     return m_MIDI;
120 }
121
122 const MIDI::MIDIFile& IR2MIDICompiler::GetMIDI() const
123 {
124     return m_MIDI;
125 }
126
127 void IR2MIDICompiler::operator()(const IR::TrackList& ir)
128 {
129     CheckForUnprocessedAttributes(ir.Attributes);
130
131     for (auto&& i : ir.Tracks)
132     {
133         CheckForUnprocessedAttributes(i.Attributes);
134         EnsureTrackInitialized(i.Number);
135     }
136
137     for (auto&& i : ir.Tracks)
138     {
139         GetTrackContext(i.Number).SetLastEventTime(GetLastEventTime());
140
141         for (auto&& j : i.Items)
142         {
143             CheckForUnprocessedAttributes(j.Attributes);
144             GetTrackContext(i.Number).EnterBlock();
145             CompileBlock(i.Number, j.Block);
146         }
147     }
148
149     UpdateLastEventTime();
150 }
151
152 void IR2MIDICompiler::operator()(const AST::Command& ast)
153 {
154     auto itProc = m_CommandProcessors.find(ast.Name);
155
156     if (itProc == m_CommandProcessors.end())
157     {
158         AddMessage(
159             Message::MessageItem{
160                 Message::MessageKind::Error,
161                 Message::MessageID::InvalidCommandName,
162                 m_IR.Name,
163                 ast.Location,
164                 {ast.Name}
165             }
166         );
167     }
168     else
169     {
170         try
171         {
172             itProc->second->Process(ast);
173             UpdateLastEventTime();
174         }
175         catch (const Exceptions::MessageException& e)
176         {
177             AddMessage(e.Item);
178         }
179     }
180 }
181
182 void IR2MIDICompiler::operator()(int trackNumber, const IR::Event& ev)
183 {
184     EventConverter ec(GetTrackContext(trackNumber), ev.Time, trackNumber);
185     ev.Value.apply_visitor(ec);
186 }
187
188 void IR2MIDICompiler::operator()(int trackNumber, const IR::BlockReference& blockRef)
189 {
190     CompileBlock(trackNumber, blockRef);
191 }
192
193 void IR2MIDICompiler::InitializeCommandProcessors()
194 {
195     m_CommandProcessors["panpot"] = CreatePanpotCommandProcessor(this);
196     m_CommandProcessors["program"] = CreateProgramCommandProcessor(this);
197     m_CommandProcessors["tempo"] = CreateTempoCommandProcessor(this);
198     m_CommandProcessors["volume"] = CreateVolumeCommandProcessor(this);
199 }
200
201 bool IR2MIDICompiler::CompileTrackBlock(const std::string& trackBlockName)
202 {
203     auto itTrack = m_IR.TrackBlockNameMap.find(trackBlockName);
204
205     if (itTrack == m_IR.TrackBlockNameMap.end())
206     {
207         throw Exceptions::MessageException(
208             Message::MessageItem{
209                 Message::MessageKind::Error,
210                 Message::MessageID::NoSuchCompositionName,
211                 m_IR.Name,
212                 {0, 0},
213                 {trackBlockName}
214             }
215         );
216     }
217
218     // with bounds checking
219     CheckForUnprocessedAttributes(m_IR.TrackBlocks.at(itTrack->second.ID).Attributes);
220
221     for (auto&& i : m_IR.TrackBlocks[itTrack->second.ID].Blocks)
222     {
223         i.apply_visitor(*this);
224     }
225
226     return true;
227 }
228
229 void IR2MIDICompiler::CompileBlock(int trackNumber, IR::BlockReference blockRef)
230 {
231     const auto& block = m_IR.Blocks.at(blockRef.ID);
232     CheckForUnprocessedAttributes(block.Attributes);
233
234     boost::variant<int> varTrackNumber = trackNumber;
235
236     for (auto&& i : block.Events)
237     {
238         boost::apply_visitor(*this, varTrackNumber, i);
239     }
240 }
241
242 void IR2MIDICompiler::Finalize()
243 {
244     for (std::size_t i = 0; i < m_MIDI.Tracks.size(); i++)
245     {
246         GetTrackContext(i).SortEvents();
247
248         int prevTime = 0;
249
250         for (auto&& j : GetTrackContext(i).GetEvents())
251         {
252             m_MIDI.Tracks[i].Events.push_back(MIDI::MIDIEvent{j.AbsoluteTime - prevTime, j.Event});
253             prevTime = j.AbsoluteTime;
254         }
255
256         m_MIDI.Tracks[i].Events.push_back(MIDI::MIDIEvent{50, MIDI::MetaEvent{MIDI::MetaEventKind::EndOfTrack}});
257     }
258 }
259
260 void IR2MIDICompiler::CheckForUnprocessedAttributes(const std::vector<AST::Attribute>& attributes)
261 {
262     if (!attributes.empty())
263     {
264         throw Exceptions::MessageException(
265             Message::MessageItem{
266                 Message::MessageKind::FetalError,
267                 Message::MessageID::UnprocessedAttribute,
268                 m_IR.Name,
269                 attributes.at(0).Location,
270                 {attributes.at(0).Name}
271             }
272         );
273     }
274 }
275
276 void IR2MIDICompiler::EnsureTrackInitialized(int number)
277 {
278     if (!(0 <= number && number < MIDI::TrackNumberSafeLimit))
279     {
280         throw Exceptions::MessageException(
281             Message::MessageItem{
282                 Message::MessageKind::FetalError,
283                 Message::MessageID::TrackNumberIsOutOfSafeRange,
284                 m_IR.Name,
285                 {0, 0},
286                 {std::to_string(number), std::to_string(MIDI::TrackNumberSafeLimit)}
287             }
288         );
289     }
290
291     if (static_cast<std::size_t>(number) >= m_MIDI.Tracks.size())
292     {
293         m_MIDI.Tracks.resize(number + 1);
294         m_Contexts.resize(number + 1, TrackCompilerContext(GetLastEventTime()));
295     }
296 }
297
298 int IR2MIDICompiler::GetLastEventTime() const
299 {
300     return m_LastEventTime;
301 }
302
303 void IR2MIDICompiler::UpdateLastEventTime()
304 {
305     for (auto&& i : m_Contexts)
306     {
307         m_LastEventTime = std::max(i.GetLastEventTime(), m_LastEventTime);
308     }
309
310     for (auto&& i : m_Contexts)
311     {
312         i.SetLastEventTime(m_LastEventTime);
313     }
314 }
315
316 MIDI::MIDITrack& IR2MIDICompiler::GetTrack(int trackNumber)
317 {
318     EnsureTrackInitialized(trackNumber);
319     return m_MIDI.Tracks[static_cast<std::size_t>(trackNumber)];
320 }
321
322 std::string IR2MIDICompiler::GetSourceName() const
323 {
324     return m_IR.Name;
325 }
326
327 TrackCompilerContext& IR2MIDICompiler::GetTrackContext(int trackNumber)
328 {
329     EnsureTrackInitialized(trackNumber);
330     return m_Contexts[static_cast<std::size_t>(trackNumber)];
331 }
332
333 } // namespace IR2MIDI
334
335 } // namespace YAMML