#pragma once
+#include <string>
#include <vector>
#include <midi/event.hpp>
int m_LastEventTime = 0;
};
+class IIR2MIDICompiler
+{
+public:
+ virtual ~IIR2MIDICompiler() = default;
+
+ virtual std::string GetSourceName() const = 0;
+ virtual TrackCompilerContext& GetTrackContext(int trackNumber) = 0;
+};
+
} // namespace IR2MIDI
} // namespace YAMML
namespace IR2MIDI
{
-class IR2MIDICompiler : public Compiler::CompilerBase, public boost::static_visitor<>
+class IR2MIDICompiler : public Compiler::CompilerBase, public IIR2MIDICompiler, public boost::static_visitor<>
{
public:
explicit IR2MIDICompiler(const IR::Module& ir) : m_IR(ir)
{
}
+ virtual ~IR2MIDICompiler() = default;
+
bool Compile(const std::string& entryPoint);
MIDI::MIDIFile& GetMIDI();
void operator()(int trackNumber, const IR::Event& ev);
void operator()(int trackNumber, const IR::BlockReference& blockRef);
+ virtual std::string GetSourceName() const override;
+ virtual TrackCompilerContext& GetTrackContext(int trackNumber) override;
+
private:
bool CompileTrackBlock(const std::string& trackBlockName);
void CompileBlock(int trackNumber, IR::BlockReference blockRef);
void EnsureTrackInitialized(int number);
MIDI::MIDITrack& GetTrack(int trackNumber);
- TrackCompilerContext& GetTrackContext(int trackNumber);
IR::Module m_IR;
MIDI::MIDIFile m_MIDI;
UnknownInComposition2IR,
UnknownInIR2MIDI,
UnprocessedAttribute,
- UnprocessedCommand,
DuplicatedCompositionName,
DuplicatedPhraseName,
NoSuchPhraseName,
GrammarPhrase2,
GrammarPhrase3,
GrammarPhrase4,
- GrammarPhraseName
+ GrammarPhraseName,
+
+ // IR2MIDI
+ InvalidCommandName,
+ WrongNumberOfCommandArguments,
+ WrongTypeOfCommandArgument,
+
+ InvalidTempo
};
} // namespace Message
{Message::MessageID::UnknownInPhrase2IR, ICEMessage + " (Phrase2IR: Phrase = '{0}', Message = '{1}')"},
{Message::MessageID::UnknownInComposition2IR, ICEMessage + " (Composition2IR: Composition = '{0}', Message = '{1}')"},
{Message::MessageID::UnknownInIR2MIDI, ICEMessage + " (IR2MIDI: Message = '{0}')"},
- {Message::MessageID::UnprocessedCommand, ICEMessage + " (Unprocessed command: '{0}')"},
{Message::MessageID::UnprocessedAttribute, ICEMessage + " (Unprocessed attribute: '{0}')"},
{Message::MessageID::DuplicatedCompositionName, "composition named '{0}' is already defined"},
{Message::MessageID::DuplicatedPhraseName, "phrase named '{0}' is already defined"},
{Message::MessageID::GrammarPhrase2, "parse error: GrammarPhrase2"},
{Message::MessageID::GrammarPhrase3, "parse error: GrammarPhrase3"},
{Message::MessageID::GrammarPhrase4, "parse error: GrammarPhrase4"},
- {Message::MessageID::GrammarPhraseName, "parse error: GrammarPhraseName"}
+ {Message::MessageID::GrammarPhraseName, "parse error: GrammarPhraseName"},
+
+ // IR2MIDI
+ {Message::MessageID::InvalidCommandName, "invalid command '{0}'"},
+ {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"}
},
m_pStdErrWriter{pStdErrWriter}
{
set(IR2MIDIHeaders
../../include/ir2midi/context.hpp
../../include/ir2midi/ir2midi.hpp
+ command_tempo.hpp
)
set(IR2MIDISources
+ command_tempo.cpp
context.cpp
ir2midi.cpp
)
--- /dev/null
+
+#include <boost/variant.hpp>
+
+#include "command_tempo.hpp"
+
+#include <midi/event.hpp>
+
+namespace YAMML
+{
+
+namespace IR2MIDI
+{
+
+std::vector<Message::MessageItem> ProcessTempo(IIR2MIDICompiler* pCompiler, const AST::Command& ast)
+{
+ std::vector<Message::MessageItem> messages;
+
+ if (ast.Arguments.size() != 1)
+ {
+ messages.push_back(
+ Message::MessageItem{
+ Message::MessageKind::Error,
+ Message::MessageID::WrongNumberOfCommandArguments,
+ pCompiler->GetSourceName(),
+ ast.Location,
+ {"tempo", std::to_string(ast.Arguments.size()), "1"}
+ }
+ );
+
+ return messages;
+ }
+
+ if (ast.Arguments[0].Value.type() != typeid(long))
+ {
+ messages.push_back(
+ Message::MessageItem{
+ Message::MessageKind::Error,
+ Message::MessageID::WrongTypeOfCommandArgument,
+ pCompiler->GetSourceName(),
+ ast.Location,
+ {"tempo", "1", "int"}
+ }
+ );
+
+ return messages;
+ }
+
+ auto tempo = boost::get<long>(ast.Arguments[0].Value);
+
+ if (tempo <= 0)
+ {
+ messages.push_back(
+ Message::MessageItem{
+ Message::MessageKind::Error,
+ Message::MessageID::InvalidTempo,
+ pCompiler->GetSourceName(),
+ ast.Location,
+ {std::to_string(tempo)}
+ }
+ );
+
+ return messages;
+ }
+
+ unsigned int usecPerQuater = 60 * 1'000'000 / tempo;
+
+ 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;
+}
+
+} // namespace IR2MIDI
+
+} // namespace YAMML
--- /dev/null
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include <ast/composition.hpp>
+#include <ir2midi/context.hpp>
+#include <message/message.hpp>
+
+namespace YAMML
+{
+
+namespace IR2MIDI
+{
+
+std::vector<Message::MessageItem> ProcessTempo(IIR2MIDICompiler* pCompiler, const AST::Command& ast);
+
+} // namespace IR2MIDI
+
+} // namespace YAMML
#include <ir2midi/ir2midi.hpp>
#include <message/message.hpp>
+#include "command_tempo.hpp"
+
namespace YAMML
{
void IR2MIDICompiler::operator()(const AST::Command& ast)
{
- throw Exceptions::MessageException(
- Message::MessageItem{
- Message::MessageKind::FetalError,
- Message::MessageID::UnprocessedCommand,
- m_IR.Name,
- ast.Location,
- {ast.Name}
- }
- );
+ if (ast.Name == "tempo")
+ {
+ AddMessages(ProcessTempo(this, ast));
+ }
+ else
+ {
+ AddMessage(
+ Message::MessageItem{
+ Message::MessageKind::Error,
+ Message::MessageID::InvalidCommandName,
+ m_IR.Name,
+ ast.Location,
+ {ast.Name}
+ }
+ );
+ }
}
void IR2MIDICompiler::operator()(int trackNumber, const IR::Event& ev)
return m_MIDI.Tracks[static_cast<std::size_t>(trackNumber)];
}
+std::string IR2MIDICompiler::GetSourceName() const
+{
+ return m_IR.Name;
+}
+
TrackCompilerContext& IR2MIDICompiler::GetTrackContext(int trackNumber)
{
+ EnsureTrackInitialized(trackNumber);
return m_Contexts[static_cast<std::size_t>(trackNumber)];
}
st.ASTNode.Arguments.push_back(ASTNode);
}
+ void OnParse(const AST::Literal& ast)
+ {
+ ASTNode.Value = ast;
+ }
+
AST::AttributeArgument ASTNode;
};
st.ASTNode.Arguments.push_back(ASTNode);
}
+ void OnParse(const AST::Literal& ast)
+ {
+ ASTNode = ast;
+ }
+
AST::Literal ASTNode;
};
template<typename TParentState, typename... TCommonStates>
void success(TParentState& st, TCommonStates&...)
{
- st.ASTNode.Value = ASTNode;
+ st.OnParse(ASTNode);
}
AST::Literal ASTNode;
namespace Grammar
{
-class CommandArgument : public Value
+class CommandArgument : public pegtl::seq<Value>
{
};