diff --git a/doc/guide/signals.rst b/doc/guide/signals.rst index c024513..4d6fa81 100644 --- a/doc/guide/signals.rst +++ b/doc/guide/signals.rst @@ -2,8 +2,8 @@ Signals ======= -Signal Types ------------- +Signal primitives +----------------- MuzGen use different kind of signals for sound design. @@ -49,3 +49,50 @@ The noise command generate uniform random frames. .. code-block:: [noise] + + +Signal operations +----------------- + +The signal operation commands allows us to combine signals in +different ways. + +Add +^^^ + +The ``sum`` command takes two signals as input and outputs their sum. + +.. code-block:: + + [sum [sine 220] [sine 220]] + + +Sub +^^^ + +The ``sub`` command takes two signals as input and outputs their +difference. + +.. code-block:: + + [sub [sine 460] [sine 80]] + + +Mul +^^^ + +The ``mul`` command takes two signals as input and output their product. + +.. code-block:: + + [mul 0.7 [sine 220]] + + +Div +^^^ + +The ``div`` command takes two signals as input and output their ratio. + +.. code-block:: + + [div [sine 220] 2] diff --git a/lib/Add.cpp b/lib/Add.cpp new file mode 100644 index 0000000..6114d26 --- /dev/null +++ b/lib/Add.cpp @@ -0,0 +1,33 @@ +#include "Add.hpp" + +namespace muz +{ + /*explicit*/ Add::Add(AudioConf const& conf, + std::unique_ptr lhs, + std::unique_ptr rhs) + : m_conf { conf } + , m_out_left { std::vector(m_conf.channels(), 0.0f) } + , m_out_right { std::vector(m_conf.channels(), 0.0f) } + , m_lhs { std::move(lhs) } + , m_rhs { std::move(rhs) } + { + } + + /*virtual*/ Add::~Add() + { + } + + void Add::next(std::vector& out) /*override*/ + { + m_lhs->next(m_out_left); + m_rhs->next(m_out_right); + + assert(m_out_left.size() == m_out_right.size()); + assert(m_out_left.size() == static_cast(m_conf.channels())); + + for (int i=0; i lhs, + std::unique_ptr rhs); + + virtual ~Add(); + + void next(std::vector& out) override; + private: + AudioConf m_conf; + std::vector m_out_left; + std::vector m_out_right; + std::unique_ptr m_lhs; + std::unique_ptr m_rhs; + }; +} + +#endif diff --git a/lib/AudioEngine.cpp b/lib/AudioEngine.cpp index 55bd698..bf93f00 100644 --- a/lib/AudioEngine.cpp +++ b/lib/AudioEngine.cpp @@ -5,6 +5,7 @@ namespace muz { /*explicit*/ AudioEngine::AudioEngine(AudioConf const& conf) : m_conf { conf } + , m_frame { std::vector(m_conf.channels(), 0.0f) } { check_error(Pa_Initialize()); } @@ -80,15 +81,9 @@ namespace muz return {0.0f, 0.0f}; } - auto frame = m_sig_queue.back()->next(); + m_sig_queue.back()->next(m_frame); - if (frame.empty()) - { - pop_signal(); - return next(); - } - - return frame; + return m_frame; } /*static*/ int AudioEngine::callback(void const*, void* output, @@ -103,7 +98,7 @@ namespace muz for (size_t i=0; inext(); + auto const& frame = engine->next(); for (float val: frame) { diff --git a/lib/AudioEngine.hpp b/lib/AudioEngine.hpp index 2f79c17..cc8fa2f 100644 --- a/lib/AudioEngine.hpp +++ b/lib/AudioEngine.hpp @@ -47,6 +47,7 @@ namespace muz PaStream* m_stream = nullptr; std::vector> m_sig_queue; std::mutex m_sig_mtx; + std::vector m_frame; /** * Throws an audio_error exception if err is an error. diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index a5a7da0..e49a811 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -18,6 +18,10 @@ add_library(muz-lib OBJECT Constant.cpp Sine.cpp Noise.cpp + Add.cpp + Sub.cpp + Mul.cpp + Div.cpp # Language # ======== diff --git a/lib/Compiler.cpp b/lib/Compiler.cpp index f1ce4ea..165ff72 100644 --- a/lib/Compiler.cpp +++ b/lib/Compiler.cpp @@ -5,6 +5,10 @@ #include "Constant.hpp" #include "Sine.hpp" #include "Noise.hpp" +#include "Add.hpp" +#include "Sub.hpp" +#include "Mul.hpp" +#include "Div.hpp" namespace muz { @@ -56,10 +60,10 @@ namespace muz } else { - throw compile_error { - std::string() - + "cannot compile unknown directive '" + name + "'." - }; + format_error(node->line(), + "cannot compile unknown directive '" + + name + + "'."); } } break; @@ -92,12 +96,60 @@ namespace muz auto signal = std::make_unique(m_conf); push(std::move(signal)); } + else if (name == "add") + { + check_cmd_arity(*node, 2); + + auto rhs = pop(); + auto lhs = pop(); + + auto signal = std::make_unique(m_conf, + std::move(lhs), + std::move(rhs)); + push(std::move(signal)); + } + else if (name == "sub") + { + check_cmd_arity(*node, 2); + + auto rhs = pop(); + auto lhs = pop(); + + auto signal = std::make_unique(m_conf, + std::move(lhs), + std::move(rhs)); + push(std::move(signal)); + } + else if (name == "mul") + { + check_cmd_arity(*node, 2); + + auto rhs = pop(); + auto lhs = pop(); + + auto signal = std::make_unique(m_conf, + std::move(lhs), + std::move(rhs)); + push(std::move(signal)); + } + else if (name == "div") + { + check_cmd_arity(*node, 2); + + auto rhs = pop(); + auto lhs = pop(); + + auto signal = std::make_unique
(m_conf, + std::move(lhs), + std::move(rhs)); + push(std::move(signal)); + } else { - throw compile_error { - std::string() - + "cannot compile unknown command '" + name + "'." - }; + format_error(node->line(), + "cannot compile unknown command '" + + name + + "'."); } } @@ -117,15 +169,15 @@ namespace muz { if (node.size() - 1 != static_cast(arity)) { - throw compile_error { - std::string() - + "arity mismatch for '" - + node.child(0)->value() - + "': expected <" - + std::to_string(arity) - + ">, got <" - + std::to_string(node.size() - 1) - + ">."}; + format_error(node.line(), + "arity mismatch for '" + + node.child(0)->value() + + "': expected <" + + std::to_string(arity) + + ">, got <" + + std::to_string(node.size() - 1) + + ">."); + } } } diff --git a/lib/Constant.cpp b/lib/Constant.cpp index dd5b52f..fb3920c 100644 --- a/lib/Constant.cpp +++ b/lib/Constant.cpp @@ -12,15 +12,11 @@ namespace muz { } - std::vector Constant::next() /*override*/ + void Constant::next(std::vector& out) /*override*/ { - std::vector out; - for (int i=0; i next() override; + void next(std::vector& out) override; private: AudioConf m_conf; float m_value; + }; } diff --git a/lib/Div.cpp b/lib/Div.cpp new file mode 100644 index 0000000..6692e37 --- /dev/null +++ b/lib/Div.cpp @@ -0,0 +1,33 @@ +#include "Div.hpp" + +namespace muz +{ + /*explicit*/ Div::Div(AudioConf const& conf, + std::unique_ptr lhs, + std::unique_ptr rhs) + : m_conf { conf } + , m_out_left { std::vector(m_conf.channels(), 0.0f) } + , m_out_right { std::vector(m_conf.channels(), 0.0f) } + , m_lhs { std::move(lhs) } + , m_rhs { std::move(rhs) } + { + } + + /*virtual*/ Div::~Div() + { + } + + void Div::next(std::vector& out) /*override*/ + { + m_lhs->next(m_out_left); + m_rhs->next(m_out_right); + + assert(m_out_left.size() == m_out_right.size()); + assert(m_out_left.size() == static_cast(m_conf.channels())); + + for (int i=0; i lhs, + std::unique_ptr rhs); + virtual ~Div(); + + void next(std::vector& out) override; + + private: + AudioConf m_conf; + std::vector m_out_left; + std::vector m_out_right; + + std::unique_ptr m_lhs; + std::unique_ptr m_rhs; + }; +} + +#endif diff --git a/lib/Mul.cpp b/lib/Mul.cpp new file mode 100644 index 0000000..3a88634 --- /dev/null +++ b/lib/Mul.cpp @@ -0,0 +1,33 @@ +#include "Mul.hpp" + +namespace muz +{ + /*explicit*/ Mul::Mul(AudioConf const& conf, + std::unique_ptr lhs, + std::unique_ptr rhs) + : m_conf { conf } + , m_out_left { std::vector(m_conf.channels(), 0.0f) } + , m_out_right { std::vector(m_conf.channels(), 0.0f) } + , m_lhs { std::move(lhs) } + , m_rhs { std::move(rhs) } + { + } + + /*virtual*/ Mul::~Mul() + { + } + + void Mul::next(std::vector& out) /*override*/ + { + m_lhs->next(m_out_left); + m_rhs->next(m_out_right); + + assert(m_out_left.size() == m_out_right.size()); + assert(m_out_left.size() == static_cast(m_conf.channels())); + + for (int i=0; i lhs, + std::unique_ptr rhs); + virtual ~Mul(); + + void next(std::vector& out) override; + + private: + AudioConf m_conf; + std::vector m_out_left; + std::vector m_out_right; + std::unique_ptr m_lhs; + std::unique_ptr m_rhs; + }; +} + +#endif diff --git a/lib/Noise.cpp b/lib/Noise.cpp index 5a31675..c8583fd 100644 --- a/lib/Noise.cpp +++ b/lib/Noise.cpp @@ -12,17 +12,13 @@ namespace muz { } - std::vector Noise::next() /*override*/ + void Noise::next(std::vector& out) /*override*/ { - std::vector out; - float value = m_distribution(m_rand); for (int i=0; i next() override; + void next(std::vector& out) override; private: AudioConf m_conf; std::mt19937 m_rand; diff --git a/lib/Signal.hpp b/lib/Signal.hpp index 7832658..52842e4 100644 --- a/lib/Signal.hpp +++ b/lib/Signal.hpp @@ -19,11 +19,10 @@ namespace muz virtual ~Signal(); /** - * Get the next sample. - * @return std::vector of size N for a sample of N channels or an empty vector at end. - * + * Get the next frame. + * @param out the output buffer that will contain the next frame. **/ - virtual std::vector next() = 0; + virtual void next(std::vector& out) = 0; private: }; } diff --git a/lib/Sine.cpp b/lib/Sine.cpp index 9a89b5e..31e8a62 100644 --- a/lib/Sine.cpp +++ b/lib/Sine.cpp @@ -6,6 +6,8 @@ namespace muz std::unique_ptr freq, std::unique_ptr amplitude) : m_conf { conf } + , m_out_freq {std::vector (m_conf.channels(), 0.0f)} + , m_out_amp {std::vector (m_conf.channels(), 0.0f)} , m_freq { std::move(freq) } , m_amplitude { std::move(amplitude) } { @@ -19,31 +21,28 @@ namespace muz { } - std::vector Sine::next() /*override*/ + void Sine::next(std::vector& out) /*override*/ { assert(m_freq); assert(m_amplitude); - std::vector out; - auto freqs = m_freq->next(); - auto amps = m_amplitude->next(); + m_freq->next(m_out_freq); + m_amplitude->next(m_out_amp); - if (freqs.size() != amps.size() - || freqs.size() != m_phases.size()) + if (m_out_freq.size() != m_out_amp.size() + || m_out_freq.size() != m_phases.size()) { throw signal_error {"cannot generate sine: channel number mismatch"}; } for (size_t i=0; i(m_conf.channels()); i++) { - float const value = amps[i] * std::sin(m_phases[i]); + float const value = m_out_amp[i] * std::sin(m_phases[i]); - m_phases[i] += 2 * M_PI * freqs[i] + m_phases[i] += 2 * M_PI * m_out_freq[i] / static_cast(m_conf.samplerate()); - out.push_back(value); + out[i] = value; } - - return out; } } diff --git a/lib/Sine.hpp b/lib/Sine.hpp index ae71f13..9416076 100644 --- a/lib/Sine.hpp +++ b/lib/Sine.hpp @@ -18,9 +18,12 @@ namespace muz std::unique_ptr amplitude); virtual ~Sine(); - std::vector next() override; + void next(std::vector& out) override; private: AudioConf const& m_conf; + std::vector m_out_freq; + std::vector m_out_amp; + std::unique_ptr m_freq; std::unique_ptr m_amplitude; std::vector m_phases; diff --git a/lib/Sub.cpp b/lib/Sub.cpp new file mode 100644 index 0000000..4c0113a --- /dev/null +++ b/lib/Sub.cpp @@ -0,0 +1,33 @@ +#include "Sub.hpp" + +namespace muz +{ + /*explicit*/ Sub::Sub(AudioConf const& conf, + std::unique_ptr lhs, + std::unique_ptr rhs) + : m_conf { conf } + , m_out_left { std::vector(m_conf.channels(), 0.0f) } + , m_out_right { std::vector(m_conf.channels(), 0.0f) } + , m_lhs { std::move(lhs) } + , m_rhs { std::move(rhs) } + { + } + + /*virtual*/ Sub::~Sub() + { + } + + void Sub::next(std::vector& out) /*override*/ + { + m_lhs->next(m_out_left); + m_rhs->next(m_out_right); + + assert(m_out_left.size() == m_out_right.size()); + assert(m_out_left.size() == static_cast(m_conf.channels())); + + for (int i=0; i lhs, + std::unique_ptr rhs); + virtual ~Sub(); + + void next(std::vector& out) override; + + private: + AudioConf m_conf; + std::vector m_out_left; + std::vector m_out_right; + std::unique_ptr m_lhs; + std::unique_ptr m_rhs; + }; +} + +#endif