add, sub, mul and div commands.

main
bog 2024-02-01 04:15:07 +01:00
parent d023146c65
commit 16f1d8656b
20 changed files with 416 additions and 58 deletions

View File

@ -2,8 +2,8 @@
Signals Signals
======= =======
Signal Types Signal primitives
------------ -----------------
MuzGen use different kind of signals for sound design. MuzGen use different kind of signals for sound design.
@ -49,3 +49,50 @@ The noise command generate uniform random frames.
.. code-block:: .. code-block::
[noise] [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]

33
lib/Add.cpp Normal file
View File

@ -0,0 +1,33 @@
#include "Add.hpp"
namespace muz
{
/*explicit*/ Add::Add(AudioConf const& conf,
std::unique_ptr<Signal> lhs,
std::unique_ptr<Signal> rhs)
: m_conf { conf }
, m_out_left { std::vector<float>(m_conf.channels(), 0.0f) }
, m_out_right { std::vector<float>(m_conf.channels(), 0.0f) }
, m_lhs { std::move(lhs) }
, m_rhs { std::move(rhs) }
{
}
/*virtual*/ Add::~Add()
{
}
void Add::next(std::vector<float>& 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<size_t>(m_conf.channels()));
for (int i=0; i<m_conf.channels(); i++)
{
out[i] = m_out_left[i] + m_out_right[i];
}
}
}

32
lib/Add.hpp Normal file
View File

@ -0,0 +1,32 @@
#ifndef muz_ADD_HPP
#define muz_ADD_HPP
#include "commons.hpp"
#include "Signal.hpp"
#include "AudioConf.hpp"
namespace muz
{
/**
* Sum of two input signals.
**/
class Add: public Signal
{
public:
explicit Add(AudioConf const& conf,
std::unique_ptr<Signal> lhs,
std::unique_ptr<Signal> rhs);
virtual ~Add();
void next(std::vector<float>& out) override;
private:
AudioConf m_conf;
std::vector<float> m_out_left;
std::vector<float> m_out_right;
std::unique_ptr<Signal> m_lhs;
std::unique_ptr<Signal> m_rhs;
};
}
#endif

View File

