You're Invited:Meet the Socket Team at RSAC and BSidesSF 2026, March 23–26.RSVP
Socket
Book a DemoSign in
Socket

synthizer

Package Overview
Dependencies
Maintainers
1
Versions
50
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

synthizer - pypi Package Compare versions

Comparing version
0.11.6
to
0.12.0
+64
synthizer-vendored/examples/fast_sine_bank.cpp
/**
* Run through the wave types supported by the fast sine bank generator.
* */
#include "example_common.h"
#include "synthizer.h"
#include "synthizer_constants.h"
#include <assert.h>
#include <chrono>
#include <math.h>
#include <stdio.h>
#include <thread>
void driveGenerator(syz_Handle g) {
for (double freq = 300.0; freq > 100.0; freq -= 5.0) {
CHECKED(syz_setD(g, SYZ_P_FREQUENCY, freq));
std::this_thread::sleep_for(std::chrono::milliseconds(30));
}
}
int main() {
syz_Handle context, generator, source;
CHECKED(syz_initialize());
CHECKED(syz_createContext(&context, NULL, NULL));
CHECKED(syz_createDirectSource(&source, context, NULL, NULL, NULL));
printf("Sine\n");
CHECKED(syz_createFastSineBankGeneratorSine(&generator, context, 300.0, NULL, NULL, NULL));
CHECKED(syz_sourceAddGenerator(source, generator));
driveGenerator(generator);
syz_handleDecRef(generator);
generator = 0;
printf("Square partials=10\n");
CHECKED(syz_createFastSineBankGeneratorSquare(&generator, context, 300.0, 10, NULL, NULL, NULL));
CHECKED(syz_sourceAddGenerator(source, generator));
driveGenerator(generator);
syz_handleDecRef(generator);
generator = 0;
printf("triangle partials=10\n");
CHECKED(syz_createFastSineBankGeneratorTriangle(&generator, context, 300.0, 10, NULL, NULL, NULL));
CHECKED(syz_sourceAddGenerator(source, generator));
driveGenerator(generator);
syz_handleDecRef(generator);
generator = 0;
printf("sawtooth partials=30\n");
CHECKED(syz_createFastSineBankGeneratorSaw(&generator, context, 300.0, 30, NULL, NULL, NULL));
CHECKED(syz_sourceAddGenerator(source, generator));
driveGenerator(generator);
syz_handleDecRef(generator);
generator = 0;
syz_handleDecRef(source);
syz_handleDecRef(generator);
syz_handleDecRef(context);
syz_shutdown();
return 0;
}
#pragma once
/** A sine bank based off the following identities:
*
* ```
* sin(a + b) = sin(a) * cos(b) + cos(a) * sin(b);
* cos(a + b) = cos(a) * cos(b) - sin(a) * sin(b)
* ```
*
* By holding b constant, we can do the entire thing without any actual trig functions after initialization. That is a
* is the advancing/output angle.
*
* This uses templates for autovectorization of computation, primarily (but not only) for multiples of 4. To keep
* accuracy, it resynchronizes each time `fillBlock` is called.
*
* SR is hardcoded at the Synthizer samplerate, as per usual.
* */
#include <algorithm>
#include <math.h> // Older gcc doesn't like cmath because sinf and cosf don't exist.
#include <vector>
#include "synthizer/config.hpp"
#include "synthizer/math.hpp"
#include "synthizer/memory.hpp"
namespace synthizer {
/**
* Configuration for an individual sine wave.
* */
class SineWaveConfig {
public:
SineWaveConfig(double _freq_mul, double _phase, double _gain) : phase(_phase), freq_mul(_freq_mul), gain(_gain) {}
/* Phase, in the range 0.0 to 1.0. */
double phase;
/* Frequency, as a multiplier of the base frequency. E.g 1.0 is the base frequency, 2.0 the first harmonic. */
double freq_mul;
double gain;
};
class FastSineBank {
public:
FastSineBank(double _frequency);
void addWave(const SineWaveConfig &wave);
void clearWaves();
void setFrequency(double frequency);
/* Fill a block of `SAMPLES` length with the output from this generator. */
template <std::size_t SAMPLES, bool ADD = true> void fillBlock(float *out);
private:
template <std::size_t SAMPLES, std::size_t WAVES> void fillBlockHelper(float *out, std::size_t start);
deferred_vector<SineWaveConfig> waves;
double frequency;
// To allow for frequency changes without artifacts or fading, we must store time on a per-wave basis.
class WaveState {
public:
WaveState(double _time) : time(_time) {}
// Time for this wave specifically in the range 0.0 to 1.0.
double time = 0.0;
};
std::vector<WaveState> wave_states;
};
FastSineBank::FastSineBank(double _frequency) : frequency(_frequency) {}
template <std::size_t SAMPLES, std::size_t WAVES> void FastSineBank::fillBlockHelper(float *out, std::size_t start) {
/* sa=sin(a), ca=cos(a), etc. */
float sa[WAVES], ca[WAVES], sb[WAVES], cb[WAVES], gains[WAVES];
// Initialize our variables using real values.
for (std::size_t i = 0; i < WAVES; i++) {
std::size_t wave_ind = i + start;
double freq = this->waves[wave_ind].freq_mul * this->frequency;
double t = 2 * PI * this->wave_states[wave_ind].time;
// Advance the wave's time.
this->wave_states[wave_ind].time =
fmod(this->wave_states[wave_ind].time + freq * (SAMPLES / (double)config::SR), 1.0);
sa[i] = sinf(t);
ca[i] = cosf(t);
// How many radians does this wave advance by per sample?
double b = 2 * PI * freq / config::SR;
sb[i] = sinf(b);
cb[i] = cosf(b);
gains[i] = waves[wave_ind].gain;
}
for (std::size_t s = 0; s < SAMPLES; s++) {
for (std::size_t i = 0; i < WAVES; i++) {
float new_sa = sa[i] * cb[i] + ca[i] * sb[i];
float new_ca = ca[i] * cb[i] - sa[i] * sb[i];
out[s] += gains[i] * sa[i];
sa[i] = new_sa;
ca[i] = new_ca;
}
}
}
template <std::size_t SAMPLES, bool ADD> void FastSineBank::fillBlock(float *out) {
if (ADD == false) {
std::fill(out, out + SAMPLES, 0.0);
}
// break this down by 16, 8, 4, 2, 1. Gives the compiler lots of autovectorization room.
std::size_t i = 0;
#define BLOCK(X) \
for (; i < this->waves.size() / X * X; i += X) { \
this->fillBlockHelper<SAMPLES, X>(out, i); \
}
BLOCK(32)
BLOCK(16)
BLOCK(8)
BLOCK(4)
BLOCK(1)
#undef BLOCK
}
void FastSineBank::addWave(const SineWaveConfig &wave) {
this->waves.push_back(wave);
this->wave_states.emplace_back(FastSineBank::WaveState{wave.phase});
}
void FastSineBank::clearWaves() {
this->waves.clear();
this->wave_states.clear();
}
void FastSineBank::setFrequency(double _frequency) { this->frequency = _frequency; }
} // namespace synthizer
#pragma once
#include "synthizer.h"
#include "synthizer/fast_sine_bank.hpp"
#include "synthizer/generator.hpp"
#include "synthizer/property_internals.hpp"
#include <memory>
#include <optional>
namespace synthizer {
class Context;
class FastSineBankGenerator : public Generator {
public:
FastSineBankGenerator(const std::shared_ptr<Context> &context, const syz_SineBankConfig *cfg);
int getObjectType() override;
unsigned int getChannels() override;
void generateBlock(float *output, FadeDriver *gain_driver) override;
std::optional<double> startGeneratorLingering() override;
#define PROPERTY_CLASS SineBankGenerator
#define PROPERTY_BASE Generator
#define PROPERTY_LIST SINE_BANK_GENERATOR_PROPERTIES
#include "synthizer/property_impl.hpp"
private:
FastSineBank bank;
};
} // namespace synthizer
#include "synthizer/generators/fast_sine_bank.hpp"
#include "synthizer.h"
#include "synthizer/block_buffer_cache.hpp"
#include "synthizer/c_api.hpp"
#include "synthizer/config.hpp"
#include "synthizer/context.hpp"
#include "synthizer/math.hpp"
#include <math.h>
#include <memory>
#include <optional>
#include <vector>
namespace synthizer {
FastSineBankGenerator::FastSineBankGenerator(const std::shared_ptr<Context> &context, const syz_SineBankConfig *cfg)
: Generator(context), bank(cfg->initial_frequency) {
for (unsigned int i = 0; i < cfg->wave_count; i++) {
SineWaveConfig wave{cfg->waves[i].frequency_mul, cfg->waves[i].phase, cfg->waves[i].gain};
this->bank.addWave(wave);
}
// Make sure the property subsystem starts at the right place.
this->setFrequency(cfg->initial_frequency, false);
}
int FastSineBankGenerator::getObjectType() { return SYZ_OTYPE_FAST_SINE_BANK_GENERATOR; }
unsigned int FastSineBankGenerator::getChannels() { return 1; }
// The weird parameter name gd is due to what is basically a false positive warning from MSVC, which doesn't like that
// the base class has a private member called gain_driver.
void FastSineBankGenerator::generateBlock(float *output, FadeDriver *gd) {
// For now, we round-trip through a temporary buffer unconditionally to apply the gain; in future, this restriction
// may be lifted due to improvements in the underlying fade architecture. Note that as is standard with Synthizer
// objects, the bank adds.
auto tmp_buffer = acquireBlockBuffer(true);
this->bank.setFrequency(this->getFrequency());
this->bank.fillBlock<config::BLOCK_SIZE>(tmp_buffer);
gd->drive(this->getContextRaw()->getBlockTime(), [&](auto &&gain_cb) {
for (unsigned int i = 0; i < config::BLOCK_SIZE; i++) {
output[i] += tmp_buffer[i] * gain_cb(i);
}
});
}
std::optional<double> FastSineBankGenerator::startGeneratorLingering() {
this->setGain(0.0);
return 2 * (config::BLOCK_SIZE / (double)config::SR);
}
/**
* Apply the Lanczos sigma approximation to some sine waves, assuming that they are in order and that the first wave is
* the fundamental. This function assumes that these are integral harmonics and that the vec is non-empty and in order
* of increasing frequency.
*
* This is a strategy to reduce the gibbs phenomenon when approximating fourier series.
* */
static void sigmaApproximate(std::vector<syz_SineBankWave> *waves) {
// Per wolfram, rewrite the fourier series as:
// `f(theta)=1/2a_0+sum_(n=1)^(m-1)sinc((npi)/(2m))[a_ncos(ntheta)+b_nsin(ntheta)]` where `m` is the last term.
//
// Wolfram is unclear on the meaning of "last term" here: in the normal fourier series, the last term would not be
// included by the above sum. There is another definition which they claim is incorrect where the upper end of the
// sum is `m`, plus a few other variations. It probably doesn't matter much as long as we never evaluate the sinc
// function outside the interval `(0, pi/2]` (0 is a special case, and not included by any definition, and any value
// after `pi/2` is negative). As a consequence we take the interpretation that `m= h + 1` where h is the highest
// harmonic, and split the difference. At the end of the day, if this sounds good enough that's all we care about.
double m = waves->back().frequency_mul + 1;
// should always be exactly an integer.
assert((unsigned int)m == m);
// In practice, the above is actually a multiplication on the gain of each term, and we already have those gains. To
// do this, run over the array and multiply them in.
for (auto &w : *waves) {
double n = w.frequency_mul;
// Sinc function. We know the parameter can never be zero.
double sigma = sin(PI * n / (2 * m));
w.gain *= sigma;
}
}
/**
* Normalize a set of waves.
* */
static void normalizeSeries(std::vector<syz_SineBankWave> *waves) {
double gsum = 0.0;
for (auto &w : *waves) {
gsum += w.gain;
}
double nf = 1.0 / gsum;
for (auto &w : *waves) {
w.gain *= nf;
}
}
static std::vector<syz_SineBankWave> buildSquareSeries(unsigned int partials) {
std::vector<syz_SineBankWave> out{};
for (unsigned int p = 0; p < partials; p++) {
double gain = 1.0 / (2 * p + 1);
out.emplace_back(syz_SineBankWave{(double)(p * 2 + 1), 0.0, gain});
}
sigmaApproximate(&out);
normalizeSeries(&out);
return out;
}
static std::vector<syz_SineBankWave> buildTriangleSeries(unsigned int partials) {
std::vector<syz_SineBankWave> out;
double sign = 1.0;
for (unsigned int i = 0; i < partials; i++) {
double n = 2 * i + 1;
out.emplace_back(syz_SineBankWave{(double)n, 0.0, sign / (n * n)});
sign *= -1.0;
}
// Don't sigma approximate: it makes things much worse audibly at low partial counts.
normalizeSeries(&out);
return out;
}
static std::vector<syz_SineBankWave> buildSawtoothSeries(unsigned int partials) {
std::vector<syz_SineBankWave> out;
// `$ x(t)=-{\frac {2}{\pi }}\sum _{k=1}^{\infty }{\frac {{\left(-1\right)}^{k}}{k}}\sin \left(2\pi kt\right) $`
// but we move the outer negation into the sum and let our normalizer handle the multiple on the gain.
double sign = -1.0;
for (unsigned int i = 1; i <= partials; i++) {
out.emplace_back(syz_SineBankWave{(double)i, 0.0, sign / (double)i});
sign *= -1.0;
}
// Don't sigma approximate: it makes things much worse audibly at low partial counts.
normalizeSeries(&out);
return out;
}
} // namespace synthizer
using namespace synthizer;
SYZ_CAPI void syz_initSineBankConfig(struct syz_SineBankConfig *cfg) { *cfg = syz_SineBankConfig{}; }
SYZ_CAPI syz_ErrorCode syz_createFastSineBankGenerator(syz_Handle *out, syz_Handle context,
const struct syz_SineBankConfig *bank_config, void *config,
void *userdata,
syz_UserdataFreeCallback *userdata_free_callback) {
SYZ_PROLOGUE(void) config;
auto ctx = fromC<Context>(context);
auto x = ctx->createObject<FastSineBankGenerator>(bank_config);
*out = toC(x);
return syz_handleSetUserdata(*out, userdata, userdata_free_callback);
SYZ_EPILOGUE
}
SYZ_CAPI syz_ErrorCode syz_createFastSineBankGeneratorSine(syz_Handle *out, syz_Handle context,
double initial_frequency, void *config, void *userdata,
syz_UserdataFreeCallback *userdata_free_callback) {
static const struct syz_SineBankWave wave { 1.0, 0.0, 1.0 };
struct syz_SineBankConfig cfg;
cfg.waves = &wave;
cfg.wave_count = 1;
cfg.initial_frequency = initial_frequency;
return syz_createFastSineBankGenerator(out, context, &cfg, config, userdata, userdata_free_callback);
}
static syz_ErrorCode createSineBankFromVec(syz_Handle *out, syz_Handle context, double initial_frequency,
std::vector<syz_SineBankWave> *waves, void *userdata,
syz_UserdataFreeCallback *userdata_free_callback) {
struct syz_SineBankConfig cfg;
cfg.waves = &(*waves)[0];
cfg.wave_count = waves->size();
cfg.initial_frequency = initial_frequency;
return syz_createFastSineBankGenerator(out, context, &cfg, NULL, userdata, userdata_free_callback);
}
SYZ_CAPI syz_ErrorCode syz_createFastSineBankGeneratorSquare(syz_Handle *out, syz_Handle context,
double initial_frequency, unsigned int partials,
void *config, void *userdata,
syz_UserdataFreeCallback *userdata_free_callback) {
SYZ_PROLOGUE(void) config;
auto waves = buildSquareSeries(partials);
return createSineBankFromVec(out, context, initial_frequency, &waves, userdata, userdata_free_callback);
SYZ_EPILOGUE
}
SYZ_CAPI syz_ErrorCode syz_createFastSineBankGeneratorTriangle(syz_Handle *out, syz_Handle context,
double initial_frequency, unsigned int partials,
void *config, void *userdata,
syz_UserdataFreeCallback *userdata_free_callback) {
SYZ_PROLOGUE(void) config;
auto waves = buildTriangleSeries(partials);
return createSineBankFromVec(out, context, initial_frequency, &waves, userdata, userdata_free_callback);
SYZ_EPILOGUE
}
SYZ_CAPI syz_ErrorCode syz_createFastSineBankGeneratorSaw(syz_Handle *out, syz_Handle context, double initial_frequency,
unsigned int partials, void *config, void *userdata,
syz_UserdataFreeCallback *userdata_free_callback) {
SYZ_PROLOGUE(void) config;
auto waves = buildSawtoothSeries(partials);
return createSineBankFromVec(out, context, initial_frequency, &waves, userdata, userdata_free_callback);
SYZ_EPILOGUE
}
/**
* Tests the accuracy of the FastSineBank.
*
* Constants are hardcoded in main.
* */
#include <cstdio>
#include <math.h>
#include <vector>
#include "synthizer/config.hpp"
#include "synthizer/fast_sine_bank.hpp"
#include "synthizer/math.hpp"
using namespace synthizer;
class SlowSineBank {
public:
SlowSineBank(double _frequency) : frequency(_frequency) {}
void fillBlock(float *block);
void addWave(const SineWaveConfig &wave) { this->waves.push_back(wave); }
double frequency;
std::vector<SineWaveConfig> waves;
double time = 0.0;
};
void SlowSineBank::fillBlock(float *block) {
// To match FastSineBank as much as possible, we increment time as we go, then resync it at the end.
double old_t = this->time;
for (std::size_t i = 0; i < config::BLOCK_SIZE; i++) {
for (auto &w : this->waves) {
double freq = w.freq_mul * this->frequency;
double t = 2 * PI * (freq * this->time + w.phase);
block[i] += ((float)w.gain) * sinf(t);
}
this->time = fmod(this->time + 1.0 / config::SR, 1.0);
}
this->time = fmod(old_t + config::BLOCK_SIZE / (double)config::SR, 1.0);
}
int main() {
FastSineBank fast{300.0};
SlowSineBank slow{300.0};
std::vector<SineWaveConfig> waves{{1.0, 0.0, 0.5}, {2.0, 0.1, 0.2}, {3.0, 0.03, 0.2}, {4.0, 0.0, 0.01}};
for (auto &w : waves) {
slow.addWave(w);
fast.addWave(w);
}
// This value was determined empirically, and then we decided whether we were good with it or not after the fact.
double max_diff = 0.0002;
for (std::size_t block = 0; block < 1000000; block++) {
float slow_res[config::BLOCK_SIZE] = {0.0f};
float fast_res[config::BLOCK_SIZE] = {0.0f};
slow.fillBlock(slow_res);
fast.fillBlock<config::BLOCK_SIZE, false>(fast_res);
for (std::size_t off = 0; off < config::BLOCK_SIZE; off++) {
double diff = slow_res[off] - fast_res[off];
if (fabs(diff) > max_diff) {
printf("Failed\n");
printf("Diff at %zu is %f\n", block * config::BLOCK_SIZE + off, diff);
printf("slow=%f fast=%f\n", slow_res[off], fast_res[off]);
printf("Block %zu offset %zu\n", block, off);
return 1;
}
}
}
return 0;
}
+1
-1
Metadata-Version: 1.0
Name: synthizer
Version: 0.11.6
Version: 0.12.0
Summary: UNKNOWN

