diff --git a/conf.py b/conf.py index 6c30a43abb1f1..9e185fd6a4de1 100644 --- a/conf.py +++ b/conf.py @@ -225,6 +225,7 @@ def autoapi_prepare_jinja_env(jinja_env): "ports/nordic/peripherals", "ports/nordic/usb", "ports/raspberrypi/sdk", + "ports/raspberrypi/pioasm", "ports/raspberrypi/lib", "ports/silabs/gecko_sdk", "ports/silabs/tools", diff --git a/ports/unix/variants/coverage/mpconfigvariant.mk b/ports/unix/variants/coverage/mpconfigvariant.mk index 9721c2803e25c..628b0f5b9699a 100644 --- a/ports/unix/variants/coverage/mpconfigvariant.mk +++ b/ports/unix/variants/coverage/mpconfigvariant.mk @@ -56,6 +56,7 @@ SRC_BITMAP := \ shared-bindings/synthio/LFO.c \ shared-bindings/synthio/Note.c \ shared-bindings/synthio/Biquad.c \ + shared-bindings/synthio/BlockBiquad.c \ shared-bindings/synthio/Synthesizer.c \ shared-bindings/traceback/__init__.c \ shared-bindings/util.c \ @@ -87,6 +88,7 @@ SRC_BITMAP := \ shared-module/synthio/LFO.c \ shared-module/synthio/Note.c \ shared-module/synthio/Biquad.c \ + shared-module/synthio/BlockBiquad.c \ shared-module/synthio/Synthesizer.c \ shared-module/traceback/__init__.c \ shared-module/zlib/__init__.c \ diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index 61516561822c7..dc3d35d8df22f 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -717,6 +717,7 @@ SRC_SHARED_MODULE_ALL = \ supervisor/__init__.c \ supervisor/StatusBar.c \ synthio/Biquad.c \ + synthio/BlockBiquad.c \ synthio/LFO.c \ synthio/Math.c \ synthio/MidiTrack.c \ diff --git a/shared-bindings/synthio/BlockBiquad.c b/shared-bindings/synthio/BlockBiquad.c new file mode 100644 index 0000000000000..055bd3bd2667c --- /dev/null +++ b/shared-bindings/synthio/BlockBiquad.c @@ -0,0 +1,158 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2023 Jeff Epler for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "py/enum.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/synthio/BlockBiquad.h" +#include "shared-bindings/util.h" + +//| class FilterMode: +//| """The type of filter""" +//| +//| LOW_PASS: FilterMode +//| """A low-pass filter""" +//| HIGH_PASS: FilterMode +//| """A high-pass filter""" +//| BAND_PASS: FilterMode +//| """A band-pass filter""" +//| NOTCH: FilterMode +//| """A notch filter""" +//| + +MAKE_ENUM_VALUE(synthio_filter_mode_type, mode, LOW_PASS, SYNTHIO_LOW_PASS); +MAKE_ENUM_VALUE(synthio_filter_mode_type, mode, HIGH_PASS, SYNTHIO_HIGH_PASS); +MAKE_ENUM_VALUE(synthio_filter_mode_type, mode, BAND_PASS, SYNTHIO_BAND_PASS); +MAKE_ENUM_VALUE(synthio_filter_mode_type, mode, NOTCH, SYNTHIO_NOTCH); + +MAKE_ENUM_MAP(synthio_filter_mode) { + MAKE_ENUM_MAP_ENTRY(mode, LOW_PASS), + MAKE_ENUM_MAP_ENTRY(mode, HIGH_PASS), + MAKE_ENUM_MAP_ENTRY(mode, BAND_PASS), + MAKE_ENUM_MAP_ENTRY(mode, NOTCH), +}; + +static MP_DEFINE_CONST_DICT(synthio_filter_mode_locals_dict, synthio_filter_mode_locals_table); + +MAKE_PRINTER(synthio, synthio_filter_mode); + +MAKE_ENUM_TYPE(synthio, FilterMode, synthio_filter_mode); + +static synthio_filter_mode validate_synthio_filter_mode(mp_obj_t obj, qstr arg_name) { + return cp_enum_value(&synthio_filter_mode_type, obj, arg_name); +} + +//| class BlockBiquad: +//| def __init__( +//| self, +//| mode: FilterMode, +//| frequency: BlockInput, +//| Q: BlockInput = 0.7071067811865475, +//| ) -> None: +//| """Construct a biquad filter object with dynamic center frequency & q factor +//| +//| Since ``frequency`` and ``Q`` are `BlockInput` objects, they can +//| be varied dynamically. Internally, this is evaluated as "direct form 1" +//| biquad filter. +//| +//| The internal filter state x[] and y[] is not updated when the filter +//| coefficients change, and there is no theoretical justification for why +//| this should result in a stable filter output. However, in practice, +//| slowly varying the filter's characteristic frequency and sharpness +//| appears to work as you'd expect.""" + +static const mp_arg_t block_biquad_properties[] = { + { MP_QSTR_mode, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_OBJ_NULL } }, + { MP_QSTR_frequency, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_OBJ_NULL } }, + { MP_QSTR_Q, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL } }, +}; + +static mp_obj_t synthio_block_biquad_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_mode, ARG_frequency, ARG_Q }; + + mp_arg_val_t args[MP_ARRAY_SIZE(block_biquad_properties)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(block_biquad_properties), block_biquad_properties, args); + + if (args[ARG_Q].u_obj == MP_OBJ_NULL) { + args[ARG_Q].u_obj = mp_obj_new_float(MICROPY_FLOAT_CONST(0.7071067811865475)); + } + + synthio_filter_mode mode = validate_synthio_filter_mode(args[ARG_mode].u_obj, MP_QSTR_mode); + return common_hal_synthio_block_biquad_new(mode, args[ARG_frequency].u_obj, args[ARG_Q].u_obj); +} + +//| +//| mode: FilterMode +//| """The mode of filter (read-only)""" +static mp_obj_t synthio_block_biquad_get_mode(mp_obj_t self_in) { + synthio_block_biquad_t *self = MP_OBJ_TO_PTR(self_in); + return cp_enum_find(&synthio_filter_mode_type, common_hal_synthio_block_biquad_get_mode(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(synthio_block_biquad_get_mode_obj, synthio_block_biquad_get_mode); + +MP_PROPERTY_GETTER(synthio_block_biquad_mode_obj, + (mp_obj_t)&synthio_block_biquad_get_mode_obj); + +//| +//| frequency: BlockInput +//| """The central frequency (in Hz) of the filter""" +static mp_obj_t synthio_block_biquad_get_frequency(mp_obj_t self_in) { + synthio_block_biquad_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_synthio_block_biquad_get_frequency(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(synthio_block_biquad_get_frequency_obj, synthio_block_biquad_get_frequency); + +static mp_obj_t synthio_block_biquad_set_frequency(mp_obj_t self_in, mp_obj_t arg) { + synthio_block_biquad_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_synthio_block_biquad_set_frequency(self, arg); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(synthio_block_biquad_set_frequency_obj, synthio_block_biquad_set_frequency); +MP_PROPERTY_GETSET(synthio_block_biquad_frequency_obj, + (mp_obj_t)&synthio_block_biquad_get_frequency_obj, + (mp_obj_t)&synthio_block_biquad_set_frequency_obj); + + +//| +//| Q: BlockInput +//| """The sharpness (Q) of the filter""" +//| +static mp_obj_t synthio_block_biquad_get_Q(mp_obj_t self_in) { + synthio_block_biquad_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_synthio_block_biquad_get_Q(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(synthio_block_biquad_get_Q_obj, synthio_block_biquad_get_Q); + +static mp_obj_t synthio_block_biquad_set_Q(mp_obj_t self_in, mp_obj_t arg) { + synthio_block_biquad_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_synthio_block_biquad_set_Q(self, arg); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(synthio_block_biquad_set_Q_obj, synthio_block_biquad_set_Q); +MP_PROPERTY_GETSET(synthio_block_biquad_Q_obj, + (mp_obj_t)&synthio_block_biquad_get_Q_obj, + (mp_obj_t)&synthio_block_biquad_set_Q_obj); + +static const mp_rom_map_elem_t synthio_block_biquad_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_mode), MP_ROM_PTR(&synthio_block_biquad_mode_obj) }, + { MP_ROM_QSTR(MP_QSTR_frequency), MP_ROM_PTR(&synthio_block_biquad_frequency_obj) }, + { MP_ROM_QSTR(MP_QSTR_Q), MP_ROM_PTR(&synthio_block_biquad_Q_obj) }, +}; +static MP_DEFINE_CONST_DICT(synthio_block_biquad_locals_dict, synthio_block_biquad_locals_dict_table); + +static void block_biquad_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)kind; + properties_print_helper(print, self_in, block_biquad_properties, MP_ARRAY_SIZE(block_biquad_properties)); +} + +MP_DEFINE_CONST_OBJ_TYPE( + synthio_block_biquad_type_obj, + MP_QSTR_BlockBiquad, + MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, + make_new, synthio_block_biquad_make_new, + locals_dict, &synthio_block_biquad_locals_dict, + print, block_biquad_print + ); diff --git a/shared-bindings/synthio/BlockBiquad.h b/shared-bindings/synthio/BlockBiquad.h new file mode 100644 index 0000000000000..a61b60263230d --- /dev/null +++ b/shared-bindings/synthio/BlockBiquad.h @@ -0,0 +1,28 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2023 Jeff Epler for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" + +extern const mp_obj_type_t synthio_block_biquad_type_obj; +extern const mp_obj_type_t synthio_filter_mode_type; +typedef struct synthio_block_biquad synthio_block_biquad_t; + +typedef enum { + SYNTHIO_LOW_PASS, SYNTHIO_HIGH_PASS, SYNTHIO_BAND_PASS, SYNTHIO_NOTCH +} synthio_filter_mode; + + +mp_obj_t common_hal_synthio_block_biquad_get_Q(synthio_block_biquad_t *self); +void common_hal_synthio_block_biquad_set_Q(synthio_block_biquad_t *self, mp_obj_t Q); + +mp_obj_t common_hal_synthio_block_biquad_get_frequency(synthio_block_biquad_t *self); +void common_hal_synthio_block_biquad_set_frequency(synthio_block_biquad_t *self, mp_obj_t frequency); + +synthio_filter_mode common_hal_synthio_block_biquad_get_mode(synthio_block_biquad_t *self); + +mp_obj_t common_hal_synthio_block_biquad_new(synthio_filter_mode mode, mp_obj_t frequency, mp_obj_t Q); diff --git a/shared-bindings/synthio/Synthesizer.c b/shared-bindings/synthio/Synthesizer.c index 87ea44de6948d..94595013b494c 100644 --- a/shared-bindings/synthio/Synthesizer.c +++ b/shared-bindings/synthio/Synthesizer.c @@ -287,13 +287,13 @@ MP_PROPERTY_GETTER(synthio_synthesizer_blocks_obj, //| """Maximum polyphony of the synthesizer (read-only class property)""" //| -//| def low_pass_filter(cls, frequency: float, q_factor: float = 0.7071067811865475) -> Biquad: +//| def low_pass_filter(cls, frequency: float, Q: float = 0.7071067811865475) -> Biquad: //| """Construct a low-pass filter with the given parameters. //| //| ``frequency``, called f0 in the cookbook, is the corner frequency in Hz //| of the filter. //| -//| ``q_factor``, called ``Q`` in the cookbook. Controls how peaked the response will be at the cutoff frequency. A large value makes the response more peaked. +//| ``Q`` controls how peaked the response will be at the cutoff frequency. A large value makes the response more peaked. //| """ enum passfilter_arg_e { ARG_f0, ARG_Q }; @@ -328,15 +328,13 @@ static mp_obj_t synthio_synthesizer_lpf(size_t n_pos, const mp_obj_t *pos_args, MP_DEFINE_CONST_FUN_OBJ_KW(synthio_synthesizer_lpf_fun_obj, 1, synthio_synthesizer_lpf); -//| def high_pass_filter( -//| cls, frequency: float, q_factor: float = 0.7071067811865475 -//| ) -> Biquad: +//| def high_pass_filter(cls, frequency: float, Q: float = 0.7071067811865475) -> Biquad: //| """Construct a high-pass filter with the given parameters. //| //| ``frequency``, called f0 in the cookbook, is the corner frequency in Hz //| of the filter. //| -//| ``q_factor``, called ``Q`` in the cookbook. Controls how peaked the response will be at the cutoff frequency. A large value makes the response more peaked. +//| ``Q`` controls how peaked the response will be at the cutoff frequency. A large value makes the response more peaked. //| """ static mp_obj_t synthio_synthesizer_hpf(size_t n_pos, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -358,15 +356,13 @@ static mp_obj_t synthio_synthesizer_hpf(size_t n_pos, const mp_obj_t *pos_args, } -//| def band_pass_filter( -//| cls, frequency: float, q_factor: float = 0.7071067811865475 -//| ) -> Biquad: +//| def band_pass_filter(cls, frequency: float, Q: float = 0.7071067811865475) -> Biquad: //| """Construct a band-pass filter with the given parameters. //| //| ``frequency``, called f0 in the cookbook, is the center frequency in Hz //| of the filter. //| -//| ``q_factor``, called ``Q`` in the cookbook. Controls how peaked the response will be at the cutoff frequency. A large value makes the response more peaked. +//| ``Q`` Controls how peaked the response will be at the cutoff frequency. A large value makes the response more peaked. //| //| The coefficients are scaled such that the filter has a 0dB peak gain. //| """ diff --git a/shared-bindings/synthio/__init__.c b/shared-bindings/synthio/__init__.c index fbc5cd559afb5..207d7afafa89f 100644 --- a/shared-bindings/synthio/__init__.c +++ b/shared-bindings/synthio/__init__.c @@ -17,6 +17,7 @@ #include "shared-bindings/synthio/__init__.h" #include "shared-bindings/synthio/Biquad.h" +#include "shared-bindings/synthio/BlockBiquad.h" #include "shared-bindings/synthio/LFO.h" #include "shared-bindings/synthio/Math.h" #include "shared-bindings/synthio/MidiTrack.h" @@ -307,6 +308,8 @@ MP_DEFINE_CONST_FUN_OBJ_VAR(synthio_lfo_tick_obj, 1, synthio_lfo_tick); static const mp_rom_map_elem_t synthio_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_synthio) }, { MP_ROM_QSTR(MP_QSTR_Biquad), MP_ROM_PTR(&synthio_biquad_type_obj) }, + { MP_ROM_QSTR(MP_QSTR_BlockBiquad), MP_ROM_PTR(&synthio_block_biquad_type_obj) }, + { MP_ROM_QSTR(MP_QSTR_FilterMode), MP_ROM_PTR(&synthio_filter_mode_type) }, { MP_ROM_QSTR(MP_QSTR_Math), MP_ROM_PTR(&synthio_math_type) }, { MP_ROM_QSTR(MP_QSTR_MathOperation), MP_ROM_PTR(&synthio_math_operation_type) }, { MP_ROM_QSTR(MP_QSTR_MidiTrack), MP_ROM_PTR(&synthio_miditrack_type) }, diff --git a/shared-module/synthio/Biquad.c b/shared-module/synthio/Biquad.c index 366d516455224..1418b86fd17bb 100644 --- a/shared-module/synthio/Biquad.c +++ b/shared-module/synthio/Biquad.c @@ -6,6 +6,7 @@ #include #include "shared-bindings/synthio/Biquad.h" +#include "shared-bindings/synthio/BlockBiquad.h" #include "shared-module/synthio/Biquad.h" mp_obj_t common_hal_synthio_new_lpf(mp_float_t w0, mp_float_t Q) { @@ -74,20 +75,27 @@ 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 (15) 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); + if (biquad_obj == mp_const_none) { + return; + } + if (mp_obj_is_type(biquad_obj, &synthio_block_biquad_type_obj)) { + return; + } + if (mp_obj_is_type(biquad_obj, (const mp_obj_type_t *)&synthio_biquad_type_obj)) { 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]); + return; } + mp_raise_TypeError_varg(MP_ERROR_TEXT("%q must be of type %q or %q, not %q"), MP_QSTR_filter, MP_QSTR_Biquad, MP_QSTR_BlockBiquad, mp_obj_get_type(biquad_obj)->name); + } void synthio_biquad_filter_reset(biquad_filter_state *st) { diff --git a/shared-module/synthio/Biquad.h b/shared-module/synthio/Biquad.h index b33a6230caf86..a0dac47a2f93e 100644 --- a/shared-module/synthio/Biquad.h +++ b/shared-module/synthio/Biquad.h @@ -8,6 +8,8 @@ #include "py/obj.h" +#define BIQUAD_SHIFT (15) + typedef struct { int32_t a1, a2, b0, b1, b2; int32_t x[2], y[2]; diff --git a/shared-module/synthio/BlockBiquad.c b/shared-module/synthio/BlockBiquad.c new file mode 100644 index 0000000000000..361a5a0ce0115 --- /dev/null +++ b/shared-module/synthio/BlockBiquad.c @@ -0,0 +1,130 @@ + +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2023 Jeff Epler for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include +#include "shared-bindings/synthio/BlockBiquad.h" +#include "shared-module/synthio/BlockBiquad.h" +#include "shared-module/synthio/block.h" + +typedef struct { + mp_float_t s, c; +} sincos_result_t; + +#define FOUR_OVER_PI (4 / M_PI) +static void fast_sincos(mp_float_t theta, sincos_result_t *result) { + mp_float_t x = (theta * FOUR_OVER_PI) - 1; + mp_float_t x2 = x * x, x3 = x2 * x, x4 = x2 * x2, x5 = x2 * x3; + mp_float_t c0 = 0.70708592, + c1x = -0.55535724 * x, + c2x2 = -0.21798592 * x2, + c3x3 = 0.05707685 * x3, + c4x4 = 0.0109 * x4, + c5x5 = -0.00171961 * x5; + + mp_float_t evens = c4x4 + c2x2 + c0, odds = c5x5 + c3x3 + c1x; + result->c = evens + odds; + result->s = evens - odds; +} + +mp_obj_t common_hal_synthio_block_biquad_new(synthio_filter_mode mode, mp_obj_t f0, mp_obj_t Q) { + synthio_block_biquad_t *self = mp_obj_malloc(synthio_block_biquad_t, &synthio_block_biquad_type_obj); + self->mode = mode; + synthio_block_assign_slot(f0, &self->f0, MP_QSTR_frequency); + synthio_block_assign_slot(Q, &self->Q, MP_QSTR_Q); + return MP_OBJ_FROM_PTR(self); +} + +synthio_filter_mode common_hal_synthio_block_biquad_get_mode(synthio_block_biquad_t *self) { + return self->mode; +} + +mp_obj_t common_hal_synthio_block_biquad_get_Q(synthio_block_biquad_t *self) { + return self->Q.obj; +} + +void common_hal_synthio_block_biquad_set_Q(synthio_block_biquad_t *self, mp_obj_t Q) { + synthio_block_assign_slot(Q, &self->Q, MP_QSTR_Q); +} + +mp_obj_t common_hal_synthio_block_biquad_get_frequency(synthio_block_biquad_t *self) { + return self->f0.obj; +} + +void common_hal_synthio_block_biquad_set_frequency(synthio_block_biquad_t *self, mp_obj_t frequency) { + synthio_block_assign_slot(frequency, &self->f0, MP_QSTR_frequency); +} + +static int32_t biquad_scale_arg_float(mp_float_t arg) { + return (int32_t)MICROPY_FLOAT_C_FUN(round)(MICROPY_FLOAT_C_FUN(ldexp)(arg, BIQUAD_SHIFT)); +} + +static int float_equal_or_update( + mp_float_t *cached, + mp_float_t new) { + // uses memcmp to avoid error about equality float comparison + if (memcmp(cached, &new, sizeof(mp_float_t))) { + *cached = new; + return false; + } + return true; +} + +void common_hal_synthio_block_biquad_tick(mp_obj_t self_in, biquad_filter_state *filter_state) { + synthio_block_biquad_t *self = MP_OBJ_TO_PTR(self_in); + + mp_float_t W0 = synthio_block_slot_get(&self->f0) * synthio_global_W_scale; + mp_float_t Q = synthio_block_slot_get(&self->Q); + + // n.b., assumes that the `mode` field is read-only + // n.b., use of `&` is deliberate, avoids short-circuiting behavior + if (float_equal_or_update(&self->cached_W0, W0) & float_equal_or_update(&self->cached_Q, Q)) { + return; + } + + sincos_result_t sc; + fast_sincos(W0, &sc); + + mp_float_t alpha = sc.s / (2 * Q); + + mp_float_t a0, a1, a2, b0, b1, b2; + + a0 = 1 + alpha; + a1 = -2 * sc.c; + a2 = 1 - alpha; + + switch (self->mode) { + default: + case SYNTHIO_LOW_PASS: + b2 = b0 = (1 - sc.c) * .5; + b1 = 1 - sc.c; + break; + + case SYNTHIO_HIGH_PASS: + b2 = b0 = (1 + sc.c) * .5; + b1 = -(1 + sc.c); + break; + + case SYNTHIO_BAND_PASS: + b0 = alpha; + b1 = 0; + b2 = -b0; + break; + + case SYNTHIO_NOTCH: + b0 = 1; + b1 = -2 * sc.c; + b2 = 1; + } + + mp_float_t recip_a0 = 1 / a0; + + filter_state->a1 = biquad_scale_arg_float(a1 * recip_a0); + filter_state->a2 = biquad_scale_arg_float(a2 * recip_a0); + filter_state->b0 = biquad_scale_arg_float(b0 * recip_a0); + filter_state->b1 = biquad_scale_arg_float(b1 * recip_a0); + filter_state->b2 = biquad_scale_arg_float(b2 * recip_a0); +} diff --git a/shared-module/synthio/BlockBiquad.h b/shared-module/synthio/BlockBiquad.h new file mode 100644 index 0000000000000..f94c158a65139 --- /dev/null +++ b/shared-module/synthio/BlockBiquad.h @@ -0,0 +1,21 @@ + +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2023 Jeff Epler for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "shared-bindings/synthio/BlockBiquad.h" +#include "shared-module/synthio/Biquad.h" +#include "shared-module/synthio/block.h" + +typedef struct synthio_block_biquad { + mp_obj_base_t base; + synthio_filter_mode mode; + synthio_block_slot_t f0, Q; + mp_float_t cached_W0, cached_Q; +} synthio_block_biquad_t; + +void common_hal_synthio_block_biquad_tick(mp_obj_t self_in, biquad_filter_state *filter_state); diff --git a/shared-module/synthio/__init__.c b/shared-module/synthio/__init__.c index 630c3de86988c..bd1ca7127a90b 100644 --- a/shared-module/synthio/__init__.c +++ b/shared-module/synthio/__init__.c @@ -8,12 +8,15 @@ #include "shared-module/synthio/__init__.h" #include "shared-bindings/synthio/__init__.h" #include "shared-module/synthio/Biquad.h" +#include "shared-module/synthio/BlockBiquad.h" #include "shared-module/synthio/Note.h" #include "py/runtime.h" #include #include -mp_float_t synthio_global_rate_scale; +#define MP_PI MICROPY_FLOAT_CONST(3.14159265358979323846) + +mp_float_t synthio_global_rate_scale, synthio_global_W_scale; uint8_t synthio_global_tick; static const int16_t square_wave[] = {-32768, 32767}; @@ -335,6 +338,9 @@ void synthio_synth_synthesize(synthio_synth_t *synth, uint8_t **bufptr, uint32_t mp_obj_t filter_obj = synthio_synth_get_note_filter(note_obj); if (filter_obj != mp_const_none) { synthio_note_obj_t *note = MP_OBJ_TO_PTR(note_obj); + if (mp_obj_is_type(filter_obj, &synthio_block_biquad_type_obj)) { + common_hal_synthio_block_biquad_tick(filter_obj, ¬e->filter_state); + } synthio_biquad_filter_samples(¬e->filter_state, tmp_buffer32, dur); } @@ -490,7 +496,9 @@ uint32_t synthio_frequency_convert_scaled_to_dds(uint64_t frequency_scaled, int3 } void shared_bindings_synthio_lfo_tick(uint32_t sample_rate) { - synthio_global_rate_scale = (mp_float_t)SYNTHIO_MAX_DUR / sample_rate; + mp_float_t recip_sample_rate = MICROPY_FLOAT_CONST(1.) / sample_rate; + synthio_global_rate_scale = SYNTHIO_MAX_DUR * recip_sample_rate; + synthio_global_W_scale = (2 * MP_PI) * recip_sample_rate; synthio_global_tick++; } diff --git a/shared-module/synthio/__init__.h b/shared-module/synthio/__init__.h index b0bca1efcffe4..d40e1df86854c 100644 --- a/shared-module/synthio/__init__.h +++ b/shared-module/synthio/__init__.h @@ -88,6 +88,6 @@ int synthio_lfo_step(synthio_lfo_state_t *state, uint16_t dur); int synthio_sweep_step(synthio_lfo_state_t *state, uint16_t dur); int synthio_sweep_in_step(synthio_lfo_state_t *state, uint16_t dur); -extern mp_float_t synthio_global_rate_scale; +extern mp_float_t synthio_global_rate_scale, synthio_global_W_scale; extern uint8_t synthio_global_tick; void shared_bindings_synthio_lfo_tick(uint32_t sample_rate); diff --git a/tests/circuitpython-manual/synthio/note/blockfilter.py b/tests/circuitpython-manual/synthio/note/blockfilter.py new file mode 100644 index 0000000000000..e1638a995924a --- /dev/null +++ b/tests/circuitpython-manual/synthio/note/blockfilter.py @@ -0,0 +1,67 @@ +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.15, decay_time=0, release_time=0.08, attack_level=1.0, 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) +sweep = np.linspace(-32767, 32767, num=SAMPLE_SIZE, endpoint=True, dtype=np.int16) + +lfos_of_interest = [] + + +def synthesize(synth): + freq_sweep = synthio.LFO( + sweep, offset=synthio.midi_to_hz(72), scale=synthio.midi_to_hz(72), rate=1, once=True + ) + + for biquad in ( + None, + synthio.BlockBiquad(synthio.FilterMode.LOW_PASS, freq_sweep), + synthio.BlockBiquad(synthio.FilterMode.HIGH_PASS, freq_sweep), + synthio.BlockBiquad(synthio.FilterMode.BAND_PASS, freq_sweep, Q=8), + synthio.BlockBiquad(synthio.FilterMode.NOTCH, freq_sweep, Q=8), + ): + n = synthio.Note( + frequency=synthio.midi_to_hz(72), + envelope=envelope, + filter=biquad, + waveform=sine, + ) + + freq_sweep.retrigger() + synth.press(n) + print("n", n.frequency) + yield 24 * 6 + synth.release_all() + yield 24 + + +with wave.open("blockfilter.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) diff --git a/tests/circuitpython/synthio_block_biquad.py b/tests/circuitpython/synthio_block_biquad.py new file mode 100644 index 0000000000000..db7cddd987dc9 --- /dev/null +++ b/tests/circuitpython/synthio_block_biquad.py @@ -0,0 +1,16 @@ +from synthnotehelper import * +from synthio import BlockBiquad, FilterMode +import random + +random.seed(41) + +white_noise = array.array('h', [random.randint(-32000, 32000) for i in range(600)]) + +@synth_test_rms +def gen(synth): + l = LFO(sweep, offset=1440, scale=2880, rate=.025, once=True) + yield [l] + b = BlockBiquad(FilterMode.LOW_PASS, l, Q=.5**.5) + n = Note(100, filter=b, waveform=white_noise) + synth.press(n) + yield 20 diff --git a/tests/circuitpython/synthio_block_biquad.py.exp b/tests/circuitpython/synthio_block_biquad.py.exp new file mode 100644 index 0000000000000..0a4c854ab27ac --- /dev/null +++ b/tests/circuitpython/synthio_block_biquad.py.exp @@ -0,0 +1,640 @@ +0.0 0.5233163941292499 -1435.412246704102 +0.03125 0.5888741116989962 -1430.912384033203 +0.0625 0.5513501362374322 -1426.412521362305 +0.09375 0.5533979914194288 -1421.912658691406 +0.125 0.5398571609561279 -1417.412796020508 +0.15625 0.5468367798735001 -1412.912933349609 +0.1875 0.5634925509027698 -1408.413070678711 +0.21875 0.5645661363583713 -1403.913208007813 +0.25 0.5549288487251397 -1399.413345336914 +0.28125 0.5838228624458941 -1394.913482666016 +0.3125 0.5854923148763805 -1390.413619995117 +0.34375 0.5431788385492597 -1385.913757324219 +0.375 0.5970661628380437 -1381.41389465332 +0.40625 0.5555455655995383 -1376.914031982422 +0.4375 0.5928591556206007 -1372.414169311523 +0.46875 0.5813652534873269 -1367.914306640625 +0.5 0.545093375939561 -1363.414443969727 +0.53125 0.5440081666013258 -1358.914581298828 +0.5625 0.5613578721915968 -1354.41471862793 +0.59375 0.5373168087762234 -1349.914855957031 +0.625 0.5821319953822574 -1345.414993286133 +0.65625 0.5556490959021101 -1340.915130615234 +0.6875 0.5872875243764462 -1336.415267944336 +0.71875 0.5806883993533669 -1331.915405273438 +0.75 0.5852815758110332 -1327.415542602539 +0.78125 0.5703388819148339 -1322.915679931641 +0.8125 0.5759793425661491 -1318.415817260742 +0.84375 0.5508243231539421 -1313.915954589844 +0.875 0.560820014468103 -1309.416091918945 +0.90625 0.5421378527391773 -1304.916229248047 +0.9375 0.5420010042238626 -1300.416366577148 +0.96875 0.5491701137529543 -1295.91650390625 +1.0 0.5608312495809731 -1291.416641235352 +1.03125 0.5568380740898306 -1286.916778564453 +1.0625 0.5442811106044603 -1282.416915893555 +1.09375 0.5600122444984227 -1277.917053222656 +1.125 0.5758360307668417 -1273.417190551758 +1.15625 0.548131284515931 -1268.917327880859 +1.1875 0.5510493976289011 -1264.41746520996 +1.21875 0.5776113626962765 -1259.917602539063 +1.25 0.5470687072188508 -1255.417739868164 +1.28125 0.5650406419917773 -1250.917877197266 +1.3125 0.5860837091378376 -1246.418014526367 +1.34375 0.5720454282655103 -1241.918151855469 +1.375 0.5682908808089936 -1237.41828918457 +1.40625 0.5565669034789931 -1232.918426513672 +1.4375 0.5225427569415927 -1228.418563842773 +1.46875 0.528656799922158 -1223.918701171875 +1.5 0.5388063364749288 -1219.418838500977 +1.53125 0.5568995892348234 -1214.918975830078 +1.5625 0.5538175590768109 -1210.41911315918 +1.59375 0.5584539013258073 -1205.919250488281 +1.625 0.5497393833455019 -1201.419387817383 +1.65625 0.542731624632793 -1196.919525146485 +1.6875 0.5717515951534967 -1192.419662475586 +1.71875 0.5670740525571515 -1187.919799804688 +1.75 0.5661384126400268 -1183.41993713379 +1.78125 0.5499975057148479 -1178.920074462891 +1.8125 0.5319799725055219 -1174.420211791993 +1.84375 0.5530230503761957 -1169.920349121094 +1.875 0.5634698591133656 -1165.420486450196 +1.90625 0.5470904117219204 -1160.920623779297 +1.9375 0.5462323074916723 -1156.420761108398 +1.96875 0.5186497350082877 -1151.9208984375 +2.0 0.5524974140870907 -1147.421035766602 +2.03125 0.5684251638259989 -1142.921173095704 +2.0625 0.5373369141277137 -1138.421310424805 +2.09375 0.5623155902535638 -1133.921447753906 +2.125 0.5151661907370962 -1129.421585083008 +2.15625 0.5249581930617114 -1124.92172241211 +2.1875 0.5817756160077248 -1120.421859741211 +2.21875 0.5611413205776463 -1115.921997070313 +2.25 0.5991850139596161 -1111.422134399415 +2.28125 0.6042035347204486 -1106.922271728516 +2.3125 0.5513687575596959 -1102.422409057618 +2.34375 0.5568562879220313 -1097.922546386719 +2.375 0.5545761375854527 -1093.422683715821 +2.40625 0.5243707673660577 -1088.922821044922 +2.4375 0.5801785537954141 -1084.422958374024 +2.46875 0.562707323195996 -1079.923095703125 +2.5 0.595908131259724 -1075.423233032227 +2.53125 0.5725333638693439 -1070.923370361329 +2.5625 0.5434214559798377 -1066.42350769043 +2.59375 0.5462209512439381 -1061.923645019532 +2.625 0.56504689595266 -1057.423782348633 +2.65625 0.5583379851037877 -1052.923919677735 +2.6875 0.5504754345671578 -1048.424057006837 +2.71875 0.5540173423293693 -1043.924194335938 +2.75 0.5569208544474369 -1039.42433166504 +2.78125 0.5485128805906287 -1034.924468994141 +2.8125 0.5402199679595928 -1030.424606323243 +2.84375 0.5891253549156891 -1025.924743652345 +2.875 0.5413469679319655 -1021.424880981446 +2.90625 0.5621087907587169 -1016.925018310548 +2.9375 0.5809697135256842 -1012.425155639649 +2.96875 0.5412814012283086 -1007.925292968751 +3.0 0.5634686024733107 -1003.425430297852 +3.03125 0.5125218500024266 -998.9255676269536 +3.0625 0.5593353988827886 -994.4257049560556 +3.09375 0.573363079810572 -989.9258422851567 +3.125 0.5874805194410147 -985.4259796142587 +3.15625 0.5575262864848901 -980.9261169433603 +3.1875 0.5682703663517215 -976.4262542724618 +3.21875 0.5529682773272279 -971.9263916015634 +3.25 0.5788602199430506 -967.4265289306654 +3.28125 0.5699266266203952 -962.9266662597665 +3.3125 0.5508431951087346 -958.4268035888681 +3.34375 0.5545311015714406 -953.9269409179697 +3.375 0.5703749824534138 -949.4270782470712 +3.40625 0.5765930750929544 -944.9272155761732 +3.4375 0.5591865689411526 -940.4273529052743 +3.46875 0.5438688115680163 -935.9274902343764 +3.5 0.5770443322483677 -931.4276275634775 +3.53125 0.5599071331907393 -926.927764892579 +3.5625 0.5737737977946773 -922.4279022216811 +3.59375 0.5869385459050536 -917.9280395507822 +3.625 0.5639195135570374 -913.4281768798842 +3.65625 0.5529386113952421 -908.9283142089857 +3.6875 0.5890100436051251 -904.4284515380868 +3.71875 0.5523201537804379 -899.9285888671889 +3.75 0.5436066445351337 -895.4287261962904 +3.78125 0.5537370310128638 -890.928863525392 +3.8125 0.549814617558703 -886.4290008544936 +3.84375 0.6008523314436754 -881.9291381835951 +3.875 0.5325945534931562 -877.4292755126967 +3.90625 0.5611291614214771 -872.9294128417982 +3.9375 0.5587624222275791 -868.4295501708998 +3.96875 0.5381390146844121 -863.9296875000014 +4.0 0.5631265263936958 -859.4298248291029 +4.03125 0.5560699557405984 -854.9299621582045 +4.0625 0.5754277591982327 -850.4300994873065 +4.09375 0.5915981824253665 -845.9302368164076 +4.125 0.5357680930345895 -841.4303741455092 +4.15625 0.5879893618274627 -836.9305114746107 +4.1875 0.5464188455812566 -832.4306488037123 +4.21875 0.5706438108408328 -827.9307861328143 +4.25 0.5635922796535459 -823.4309234619159 +4.28125 0.5697220610856014 -818.931060791017 +4.3125 0.5517936403744668 -814.4311981201186 +4.34375 0.6004242476521108 -809.9313354492201 +4.375 0.5925180334425226 -805.4314727783221 +4.40625 0.5787811658254868 -800.9316101074237 +4.4375 0.5380560322154883 -796.4317474365248 +4.46875 0.5642541286963015 -791.9318847656264 +4.5 0.5584378411315557 -787.4320220947279 +4.53125 0.5642250866391942 -782.9321594238299 +4.5625 0.5742079044117807 -778.4322967529315 +4.59375 0.5738315641901234 -773.9324340820326 +4.625 0.5461171957884638 -769.4325714111342 +4.65625 0.5533378859109117 -764.9327087402362 +4.6875 0.581344255722229 -760.4328460693378 +4.71875 0.5428141298106971 -755.9329833984393 +4.75 0.5580035009121508 -751.4331207275409 +4.78125 0.5534871016385341 -746.933258056642 +4.8125 0.5578909520468173 -742.433395385744 +4.84375 0.5848112793586618 -737.9335327148456 +4.875 0.583573451167756 -733.4336700439471 +4.90625 0.5708054463460115 -728.9338073730487 +4.9375 0.5651886055827023 -724.4339447021498 +4.96875 0.5525625982940899 -719.9340820312518 +5.0 0.5752696487042543 -715.4342193603534 +5.03125 0.5647695964087438 -710.9343566894549 +5.0625 0.5513871041249724 -706.4344940185565 +5.09375 0.5596974518428355 -701.9346313476576 +5.125 0.5516503097910866 -697.4347686767592 +5.15625 0.5717992890171914 -692.9349060058612 +5.1875 0.5662466697073542 -688.4350433349628 +5.21875 0.5578416082514302 -683.9351806640639 +5.25 0.5583177099904105 -679.4353179931654 +5.28125 0.5624997177639164 -674.935455322267 +5.3125 0.5740358754080997 -670.4355926513686 +5.34375 0.5198447100680302 -665.9357299804701 +5.375 0.5812101373266519 -661.4358673095712 +5.40625 0.5690255464131015 -656.9360046386728 +5.4375 0.5698286073996001 -652.4361419677748 +5.46875 0.5212701389821012 -647.9362792968759 +5.5 0.6008879107808415 -643.4364166259775 +5.53125 0.5354583608110104 -638.9365539550786 +5.5625 0.5659721425363654 -634.4366912841806 +5.59375 0.5508335101466897 -629.9368286132817 +5.625 0.5748322041853707 -625.4369659423833 +5.65625 0.5112195310881697 -620.9371032714853 +5.6875 0.5503257418930935 -616.4372406005864 +5.71875 0.5483893261970563 -611.937377929688 +5.75 0.5586441290230967 -607.4375152587891 +5.78125 0.529618320202 -602.9376525878911 +5.8125 0.5583584354187394 -598.4377899169926 +5.84375 0.5119880230797152 -593.9379272460935 +5.875 0.5685560193282417 -589.4380645751953 +5.90625 0.5662137269319073 -584.9382019042969 +5.9375 0.548174405494183 -580.4383392333984 +5.96875 0.5723998468214697 -575.9384765625002 +6.0 0.5454906289769358 -571.4386138916011 +6.03125 0.5151144863097763 -566.9387512207027 +6.0625 0.5408932263910434 -562.4388885498047 +6.09375 0.5812600815615564 -557.939025878906 +6.125 0.5640642789476607 -553.4391632080074 +6.15625 0.5244641070494527 -548.9393005371087 +6.1875 0.5661904070460153 -544.4394378662105 +6.21875 0.5244075931431626 -539.9395751953118 +6.25 0.5525887404821925 -535.4397125244132 +6.28125 0.5020311692655093 -530.9398498535152 +6.3125 0.5036647252695824 -526.4399871826165 +6.34375 0.5441081599351917 -521.9401245117178 +6.375 0.5501128379371436 -517.4402618408189 +6.40625 0.5584356803834504 -512.940399169921 +6.4375 0.5053175171179443 -508.4405364990225 +6.46875 0.5041253886563949 -503.9406738281236 +6.5 0.5417281294449045 -499.4408111572252 +6.53125 0.5281308297848274 -494.9409484863268 +6.5625 0.55178896455521 -490.4410858154286 +6.59375 0.4901484208544025 -485.9412231445301 +6.625 0.564525485344044 -481.441360473631 +6.65625 0.4792626124438369 -476.9414978027328 +6.6875 0.5228646503684022 -472.4416351318346 +6.71875 0.4933138687761552 -467.9417724609359 +6.75 0.533761220406419 -463.4419097900372 +6.78125 0.5246718953730372 -458.9420471191386 +6.8125 0.5184862456752544 -454.4421844482406 +6.84375 0.534162821911546 -449.9423217773419 +6.875 0.5013670872181356 -445.442459106443 +6.90625 0.5388662152086168 -440.9425964355451 +6.9375 0.5218804778215822 -436.4427337646464 +6.96875 0.5736817891820368 -431.9428710937477 +7.0 0.5295169584694084 -427.4430084228491 +7.03125 0.549346460553386 -422.9431457519509 +7.0625 0.5278099495347873 -418.4432830810526 +7.09375 0.5092380846918898 -413.9434204101535 +7.125 0.4941508140939982 -409.4435577392551 +7.15625 0.5169463569183504 -404.9436950683569 +7.1875 0.4953175790791144 -400.4438323974584 +7.21875 0.516131378541008 -395.94396972656 +7.25 0.5261398073442543 -391.4441070556611 +7.28125 0.5008081946276275 -386.9442443847627 +7.3125 0.5237017380224776 -382.4443817138647 +7.34375 0.5269585850106822 -377.944519042966 +7.375 0.5403925301790693 -373.4446563720671 +7.40625 0.4912870467203073 -368.9447937011685 +7.4375 0.4860205498245958 -364.4449310302705 +7.46875 0.5076678968617992 -359.9450683593718 +7.5 0.5751840851181132 -355.4452056884732 +7.53125 0.5145785285700734 -350.9453430175749 +7.5625 0.5070222400192178 -346.4454803466763 +7.59375 0.5449130006453371 -341.9456176757776 +7.625 0.4998507782411507 -337.4457550048789 +7.65625 0.5012363391565783 -332.945892333981 +7.6875 0.5292675268030196 -328.4460296630825 +7.71875 0.5513583636674681 -323.9461669921836 +7.75 0.5030802143058754 -319.4463043212852 +7.78125 0.4905981840379553 -314.9464416503868 +7.8125 0.5274428911101353 -310.4465789794883 +7.84375 0.546627531334603 -305.9467163085901 +7.875 0.5151010996472523 -301.446853637691 +7.90625 0.4839249262021641 -296.9469909667926 +7.9375 0.5174238046942224 -292.4471282958946 +7.96875 0.5346946445719737 -287.9472656249959 +8.0 0.5313876108222209 -283.4474029540972 +8.03125 0.5095780489919427 -278.9475402831986 +8.0625 0.4911799576606958 -274.4476776123004 +8.09375 0.547494146625657 -269.9478149414017 +8.125 0.5038670653388587 -265.447952270503 +8.15625 0.5285942339808058 -260.9480895996051 +8.1875 0.5329857592119569 -256.4482269287064 +8.21875 0.5119534725212935 -251.9483642578077 +8.25 0.5033797213494831 -247.4485015869091 +8.28125 0.5177132418754256 -242.9486389160109 +8.3125 0.5538903262602162 -238.4487762451124 +8.34375 0.5368218985177576 -233.9489135742135 +8.375 0.5263842343389778 -229.4490509033151 +8.40625 0.5138763360031863 -224.9491882324169 +8.4375 0.5269683356839487 -220.4493255615184 +8.46875 0.5484451242131595 -215.94946289062 +8.5 0.5181561664959421 -211.4496002197211 +8.53125 0.538460254709516 -206.9497375488227 +8.5625 0.5396058249701871 -202.4498748779247 +8.59375 0.5183117701795767 -197.9500122070258 +8.625 0.5331941397833627 -193.4501495361271 +8.65625 0.5446388875727108 -188.9502868652285 +8.6875 0.5417772369865942 -184.4504241943305 +8.71875 0.5223062196070183 -179.9505615234318 +8.75 0.5290697979854333 -175.4506988525332 +8.78125 0.5297126110364021 -170.9508361816349 +8.8125 0.5450719527420691 -166.4509735107363 +8.84375 0.539206064335114 -161.9511108398376 +8.875 0.5272662938292431 -157.4512481689389 +8.90625 0.5374506762705708 -152.951385498041 +8.9375 0.5296481490281893 -148.4515228271425 +8.96875 0.5242848002098146 -143.9516601562434 +9.0 0.5342325745513286 -139.4517974853452 +9.03125 0.5304651103728572 -134.9519348144468 +9.0625 0.534625982617119 -130.4520721435483 +9.09375 0.5388630124495862 -125.9522094726499 +9.125 0.5419169053977618 -121.452346801751 +9.15625 0.5387306344924848 -116.9524841308526 +9.1875 0.5411771554428 -112.4526214599546 +9.21875 0.534663506650105 -107.9527587890559 +9.25 0.5432956058032731 -103.4528961181572 +9.28125 0.5452798871015233 -98.95303344725835 +9.3125 0.5400449469782619 -94.45317077636037 +9.34375 0.5440614233919323 -89.9533081054617 +9.375 0.5323203781216296 -85.45344543456304 +9.40625 0.5430447586723063 -80.95358276366505 +9.4375 0.5446712927539446 -76.45372009276616 +9.46875 0.5459524897592061 -71.9538574218675 +9.5 0.5460639734792513 -67.45399475096883 +9.53125 0.5494256027817332 -62.95413208007085 +9.5625 0.5457119306826957 -58.45426940917241 +9.59375 0.5515349992393668 -53.95440673827352 +9.625 0.5485894873057759 -49.45454406737508 +9.65625 0.5546036928766149 -44.95468139647664 +9.6875 0.5520760480678739 -40.45481872557843 +9.71875 0.5540796441777628 -35.95495605468 +9.75 0.5520483826700068 -31.45509338378088 +9.78125 0.5559964042093331 -26.95523071288267 +9.8125 0.5561241070465571 -22.45536804198446 +9.84375 0.553345490317982 -17.95550537108579 +9.875 0.5604416124584247 -13.45564270018713 +9.90625 0.5593413403554656 -8.955780029288462 +9.9375 0.5608126552225947 -4.455917358390479 +9.96875 0.5575838559074312 0.04394531250841283 +10.0 0.5654348388131066 4.54380798340685 +10.03125 0.5616753470922725 9.043670654305288 +10.0625 0.5639929263984667 13.54353332520373 +10.09375 0.5587586865317684 18.04339599610239 +10.125 0.5734582332480533 22.54325866700106 +10.15625 0.5732497606922596 27.04312133789927 +10.1875 0.5749851529847351 31.54298400879748 +10.21875 0.5802485173239508 36.04284667969637 +10.25 0.5738886067903813 40.54270935059503 +10.28125 0.5802850622636567 45.04257202149324 +10.3125 0.5777911320327425 49.54243469239145 +10.34375 0.5810982716930228 54.04229736329034 +10.375 0.5871746611246479 58.54216003418901 +10.40625 0.5782109696519912 63.04202270508745 +10.4375 0.5892671600317005 67.54188537598566 +10.46875 0.5866793190995349 72.04174804688409 +10.5 0.5845631933863139 76.54161071778299 +10.53125 0.5866080651713119 81.04147338868142 +10.5625 0.5816092770763072 85.54133605957986 +10.59375 0.5937762168930567 90.0411987304783 +10.625 0.5911745770284834 94.54106140137696 +10.65625 0.5877656317687221 99.04092407227517 +10.6875 0.5953735471025981 103.5407867431738 +10.71875 0.59160362529701 108.0406494140725 +10.75 0.5962187931680832 112.5405120849709 +10.78125 0.5883415636639483 117.0403747558692 +10.8125 0.5839910949801117 121.5402374267676 +10.84375 0.6010654911818396 126.0401000976665 +10.875 0.5970559532030666 130.5399627685651 +10.90625 0.5940572500168175 135.0398254394634 +10.9375 0.5732606837883286 139.5396881103616 +10.96875 0.5381164603175737 144.0395507812602 +11.0 0.5351711728901959 148.5394134521591 +11.03125 0.5183835078058662 153.0392761230576 +11.0625 0.5780151556478285 157.5391387939558 +11.09375 0.5247987291248501 162.0390014648542 +11.125 0.5425038966652155 166.5388641357529 +11.15625 0.5391587825768379 171.0387268066515 +11.1875 0.5758966434662992 175.5385894775497 +11.21875 0.5488268299762037 180.0384521484484 +11.25 0.5468267934304349 184.5383148193469 +11.28125 0.5296925278012772 189.0381774902453 +11.3125 0.5888686851451086 193.5380401611437 +11.34375 0.5352789347497206 198.0379028320426 +11.375 0.5533386912535877 202.5377655029411 +11.40625 0.5510777323399219 207.0376281738393 +11.4375 0.5869368732055307 211.5374908447377 +11.46875 0.5598609814741869 216.0373535156364 +11.5 0.5584633639894287 220.5372161865353 +11.53125 0.5412643632623084 225.0370788574335 +11.5625 0.6000724464415632 229.5369415283317 +11.59375 0.5465718645508223 234.0368041992303 +11.625 0.564507733573807 238.5366668701292 +11.65625 0.5632366936218962 243.0365295410274 +11.6875 0.598037908932081 247.5363922119257 +11.71875 0.5713189573698724 252.0362548828243 +11.75 0.5701763115892213 256.536117553723 +11.78125 0.5530012621580137 261.0359802246214 +11.8125 0.6114605286286333 265.5358428955199 +11.84375 0.5585243802662351 270.0357055664183 +11.875 0.5759449941332803 274.535568237317 +11.90625 0.5754924828837037 279.0354309082154 +11.9375 0.6092916917219224 283.5352935791138 +11.96875 0.5830561194579957 288.0351562500125 +12.0 0.5820349500078187 292.5350189209112 +12.03125 0.5649457377639776 297.0348815918094 +12.0625 0.6230816110324387 301.5347442627076 +12.09375 0.5709201383977375 306.0346069336065 +12.125 0.5876069042702596 310.5344696045051 +12.15625 0.5878231568638521 315.0343322754034 +12.1875 0.6208411580327887 319.5341949463016 +12.21875 0.5950273927755796 324.0340576172002 +12.25 0.5941588153659908 328.5339202880991 +12.28125 0.5770586958248074 333.0337829589976 +12.3125 0.634979908874035 337.5336456298958 +12.34375 0.5836739789055577 342.0335083007942 +12.375 0.5994445000638317 346.5333709716929 +12.40625 0.6003259429209249 351.0332336425915 +12.4375 0.6326380962952464 355.53309631349 +12.46875 0.6071836598135066 360.0329589843884 +12.5 0.6065060218610247 364.5328216552869 +12.53125 0.5893706914599263 369.0326843261853 +12.5625 0.6467316765872678 373.532546997084 +12.59375 0.5966873599268746 378.0324096679826 +12.625 0.6114690168226161 382.5322723388811 +12.65625 0.6130051754082282 387.0321350097793 +12.6875 0.6445522122006491 391.5319976806777 +12.71875 0.6195674199220667 396.0318603515766 +12.75 0.6190924514029002 400.5317230224753 +12.78125 0.601888235067373 405.0315856933735 +12.8125 0.6586365836569614 409.5314483642717 +12.84375 0.6098974600186474 414.0313110351703 +12.875 0.6236866064045412 418.5311737060692 +12.90625 0.6259159416467512 423.0310363769676 +12.9375 0.6566082212735398 427.5308990478658 +12.96875 0.6321466527481859 432.0307617187643 +13.0 0.6318346606972041 436.530624389663 +13.03125 0.614599313654773 441.0304870605615 +13.0625 0.6705283273461649 445.53034973146 +13.09375 0.6232986816970667 450.0302124023584 +13.125 0.6361400971100881 454.530075073257 +13.15625 0.6389446712306796 459.0299377441554 +13.1875 0.6686892707573188 463.5298004150538 +13.21875 0.6449864346003898 468.0296630859526 +13.25 0.6442914315668125 472.5295257568512 +13.28125 0.6272181360738232 477.0293884277494 +13.3125 0.6819845711375629 481.5292510986477 +13.34375 0.6368909041192503 486.0291137695466 +13.375 0.6488529681516589 490.5289764404453 +13.40625 0.6517436895129136 495.0288391113435 +13.4375 0.680690254581632 499.5287017822417 +13.46875 0.6580798929597741 504.0285644531403 +13.5 0.6568191976912985 508.5284271240392 +13.53125 0.6399436106511786 513.0282897949376 +13.5625 0.6933983577448943 517.5281524658358 +13.59375 0.6504292030516948 522.0280151367343 +13.625 0.6616640063279428 526.5278778076331 +13.65625 0.6645837368471335 531.0277404785315 +13.6875 0.6930362416315237 535.52760314943 +13.71875 0.6710562161167619 540.0274658203285 +13.75 0.6692539959116338 544.527328491227 +13.78125 0.6526921990411808 549.0271911621254 +13.8125 0.5145585911644841 553.527053833024 +13.84375 0.108799138638648 558.0269165039226 +13.875 0.08959367900381817 562.5267791748212 +13.90625 0.1070956643554223 567.0266418457194 +13.9375 0.1087724263648484 571.5265045166177 +13.96875 0.1106221411380315 576.0263671875166 +14.0 0.1134255133002185 580.5262298584153 +14.03125 0.1083000649330917 585.0260925293135 +14.0625 0.1144187792980931 589.5259552002117 +14.09375 0.1129808309621203 594.0258178711105 +14.125 0.09235243262500717 598.5256805420092 +14.15625 0.1099638007181997 603.0255432129077 +14.1875 0.1116974135793035 607.5254058838059 +14.21875 0.1145665008050144 612.0252685547043 +14.25 0.1164288467215701 616.5251312256031 +14.28125 0.1110493652205335 621.0249938965017 +14.3125 0.1168054416225558 625.5248565674 +14.34375 0.1168780477017844 630.0247192382985 +14.375 0.09512653014879378 634.5245819091971 +14.40625 0.1126620735370587 639.0244445800954 +14.4375 0.1146116668047789 643.524307250994 +14.46875 0.1183433183805806 648.0241699218926 +14.5 0.1192721157687402 652.5240325927912 +14.53125 0.1136771259979949 657.0238952636894 +14.5625 0.1191846903025063 661.5237579345878 +14.59375 0.1205410669858625 666.0236206054866 +14.625 0.09789373366721922 670.5234832763853 +14.65625 0.1152428871949321 675.0233459472836 +14.6875 0.1175161720598944 679.5232086181818 +14.71875 0.1219851247552673 684.0230712890805 +14.75 0.1219363210636846 688.5229339599792 +14.78125 0.1161924384899548 693.0227966308777 +14.8125 0.121613092822123 697.5226593017759 +14.84375 0.1239764518385377 702.0225219726744 +14.875 0.1006275983059951 706.5223846435731 +14.90625 0.1176704324771989 711.0222473144717 +14.9375 0.1204391753000987 715.52210998537 +14.96875 0.1255068643761589 720.0219726562685 +15.0 0.1244675562062874 724.5218353271671 +15.03125 0.1185906869307071 729.0216979980654 +15.0625 0.1240443938607149 733.521560668964 +15.09375 0.1272077423687175 738.0214233398626 +15.125 0.1033825383248399 742.5212860107612 +15.15625 0.1200114946440536 747.0211486816594 +15.1875 0.1233097800113023 751.5210113525578 +15.21875 0.1288641310828331 756.0208740234566 +15.25 0.1268875297914646 760.5207366943554 +15.28125 0.12087960596437 765.0205993652536 +15.3125 0.1264902986426788 769.5204620361518 +15.34375 0.1302217830538807 774.0203247070505 +15.375 0.1061117949192766 778.5201873779494 +15.40625 0.1222651837998628 783.0200500488477 +15.4375 0.1262036556506407 787.5199127197459 +15.46875 0.1321358868554278 792.0197753906444 +15.5 0.129238051588965 796.5196380615431 +15.53125 0.1230846180806053 801.0195007324417 +15.5625 0.1289600670941163 805.5193634033401 +15.59375 0.1330455097028878 810.0192260742385 +15.625 0.108769962921361 814.5190887451371 +15.65625 0.1244433321273532 819.0189514160355 +15.6875 0.1290258830403304 823.5188140869341 +15.71875 0.1352370036459023 828.0186767578327 +15.75 0.1314671437798762 832.5185394287313 +15.78125 0.1252368151393506 837.0184020996295 +15.8125 0.131441815952481 841.5182647705278 +15.84375 0.1357299574910208 846.0181274414267 +15.875 0.1113932434078271 850.5179901123254 +15.90625 0.1265361742193656 855.0178527832236 +15.9375 0.1318612186764555 859.5177154541218 +15.96875 0.1382446377482632 864.0175781250205 +16.0 0.1336759098607705 868.5174407959194 +16.03125 0.1273188319543719 873.0173034668177 +16.0625 0.1338980911212611 877.5171661377159 +16.09375 0.1382577621198477 882.0170288086144 +16.125 0.1139611850833507 886.5168914795131 +16.15625 0.1285969599331371 891.0167541504117 +16.1875 0.1346292697549597 895.5166168213101 +16.21875 0.1411076553823318 900.0164794922086 +16.25 0.1357901651555739 904.5163421631071 +16.28125 0.1293759308741255 909.0162048340055 +16.3125 0.1363674152928493 913.5160675049041 +16.34375 0.1406635128826926 918.0159301758027 +16.375 0.1164584394285753 922.5157928467013 +16.40625 0.1306157686432045 927.0156555175995 +16.4375 0.1373575035236127 931.5155181884979 +16.46875 0.1438759717421607 936.0153808593967 +16.5 0.1378728348873188 940.5152435302954 +16.53125 0.1313814762854565 945.0151062011936 +16.5625 0.1388294166832878 949.5149688720919 +16.59375 0.1429608054928704 954.0148315429906 +16.625 0.1188718755744542 958.5146942138894 +16.65625 0.1325990755893989 963.0145568847877 +16.6875 0.1400444821126768 967.5144195556859 +16.71875 0.146553881491509 972.0142822265846 +16.75 0.1399013059023452 976.5141448974832 +16.78125 0.1333688628257788 981.0140075683817 +16.8125 0.1412684645777973 985.5138702392801 +16.84375 0.1451646202140146 990.0137329101786 +16.875 0.1212109486603593 994.5135955810772 +16.90625 0.1345537723711169 999.0134582519755 +16.9375 0.1426904521953445 1003.513320922874 +16.96875 0.1491053051026527 1008.013183593773 +17.0 0.1418836982030045 1012.513046264671 +17.03125 0.1353621949607738 1017.012908935569 +17.0625 0.1436714338847207 1021.512771606468 +17.09375 0.1472982320063139 1026.012634277367 +17.125 0.1234753641227667 1030.512496948265 +17.15625 0.1365078164779559 1035.012359619164 +17.1875 0.1452920070879705 1039.512222290062 +17.21875 0.1515845901806397 1044.012084960961 +17.25 0.1438121245070723 1048.511947631859 +17.28125 0.1373257469191369 1053.011810302758 +17.3125 0.1460464635695965 1057.511672973656 +17.34375 0.1493487989834957 1062.011535644554 +17.375 0.1256713638206061 1066.511398315453 +17.40625 0.1384370178025034 1071.011260986352 +17.4375 0.1478343935958201 1075.51112365725 +17.46875 0.1539753906613792 1080.010986328149 +17.5 0.1457186058778294 1084.510848999047 +17.53125 0.1392901345579448 1089.010711669946 +17.5625 0.1483755109702189 1093.510574340844 +17.59375 0.1513463560457777 1098.010437011743 +17.625 0.1277705208353705 1102.510299682641 +17.65625 0.1403609732609399 1107.010162353539 +17.6875 0.1503210383547008 1111.510025024438 +17.71875 0.1562963809432098 1116.009887695337 +17.75 0.1475940413340046 1120.509750366235 +17.78125 0.1412528178631917 1125.009613037134 +17.8125 0.1506783240915595 1129.509475708032 +17.84375 0.1533055765365748 1134.009338378931 +17.875 0.1298077032152436 1138.509201049829 +17.90625 0.1422863915151054 1143.009063720728 +17.9375 0.1527476100657249 1147.508926391626 +17.96875 0.1585159083042 1152.008789062525 +18.0 0.1494341322857593 1156.508651733423 +18.03125 0.1432015539256189 1161.008514404322 +18.0625 0.1529348284731086 1165.50837707522 +18.09375 0.1552267558286024 1170.008239746119 +18.125 0.1317910699193585 1174.508102417017 +18.15625 0.1442192159649347 1179.007965087916 +18.1875 0.1551209608447591 1183.507827758814 +18.21875 0.1606807400708936 1188.007690429713 +18.25 0.1512314509665016 1192.507553100611 +18.28125 0.1451804195486575 1197.00741577151 +18.3125 0.1551359587623907 1201.507278442408 +18.34375 0.1571089814136766 1206.007141113307 +18.375 0.1337031293002361 1210.507003784206 +18.40625 0.1461368038403368 1215.006866455104 +18.4375 0.1574608183547282 1219.506729126002 +18.46875 0.1627737158145106 1224.006591796901 +18.5 0.1530236451343935 1228.506454467799 +18.53125 0.1471417794018446 1233.006317138698 +18.5625 0.1573060260229136 1237.506179809596 +18.59375 0.15896897913816 1242.006042480495 +18.625 0.1355600881678751 1246.505905151393 +18.65625 0.1480584372175698 1251.005767822292 +18.6875 0.1597602232805594 1255.50563049319 +18.71875 0.1647843002499096 1260.005493164089 +18.75 0.154795575289511 1264.505355834987 +18.78125 0.1491040672771789 1269.005218505886 +18.8125 0.1594358006329524 1273.505081176784 +18.84375 0.1608213788331513 1278.004943847683 +18.875 0.1373690815850701 1282.504806518581 +18.90625 0.1499994670079943 1287.00466918948 +18.9375 0.1620018993075552 1291.504531860378 +18.96875 0.1667401200106168 1296.004394531277 +19.0 0.1565241117081126 1300.504257202175 +19.03125 0.1510690468537137 1305.004119873074 +19.0625 0.1615109581846284 1309.503982543972 +19.09375 0.1626530516578939 1314.003845214871 +19.125 0.1391312982048554 1318.503707885769 +19.15625 0.151919662057496 1323.003570556668 +19.1875 0.1641978171209087 1327.503433227566 +19.21875 0.1686452485539238 1332.003295898465 +19.25 0.1582725315945457 1336.503158569363 +19.28125 0.1530360931041787 1341.003021240262 +19.3125 0.1635578813907874 1345.50288391116 +19.34375 0.1644948243532116 1350.002746582059 +19.375 0.1408611046153436 1354.502609252957 +19.40625 0.1538427847796541 1359.002471923856 +19.4375 0.1663685486438876 1363.502334594754 +19.46875 0.1704761147471038 1368.002197265653 +19.5 0.1599699794273519 1372.502059936551 +19.53125 0.1550181083978423 1377.00192260745 +19.5625 0.1655651615001207 1381.501785278348 +19.59375 0.1663309418141449 1386.001647949247 +19.625 0.1425702307382188 1390.501510620146 +19.65625 0.1557541316319879 1395.001373291044 +19.6875 0.1685153264700657 1399.501235961942 +19.71875 0.1722438810866091 1404.001098632841 +19.75 0.161674402461118 1408.50096130374 +19.78125 0.1569821598878027 1413.000823974638 +19.8125 0.1675501695895054 1417.500686645536 +19.84375 0.1681815453936252 1422.000549316435 +19.875 0.1442381996598139 1426.500411987333 +19.90625 0.1576679614425247 1431.000274658232 +19.9375 0.1706214070347111 1435.50013732913 +19.96875 0.1739651242968984 1440.000000000029