OSDN Git Service

コマンド処理の方法を改良し、program コマンドを追加
authorstarg <starg@users.osdn.me>
Thu, 25 Aug 2016 02:00:11 +0000 (11:00 +0900)
committerstarg <starg@users.osdn.me>
Thu, 25 Aug 2016 02:00:11 +0000 (11:00 +0900)
15 files changed:
include/ir2midi/command.hpp [new file with mode: 0644]
include/ir2midi/ir2midi.hpp
include/message/id.hpp
include/midi/limits.hpp [new file with mode: 0644]
src/ast2ir/composition2ir.cpp
src/ast2ir/pch.hpp
src/ast2ir/phrase2ir.cpp
src/driver/msgcallback.cpp
src/ir2midi/CMakeLists.txt
src/ir2midi/command_program.cpp [new file with mode: 0644]
src/ir2midi/command_program.hpp [new file with mode: 0644]
src/ir2midi/command_tempo.cpp
src/ir2midi/command_tempo.hpp
src/ir2midi/ir2midi.cpp
src/ir2midi/pch.hpp

diff --git a/include/ir2midi/command.hpp b/include/ir2midi/command.hpp
new file mode 100644 (file)
index 0000000..31fb32b
--- /dev/null
@@ -0,0 +1,44 @@
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include <ast/composition.hpp>
+#include <exceptions/messageexception.hpp>
+#include <ir2midi/context.hpp>
+#include <message/message.hpp>
+
+namespace YAMML
+{
+
+namespace IR2MIDI
+{
+
+class ICommandProcessor
+{
+public:
+    virtual ~ICommandProcessor() = default;
+
+    virtual void Process(const AST::Command& ast) = 0;
+
+protected:
+    virtual IIR2MIDICompiler* GetCompiler() = 0;
+
+    void ThrowMessage(Message::MessageID id, const AST::SourceLocation& location, const std::vector<std::string>& args)
+    {
+        throw Exceptions::MessageException(
+            Message::MessageItem{
+                Message::MessageKind::Error,
+                id,
+                GetCompiler()->GetSourceName(),
+                location,
+                args
+            }
+        );
+    }
+};
+
+} // namespace IR2MIDI
+
+} // namespace YAMML
index 044883a..5087bd1 100644 (file)
@@ -1,7 +1,9 @@
 
 #pragma once
 
+#include <memory>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include <boost/variant.hpp>
@@ -9,6 +11,7 @@
 #include <ast/composition.hpp>
 #include <compiler/base.hpp>
 #include <ir/module.hpp>
+#include <ir2midi/command.hpp>
 #include <ir2midi/context.hpp>
 #include <midi/file.hpp>
 
@@ -23,13 +26,18 @@ class IR2MIDICompiler : public Compiler::CompilerBase, public IIR2MIDICompiler,
 public:
     explicit IR2MIDICompiler(const IR::Module& ir) : m_IR(ir)
     {
+        InitializeCommandProcessors();
     }
 
     template<typename T>
     IR2MIDICompiler(const IR::Module& ir, T func) : CompilerBase(func), m_IR(ir)
     {
+        InitializeCommandProcessors();
     }
 
+    IR2MIDICompiler(const IR2MIDICompiler&) = delete;
+    IR2MIDICompiler& operator=(const IR2MIDICompiler&) = delete;
+
     virtual ~IR2MIDICompiler() = default;
 
     bool Compile(const std::string& entryPoint);
@@ -47,6 +55,7 @@ public:
     virtual TrackCompilerContext& GetTrackContext(int trackNumber) override;
 
 private:
+    void InitializeCommandProcessors();
     bool CompileTrackBlock(const std::string& trackBlockName);
     void CompileBlock(int trackNumber, IR::BlockReference blockRef);
     void Finalize();
@@ -59,6 +68,7 @@ private:
     IR::Module m_IR;
     MIDI::MIDIFile m_MIDI;
     std::vector<TrackCompilerContext> m_Contexts;