@@ -5,0 +5,0 @@ Home-page: https://synthizer.github.io

@@ -11,3 +11,3 @@ import os

VERSION = "0.11.6"
VERSION = "0.12.0"

@@ -14,0 +14,0 @@ # A helper for rmtree. On Windows, read-only files can't be deleted by rmtree, so we make them not readonly.

@@ -9,3 +9,3 @@ cmake_minimum_required(VERSION 3.15.0)

# Synthizer version.
add_compile_definitions(SYZ_MAJOR=0 SYZ_MINOR=11 SYZ_PATCH=5)
add_compile_definitions(SYZ_MAJOR=0 SYZ_MINOR=11 SYZ_PATCH=6)

@@ -149,2 +149,3 @@ include(CTest)

src/generators/buffer.cpp
src/generators/fast_sine_bank.cpp
src/generators/noise.cpp

@@ -206,2 +207,3 @@ src/generators/streaming.cpp

example(events cpp)
example(fast_sine_bank cpp)
example(load_libsndfile c)

@@ -218,2 +220,3 @@ example(play_note c)

target_link_libraries(delay_line_test synthizer)
add_test(NAME delay_line COMMAND delay_line_test)

@@ -240,5 +243,7 @@ add_executable(file_test file_test.cpp)

target_link_libraries(test_latch synthizer)
add_test(NAME latch COMMAND test_latch)
add_executable(test_verify_properties test/verify_properties.cpp)
target_link_libraries(test_verify_properties synthizer)
add_test(NAME verify_properties COMMAND test_verify_properties)

