Skip to content

Instantly share code, notes, and snippets.

@Xenakios
Created October 13, 2025 18:53
Show Gist options
  • Select an option

  • Save Xenakios/35d53ce6a2c2d86c15164af168b3097c to your computer and use it in GitHub Desktop.

Select an option

Save Xenakios/35d53ce6a2c2d86c15164af168b3097c to your computer and use it in GitHub Desktop.
#include <pybind11/pybind11.h>
#include <pybind11/buffer_info.h>
#include <pybind11/stl.h>
#include <pybind11/numpy.h>
#include "../Common/xap_breakpoint_envelope.h"
#include "sst/basic-blocks/dsp/EllipticBlepOscillators.h"
#include "sst/basic-blocks/dsp/DPWSawPulseOscillator.h"
#include <print>
namespace py = pybind11;
inline py::array_t<float> generate_tone(int tone_type, xenakios::Envelope &pitch_env,
xenakios::Envelope &volume_env, double sr, double duration)
{
tone_type = std::clamp(tone_type, 0, 5);
pitch_env.sortPoints();
volume_env.sortPoints();
int chans = 1;
int frames = duration * sr;
py::buffer_info binfo(
nullptr, /* Pointer to buffer */
sizeof(float), /* Size of one scalar */
py::format_descriptor<float>::format(), /* Python struct-style format descriptor */
2, /* Number of dimensions */
{chans, frames}, /* Buffer dimensions */
{sizeof(float) * frames, /* Strides (in bytes) for each index */
sizeof(float)});
py::array_t<float> output_audio{binfo};
sst::basic_blocks::dsp::EBApproxSin<> osc_sin;
sst::basic_blocks::dsp::EBTri<> osc_tri;
sst::basic_blocks::dsp::EBSaw<> osc_saw;
sst::basic_blocks::dsp::EBPulse<> osc_pulse;
sst::basic_blocks::dsp::DPWSawOscillator<> osc_saw_dpw;
sst::basic_blocks::dsp::DPWPulseOscillator<> osc_pulse_dpw;
osc_sin.setSampleRate(sr);
osc_tri.setSampleRate(sr);
osc_saw.setSampleRate(sr);
osc_pulse.setSampleRate(sr);
const size_t blocksize = 8;
int framecount = 0;
float *writebuf = output_audio.mutable_data(0);
volume_env.clearOutputBlock();
while (framecount < frames)
{
size_t framestoprocess = std::min<int>(blocksize, frames - framecount);
double tpos = framecount / sr;
double pitch = pitch_env.getValueAtPosition(tpos);
pitch = std::clamp(pitch, 0.0, 136.0);
double hz = 440.0 * std::pow(2.0, 1.0 / 12 * (pitch - 69.0));
if (tone_type == 0)
osc_sin.setFrequency(hz);
else if (tone_type == 1)
osc_tri.setFrequency(hz);
else if (tone_type == 2)
osc_saw.setFrequency(hz);
else if (tone_type == 3)
osc_pulse.setFrequency(hz);
else if (tone_type == 4)
osc_saw_dpw.setFrequency(hz, 1.0 / sr);
else if (tone_type == 5)
osc_pulse_dpw.setFrequency(hz, 1.0 / sr);
volume_env.processBlock(tpos, sr, 0, blocksize);
for (int i = 0; i < framestoprocess; ++i)
{
float sample = 0.0f;
if (tone_type == 0)
sample = osc_sin.step();
else if (tone_type == 1)
sample = osc_tri.step();
else if (tone_type == 2)
sample = osc_saw.step();
else if (tone_type == 3)
sample = osc_pulse.step();
else if (tone_type == 4)
sample = osc_saw_dpw.step();
else if (tone_type == 5)
sample = osc_pulse_dpw.step();
// double gain = xenakios::decibelsToGain(volume_env.outputBlock[i]);
double gain = volume_env.outputBlock[i];
gain = std::clamp(gain, 0.0, 1.0);
gain = gain * gain * gain;
writebuf[framecount + i] = sample * gain * 0.8;
}
framecount += blocksize;
}
return output_audio;
}
void init_py4(py::module_ &m, py::module_ &m_const)
{
using namespace pybind11::literals;
m.def("generate_tone", &generate_tone, "tone_type"_a, "pitch"_a, "volume"_a, "samplerate"_a,
"duration"_a);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment