synthio: apply biquad filters during synthesis
This commit is contained in:
parent
fed8d5825b
commit
51027974e5
|
@ -41,7 +41,7 @@ static const mp_arg_t note_properties[] = {
|
|||
{ MP_QSTR_bend, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_INT(0) } },
|
||||
{ MP_QSTR_waveform, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_envelope, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_filter, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_INT(1) } },
|
||||
{ MP_QSTR_filter, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_NONE } },
|
||||
{ MP_QSTR_ring_frequency, MP_ARG_OBJ, {.u_obj = MP_ROM_INT(0) } },
|
||||
{ MP_QSTR_ring_bend, MP_ARG_OBJ, {.u_obj = MP_ROM_INT(0) } },
|
||||
{ MP_QSTR_ring_waveform, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_NONE } },
|
||||
|
@ -56,6 +56,7 @@ static const mp_arg_t note_properties[] = {
|
|||
//| envelope: Optional[Envelope] = None,
|
||||
//| amplitude: BlockInput = 0.0,
|
||||
//| bend: BlockInput = 0.0,
|
||||
//| filter: Optional[Biquad] = None,
|
||||
//| ring_frequency: float = 0.0,
|
||||
//| ring_bend: float = 0.0,
|
||||
//| ring_waveform: Optional[ReadableBuffer] = 0.0,
|
||||
|
@ -97,17 +98,21 @@ MP_PROPERTY_GETSET(synthio_note_frequency_obj,
|
|||
(mp_obj_t)&synthio_note_get_frequency_obj,
|
||||
(mp_obj_t)&synthio_note_set_frequency_obj);
|
||||
|
||||
//| filter: bool
|
||||
//| """True if the note should be processed via the synthesizer's FIR filter."""
|
||||
//| filter: Optional[Biquad]
|
||||
//| """If not None, the output of this Note is filtered according to the provided coefficients.
|
||||
//|
|
||||
//| Construct an appropriate filter by calling a filter-making method on the
|
||||
//| `Synthesizer` object where you plan to play the note, as filter coefficients depend
|
||||
//| on the sample rate"""
|
||||
STATIC mp_obj_t synthio_note_get_filter(mp_obj_t self_in) {
|
||||
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
return mp_obj_new_bool(common_hal_synthio_note_get_filter(self));
|
||||
return common_hal_synthio_note_get_filter_obj(self);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_filter_obj, synthio_note_get_filter);
|
||||
|
||||
STATIC mp_obj_t synthio_note_set_filter(mp_obj_t self_in, mp_obj_t arg) {
|
||||
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
common_hal_synthio_note_set_filter(self, mp_obj_is_true(arg));
|
||||
common_hal_synthio_note_set_filter(self, arg);
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_filter_obj, synthio_note_set_filter);
|
||||
|
|
|
@ -9,8 +9,8 @@ typedef enum synthio_bend_mode_e synthio_bend_mode_t;
|
|||
mp_float_t common_hal_synthio_note_get_frequency(synthio_note_obj_t *self);
|
||||
void common_hal_synthio_note_set_frequency(synthio_note_obj_t *self, mp_float_t value);
|
||||
|
||||
bool common_hal_synthio_note_get_filter(synthio_note_obj_t *self);
|
||||
void common_hal_synthio_note_set_filter(synthio_note_obj_t *self, bool value);
|
||||
mp_obj_t common_hal_synthio_note_get_filter_obj(synthio_note_obj_t *self);
|
||||
void common_hal_synthio_note_set_filter(synthio_note_obj_t *self, mp_obj_t biquad);
|
||||
|
||||
mp_obj_t common_hal_synthio_note_get_panning(synthio_note_obj_t *self);
|
||||
void common_hal_synthio_note_set_panning(synthio_note_obj_t *self, mp_obj_t value);
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include <math.h>
|
||||
#include "shared-bindings/synthio/Biquad.h"
|
||||
#include "shared-module/synthio/Biquad.h"
|
||||
|
||||
mp_obj_t common_hal_synthio_new_lpf(mp_float_t w0, mp_float_t Q) {
|
||||
mp_float_t s = MICROPY_FLOAT_C_FUN(sin)(w0);
|
||||
|
@ -92,3 +93,47 @@ mp_obj_t common_hal_synthio_new_bpf(mp_float_t w0, mp_float_t Q) {
|
|||
|
||||
return namedtuple_make_new((const mp_obj_type_t *)&synthio_biquad_type_obj, MP_ARRAY_SIZE(out_args), 0, out_args);
|
||||
}
|
||||
|
||||
#define BIQUAD_SHIFT (16)
|
||||
STATIC int32_t biquad_scale_arg_obj(mp_obj_t arg) {
|
||||
return (int32_t)MICROPY_FLOAT_C_FUN(round)(MICROPY_FLOAT_C_FUN(ldexp)(mp_obj_get_float(arg), BIQUAD_SHIFT));
|
||||
}
|
||||
void synthio_biquad_filter_assign(biquad_filter_state *st, mp_obj_t biquad_obj) {
|
||||
if (biquad_obj != mp_const_none) {
|
||||
mp_arg_validate_type(biquad_obj, (const mp_obj_type_t *)&synthio_biquad_type_obj, MP_QSTR_filter);
|
||||
mp_obj_tuple_t *biquad = (mp_obj_tuple_t *)MP_OBJ_TO_PTR(biquad_obj);
|
||||
st->a1 = biquad_scale_arg_obj(biquad->items[0]);
|
||||
st->a2 = biquad_scale_arg_obj(biquad->items[1]);
|
||||
st->b0 = biquad_scale_arg_obj(biquad->items[2]);
|
||||
st->b1 = biquad_scale_arg_obj(biquad->items[3]);
|
||||
st->b2 = biquad_scale_arg_obj(biquad->items[4]);
|
||||
}
|
||||
}
|
||||
|
||||
void synthio_biquad_filter_samples(biquad_filter_state *st, int32_t *out, const int32_t *in, size_t n, size_t stride) {
|
||||
int32_t a1 = st->a1;
|
||||
int32_t a2 = st->a2;
|
||||
int32_t b0 = st->b0;
|
||||
int32_t b1 = st->b1;
|
||||
int32_t b2 = st->b2;
|
||||
|
||||
int32_t x0 = st->x[0];
|
||||
int32_t x1 = st->x[1];
|
||||
int32_t y0 = st->y[0];
|
||||
int32_t y1 = st->y[1];
|
||||
|
||||
for (; n; --n, in += stride, out += stride) {
|
||||
int16_t input = *in;
|
||||
int32_t output = (b0 * input + b1 * x0 + b2 * x1 - a1 * y0 - a2 * y1) >> BIQUAD_SHIFT;
|
||||
|
||||
x1 = x0;
|
||||
x0 = input;
|
||||
y1 = y0;
|
||||
y0 = output;
|
||||
*out = output;
|
||||
}
|
||||
st->x[0] = x0;
|
||||
st->x[1] = x1;
|
||||
st->y[0] = y0;
|
||||
st->y[1] = y1;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* This file is part of the Micro Python project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2023 Jeff Epler for Adafruit Industries
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "py/obj.h"
|
||||
|
||||
typedef struct {
|
||||
int32_t a1, a2, b0, b1, b2;
|
||||
int32_t x[2], y[2];
|
||||
} biquad_filter_state;
|
||||
|
||||
void synthio_biquad_filter_assign(biquad_filter_state *st, mp_obj_t biquad_obj);
|
||||
void synthio_biquad_filter_samples(biquad_filter_state *st, int32_t *out, const int32_t *in, size_t n, size_t stride);
|
|
@ -40,12 +40,13 @@ void common_hal_synthio_note_set_frequency(synthio_note_obj_t *self, mp_float_t
|
|||
self->frequency_scaled = synthio_frequency_convert_float_to_scaled(val);
|
||||
}
|
||||
|
||||
bool common_hal_synthio_note_get_filter(synthio_note_obj_t *self) {
|
||||
return self->filter;
|
||||
mp_obj_t common_hal_synthio_note_get_filter_obj(synthio_note_obj_t *self) {
|
||||
return self->filter_obj;
|
||||
}
|
||||
|
||||
void common_hal_synthio_note_set_filter(synthio_note_obj_t *self, bool value_in) {
|
||||
self->filter = value_in;
|
||||
void common_hal_synthio_note_set_filter(synthio_note_obj_t *self, mp_obj_t filter_in) {
|
||||
synthio_biquad_filter_assign(&self->filter_state, filter_in);
|
||||
self->filter_obj = filter_in;
|
||||
}
|
||||
|
||||
mp_float_t common_hal_synthio_note_get_ring_frequency(synthio_note_obj_t *self) {
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "shared-module/synthio/__init__.h"
|
||||
#include "shared-module/synthio/Biquad.h"
|
||||
#include "shared-module/synthio/LFO.h"
|
||||
#include "shared-bindings/synthio/__init__.h"
|
||||
|
||||
|
@ -37,12 +38,14 @@ typedef struct synthio_note_obj {
|
|||
|
||||
mp_float_t frequency, ring_frequency;
|
||||
mp_obj_t waveform_obj, envelope_obj, ring_waveform_obj;
|
||||
mp_obj_t filter_obj;
|
||||
|
||||
biquad_filter_state filter_state;
|
||||
|
||||
int32_t sample_rate;
|
||||
|
||||
int32_t frequency_scaled;
|
||||
int32_t ring_frequency_scaled, ring_frequency_bent;
|
||||
bool filter;
|
||||
|
||||
mp_buffer_info_t waveform_buf;
|
||||
mp_buffer_info_t ring_waveform_buf;
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
#include "shared-module/synthio/__init__.h"
|
||||
#include "shared-bindings/synthio/__init__.h"
|
||||
#include "shared-module/synthio/Biquad.h"
|
||||
#include "shared-module/synthio/Note.h"
|
||||
#include "py/runtime.h"
|
||||
#include <math.h>
|
||||
|
@ -309,37 +310,15 @@ static void synth_note_into_buffer(synthio_synth_t *synth, int chan, int32_t *ou
|
|||
}
|
||||
}
|
||||
|
||||
STATIC void run_fir(synthio_synth_t *synth, int32_t *out_buffer32, uint16_t dur) {
|
||||
int16_t *coeff = (int16_t *)synth->filter_bufinfo.buf;
|
||||
size_t fir_len = synth->filter_bufinfo.len;
|
||||
int32_t *in_buf = synth->filter_buffer;
|
||||
|
||||
|
||||
int synth_chan = synth->channel_count;
|
||||
// FIR and copy values to output buffer
|
||||
for (int16_t i = 0; i < dur * synth_chan; i++) {
|
||||
int32_t acc = 0;
|
||||
for (size_t j = 0; j < fir_len; j++) {
|
||||
// shift 5 here is good for up to 32 filtered voices, else might wrap
|
||||
acc = acc + (in_buf[j * synth_chan] * (coeff[j] >> 5));
|
||||
}
|
||||
*out_buffer32++ = acc >> 10;
|
||||
in_buf++;
|
||||
}
|
||||
|
||||
// Move values down so that they get filtered next time
|
||||
memmove(synth->filter_buffer, &synth->filter_buffer[dur * synth_chan], fir_len * sizeof(int32_t) * synth_chan);
|
||||
}
|
||||
|
||||
STATIC bool synthio_synth_get_note_filtered(mp_obj_t note_obj) {
|
||||
STATIC mp_obj_t synthio_synth_get_note_filter(mp_obj_t note_obj) {
|
||||
if (note_obj == mp_const_none) {
|
||||
return false;
|
||||
return mp_const_none;
|
||||
}
|
||||
if (!mp_obj_is_small_int(note_obj)) {
|
||||
synthio_note_obj_t *note = MP_OBJ_TO_PTR(note_obj);
|
||||
return note->filter;
|
||||
return note->filter_obj;
|
||||
}
|
||||
return true;
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
void synthio_synth_synthesize(synthio_synth_t *synth, uint8_t **bufptr, uint32_t *buffer_length, uint8_t channel) {
|
||||
|
@ -360,30 +339,24 @@ void synthio_synth_synthesize(synthio_synth_t *synth, uint8_t **bufptr, uint32_t
|
|||
synth->span.dur -= dur;
|
||||
|
||||
int32_t out_buffer32[dur * synth->channel_count];
|
||||
|
||||
if (synth->filter_buffer) {
|
||||
int32_t *filter_start = &synth->filter_buffer[synth->filter_bufinfo.len * synth->channel_count];
|
||||
memset(filter_start, 0, dur * synth->channel_count * sizeof(int32_t));
|
||||
|
||||
for (int chan = 0; chan < CIRCUITPY_SYNTHIO_MAX_CHANNELS; chan++) {
|
||||
mp_obj_t note_obj = synth->span.note_obj[chan];
|
||||
if (!synthio_synth_get_note_filtered(note_obj)) {
|
||||
continue;
|
||||
}
|
||||
synth_note_into_buffer(synth, chan, filter_start, dur);
|
||||
}
|
||||
|
||||
run_fir(synth, out_buffer32, dur);
|
||||
} else {
|
||||
memset(out_buffer32, 0, sizeof(out_buffer32));
|
||||
}
|
||||
memset(out_buffer32, 0, sizeof(out_buffer32));
|
||||
|
||||
for (int chan = 0; chan < CIRCUITPY_SYNTHIO_MAX_CHANNELS; chan++) {
|
||||
mp_obj_t note_obj = synth->span.note_obj[chan];
|
||||
if (synth->filter_buffer && synthio_synth_get_note_filtered(note_obj)) {
|
||||
continue;
|
||||
mp_obj_t filter_obj = synthio_synth_get_note_filter(note_obj);
|
||||
if (filter_obj == mp_const_none) {
|
||||
synth_note_into_buffer(synth, chan, out_buffer32, dur);
|
||||
} else {
|
||||
synthio_note_obj_t *note = MP_OBJ_TO_PTR(note_obj);
|
||||
int32_t filter_buffer32[dur * synth->channel_count];
|
||||
memset(filter_buffer32, 0, sizeof(filter_buffer32));
|
||||
|
||||
synth_note_into_buffer(synth, chan, filter_buffer32, dur);
|
||||
int synth_chan = synth->channel_count;
|
||||
for (int i = 0; i < synth_chan; i++) {
|
||||
synthio_biquad_filter_samples(¬e->filter_state, &out_buffer32[i], &filter_buffer32[i], dur, i);
|
||||
}
|
||||
}
|
||||
synth_note_into_buffer(synth, chan, out_buffer32, dur);
|
||||
}
|
||||
|
||||
int16_t *out_buffer16 = (int16_t *)(void *)synth->buffers[synth->buffer_index];
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
import sys
|
||||
|
||||
sys.path.insert(
|
||||
0, f"{__file__.rpartition('/')[0] or '.'}/../../../../frozen/Adafruit_CircuitPython_Wave"
|
||||
)
|
||||
|
||||
import random
|
||||
import audiocore
|
||||
import synthio
|
||||
from ulab import numpy as np
|
||||
import adafruit_wave as wave
|
||||
|
||||
random.seed(9)
|
||||
|
||||
envelope = synthio.Envelope(
|
||||
attack_time=0, decay_time=0, release_time=0, attack_level=0.8, sustain_level=1.0
|
||||
)
|
||||
|
||||
SAMPLE_SIZE = 1024
|
||||
VOLUME = 14700
|
||||
sine = np.array(
|
||||
np.sin(np.linspace(0, 2 * np.pi, SAMPLE_SIZE, endpoint=False)) * VOLUME,
|
||||
dtype=np.int16,
|
||||
)
|
||||
noise = np.array([random.randint(-VOLUME, VOLUME) for i in range(SAMPLE_SIZE)], dtype=np.int16)
|
||||
bend_out = np.linspace(0, 32767, num=SAMPLE_SIZE, endpoint=True, dtype=np.int16)
|
||||
|
||||
|
||||
def synthesize(synth):
|
||||
for waveform in (sine, None, noise):
|
||||
for biquad in (
|
||||
None,
|
||||
synth.low_pass_filter(330),
|
||||
synth.low_pass_filter(660),
|
||||
synth.high_pass_filter(330),
|
||||
synth.high_pass_filter(660),
|
||||
synth.band_pass_filter(330),
|
||||
synth.band_pass_filter(660),
|
||||
):
|
||||
n = synthio.Note(
|
||||
frequency=80,
|
||||
envelope=envelope,
|
||||
filter=biquad,
|
||||
waveform=waveform,
|
||||
bend=synthio.LFO(bend_out, once=True, rate=1 / 2, scale=5),
|
||||
)
|
||||
|
||||
synth.press(n)
|
||||
print(synth, n)
|
||||
yield 2 * 48000 // 256
|
||||
synth.release_all()
|
||||
yield 36
|
||||
|
||||
|
||||
with wave.open("biquad.wav", "w") as f:
|
||||
f.setnchannels(1)
|
||||
f.setsampwidth(2)
|
||||
f.setframerate(48000)
|
||||
synth = synthio.Synthesizer(sample_rate=48000)
|
||||
for n in synthesize(synth):
|
||||
for i in range(n):
|
||||
result, data = audiocore.get_buffer(synth)
|
||||
f.writeframes(data)
|
|
@ -1,53 +0,0 @@
|
|||
import sys
|
||||
|
||||
sys.path.insert(
|
||||
0, f"{__file__.rpartition('/')[0] or '.'}/../../../../frozen/Adafruit_CircuitPython_Wave"
|
||||
)
|
||||
|
||||
import random
|
||||
import audiocore
|
||||
import synthio
|
||||
from ulab import numpy as np
|
||||
import adafruit_wave as wave
|
||||
import mkfilter
|
||||
|
||||
random.seed(9)
|
||||
|
||||
envelope = synthio.Envelope(
|
||||
attack_time=0.1, decay_time=0.05, release_time=0.2, attack_level=0.8, sustain_level=0.8
|
||||
)
|
||||
|
||||
SAMPLE_SIZE = 1024
|
||||
bend_out = np.linspace(0, 32767, num=SAMPLE_SIZE, endpoint=True, dtype=np.int16)
|
||||
|
||||
filter_rectangular = mkfilter.LPF(48000, 800, 13)
|
||||
filter_rectangular_big = mkfilter.LPF(48000, 800, 59)
|
||||
filter_blackman = mkfilter.LPF(48000, 800, 59, win=mkfilter.blackman)
|
||||
print(filter_blackman)
|
||||
|
||||
|
||||
def synthesize(synth):
|
||||
n = synthio.Note(
|
||||
frequency=120,
|
||||
envelope=envelope,
|
||||
filter=True,
|
||||
bend=synthio.LFO(bend_out, once=True, rate=1 / 2, scale=5),
|
||||
)
|
||||
|
||||
synth.press(n)
|
||||
print(synth, n)
|
||||
yield 2 * 48000 // 256
|
||||
synth.release_all()
|
||||
yield 36
|
||||
|
||||
|
||||
with wave.open("fir.wav", "w") as f:
|
||||
f.setnchannels(1)
|
||||
f.setsampwidth(2)
|
||||
f.setframerate(48000)
|
||||
for filter_coeffs in [None, filter_rectangular, filter_rectangular_big, filter_blackman]:
|
||||
synth = synthio.Synthesizer(sample_rate=48000, filter=filter_coeffs)
|
||||
for n in synthesize(synth):
|
||||
for i in range(n):
|
||||
result, data = audiocore.get_buffer(synth)
|
||||
f.writeframes(data)
|
|
@ -1,105 +0,0 @@
|
|||
try:
|
||||
from ulab import numpy as np
|
||||
except ImportError:
|
||||
import numpy as np
|
||||
|
||||
|
||||
def lpf(fS, f, N, win=lambda N: 1):
|
||||
if not (N & 1):
|
||||
raise ValueError("filter length must be odd")
|
||||
h = np.sinc(2 * f / fS * (np.arange(N) - (N - 1) / 2))
|
||||
h = h * win(N)
|
||||
return h * (1 / np.sum(h))
|
||||
|
||||
|
||||
def hpf(fS, f, N, win=lambda N: 1):
|
||||
if not (N & 1):
|
||||
raise ValueError("filter length must be odd")
|
||||
h = -lpf(fS, f, N)
|
||||
h = h * win(N)
|
||||
h[(N - 1) // 2] += 1
|
||||
return h
|
||||
|
||||
|
||||
def brf(fS, fL, NL, fH, NH, win=lambda N: 1):
|
||||
hlpf = lpf(fS, fL, NL, win)
|
||||
hhpf = hpf(fS, fH, NH, win)
|
||||
|
||||
if NH > NL:
|
||||
h = hhpf
|
||||
h[(NH - NL) // 2 : (NH - NL) // 2 + NL] += hlpf
|
||||
else:
|
||||
h = hlpf
|
||||
h[(NL - NH) // 2 : (NL - NH) // 2 + NH] += hhpf
|
||||
|
||||
return h
|
||||
|
||||
|
||||
def bpf(fS, fL, NL, fH, NH, win=lambda N: 1):
|
||||
hlpf = lpf(fS, fL, NL, win)
|
||||
hhpf = hpf(fS, fH, NH, win)
|
||||
return np.convolve(hlpf, hhpf)
|
||||
|
||||
|
||||
def blackman(M):
|
||||
n = np.arange(1 - M, M, 2)
|
||||
return 0.42 + 0.5 * np.cos(np.pi * n / (M - 1)) + 0.08 * np.cos(2.0 * np.pi * n / (M - 1))
|
||||
|
||||
|
||||
def tosynthio(coeffs):
|
||||
result = np.array(coeffs * 32767, dtype=np.int16)
|
||||
return trim_zeros(result)
|
||||
|
||||
|
||||
def trim_zeros(arr):
|
||||
i = 0
|
||||
j = len(arr) - 1
|
||||
while i < len(arr) and arr[i] == 0:
|
||||
i += 1
|
||||
while j > i and arr[j] == 0:
|
||||
j -= 1
|
||||
return arr[i : j + 1]
|
||||
|
||||
|
||||
# fiiir.com uses factor 4.6 for blackman window, 0.91 for rectangular
|
||||
def ntaps(fS, fB, factor=4.6):
|
||||
b = fB / fS
|
||||
return round(factor / b) | 1
|
||||
|
||||
|
||||
def LPF(*args, **kw):
|
||||
return tosynthio(lpf(*args, **kw))
|
||||
|
||||
|
||||
def HPF(*args, **kw):
|
||||
return tosynthio(hpf(*args, **kw))
|
||||
|
||||
|
||||
def BRF(*args, **kw):
|
||||
return tosynthio(brf(*args, **kw))
|
||||
|
||||
|
||||
def BPF(*args, **kw):
|
||||
return tosynthio(bpf(*args, **kw))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("lpf(24000, 2040, 13) # 1920Hz transition window")
|
||||
print(list(lpf(24000, 2040, 13)))
|
||||
|
||||
print("hpf(24000, 9600, 13) # 960Hz transition window")
|
||||
print(list(hpf(24000, 9600, 23)))
|
||||
|
||||
print("bpf(24000, 1200, 11, 3960, 15) # 2400Hz, 1600Hz transition windows")
|
||||
print(list(bpf(24000, 1200, 11, 3960, 15)))
|
||||
|
||||
print("brf(24000, 960, 19, 2400, 13) # 1200, 1800Hz transition windows")
|
||||
brf_tst = brf(24000, 960, 19, 2400, 13)
|
||||
print(brf_tst)
|
||||
|
||||
print("brf(24000, 960, 13, 2400, 19) # 1200, 1800Hz transition windows")
|
||||
brf_tst = brf(24000, 960, 13, 2400, 19)
|
||||
print(brf_tst)
|
||||
|
||||
print("lpf(1, 0.1, 59, blackman) # 1920Hz transition window, blackman")
|
||||
print(lpf(1, 0.1, 59, blackman))
|
|
@ -1,10 +1,10 @@
|
|||
()
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||
(Note(frequency=830.6076004423605, panning=0.0, amplitude=1.0, bend=0.0, waveform=None, envelope=None, filter=True, ring_frequency=0.0, ring_bend=0.0, ring_waveform=None),)
|
||||
(Note(frequency=830.6076004423605, panning=0.0, amplitude=1.0, bend=0.0, waveform=None, envelope=None, filter=None, ring_frequency=0.0, ring_bend=0.0, ring_waveform=None),)
|
||||
[-16383, -16383, -16383, -16383, 16382, 16382, 16382, 16382, 16382, -16383, -16383, -16383, -16383, -16383, 16382, 16382, 16382, 16382, 16382, -16383, -16383, -16383, -16383, -16383]
|
||||
(Note(frequency=830.6076004423605, panning=0.0, amplitude=1.0, bend=0.0, waveform=None, envelope=None, filter=True, ring_frequency=0.0, ring_bend=0.0, ring_waveform=None), Note(frequency=830.6076004423605, panning=0.0, amplitude=1.0, bend=0.0, waveform=None, envelope=None, filter=True, ring_frequency=0.0, ring_bend=0.0, ring_waveform=None))
|
||||
(Note(frequency=830.6076004423605, panning=0.0, amplitude=1.0, bend=0.0, waveform=None, envelope=None, filter=None, ring_frequency=0.0, ring_bend=0.0, ring_waveform=None), Note(frequency=830.6076004423605, panning=0.0, amplitude=1.0, bend=0.0, waveform=None, envelope=None, filter=None, ring_frequency=0.0, ring_bend=0.0, ring_waveform=None))
|
||||
[-1, -1, -1, -1, -1, -1, -1, -1, 28045, -1, -1, -1, -1, -28046, -1, -1, -1, -1, 28045, -1, -1, -1, -1, -28046]
|
||||
(Note(frequency=830.6076004423605, panning=0.0, amplitude=1.0, bend=0.0, waveform=None, envelope=None, filter=True, ring_frequency=0.0, ring_bend=0.0, ring_waveform=None),)
|
||||
(Note(frequency=830.6076004423605, panning=0.0, amplitude=1.0, bend=0.0, waveform=None, envelope=None, filter=None, ring_frequency=0.0, ring_bend=0.0, ring_waveform=None),)
|
||||
[-1, -1, -1, 28045, -1, -1, -1, -1, -1, -1, -1, -1, 28045, -1, -1, -1, -1, -28046, -1, -1, -1, -1, 28045, -1]
|
||||
(-5242, 5241)
|
||||
(-10484, 10484)
|
||||
|
|
Loading…
Reference in New Issue