@@ -251,19 +256,17 @@ # can't be an actual unit test until we have silent contexts.

target_link_libraries(test_generation_thread synthizer)
add_test(NAME generation_thread COMMAND test_generation_thread)
add_executable(test_double_refcount test/double_refcount.c)
target_link_libraries(test_double_refcount synthizer)
add_test(NAME double_refcount COMMAND test_double_refcount)
add_executable(test_property_automation_timeline test/property_automation_timeline.cpp)
target_link_libraries(test_property_automation_timeline synthizer)
add_test(NAME property_automation_timeline COMMAND test_property_automation_timeline)
add_executable(test_seeking test/seeking.cpp)
target_link_libraries(test_seeking synthizer)
add_test(NAME delay_line COMMAND delay_line_test)
add_test(NAME latch COMMAND test_latch)
add_test(NAME verify_properties COMMAND test_verify_properties)
add_test(NAME generation_thread COMMAND test_generation_thread)
add_test(NAME double_refcount COMMAND test_double_refcount)
add_test(NAME property_automation_timeline COMMAND test_property_automation_timeline)
add_executable(test_fast_sine_accuracy test/fast_sine_accuracy.cpp)
target_link_libraries(test_fast_sine_accuracy synthizer)
endif()

@@ -270,0 +273,0 @@