+    std::unordered_map<std::string, std::unique_ptr<ICommandProcessor>> m_CommandProcessors;
 };
 
 } // namespace IR2MIDI
index 5a24790..78b1369 100644 (file)
@@ -78,7 +78,8 @@ enum class MessageID : int
     WrongNumberOfCommandArguments,
     WrongTypeOfCommandArgument,
 
-    InvalidTempo
+    InvalidTempo,
+    InvalidProgram
 };
 
 } // namespace Message
diff --git a/include/midi/limits.hpp b/include/midi/limits.hpp
new file mode 100644 (file)
index 0000000..a609fe5
--- /dev/null
@@ -0,0 +1,16 @@
+
+#pragma once
+
+namespace YAMML
+{
+
+namespace MIDI
+{
+
+constexpr int TickPerQuarter = 960;
+constexpr int TrackNumberLimit = 16;
+constexpr int TrackNumberSafeLimit = 256;
+
+} // namespace MIDI
+
+} // namespace YAMML
index 5b2f06e..2f65c1b 100644 (file)
@@ -5,6 +5,7 @@
 
 #include <exceptions/messageexception.hpp>
 #include <message/message.hpp>
+#include <midi/limits.hpp>
 
 #include "composition2ir.hpp"
 #include "containerutil.hpp"
