From a4dd53c72c54d9874861fa7bf9c77c19aeaa104b Mon Sep 17 00:00:00 2001 From: starg Date: Thu, 25 Aug 2016 11:00:11 +0900 Subject: [PATCH] =?utf8?q?=E3=82=B3=E3=83=9E=E3=83=B3=E3=83=89=E5=87=A6?= =?utf8?q?=E7=90=86=E3=81=AE=E6=96=B9=E6=B3=95=E3=82=92=E6=94=B9=E8=89=AF?= =?utf8?q?=E3=81=97=E3=80=81program=20=E3=82=B3=E3=83=9E=E3=83=B3=E3=83=89?= =?utf8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- include/ir2midi/command.hpp | 44 ++++++++++++++ include/ir2midi/ir2midi.hpp | 10 +++ include/message/id.hpp | 3 +- include/midi/limits.hpp | 16 +++++ src/ast2ir/composition2ir.cpp | 7 +-- src/ast2ir/pch.hpp | 1 + src/ast2ir/phrase2ir.cpp | 12 ++-- src/driver/msgcallback.cpp | 3 +- src/ir2midi/CMakeLists.txt | 3 + src/ir2midi/command_program.cpp | 131 ++++++++++++++++++++++++++++++++++++++++ src/ir2midi/command_program.hpp | 19 ++++++ src/ir2midi/command_tempo.cpp | 110 ++++++++++++++++++--------------- src/ir2midi/command_tempo.hpp | 8 +-- src/ir2midi/ir2midi.cpp | 33 +++++++--- src/ir2midi/pch.hpp | 4 ++ 15 files changed, 329 insertions(+), 75 deletions(-) create mode 100644 include/ir2midi/command.hpp create mode 100644 include/midi/limits.hpp create mode 100644 src/ir2midi/command_program.cpp create mode 100644 src/ir2midi/command_program.hpp diff --git a/include/ir2midi/command.hpp b/include/ir2midi/command.hpp new file mode 100644 index 0000000..31fb32b --- /dev/null +++ b/include/ir2midi/command.hpp @@ -0,0 +1,44 @@ + +#pragma once + +#include +#include + +#include +#include +#include +#include + +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& args) + { + throw Exceptions::MessageException( + Message::MessageItem{ + Message::MessageKind::Error, + id, + GetCompiler()->GetSourceName(), + location, + args + } + ); + } +}; + +} // namespace IR2MIDI + +} // namespace YAMML diff --git a/include/ir2midi/ir2midi.hpp b/include/ir2midi/ir2midi.hpp index 044883a..5087bd1 100644 --- a/include/ir2midi/ir2midi.hpp +++ b/include/ir2midi/ir2midi.hpp @@ -1,7 +1,9 @@ #pragma once +#include #include +#include #include #include @@ -9,6 +11,7 @@ #include #include #include +#include #include #include @@ -23,13 +26,18 @@ class IR2MIDICompiler : public Compiler::CompilerBase, public IIR2MIDICompiler, public: explicit IR2MIDICompiler(const IR::Module& ir) : m_IR(ir) { + InitializeCommandProcessors(); } template 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 m_Contexts; + std::unordered_map> m_CommandProcessors; }; } // namespace IR2MIDI diff --git a/include/message/id.hpp b/include/message/id.hpp index 5a24790..78b1369 100644 --- a/include/message/id.hpp +++ b/include/message/id.hpp @@ -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 index 0000000..a609fe5 --- /dev/null +++ b/include/midi/limits.hpp @@ -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 diff --git a/src/ast2ir/composition2ir.cpp b/src/ast2ir/composition2ir.cpp index 5b2f06e..2f65c1b 100644 --- a/src/ast2ir/composition2ir.cpp +++ b/src/ast2ir/composition2ir.cpp @@ -5,6 +5,7 @@ #include #include +#include #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)} } ); } diff --git a/src/ast2ir/pch.hpp b/src/ast2ir/pch.hpp index 18c21e5..66465f7 100644 --- a/src/ast2ir/pch.hpp +++ b/src/ast2ir/pch.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include diff --git a/src/ast2ir/phrase2ir.cpp b/src/ast2ir/phrase2ir.cpp index e5d91c7..d1daf49 100644 --- a/src/ast2ir/phrase2ir.cpp +++ b/src/ast2ir/phrase2ir.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -18,6 +19,7 @@ #include #include #include +#include #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 { public: @@ -40,16 +40,16 @@ public: if (ast.Modifier.value().type() == typeid(AST::SimpleDurationModifierDots)) { long pow2 = std::lround(std::pow(2, boost::get(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.Modifier.value()).Number; + return MIDI::TickPerQuarter * 4 / (ast.Base.Number / 2) / boost::get(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 Phrase2IRCompiler::operator()(const AST::NoteSequenceStatement& ast) { - m_DefaultDuration = TickPerQuarter; + m_DefaultDuration = MIDI::TickPerQuarter; m_DefaultOctave = 5; if (ast.Attributes.empty()) diff --git a/src/driver/msgcallback.cpp b/src/driver/msgcallback.cpp index 0caea81..f13a491 100644 --- a/src/driver/msgcallback.cpp +++ b/src/driver/msgcallback.cpp @@ -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} { diff --git a/src/ir2midi/CMakeLists.txt b/src/ir2midi/CMakeLists.txt index 61cc650..e3b0cee 100644 --- a/src/ir2midi/CMakeLists.txt +++ b/src/ir2midi/CMakeLists.txt @@ -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 index 0000000..18ab698 --- /dev/null +++ b/src/ir2midi/command_program.cpp @@ -0,0 +1,131 @@ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "command_program.hpp" + +namespace YAMML +{ + +namespace IR2MIDI +{ + +class ProgramCommandProcessor final : public ICommandProcessor, public boost::static_visitor +{ +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(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(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(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 CreateProgramCommandProcessor(IIR2MIDICompiler* pCompiler) +{ + return std::make_unique(pCompiler); +} + +} // namespace IR2MIDI + +} // namespace YAMML diff --git a/src/ir2midi/command_program.hpp b/src/ir2midi/command_program.hpp new file mode 100644 index 0000000..6fc2005 --- /dev/null +++ b/src/ir2midi/command_program.hpp @@ -0,0 +1,19 @@ + +#pragma once + +#include + +#include +#include + +namespace YAMML +{ + +namespace IR2MIDI +{ + +std::unique_ptr CreateProgramCommandProcessor(IIR2MIDICompiler* pCompiler); + +} // namespace IR2MIDI + +} // namespace YAMML diff --git a/src/ir2midi/command_tempo.cpp b/src/ir2midi/command_tempo.cpp index 2f978bf..c4bf575 100644 --- a/src/ir2midi/command_tempo.cpp +++ b/src/ir2midi/command_tempo.cpp @@ -1,82 +1,94 @@ -#include +#include +#include +#include -#include "command_tempo.hpp" +#include +#include +#include #include +#include "command_tempo.hpp" + namespace YAMML { namespace IR2MIDI { -std::vector ProcessTempo(IIR2MIDICompiler* pCompiler, const AST::Command& ast) +class TempoCommandProcessor final : public ICommandProcessor { - std::vector 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(ast.Arguments[0].Value); + + GetCompiler()->GetTrackContext(0).PushEvent( + 0, + MIDI::MetaEvent{ + MIDI::MetaEventKind::SetTempo, + { + static_cast((usecPerQuater & 0xFF0000) >> 16), + static_cast((usecPerQuater & 0xFF00) >> 8), + static_cast(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(ast.Arguments[0].Value); + auto tempo = boost::get(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((usecPerQuater & 0xFF0000) >> 16), - static_cast((usecPerQuater & 0xFF00) >> 8), - static_cast(usecPerQuater & 0xFF) - } - } - ); - - return messages; +std::unique_ptr CreateTempoCommandProcessor(IIR2MIDICompiler* pCompiler) +{ + return std::make_unique(pCompiler); } } // namespace IR2MIDI diff --git a/src/ir2midi/command_tempo.hpp b/src/ir2midi/command_tempo.hpp index f4c0a1c..32ab378 100644 --- a/src/ir2midi/command_tempo.hpp +++ b/src/ir2midi/command_tempo.hpp @@ -1,12 +1,10 @@ #pragma once -#include -#include +#include -#include +#include #include -#include namespace YAMML { @@ -14,7 +12,7 @@ namespace YAMML namespace IR2MIDI { -std::vector ProcessTempo(IIR2MIDICompiler* pCompiler, const AST::Command& ast); +std::unique_ptr CreateTempoCommandProcessor(IIR2MIDICompiler* pCompiler); } // namespace IR2MIDI diff --git a/src/ir2midi/ir2midi.cpp b/src/ir2midi/ir2midi.cpp index 8279f4c..03524c3 100644 --- a/src/ir2midi/ir2midi.cpp +++ b/src/ir2midi/ir2midi.cpp @@ -8,7 +8,9 @@ #include #include #include +#include +#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 #include +#include #include +#include +#include #include +#include #include -- 2.11.0