@@ -21,2 +21,3 @@ #pragma once

SYZ_OTYPE_AUTOMATION_BATCH,
SYZ_OTYPE_FAST_SINE_BANK_GENERATOR,
};

@@ -100,2 +101,4 @@

SYZ_P_SUGGESTED_AUTOMATION_TIME,
SYZ_P_FREQUENCY,
};

@@ -102,0 +105,0 @@

@@ -266,2 +266,35 @@ #pragma once

struct syz_SineBankWave {
double frequency_mul;
double phase;
double gain;
};
struct syz_SineBankConfig {
const struct syz_SineBankWave *waves;
unsigned long long wave_count;
double initial_frequency;
};
SYZ_CAPI void syz_initSineBankConfig(struct syz_SineBankConfig *cfg);
SYZ_CAPI syz_ErrorCode syz_createFastSineBankGenerator(syz_Handle *out, syz_Handle context,
const struct syz_SineBankConfig *bank_config, void *config,
void *userdata,
syz_UserdataFreeCallback *userdata_free_callback);
SYZ_CAPI syz_ErrorCode syz_createFastSineBankGeneratorSine(syz_Handle *out, syz_Handle context,
double initial_frequency, void *config, void *userdata,
syz_UserdataFreeCallback *userdata_free_callback);
SYZ_CAPI syz_ErrorCode syz_createFastSineBankGeneratorTriangle(syz_Handle *out, syz_Handle context,
double initial_frequency, unsigned int partials,
void *config, void *userdata,
syz_UserdataFreeCallback *userdata_free_callback);
SYZ_CAPI syz_ErrorCode syz_createFastSineBankGeneratorSquare(syz_Handle *out, syz_Handle context,
double initial_frequency, unsigned int partials,
void *config, void *userdata,
syz_UserdataFreeCallback *userdata_free_callback);
SYZ_CAPI syz_ErrorCode syz_createFastSineBankGeneratorSaw(syz_Handle *out, syz_Handle context, double initial_frequency,
unsigned int partials, void *config, void *userdata,
syz_UserdataFreeCallback *userdata_free_callback);
struct syz_RouteConfig {

@@ -268,0 +301,0 @@ double gain;

@@ -110,7 +110,8 @@ #pragma once

std::size_t can_lose = this->current_item - HISTORY_LENGTH;
auto keep_start = this->items.begin() + can_lose;
std::size_t drop_count = this->current_item - HISTORY_LENGTH;
auto keep_start = this->items.begin() + drop_count;
std::copy(keep_start, this->items.end(), this->items.begin());
this->items.erase(this->items.begin() + HISTORY_LENGTH, this->items.end());
this->current_item = HISTORY_LENGTH - 1;
this->items.erase(this->items.end() - drop_count, this->items.end());
// We have indices 0 to `HISTORY_LENGTH - 1` as the history; the actual current item is 1 after that.
this->current_item = HISTORY_LENGTH;
}

@@ -117,0 +118,0 @@

@@ -103,4 +103,6 @@ #pragma once

#define SINE_BANK_GENERATOR_PROPERTIES DOUBLE_P(SYZ_P_FREQUENCY, frequency, Frequency, 0.0, P_DOUBLE_MAX, 440.0)
#ifdef __cplusplus
}
#endif

@@ -7,4 +7,5 @@ #include "synthizer.h"

#include <cmath>
#include <cstdio>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <vector>

@@ -14,3 +15,3 @@

bool floatCmp(double a, double b) { return std::abs(a - b) < 0.000001; }
bool floatCmp(double a, double b) { return abs(a - b) < 0.000001; }

@@ -23,3 +24,3 @@ struct Point {

int main() {
void testCurve() {
double tick_delta = config::BLOCK_SIZE / (double)config::SR;

@@ -64,7 +65,7 @@ double time = 0.0;

printf("Expected %f but got %f at time %f\n", exp, x[0], time);
return 1;
exit(EXIT_FAILURE);
}
} else {
printf("Timeline ended early at time %f\n", time);
return 1;
exit(EXIT_FAILURE);
}

@@ -74,3 +75,63 @@ }

printf("Success\n");
}
class IdentityPoint {
public:
IdentityPoint(double t) : value(t) {}
double getTime() { return this->value; }
double value;
};
/**
* Test a timeline which "runs ahead". Exercises logic which gets rid of unneeded points.
* */
void testLongTimeline() {
auto tl = GenericTimeline<IdentityPoint, 10>();
unsigned int i = 0, last_added = 0;
// Add an initial 10 points.
for (i = 0; i < 10; i++) {
tl.addItem(IdentityPoint((double)i));
}
// We want the 10th point for convenience; always having one in the future for the next loop avoids a weird special
// case where the first iteration returns the wrong value w.r.t. what we check because there is no point "after" the
// start time.
last_added = i;
tl.addItem(IdentityPoint(last_added));
for (; i < 10000; i++) {
// Drive the timeline to time i-0.5, which gives us the next point as i.
tl.tick((double)i - 0.5);
// We know there are at least 10 points; check them all.
for (unsigned int d = 0; d < 10; d++) {
double p = (double)i - d;
auto val = tl.getItem(-d);
if (!val) {
printf("Expected point at %f but didn't find one", p);
exit(EXIT_FAILURE);
}
if ((*val)->value != (double)p) {
printf("Expected %f but got %f at %i\n", (double)p, (*val)->value, -d);
exit(EXIT_FAILURE);
}
}
// So the timeline always grows, add two items.
for (unsigned int _unused = 0; _unused < 2; _unused++) {
last_added += 1;
tl.addItem(IdentityPoint((double)last_added));
}
}
printf("Success\n");
}
int main() {
printf("Running curve test\n");
testCurve();
printf("Testing long GenericTimeline\n");
testLongTimeline();
return 0;
}
Metadata-Version: 1.0
Name: synthizer
Version: 0.11.6
Version: 0.12.0
Summary: UNKNOWN