@@ -15,8 +16,6 @@ namespace YAMML
 namespace AST2IR
 {
 
-constexpr int TrackNumberLimit = 16;
-
 Composition2IRCompiler::Composition2IRCompiler(Compiler::CompilerBase& parentCompiler, IR::Module& ir)
     : NestedCompilerBase(parentCompiler), m_IR(ir)
 {
@@ -78,7 +77,7 @@ IR::TrackBlock::BlockType Composition2IRCompiler::operator()(const AST::TrackLis
 
 IR::Track Composition2IRCompiler::Compile(const AST::TrackBlock& ast)
 {
-    if (!(0 <= ast.TrackNumber && ast.TrackNumber < TrackNumberLimit))
+    if (!(0 <= ast.TrackNumber && ast.TrackNumber < MIDI::TrackNumberLimit))
     {
         throw Exceptions::MessageException(
             Message::MessageItem{
@@ -86,7 +85,7 @@ IR::Track Composition2IRCompiler::Compile(const AST::TrackBlock& ast)
                 Message::MessageID::TrackNumberIsOutOfPreferredRange,
                 m_IR.Name,
                 ast.Location,
-                {std::to_string(ast.TrackNumber), std::to_string(TrackNumberLimit)}
+                {std::to_string(ast.TrackNumber), std::to_string(MIDI::TrackNumberLimit)}
             }
         );
     }
index 18c21e5..66465f7 100644 (file)
@@ -10,6 +10,7 @@
 #include <functional>
 #include <iterator>
 #include <string>
+#include <typeinfo>
 #include <utility>
 #include <vector>
 
index e5d91c7..d1daf49 100644 (file)
@@ -6,6 +6,7 @@
 #include <deque>
 #include <exception>
 #include <string>
+#include <typeinfo>
 #include <vector>
 
 #include <boost/optional.hpp>
@@ -18,6 +19,7 @@
 #include <message/message.hpp>
 #include <ir/block.hpp>
 #include <ir/module.hpp>
+#include <midi/limits.hpp>
 
 #include "containerutil.hpp"
 #include "phrase2ir.hpp"
@@ -28,8 +30,6 @@ namespace YAMML
 namespace AST2IR
 {
 
-constexpr int TickPerQuarter = 960;
-
 class DurationCalculator final : public boost::static_visitor<int>
 {
 public:
@@ -40,16 +40,16 @@ public:
             if (ast.Modifier.value().type() == typeid(AST::SimpleDurationModifierDots))
             {
                 long pow2 = std::lround(std::pow(2, boost::get<AST::SimpleDurationModifierDots>(ast.Modifier.value()).Count));
-                return TickPerQuarter * 4 / (ast.Base.Number / 2) - TickPerQuarter * 4 / (ast.Base.Number * pow2);
+                return MIDI::TickPerQuarter * 4 / (ast.Base.Number / 2) - MIDI::TickPerQuarter * 4 / (ast.Base.Number * pow2);
             }
             else
             {
-                return TickPerQuarter * 4 / (ast.Base.Number / 2) / boost::get<AST::SimpleDurationModifier>(ast.Modifier.value()).Number;
+                return MIDI::TickPerQuarter * 4 / (ast.Base.Number / 2) / boost::get<AST::SimpleDurationModifier>(ast.Modifier.value()).Number;
             }
         }
         else
         {
-            return TickPerQuarter * 4 / ast.Base.Number;
+            return MIDI::TickPerQuarter * 4 / ast.Base.Number;
         }
     }
 
@@ -104,7 +104,7 @@ bool Phrase2IRCompiler::Compile(const AST::Phrase& ast, IR::BlockReference index
 
 std::vector<IR::Block::EventType> Phrase2IRCompiler::operator()(const AST::NoteSequenceStatement& ast)
 {
-    m_DefaultDuration = TickPerQuarter;
+    m_DefaultDuration = MIDI::TickPerQuarter;
     m_DefaultOctave = 5;
 
     if (ast.Attributes.empty())
index 0caea81..f13a491 100644 (file)
@@ -99,7 +99,8 @@ MessagePrinter::MessagePrinter(IStdErrWriter* pStdErrWriter)
         {Message::MessageID::WrongNumberOfCommandArguments, "wrong number of arguments passed to command '{0}'; {2} expected, {1} found"},
         {Message::MessageID::WrongTypeOfCommandArgument, "command argument {1} has a wrong type; expecting '{2}' here"},
 
-        {Message::MessageID::InvalidTempo, "invalid tempo value '{0}'"}
+        {Message::MessageID::InvalidTempo, "invalid tempo value '{0}'"},
+        {Message::MessageID::InvalidProgram, "invalid program name"}
     },
     m_pStdErrWriter{pStdErrWriter}
 {
index 61cc650..e3b0cee 100644 (file)
@@ -1,11 +1,14 @@
 
 set(IR2MIDIHeaders
+    ../../include/ir2midi/command.hpp
     ../../include/ir2midi/context.hpp
     ../../include/ir2midi/ir2midi.hpp
+    command_program.hpp
     command_tempo.hpp
 )
 
 set(IR2MIDISources
+    command_program.cpp
     command_tempo.cpp
     context.cpp
     ir2midi.cpp
diff --git a/src/ir2midi/command_program.cpp b/src/ir2midi/command_program.cpp
new file mode 100644 (file)
index 0000000..18ab698
--- /dev/null
@@ -0,0 +1,131 @@
+
+#include <memory>
+#include <string>
+#include <typeinfo>
+
+#include <boost/variant.hpp>
+
+#include <exceptions/invalidarg.hpp>
+#include <ir2midi/command.hpp>
+#include <message/id.hpp>
+#include <midi/event.hpp>
+#include <midi/limits.hpp>
+
+#include "command_program.hpp"
+
+namespace YAMML
+{
+
+namespace IR2MIDI
+{
+
+class ProgramCommandProcessor final : public ICommandProcessor, public boost::static_visitor<int>
+{
+public:
+    explicit ProgramCommandProcessor(IIR2MIDICompiler* pCompiler) : m_pCompiler(pCompiler)
+    {
+    }
+
+    virtual ~ProgramCommandProcessor() = default;
+
+    virtual IIR2MIDICompiler* GetCompiler() override
+    {
+        return m_pCompiler;
+    }
+
+    virtual void Process(const AST::Command& ast) override
+    {
+        ValidateArguments(ast);
+
+        auto channel = boost::get<long>(ast.Arguments[0].Value);
+
+        try
+        {
+            GetCompiler()->GetTrackContext(channel).PushEvent(0, MIDI::ProgramChange{channel, ast.Arguments[1].Value.apply_visitor(*this)});
+        }
+        catch (const Exceptions::InvalidArgumentException&)
+        {
+            ThrowMessage(
+                Message::MessageID::InvalidProgram,
+                ast.Location,
+                {}
+            );
+        }
+    }
+
+    void ValidateArguments(const AST::Command& ast)
+    {
+        if (ast.Arguments.size() != 2)
+        {
+            ThrowMessage(
+                Message::MessageID::WrongNumberOfCommandArguments,
+                ast.Location,
+                {"program", std::to_string(ast.Arguments.size()), "2"}
+            );
+        }
+
+        if (ast.Arguments[0].Value.type() != typeid(long))
+        {
+            ThrowMessage(
+                Message::MessageID::WrongTypeOfCommandArgument,
+                ast.Location,
+                {"program", "1", "int"}
+            );
+        }
+
+        if ((ast.Arguments[1].Value.type() != typeid(long)) && (ast.Arguments[1].Value.type() != typeid(std::string)))
+        {
+            ThrowMessage(
+                Message::MessageID::WrongTypeOfCommandArgument,
+                ast.Location,
+                {"program", "2", "int/string"}
+            );
+        }
+
+        auto channel = boost::get<long>(ast.Arguments[0].Value);
+
+        if (!(0 <= channel && channel < MIDI::TrackNumberLimit))
+        {
+            ThrowMessage(
+                Message::MessageID::TrackNumberIsOutOfPreferredRange,
+                ast.Location,
+                {std::to_string(channel), std::to_string(MIDI::TrackNumberLimit)}
+            );
+        }
+    }
+
+    int operator()(const long& n)
+    {
+        if ((0 <= n) && (n < 128))
+        {
+            return static_cast<int>(n);
+        }
+        else
+        {
+            throw Exceptions::InvalidArgumentException("IR2MIDI::ProgramCommandProcessor::operator()(const long&)");
+        }
+    }
+
+    int operator()(const double&)
+    {
+        throw Exceptions::InvalidArgumentException("IR2MIDI::ProgramCommandProcessor::operator()(const double&)");
+    }
+
+    int operator()(const std::string&)
+    {
+        // TODO
+        throw Exceptions::InvalidArgumentException("IR2MIDI::ProgramCommandProcessor::operator()(const std::string&)");
+    }
+
+private:
+    IIR2MIDICompiler* m_pCompiler;
+};
+
+std::unique_ptr<ICommandProcessor> CreateProgramCommandProcessor(IIR2MIDICompiler* pCompiler)
+{
+    return std::make_unique<ProgramCommandProcessor>(pCompiler);
+}
+
+} // namespace IR2MIDI
+
+} // namespace YAMML
diff --git a/src/ir2midi/command_program.hpp b/src/ir2midi/command_program.hpp
new file mode 100644 (file)
index 0000000..6fc2005
--- /dev/null
@@ -0,0 +1,19 @@
+
+#pragma once
+
+#include <memory>
+
+#include <ir2midi/command.hpp>
+#include <ir2midi/context.hpp>
+
+namespace YAMML
+{
+
+namespace IR2MIDI
+{
+
+std::unique_ptr<ICommandProcessor> CreateProgramCommandProcessor(IIR2MIDICompiler* pCompiler);
+
+} // namespace IR2MIDI
+
+} // namespace YAMML
index 2f978bf..c4bf575 100644 (file)
@@ -1,82 +1,94 @@
 
-#include <boost/variant.hpp>
+#include <memory>
+#include <string>
+#include <typeinfo>
 
-#include "command_tempo.hpp"
+#include <boost/variant.hpp>
 
+#include <ir2midi/command.hpp>
+#include <message/id.hpp>
 #include <midi/event.hpp>
 
+#include "command_tempo.hpp"
+
 namespace YAMML
 {
 
 namespace IR2MIDI
 {
 
-std::vector<Message::MessageItem> ProcessTempo(IIR2MIDICompiler* pCompiler, const AST::Command& ast)
+class TempoCommandProcessor final : public ICommandProcessor
 {
-    std::vector<Message::MessageItem> messages;
+public:
+    explicit TempoCommandProcessor(IIR2MIDICompiler* pCompiler) : m_pCompiler(pCompiler)
+    {
+    }
 
-    if (ast.Arguments.size() != 1)
+    virtual ~TempoCommandProcessor() = default;
+
+    virtual IIR2MIDICompiler* GetCompiler() override
     {
-        messages.push_back(
-            Message::MessageItem{
-                Message::MessageKind::Error,
-                Message::MessageID::WrongNumberOfCommandArguments,
-                pCompiler->GetSourceName(),
-                ast.Location,
-                {"tempo", std::to_string(ast.Arguments.size()), "1"}
+        return m_pCompiler;
+    }
+
+    virtual void Process(const AST::Command& ast) override
+    {
+        ValidateArguments(ast);
+
+        unsigned int usecPerQuater = 60 * 1'000'000 / boost::get<long>(ast.Arguments[0].Value);
+
+        GetCompiler()->GetTrackContext(0).PushEvent(
+            0,
+            MIDI::MetaEvent{
+                MIDI::MetaEventKind::SetTempo,
+                {
+                    static_cast<std::uint8_t>((usecPerQuater & 0xFF0000) >> 16),
+                    static_cast<std::uint8_t>((usecPerQuater & 0xFF00) >> 8),
+                    static_cast<std::uint8_t>(usecPerQuater & 0xFF)
+                }
             }
         );
-
-        return messages;
     }
 
-    if (ast.Arguments[0].Value.type() != typeid(long))
+    void ValidateArguments(const AST::Command& ast)
     {
-        messages.push_back(
-            Message::MessageItem{
-                Message::MessageKind::Error,
+        if (ast.Arguments.size() != 1)
+        {
+            ThrowMessage(
+                Message::MessageID::WrongNumberOfCommandArguments,
+                ast.Location,
+                {"tempo", std::to_string(ast.Arguments.size()), "1"}
+            );
+        }
+
+        if (ast.Arguments[0].Value.type() != typeid(long))
+        {
+            ThrowMessage(
                 Message::MessageID::WrongTypeOfCommandArgument,
-                pCompiler->GetSourceName(),
                 ast.Location,
                 {"tempo", "1", "int"}
-            }
-        );
-
-        return messages;
-    }
+            );
+        }
 
-    auto tempo = boost::get<long>(ast.Arguments[0].Value);
+        auto tempo = boost::get<long>(ast.Arguments[0].Value);
 
-    if (tempo <= 0)
-    {
-        messages.push_back(
-            Message::MessageItem{
-                Message::MessageKind::Error,
+        if (tempo <= 0)
+        {
+            ThrowMessage(
                 Message::MessageID::InvalidTempo,
-                pCompiler->GetSourceName(),
                 ast.Location,
                 {std::to_string(tempo)}
-            }
-        );
-
-        return messages;
+            );
+        }
     }
 
-    unsigned int usecPerQuater = 60 * 1'000'000 / tempo;
+private:
+    IIR2MIDICompiler* m_pCompiler;
+};
 
-    pCompiler->GetTrackContext(0).PushEvent(
-        0,
-        MIDI::MetaEvent{
-            MIDI::MetaEventKind::SetTempo,
-            {
-                static_cast<std::uint8_t>((usecPerQuater & 0xFF0000) >> 16),
-                static_cast<std::uint8_t>((usecPerQuater & 0xFF00) >> 8),
-                static_cast<std::uint8_t>(usecPerQuater & 0xFF)
-            }
-        }
-    );
-
-    return messages;
+std::unique_ptr<ICommandProcessor> CreateTempoCommandProcessor(IIR2MIDICompiler* pCompiler)
+{
+    return std::make_unique<TempoCommandProcessor>(pCompiler);
 }
 
 } // namespace IR2MIDI
index f4c0a1c..32ab378 100644 (file)
@@ -1,12 +1,10 @@
 
 #pragma once
 
-#include <string>
-#include <vector>
+#include <memory>
 
-#include <ast/composition.hpp>
+#include <ir2midi/command.hpp>
 #include <ir2midi/context.hpp>
-#include <message/message.hpp>
 
 namespace YAMML
 {
@@ -14,7 +12,7 @@ namespace YAMML
 namespace IR2MIDI
 {
 
-std::vector<Message::MessageItem> ProcessTempo(IIR2MIDICompiler* pCompiler, const AST::Command& ast);
+std::unique_ptr<ICommandProcessor> CreateTempoCommandProcessor(IIR2MIDICompiler* pCompiler);
 
 } // namespace IR2MIDI
 
index 8279f4c..03524c3 100644 (file)
@@ -8,7 +8,9 @@
 #include <exceptions/messageexception.hpp>
 #include <ir2midi/ir2midi.hpp>
 #include <message/message.hpp>
+#include <midi/limits.hpp>
 
+#include "command_program.hpp"
 #include "command_tempo.hpp"
 
 namespace YAMML
@@ -17,8 +19,6 @@ namespace YAMML
 namespace IR2MIDI
 {
 
-constexpr int TrackNumberSafeLimit = 256;
-
 class EventConverter final : public boost::static_visitor<>
 {
 public:
@@ -151,11 +151,9 @@ void IR2MIDICompiler::operator()(const IR::TrackList& ir)
 
 void IR2MIDICompiler::operator()(const AST::Command& ast)
 {
-    if (ast.Name == "tempo")
-    {
-        AddMessages(ProcessTempo(this, ast));
-    }
-    else
+    auto itProc = m_CommandProcessors.find(ast.Name);
+
+    if (itProc == m_CommandProcessors.end())
     {
         AddMessage(
             Message::MessageItem{
@@ -167,6 +165,17 @@ void IR2MIDICompiler::operator()(const AST::Command& ast)
             }
         );
     }
+    else
+    {
+        try
+        {
+            itProc->second->Process(ast);
+        }
+        catch (const Exceptions::MessageException& e)
+        {
+            AddMessage(e.Item);
+        }
+    }
 }
 
 void IR2MIDICompiler::operator()(int trackNumber, const IR::Event& ev)
@@ -180,6 +189,12 @@ void IR2MIDICompiler::operator()(int trackNumber, const IR::BlockReference& bloc
     CompileBlock(trackNumber, blockRef);
 }
 
+void IR2MIDICompiler::InitializeCommandProcessors()
+{
+    m_CommandProcessors["program"] = CreateProgramCommandProcessor(this);
+    m_CommandProcessors["tempo"] = CreateTempoCommandProcessor(this);
+}
+
 bool IR2MIDICompiler::CompileTrackBlock(const std::string& trackBlockName)
 {
     auto itTrack = m_IR.TrackBlockNameMap.find(trackBlockName);
@@ -257,7 +272,7 @@ void IR2MIDICompiler::CheckForUnprocessedAttributes(const std::vector<AST::Attri
 
 void IR2MIDICompiler::EnsureTrackInitialized(int number)
 {
-    if (!(0 <= number && number < TrackNumberSafeLimit))
+    if (!(0 <= number && number < MIDI::TrackNumberSafeLimit))
     {
         throw Exceptions::MessageException(
             Message::MessageItem{
@@ -265,7 +280,7 @@ void IR2MIDICompiler::EnsureTrackInitialized(int number)
                 Message::MessageID::TrackNumberIsOutOfSafeRange,
                 m_IR.Name,
                 {0, 0},
-                {std::to_string(number), std::to_string(TrackNumberSafeLimit)}
+                {std::to_string(number), std::to_string(MIDI::TrackNumberSafeLimit)}
             }
         );
     }
index 01232be..2a6f229 100644 (file)
@@ -3,7 +3,11 @@
 
 #include <algorithm>
 #include <exception>
+#include <memory>
 #include <string>
+#include <typeinfo>
+#include <unordered_map>
 #include <vector>
 
+#include <boost/optional.hpp>
 #include <boost/variant.hpp>