Compare commits

..

2 Commits

Author SHA1 Message Date
bog 16f1d8656b add, sub, mul and div commands. 2024-02-01 04:15:07 +01:00
bog d023146c65 noise command. 2024-01-31 10:23:03 +01:00
21 changed files with 509 additions and 63 deletions

View File

@ -2,8 +2,8 @@
Signals
=======
Signal Types
------------
Signal primitives
-----------------
MuzGen use different kind of signals for sound design.
@ -39,3 +39,60 @@ To generate a sine, we can use the ``sine`` command.
# sine signal with a frequency of 440 and an amplitude of 1
[sine 440 1]
Noise
^^^^^
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]

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)
: m_conf { conf }
, m_frame { std::vector<float>(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; i<frames_per_buffer; i++)
{
auto frame = engine->next();
auto const& frame = engine->next();
for (float val: frame)
{

View File

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

View File

@ -9,13 +9,22 @@ configure_file(
add_library(muz-lib OBJECT
# Audio
# =====
Signal.cpp
AudioEngine.cpp
AudioConf.cpp
# signals
# -------
Constant.cpp
Sine.cpp
Noise.cpp
Add.cpp
Sub.cpp
Mul.cpp
Div.cpp
# Language
# ========
Node.cpp
Lexer.cpp
Parser.cpp

View File

@ -1,6 +1,14 @@
#include "Compiler.hpp"
// signals
// -------
#include "Constant.hpp"
#include "Sine.hpp"
#include "Noise.hpp"
#include "Add.hpp"
#include "Sub.hpp"
#include "Mul.hpp"
#include "Div.hpp"
namespace muz
{
@ -38,23 +46,8 @@ namespace muz
compile_node(node->child(i));
}
if (name == "sine")
{
check_cmd_arity(*node, 2);
execute_cmd(name, node);
auto one = pop();
auto signal = std::make_unique<Sine>(m_conf,
std::move(pop()),
std::move(one));
push(std::move(signal));
}
else
{
throw compile_error {
std::string()
+ "cannot compile unknown command '" + name + "'."
};
}
} break;
case NODE_DIR: {
@ -67,10 +60,10 @@ namespace muz
}
else
{
throw compile_error {
std::string()
+ "cannot compile unknown directive '" + name + "'."
};
format_error<compile_error>(node->line(),
"cannot compile unknown directive '"
+ name
+ "'.");
}
} break;
@ -84,6 +77,82 @@ namespace muz
}
}
void Compiler::execute_cmd(std::string const& name,
std::shared_ptr<Node> node)
{
if (name == "sine")
{
check_cmd_arity(*node, 2);
auto one = pop();
auto signal = std::make_unique<Sine>(m_conf,
pop(),
std::move(one));
push(std::move(signal));
}
else if (name == "noise")
{
check_cmd_arity(*node, 0);
auto signal = std::make_unique<Noise>(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<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
{
format_error<compile_error>(node->line(),
"cannot compile unknown command '"
+ name
+ "'.");
}
}
void Compiler::push(std::unique_ptr<Signal> signal)
{
m_signals.push_back(std::move(signal));
@ -100,15 +169,15 @@ namespace muz
{
if (node.size() - 1 != static_cast<size_t>(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<compile_error>(node.line(),
"arity mismatch for '"
+ node.child(0)->value()
+ "': expected <"
+ std::to_string(arity)
+ ">, got <"
+ std::to_string(node.size() - 1)
+ ">.");
}
}
}

View File

@ -24,6 +24,7 @@ namespace muz
std::vector<std::unique_ptr<Signal>> compile(std::shared_ptr<Node> node);
void compile_node(std::shared_ptr<Node> node);
void execute_cmd(std::string const& name, std::shared_ptr<Node> node);
private:
AudioConf const& m_conf;

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++)
{
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);
virtual ~Constant();
std::vector<float> next() override;
void next(std::vector<float>& out) override;
private:
AudioConf m_conf;
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

24
lib/Noise.cpp Normal file
View File

@ -0,0 +1,24 @@
#include "Noise.hpp"
namespace muz
{
/*explicit*/ Noise::Noise(AudioConf const& conf)
: m_conf { conf }
{
m_rand.seed(std::chrono::steady_clock::now().time_since_epoch().count());
}
/*virtual*/ Noise::~Noise()
{
}
void Noise::next(std::vector<float>& out) /*override*/
{
float value = m_distribution(m_rand);
for (int i=0; i<m_conf.channels(); i++)
{
out[i] = value;
}
}
}

31
lib/Noise.hpp Normal file
View File

@ -0,0 +1,31 @@
#ifndef muz_NOISE_HPP
#define muz_NOISE_HPP
#include <random>
#include <chrono>
#include "commons.hpp"
#include "AudioConf.hpp"
#include "Signal.hpp"
namespace muz
{
/**
* White noise signal.
* @see Signal
**/
class Noise: public Signal
{
public:
explicit Noise(AudioConf const& conf);
virtual ~Noise();
void next(std::vector<float>& out) override;
private:
AudioConf m_conf;
std::mt19937 m_rand;
std::uniform_real_distribution<float> m_distribution {-0.5f, 0.5f};
};
}
#endif

View File

@ -19,11 +19,10 @@ namespace muz
virtual ~Signal();
/**
* Get the next sample.
* @return std::vector<float> 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<float> next() = 0;
virtual void next(std::vector<float>& out) = 0;
private:
};
}

View File

@ -6,6 +6,8 @@ namespace muz
std::unique_ptr<Signal> freq,
std::unique_ptr<Signal> amplitude)
: 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_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_amplitude);
std::vector<float> 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<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());
out.push_back(value);
out[i] = value;
}
return out;
}
}

View File

@ -18,9 +18,12 @@ namespace muz
std::unique_ptr<Signal> amplitude);
virtual ~Sine();
std::vector<float> next() override;
void next(std::vector<float>& out) override;
private:
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_amplitude;
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