@@ -5,0 +5,0 @@ Home-page: https://synthizer.github.io

@@ -23,2 +23,3 @@ MANIFEST.in

synthizer-vendored/examples/example_common.h
synthizer-vendored/examples/fast_sine_bank.cpp
synthizer-vendored/examples/load_libsndfile.c

@@ -59,2 +60,3 @@ synthizer-vendored/examples/play_note.c

synthizer-vendored/include/synthizer/faders.hpp
synthizer-vendored/include/synthizer/fast_sine_bank.hpp
synthizer-vendored/include/synthizer/filter_design.hpp

@@ -107,2 +109,3 @@ synthizer-vendored/include/synthizer/generation_thread.hpp

synthizer-vendored/include/synthizer/generators/buffer.hpp
synthizer-vendored/include/synthizer/generators/fast_sine_bank.hpp
synthizer-vendored/include/synthizer/generators/noise.hpp

@@ -158,2 +161,3 @@ synthizer-vendored/include/synthizer/generators/streaming.hpp

synthizer-vendored/src/generators/buffer.cpp
synthizer-vendored/src/generators/fast_sine_bank.cpp
synthizer-vendored/src/generators/noise.cpp

@@ -173,2 +177,3 @@ synthizer-vendored/src/generators/streaming.cpp

synthizer-vendored/test/effect_connection.cpp
synthizer-vendored/test/fast_sine_accuracy.cpp
synthizer-vendored/test/filter_repl.cpp

@@ -175,0 +180,0 @@ synthizer-vendored/test/generation_thread.cpp

@@ -5,2 +5,3 @@ #cython: auto_pickle=False

import threading
from collections import abc
from enum import Enum

@@ -14,2 +15,3 @@ import sys

from cpython.ref cimport PyObject, Py_DECREF, Py_INCREF
from weakref import ref

@@ -33,2 +35,3 @@ # We want the ability to acquire and release the GIL, which means making sure it's initialized.

cdef class SynthizerError(Exception):

@@ -49,2 +52,3 @@ cdef str message

cdef _checked(x):

@@ -59,99 +63,135 @@ if x != 0:

# These are descriptors for properties so that we don't have to continually deal with defining 6 line method pairs.
# These might eventually prove to be too slow, in which case we will have to convert to get_xxx and set_xxx methods.
# Unfortunately Cython doesn't give us a convenient way to generate such at compile time.
# Fortunately there's a way to migrate people forward if we must.
# This is the base class for all Synthizer properties
cdef class _PropertyBase:
def __delete__(self, instance):
raise RuntimeError("Deleting attributes from Synthizer objects is not possible")
cdef object _instance
cdef int property
def __init__(self, instance, property):
self._instance = ref(instance)
self.property = property
def _get_handle_checked(self):
# Any attempt to reference a non-existing object should raise an error
obj = self._instance()
if obj is None:
raise RuntimeError("Object no longer exists.")
handle = obj._get_handle()
if handle == 0:
raise RuntimeError("Object no longer exists.")
return handle
def _get_property(self):
return self.property
cdef class IntProperty(_PropertyBase):
cdef int property
cdef object conv_in
cdef object conv_out
def __init__(self, int property, conv_in = lambda x: x, conv_out = lambda x: x):
self.property = property
def __init__(self, object instance, int property, conv_in = lambda x: x, conv_out = lambda x: x):
super().__init__(instance, property)
self.conv_in = conv_in
self.conv_out = conv_out
def __get__(self, _BaseObject instance, owner):
@property
def value(self):
cdef int val
_checked(syz_getI(&val, instance.handle, self.property))
handle = self._get_handle_checked()
_checked(syz_getI(&val, handle, self.property))
return self.conv_out(val)
def __set__(self, _BaseObject instance, value):
_checked(syz_setI(instance.handle, self.property, self.conv_in(value)))
@value.setter
def value(self, value):
handle = self._get_handle_checked()
_checked(syz_setI(handle, self.property, self.conv_in(value)))
def enum_property(v, e):
return IntProperty(v, conv_in = lambda x: x.value, conv_out = lambda x: e(x))
def enum_property(instance, v, e):
return IntProperty(instance, v, conv_in = lambda x: x.value, conv_out = lambda x: e(x))
cdef class DoubleProperty(_PropertyBase):
cdef int property
def __init__(self, int property):
self.property = property
def __get__(self, _BaseObject instance, owner):
@property
def value(self):
cdef double val
_checked(syz_getD(&val, instance.handle, self.property))
handle = self._get_handle_checked()
_checked(syz_getD(&val, handle, self.property))
return val
def __set__(self, _BaseObject instance, double value):
_checked(syz_setD(instance.handle, self.property, value))
@value.setter
def value(self, double value):
handle = self._get_handle_checked()
_checked(syz_setD(handle, self.property, value))
cdef class Double3Property(_PropertyBase):
cdef int property
def __init__(self, int property):
self.property = property
def __get__(self, _BaseObject instance, owner):
@property
def value(self):
cdef double x, y, z
_checked(syz_getD3(&x, &y, &z, instance.handle, self.property))
handle = self._get_handle_checked()
_checked(syz_getD3(&x, &y, &z, handle, self.property))
return (x, y, z)
def __set__(self, _BaseObject instance, value):
_checked(syz_setD3(instance.handle, self.property, value[0], value[1], value[2]))
@value.setter
def value(self, value):
cdef double x, y, z
handle = self._get_handle_checked()
try:
x, y, z = value
except ValueError as e:
raise ValueError("Three doubles are required for Double3Property.")
_checked(syz_setD3(handle, self.property, x, y, z))
cdef class Double6Property(_PropertyBase):
cdef int property
def __init__(self, int property):
self.property = property
def __get__(self, _BaseObject instance, owner):
@property
def value(self):
cdef double x1, y1, z1, x2, y2, z2
_checked(syz_getD6(&x1, &y1, &z1, &x2, &y2, &z2, instance.handle, self.property))
handle = self._get_handle_checked()
_checked(syz_getD6(&x1, &y1, &z1, &x2, &y2, &z2, handle, self.property))
return (x1, y1, z1, x2, y2, z2)
def __set__(self, _BaseObject instance, value):
_checked(syz_setD6(instance.handle, self.property, value[0], value[1], value[2], value[3], value[4], value[5]))
@value.setter
def value(self, value):
cdef double x1, y1, z1, x2, y2, z2
handle = self._get_handle_checked()
try:
x1, y1, z1, x2, y2, z2 = value
except ValueError as e:
raise ValueError("Six doubles are required for Double6Property")
_checked(syz_setD6(handle, self.property, x1, y1, z1, x2, y2, z2))
cdef class BiquadProperty(_PropertyBase):
cdef int property
def __init__(self, int property):
self.property = property
@property
def value(self):
raise NotImplementedError("Filter properties cannot be read")
def __get__(self, _BaseObject instance, owner):
raise NotImplementedError("Filter properties cannot be read")
@value.setter
def value(self, BiquadConfig value):
handle = self._get_handle_checked()
_checked(syz_setBiquad(handle, self.property, &value.config))
def __set__(self, _BaseObject instance, BiquadConfig value):
_checked(syz_setBiquad(instance.handle, self.property, &value.config))
cdef class ObjectProperty(_PropertyBase):
cdef class ObjectProperty(_PropertyBase):
cdef int property
cdef object cls
def __init__(self, int property, cls):
self.property = property
def __init__(self, object instance, int property, cls):
super().__init__(instance, property)
self.cls = cls
def __get__(self, _BaseObject instance, owner):
raise NotImplementedError("Can't read object properties")
def __set__(self, _BaseObject instance, _BaseObject value):
_checked(syz_setO(instance.handle, self.property, value.handle if value else 0))
@property
def value(self):
raise NotImplementedError("Can't read object properties")
@value.setter
def value(self, _BaseObject value):
handle = self._get_handle_checked()
_checked(syz_setO(handle, self.property, value.handle if value else 0))
class LogLevel(Enum):