@ -5,6 +5,7 @@ namespace muz
{ {
/*explicit*/ AudioEngine::AudioEngine(AudioConf const& conf) /*explicit*/ AudioEngine::AudioEngine(AudioConf const& conf)
: m_conf { conf } : m_conf { conf }
, m_frame { std::vector<float>(m_conf.channels(), 0.0f) }
{ {
check_error(Pa_Initialize()); check_error(Pa_Initialize());
} }
@ -80,15 +81,9 @@ namespace muz
return {0.0f, 0.0f}; return {0.0f, 0.0f};
} }
auto frame = m_sig_queue.back()->next(); m_sig_queue.back()->next(m_frame);
if (frame.empty()) return m_frame;
{
pop_signal();
return next();
}
return frame;
} }
/*static*/ int AudioEngine::callback(void const*, void* output, /*static*/ int AudioEngine::callback(void const*, void* output,
@ -103,7 +98,7 @@ namespace muz
for (size_t i=0; i<frames_per_buffer; i++) for (size_t i=0; i<frames_per_buffer; i++)
{ {
auto frame = engine->next(); auto const& frame = engine->next();
for (float val: frame) for (float val: frame)
{ {

View File

@ -47,6 +47,7 @@ namespace muz
PaStream* m_stream = nullptr; PaStream* m_stream = nullptr;
std::vector<std::unique_ptr<Signal>> m_sig_queue; std::vector<std::unique_ptr<Signal>> m_sig_queue;
std::mutex m_sig_mtx; std::mutex m_sig_mtx;
std::vector<float> m_frame;
/** /**
* Throws an audio_error exception if err is an error. * Throws an audio_error exception if err is an error.

View File

@ -18,6 +18,10 @@ add_library(muz-lib OBJECT
Constant.cpp Constant.cpp
Sine.cpp Sine.cpp
Noise.cpp Noise.cpp
Add.cpp
Sub.cpp
Mul.cpp
Div.cpp
# Language # Language
# ======== # ========

View File

@ -5,6 +5,10 @@
#include "Constant.hpp" #include "Constant.hpp"
#include "Sine.hpp" #include "Sine.hpp"
#include "Noise.hpp" #include "Noise.hpp"
#include "Add.hpp"
#include "Sub.hpp"
#include "Mul.hpp"
#include "Div.hpp"
namespace muz namespace muz
{ {
@ -56,10 +60,10 @@ namespace muz
} }
else else
{ {
throw compile_error { format_error<compile_error>(node->line(),
std::string() "cannot compile unknown directive '"
+ "cannot compile unknown directive '" + name + "'." + name
}; + "'.");
} }
} break; } break;
@ -92,12 +96,60 @@ namespace muz
auto signal = std::make_unique<Noise>(m_conf); auto signal = std::make_unique<Noise>(m_conf);
push(std::move(signal)); push(std::move(signal));
} }
else if (name == "add")
{
check_cmd_arity(*node, 2);
auto rhs = pop();
auto lhs = pop();
auto signal = std::make_unique<Add>(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<Sub>(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<Mul>(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<Div>(m_conf,
std::move(lhs),
std::move(rhs));
push(std::move(signal));
}
else else
{ {
throw compile_error { format_error<compile_error>(node->line(),
std::string() "cannot compile unknown command '"
+ "cannot compile unknown command '" + name + "'." + name
}; + "'.");
} }
} }
@ -117,15 +169,15 @@ namespace muz
{ {
if (node.size() - 1 != static_cast<size_t>(arity)) if (node.size() - 1 != static_cast<size_t>(arity))
{ {
throw compile_error { format_error<compile_error>(node.line(),
std::string() "arity mismatch for '"
+ "arity mismatch for '" + node.child(0)->value()
+ node.child(0)->value() + "': expected <"
+ "': expected <" + std::to_string(arity)
+ std::to_string(arity) + ">, got <"
+ ">, got <" + std::to_string(node.size() - 1)
+ std::to_string(node.size() - 1) + ">.");
+ ">."};
} }
} }
} }

View File

@ -12,15 +12,11 @@ namespace muz
{ {
} }
std::vector<float> Constant::next() /*override*/ void Constant::next(std::vector<float>& out) /*override*/
{ {
std::vector<float> out;
for (int i=0; i<m_conf.channels(); i++) for (int i=0; i<m_conf.channels(); i++)
{ {
out.push_back(m_value); out[i] = m_value;
} }
return out;
} }
} }

View File

@ -15,10 +15,11 @@ namespace muz
explicit Constant(AudioConf const& conf, float value=0.0f); explicit Constant(AudioConf const& conf, float value=0.0f);
virtual ~Constant(); virtual ~Constant();
std::vector<float> next() override; void next(std::vector<float>& out) override;
private: private:
AudioConf m_conf; AudioConf m_conf;
float m_value; float m_value;
}; };
} }

33
lib/Div.cpp Normal file
View File

@ -0,0 +1,33 @@
#include "Div.hpp"
namespace muz
{
/*explicit*/ Div::Div(AudioConf const& conf,
std::unique_ptr<Signal> lhs,
std::unique_ptr<Signal> rhs)
: m_conf { conf }
, m_out_left { std::vector<float>(m_conf.channels(), 0.0f) }
, m_out_right { std::vector<float>(m_conf.channels(), 0.0f) }
, m_lhs { std::move(lhs) }
, m_rhs { std::move(rhs) }
{
}
/*virtual*/ Div::~Div()
{
}
void Div::next(std::vector<float>& 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<size_t>(m_conf.channels()));
for (int i=0; i<m_conf.channels(); i++)
{
out[i] = m_out_left[i] / m_out_right[i];
}
}
}