@@ -163,2 +203,3 @@ ERROR = SYZ_LOG_LEVEL_ERROR

class LoggingBackend(Enum):

@@ -168,2 +209,3 @@ NONE = SYZ_LOGGING_BACKEND_NONE

cpdef initialize(log_level=DEFAULT_VALUE, logging_backend=DEFAULT_VALUE,

@@ -211,2 +253,3 @@ libsndfile_path=DEFAULT_VALUE):

class PannerStrategy(Enum):

@@ -217,2 +260,3 @@ DELEGATE = SYZ_PANNER_STRATEGY_DELEGATE

class DistanceModel(Enum):

@@ -224,2 +268,3 @@ NONE = SYZ_DISTANCE_MODEL_NONE

cdef object _objects_by_handle = dict()

@@ -243,2 +288,3 @@ cdef object _objects_by_handle_mutex = threading.Lock()

cdef class _UserdataBox:

@@ -248,2 +294,3 @@ """An internal box for containing userdata. This exists so that we can have

buffers alive for stream handles."""
cdef object userdata

@@ -257,4 +304,7 @@ # Used by StreamHandle

cdef class _BaseObject:
cdef syz_Handle handle
cdef object __weakref__

@@ -269,2 +319,9 @@ def __init__(self, syz_Handle handle):

def __setattr__(self, name, value):
# If an object of type _PropertyBase matching name already is on this object, prevent it from being reassigned
obj = getattr(self, name, None)
if isinstance(obj, _PropertyBase):
raise RuntimeError("You cannot directly reassign property objects.")
super().__setattr__(name, value)
def dec_ref(self):

@@ -282,2 +339,5 @@ """Decrement the reference count. Must be called in order to not leak Synthizer objects."""

cpdef syz_Handle _get_handle(self):
return self.handle
cdef object _get_userdata_box(self):

@@ -305,5 +365,13 @@ cdef void *userdata

cdef class Pausable(_BaseObject):
"""Base class for anything which can be paused. Adds pause and play methods."""
cdef public DoubleProperty current_time, suggested_automation_time
def __init__(self, syz_Handle handle):
super().__init__(handle)
self.current_time = DoubleProperty(self, SYZ_P_CURRENT_TIME)
self.suggested_automation_time = DoubleProperty(self, SYZ_P_SUGGESTED_AUTOMATION_TIME)
def play(self):

@@ -315,6 +383,7 @@ _checked(syz_play(self.handle))

cdef class Event:
"""Base class for all Synthizer events"""
cpdef public Context context
cpdef public object source
cdef public Context context
cdef public object source

@@ -329,5 +398,15 @@ def __init__(self, context, source):

cdef class LoopedEvent(Event):
pass
cdef class UserAutomationEvent(Event):
cdef public unsigned long long param
def __init__(self, context, source, param):
super().__init__(context, source)
self.param = param
cdef _convert_event(syz_Event event):

@@ -338,4 +417,8 @@ if event.type == SYZ_EVENT_TYPE_FINISHED:

return LoopedEvent(_handle_to_object(event.context), _handle_to_object(event.source))
elif event.type == SYZ_EVENT_TYPE_USER_AUTOMATION:
return UserAutomationEvent(_handle_to_object(event.context), _handle_to_object(event.source), event.payload.user_automation.param)
cdef class BiquadConfig:
cdef syz_BiquadConfig config

@@ -372,2 +455,3 @@

cdef class Context(Pausable):

@@ -380,2 +464,7 @@ """The Synthizer context represents an open audio device and groups all Synthizer objects created with it into one unit.

cdef public DoubleProperty gain, default_distance_ref, default_distance_max, default_rolloff, default_closeness_boost, default_closeness_boost_distance
cdef public Double3Property position
cdef public Double6Property orientation
cdef public IntProperty default_distance_model, default_panner_strategy
def __init__(self, enable_events=False):

@@ -387,14 +476,13 @@ cdef syz_Handle handle

self.enable_events()
self.gain = DoubleProperty(self, SYZ_P_GAIN)
self.position = Double3Property(self, SYZ_P_POSITION)
self.orientation = Double6Property(self, SYZ_P_ORIENTATION)
self.default_distance_model = enum_property(self, SYZ_P_DEFAULT_DISTANCE_MODEL, lambda x: DistanceModel(x))
self.default_distance_ref = DoubleProperty(self, SYZ_P_DEFAULT_DISTANCE_REF)
self.default_distance_max = DoubleProperty(self, SYZ_P_DEFAULT_DISTANCE_MAX)
self.default_rolloff = DoubleProperty(self, SYZ_P_DEFAULT_ROLLOFF)
self.default_closeness_boost = DoubleProperty(self, SYZ_P_DEFAULT_CLOSENESS_BOOST)
self.default_closeness_boost_distance = DoubleProperty(self, SYZ_P_DEFAULT_CLOSENESS_BOOST_DISTANCE)
self.default_panner_strategy = enum_property(self, SYZ_P_DEFAULT_PANNER_STRATEGY, lambda x: PannerStrategy(x))
gain = DoubleProperty(SYZ_P_GAIN)
position = Double3Property(SYZ_P_POSITION)
orientation = Double6Property(SYZ_P_ORIENTATION)
default_distance_model = enum_property(SYZ_P_DEFAULT_DISTANCE_MODEL, lambda x: DistanceModel(x))
default_distance_ref = DoubleProperty(SYZ_P_DEFAULT_DISTANCE_REF)
default_distance_max = DoubleProperty(SYZ_P_DEFAULT_DISTANCE_MAX)
default_rolloff = DoubleProperty(SYZ_P_DEFAULT_ROLLOFF)
default_closeness_boost = DoubleProperty(SYZ_P_DEFAULT_CLOSENESS_BOOST)
default_closeness_boost_distance = DoubleProperty(SYZ_P_DEFAULT_CLOSENESS_BOOST_DISTANCE)
default_panner_strategy = enum_property(SYZ_P_DEFAULT_PANNER_STRATEGY, lambda x: PannerStrategy(x))
cpdef config_route(self, _BaseObject output, _BaseObject input, gain = 1.0, fade_time = 0.01, BiquadConfig filter = None):

@@ -439,2 +527,3 @@ cdef syz_RouteConfig config

# Used to keep errors alive per the custom stream error lifetime rules in synthizer.h.

@@ -567,2 +656,3 @@ cdef class WrappedStream:

cdef class StreamHandle(_BaseObject):

@@ -625,9 +715,19 @@ """Wraps the C API concept of a StreamHandle, which may be created in a variety of ways."""

cdef class Generator(Pausable):
"""Base class for all generators."""
pitch_bend = DoubleProperty(SYZ_P_PITCH_BEND)
gain = DoubleProperty(SYZ_P_GAIN)
cdef public DoubleProperty gain, pitch_bend
def __init__(self, syz_Handle handle):
super().__init__(handle)
self.pitch_bend = DoubleProperty(self, SYZ_P_PITCH_BEND)
self.gain = DoubleProperty(self, SYZ_P_GAIN)
cdef class StreamingGenerator(Generator):
cdef public IntProperty looping
cdef public DoubleProperty playback_position
def __init__(self, _handle = None):

@@ -637,4 +737,5 @@ if _handle is None:

super().__init__(_handle)
self.playback_position = DoubleProperty(self, SYZ_P_PLAYBACK_POSITION)
self.looping = IntProperty(self, SYZ_P_LOOPING, conv_in = int, conv_out = bool)
@staticmethod

@@ -667,3 +768,2 @@ def from_stream_params(context, protocol, path):

@staticmethod

@@ -675,4 +775,2 @@ def from_stream_handle(Context context, StreamHandle stream):

playback_position = DoubleProperty(SYZ_P_PLAYBACK_POSITION)
looping = IntProperty(SYZ_P_LOOPING, conv_in = int, conv_out = bool)

@@ -682,2 +780,10 @@ cdef class Source(Pausable):

cdef public DoubleProperty gain
cdef public BiquadProperty filter
def __init__(self, syz_Handle handle):
super().__init__(handle)
self.gain = DoubleProperty(self, SYZ_P_GAIN)
self.filter = BiquadProperty(self, SYZ_P_FILTER)
cpdef add_generator(self, generator):

@@ -693,5 +799,2 @@ """Add a generator to this source."""

gain = DoubleProperty(SYZ_P_GAIN)
filter = BiquadProperty(SYZ_P_FILTER)
cdef class DirectSource(Source) :

@@ -704,3 +807,7 @@ def __init__(self, context):

cdef class AngularPannedSource(Source):
cdef public DoubleProperty azimuth, elevation
def __init__(self, context, panner_strategy = PannerStrategy.DELEGATE, azimuth=0.0, elevation=0.0):

@@ -711,7 +818,10 @@ cdef syz_Handle ctx = context._get_handle_checked(Context)

super().__init__(out)
self.azimuth = DoubleProperty(self, SYZ_P_AZIMUTH)
self.elevation = DoubleProperty(self, SYZ_P_ELEVATION)
azimuth = DoubleProperty(SYZ_P_AZIMUTH)
elevation = DoubleProperty(SYZ_P_ELEVATION)
cdef class ScalarPannedSource(Source):
cdef public DoubleProperty panning_scalar
def __init__(self, context, panner_strategy=PannerStrategy.DELEGATE, panning_scalar=0.0):

@@ -722,4 +832,4 @@ cdef syz_Handle ctx = context._get_handle_checked(Context)

super().__init__(out)
self.panning_scalar = DoubleProperty(self, SYZ_P_PANNING_SCALAR)
panning_scalar = DoubleProperty(SYZ_P_PANNING_SCALAR)

@@ -729,2 +839,7 @@ cdef class Source3D(Source):

cdef public DoubleProperty distance_ref, distance_max, rolloff, closeness_boost, closeness_boost_distance
cdef public Double3Property position
cdef public Double6Property orientation
cdef public IntProperty distance_model
def __init__(self, context, panner_strategy=PannerStrategy.DELEGATE, position=(0.0, 0.0, 0.0)):

@@ -735,11 +850,11 @@ cdef syz_Handle ctx = context._get_handle_checked(Context)

super().__init__(out)
self.distance_model = enum_property(self, SYZ_P_DISTANCE_MODEL, lambda x: DistanceModel(x))
self.distance_ref = DoubleProperty(self, SYZ_P_DISTANCE_REF)
self.distance_max = DoubleProperty(self, SYZ_P_DISTANCE_MAX)
self.rolloff = DoubleProperty(self, SYZ_P_ROLLOFF)
self.closeness_boost = DoubleProperty(self, SYZ_P_CLOSENESS_BOOST)
self.closeness_boost_distance = DoubleProperty(self, SYZ_P_CLOSENESS_BOOST_DISTANCE)
self.position = Double3Property(self, SYZ_P_POSITION)
self.orientation = Double6Property(self, SYZ_P_ORIENTATION)
distance_model = enum_property(SYZ_P_DISTANCE_MODEL, lambda x: DistanceModel(x))
distance_ref = DoubleProperty(SYZ_P_DISTANCE_REF)
distance_max = DoubleProperty(SYZ_P_DISTANCE_MAX)
rolloff = DoubleProperty(SYZ_P_ROLLOFF)
closeness_boost = DoubleProperty(SYZ_P_CLOSENESS_BOOST)
closeness_boost_distance = DoubleProperty(SYZ_P_CLOSENESS_BOOST_DISTANCE)
position = Double3Property(SYZ_P_POSITION)
orientation = Double6Property(SYZ_P_ORIENTATION)

@@ -854,3 +969,9 @@ cdef class Buffer(_BaseObject):

cdef class BufferGenerator(Generator):
cdef public IntProperty looping
cdef public ObjectProperty buffer
cdef public DoubleProperty playback_position
def __init__(self, context):

@@ -860,8 +981,7 @@ cdef syz_Handle handle

super().__init__(handle)
self.buffer = ObjectProperty(self, SYZ_P_BUFFER, Buffer)
self.playback_position = DoubleProperty(self, SYZ_P_PLAYBACK_POSITION)
self.looping = IntProperty(self, SYZ_P_LOOPING, conv_in = int, conv_out = bool)
buffer = ObjectProperty(SYZ_P_BUFFER, Buffer)
playback_position = DoubleProperty(SYZ_P_PLAYBACK_POSITION)
looping = IntProperty(SYZ_P_LOOPING, conv_in = int, conv_out = bool)
class NoiseType(Enum):

@@ -872,3 +992,6 @@ UNIFORM = SYZ_NOISE_TYPE_UNIFORM

cdef class NoiseGenerator(Generator):
cdef public IntProperty noise_type
def __init__(self, context, channels = 1):

@@ -878,13 +1001,19 @@ cdef syz_Handle handle

super().__init__(handle)
self.noise_type = enum_property(self, SYZ_P_NOISE_TYPE, lambda x: NoiseType(x))
noise_type = enum_property(SYZ_P_NOISE_TYPE, lambda x: NoiseType(x))
cdef class GlobalEffect(_BaseObject):
cdef public BiquadProperty filter_input
cdef public DoubleProperty gain
def __init__(self, syz_Handle handle):
super().__init__(handle)
self.gain = DoubleProperty(self, SYZ_P_GAIN)
self.filter_input = BiquadProperty(self, SYZ_P_FILTER_INPUT)
cpdef reset(self):
_checked(syz_effectReset(self.handle))
gain = DoubleProperty(SYZ_P_GAIN)
filter_input = BiquadProperty(SYZ_P_FILTER_INPUT)
cdef class EchoTapConfig:

@@ -931,2 +1060,7 @@ """An echo tap. Passed to GlobalEcho.set_taps."""

cdef class GlobalFdnReverb(GlobalEffect):
cdef public DoubleProperty mean_free_path, t60, late_reflections_lf_rolloff
cdef public late_reflections_lf_reference, late_reflections_hf_rolloff, late_reflections_hf_reference
cdef public late_reflections_diffusion, late_reflections_modulation_depth, late_reflections_modulation_frequency, late_reflections_delay
def __init__(self, context):

@@ -936,12 +1070,99 @@ cdef syz_Handle handle

super().__init__(handle)
self.mean_free_path = DoubleProperty(self, SYZ_P_MEAN_FREE_PATH)
self.t60 = DoubleProperty(self, SYZ_P_T60)
self.late_reflections_lf_rolloff = DoubleProperty(self, SYZ_P_LATE_REFLECTIONS_LF_ROLLOFF)
self.late_reflections_lf_reference = DoubleProperty(self, SYZ_P_LATE_REFLECTIONS_LF_REFERENCE)
self.late_reflections_hf_rolloff = DoubleProperty(self, SYZ_P_LATE_REFLECTIONS_HF_ROLLOFF)
self.late_reflections_hf_reference = DoubleProperty(self, SYZ_P_LATE_REFLECTIONS_HF_REFERENCE)
self.late_reflections_diffusion = DoubleProperty(self, SYZ_P_LATE_REFLECTIONS_DIFFUSION)
self.late_reflections_modulation_depth = DoubleProperty(self, SYZ_P_LATE_REFLECTIONS_MODULATION_DEPTH)
self.late_reflections_modulation_frequency = DoubleProperty(self, SYZ_P_LATE_REFLECTIONS_MODULATION_FREQUENCY)
self.late_reflections_delay = DoubleProperty(self, SYZ_P_LATE_REFLECTIONS_DELAY)
mean_free_path = DoubleProperty(SYZ_P_MEAN_FREE_PATH)
t60 = DoubleProperty(SYZ_P_T60)
late_reflections_lf_rolloff = DoubleProperty(SYZ_P_LATE_REFLECTIONS_LF_ROLLOFF)
late_reflections_lf_reference = DoubleProperty(SYZ_P_LATE_REFLECTIONS_LF_REFERENCE)
late_reflections_hf_rolloff = DoubleProperty(SYZ_P_LATE_REFLECTIONS_HF_ROLLOFF)
late_reflections_hf_reference = DoubleProperty(SYZ_P_LATE_REFLECTIONS_HF_REFERENCE)
late_reflections_diffusion = DoubleProperty(SYZ_P_LATE_REFLECTIONS_DIFFUSION)
late_reflections_modulation_depth = DoubleProperty(SYZ_P_LATE_REFLECTIONS_MODULATION_DEPTH)
late_reflections_modulation_frequency = DoubleProperty(SYZ_P_LATE_REFLECTIONS_MODULATION_FREQUENCY)
late_reflections_delay = DoubleProperty(SYZ_P_LATE_REFLECTIONS_DELAY)
class InterpolationType(Enum):
NONE = SYZ_INTERPOLATION_TYPE_NONE
LINEAR = SYZ_INTERPOLATION_TYPE_LINEAR
cdef _convert_values_to_automation_array(obj):
"""Convert either a single number or sequence of numbers to an array of 6 doubles, used internally by Synthizer AutomationPoints."""
if isinstance(obj, abc.Sequence):
length = len(obj)
if length == 1:
return (obj[0], 0.0, 0.0, 0.0, 0.0, 0.0)
elif length == 3:
return (obj[0], obj[1], obj[2], 0.0, 0.0, 0.0)
elif length == 6:
return tuple(obj)
else:
raise ValueError("Automated property values require 1, 3, or 6 floats.")
return (float(obj), 0.0, 0.0, 0.0, 0.0, 0.0)
cdef class AutomationBatch(_BaseObject):
def __init__(self, context):
cdef syz_Handle handle
_checked(syz_createAutomationBatch(&handle, context._get_handle(), NULL, NULL))
super().__init__(handle)
cpdef append_property(self, float time, _PropertyBase property, value, interpolation_type):
cdef syz_AutomationCommand command
cdef syz_AutomationAppendPropertyCommand appendPropertyCommand
cdef syz_AutomationPoint automationPoint
automationPoint.interpolation_type = interpolation_type.value
automationPoint.values = _convert_values_to_automation_array(value)
automationPoint.flags = 0
appendPropertyCommand.property = property._get_property()
appendPropertyCommand.point = automationPoint
command.target = property._get_handle_checked()
command.time = time
command.type = SYZ_AUTOMATION_COMMAND_APPEND_PROPERTY
command.flags = 0
command.params.append_to_property = appendPropertyCommand
_checked(syz_automationBatchAddCommands(self.handle, 1, &command))
return self
cpdef clear_all_properties(self, _BaseObject target):
cdef syz_AutomationCommand command
command.target = target._get_handle()
command.type = SYZ_AUTOMATION_COMMAND_CLEAR_ALL_PROPERTIES
command.time = 0
command.flags = 0
_checked(syz_automationBatchAddCommands(self.handle, 1, &command))
return self
cpdef clear_property(self, _PropertyBase property):
cdef syz_AutomationCommand command
cdef syz_AutomationClearPropertyCommand clearPropertyCommand
clearPropertyCommand.property = property._get_Property()
command.target = property._get_handle_checked()
command.type = SYZ_AUTOMATION_COMMAND_CLEAR_PROPERTY
command.time = 0.0
command.flags = 0
command.params.clear_property = clearPropertyCommand
_checked(syz_automationBatchAddCommands(self.handle, 1, &command))
return self
cpdef clear_user_events(self, _BaseObject target):
cdef syz_AutomationCommand command
command.target = target._get_handle()
command.type = SYZ_AUTOMATION_COMMAND_CLEAR_EVENTS
command.time = 0.0
command.flags = 0
_checked(syz_automationBatchAddCommands(self.handle, 1, &command))
return self
cpdef send_user_event(self, float time, _BaseObject target, unsigned long long param):
cdef syz_AutomationSendUserEventCommand sendUserEventCommand
cdef syz_AutomationCommand command
sendUserEventCommand.param = param
command.target = target._get_handle()
command.type = SYZ_AUTOMATION_COMMAND_SEND_USER_EVENT
command.time = time
command.flags = 0
command.params.send_user_event = sendUserEventCommand
_checked(syz_automationBatchAddCommands(self.handle, 1, &command))
return self
def execute(self):
_checked(syz_automationBatchExecute(self.handle))

Sorry, the diff of this file is too big to display