33
lib/Div.hpp Normal file
View File

@ -0,0 +1,33 @@
#ifndef muz_DIV_HPP
#define muz_DIV_HPP
#include "commons.hpp"
#include "Signal.hpp"
#include "AudioConf.hpp"
namespace muz
{
/**
* Division of two input signals.
**/
class Div: public Signal
{
public:
explicit Div(AudioConf const& conf,
std::unique_ptr<Signal> lhs,
std::unique_ptr<Signal> rhs);
virtual ~Div();
void next(std::vector<float>& out) override;
private:
AudioConf m_conf;
std::vector<float> m_out_left;
std::vector<float> m_out_right;
std::unique_ptr<Signal> m_lhs;
std::unique_ptr<Signal> m_rhs;
};
}
#endif

33
lib/Mul.cpp Normal file
View File

@ -0,0 +1,33 @@
#include "Mul.hpp"
namespace muz
{
/*explicit*/ Mul::Mul(AudioConf const& conf,
std::unique_ptr<Signal> lhs,
std::unique_ptr<Signal> rhs)
: m_conf { conf }
, m_out_left { std::vector<float>(m_conf.channels(), 0.0f) }
, m_out_right { std::vector<float>(m_conf.channels(), 0.0f) }
, m_lhs { std::move(lhs) }
, m_rhs { std::move(rhs) }
{
}
/*virtual*/ Mul::~Mul()
{
}
void Mul::next(std::vector<float>& 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<size_t>(m_conf.channels()));
for (int i=0; i<m_conf.channels(); i++)
{
out[i] = m_out_left[i] * m_out_right[i];
}
}
}

32
lib/Mul.hpp Normal file
View File

@ -0,0 +1,32 @@
#ifndef muz_MUL_HPP
#define muz_MUL_HPP
#include "commons.hpp"
#include "Signal.hpp"
#include "AudioConf.hpp"
namespace muz
{
/**
* Product of two input signals.
**/
class Mul: public Signal
{
public:
explicit Mul(AudioConf const& conf,
std::unique_ptr<Signal> lhs,
std::unique_ptr<Signal> rhs);
virtual ~Mul();
void next(std::vector<float>& out) override;
private:
AudioConf m_conf;
std::vector<float> m_out_left;
std::vector<float> m_out_right;
std::unique_ptr<Signal> m_lhs;
std::unique_ptr<Signal> m_rhs;
};
}
#endif

View File

@ -12,17 +12,13 @@ namespace muz
{ {
} }
std::vector<float> Noise::next() /*override*/ void Noise::next(std::vector<float>& out) /*override*/
{ {
std::vector<float> out;
float value = m_distribution(m_rand); float value = m_distribution(m_rand);
for (int i=0; i<m_conf.channels(); i++) for (int i=0; i<m_conf.channels(); i++)
{ {
out.push_back(value); out[i] = value;
} }
return out;
} }
} }

View File

@ -10,13 +10,17 @@
namespace muz namespace muz
{ {
/**
* White noise signal.
* @see Signal
**/
class Noise: public Signal class Noise: public Signal
{ {
public: public:
explicit Noise(AudioConf const& conf); explicit Noise(AudioConf const& conf);
virtual ~Noise(); virtual ~Noise();
std::vector<float> next() override; void next(std::vector<float>& out) override;
private: private:
AudioConf m_conf; AudioConf m_conf;
std::mt19937 m_rand; std::mt19937 m_rand;

View File

@ -19,11 +19,10 @@ namespace muz
virtual ~Signal(); virtual ~Signal();
/** /**
* Get the next sample. * Get the next frame.
* @return std::vector<float> of size N for a sample of N channels or an empty vector at end. * @param out the output buffer that will contain the next frame.
*
**/ **/
virtual std::vector<float> next() = 0; virtual void next(std::vector<float>& out) = 0;
private: private:
}; };
} }

View File

@ -6,6 +6,8 @@ namespace muz
std::unique_ptr<Signal> freq, std::unique_ptr<Signal> freq,
std::unique_ptr<Signal> amplitude) std::unique_ptr<Signal> amplitude)
: m_conf { conf } : m_conf { conf }
, m_out_freq {std::vector<float> (m_conf.channels(), 0.0f)}
, m_out_amp {std::vector<float> (m_conf.channels(), 0.0f)}
, m_freq { std::move(freq) } , m_freq { std::move(freq) }
, m_amplitude { std::move(amplitude) } , m_amplitude { std::move(amplitude) }
{ {
@ -19,31 +21,28 @@ namespace muz
{ {
} }
std::vector<float> Sine::next() /*override*/ void Sine::next(std::vector<float>& out) /*override*/
{ {
assert(m_freq); assert(m_freq);
assert(m_amplitude); assert(m_amplitude);
std::vector<float> out; m_freq->next(m_out_freq);
auto freqs = m_freq->next(); m_amplitude->next(m_out_amp);
auto amps = m_amplitude->next();
if (freqs.size() != amps.size() if (m_out_freq.size() != m_out_amp.size()
|| freqs.size() != m_phases.size()) || m_out_freq.size() != m_phases.size())
{ {
throw signal_error {"cannot generate sine: channel number mismatch"}; throw signal_error {"cannot generate sine: channel number mismatch"};
} }
for (size_t i=0; i<static_cast<size_t>(m_conf.channels()); i++) for (size_t i=0; i<static_cast<size_t>(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<float>(m_conf.samplerate()); / static_cast<float>(m_conf.samplerate());
out.push_back(value); out[i] = value;
} }
return out;
} }
} }

View File

@ -18,9 +18,12 @@ namespace muz
std::unique_ptr<Signal> amplitude); std::unique_ptr<Signal> amplitude);
virtual ~Sine(); virtual ~Sine();
std::vector<float> next() override; void next(std::vector<float>& out) override;
private: private:
AudioConf const& m_conf; AudioConf const& m_conf;
std::vector<float> m_out_freq;
std::vector<float> m_out_amp;
std::unique_ptr<Signal> m_freq; std::unique_ptr<Signal> m_freq;
std::unique_ptr<Signal> m_amplitude; std::unique_ptr<Signal> m_amplitude;
std::vector<float> m_phases; std::vector<float> m_phases;

33
lib/Sub.cpp Normal file
View File

@ -0,0 +1,33 @@
#include "Sub.hpp"
namespace muz
{
/*explicit*/ Sub::Sub(AudioConf const& conf,
std::unique_ptr<Signal> lhs,
std::unique_ptr<Signal> rhs)
: m_conf { conf }
, m_out_left { std::vector<float>(m_conf.channels(), 0.0f) }
, m_out_right { std::vector<float>(m_conf.channels(), 0.0f) }
, m_lhs { std::move(lhs) }
, m_rhs { std::move(rhs) }
{
}
/*virtual*/ Sub::~Sub()
{
}
void Sub::next(std::vector<float>& 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<size_t>(m_conf.channels()));
for (int i=0; i<m_conf.channels(); i++)
{
out[i] = m_out_left[i] - m_out_right[i];
}
}
}

32
lib/Sub.hpp Normal file
View File

@ -0,0 +1,32 @@
#ifndef muz_SUB_HPP
#define muz_SUB_HPP
#include "commons.hpp"
#include "Signal.hpp"
#include "AudioConf.hpp"
namespace muz
{
/**
* Substraction of two input signals.
**/
class Sub: public Signal
{
public:
explicit Sub(AudioConf const& conf,
std::unique_ptr<Signal> lhs,
std::unique_ptr<Signal> rhs);
virtual ~Sub();
void next(std::vector<float>& out) override;
private:
AudioConf m_conf;
std::vector<float> m_out_left;
std::vector<float> m_out_right;
std::unique_ptr<Signal> m_lhs;
std::unique_ptr<Signal> m_rhs;
};
}
#endif