synthio: Add Synthesizer.note_state
This enables the specific use case of checking whether a note's release phase has ended, but is also potentially useful to implement a sort of "voice stealing" algorithm in Python code, which can take account of the note's envelope state as well as other factors specific to the program.
This commit is contained in:
parent
305303996d
commit
346f08f8b9
|
@ -31,6 +31,7 @@
|
|||
#include "py/binary.h"
|
||||
#include "py/objproperty.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/enum.h"
|
||||
#include "shared-bindings/util.h"
|
||||
#include "shared-bindings/synthio/Biquad.h"
|
||||
#include "shared-bindings/synthio/Synthesizer.h"
|
||||
|
@ -256,7 +257,9 @@ MP_PROPERTY_GETTER(synthio_synthesizer_sample_rate_obj,
|
|||
(mp_obj_t)&synthio_synthesizer_get_sample_rate_obj);
|
||||
|
||||
//| pressed: NoteSequence
|
||||
//| """A sequence of the currently pressed notes (read-only property)"""
|
||||
//| """A sequence of the currently pressed notes (read-only property).
|
||||
//|
|
||||
//| This does not include notes in the release phase of the envelope."""
|
||||
//|
|
||||
STATIC mp_obj_t synthio_synthesizer_obj_get_pressed(mp_obj_t self_in) {
|
||||
synthio_synthesizer_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
|
@ -268,6 +271,23 @@ MP_DEFINE_CONST_FUN_OBJ_1(synthio_synthesizer_get_pressed_obj, synthio_synthesiz
|
|||
MP_PROPERTY_GETTER(synthio_synthesizer_pressed_obj,
|
||||
(mp_obj_t)&synthio_synthesizer_get_pressed_obj);
|
||||
|
||||
//| def note_info(note: Note) -> Tuple[Optional[EnvelopeState], float]:
|
||||
//| """Get info about a note's current envelope state
|
||||
//|
|
||||
//| If the note is currently playing (including in the release phase), the returned value gives the current envelope state and the current envelope value.
|
||||
//|
|
||||
//| If the note is not playing on this synthesizer, returns the tuple ``(None, 0.0)``."""
|
||||
STATIC mp_obj_t synthio_synthesizer_obj_note_info(mp_obj_t self_in, mp_obj_t note) {
|
||||
synthio_synthesizer_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
check_for_deinit(self);
|
||||
mp_float_t vol = MICROPY_FLOAT_CONST(0.0);
|
||||
envelope_state_e state = common_hal_synthio_synthesizer_note_info(self, note, &vol);
|
||||
return MP_OBJ_NEW_TUPLE(
|
||||
cp_enum_find(&synthio_note_state_type, state),
|
||||
mp_obj_new_float(vol));
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(synthio_synthesizer_note_info_obj, synthio_synthesizer_obj_note_info);
|
||||
|
||||
//| blocks: List[BlockInput]
|
||||
//| """A list of blocks to advance whether or not they are associated with a playing note.
|
||||
//|
|
||||
|
@ -417,6 +437,7 @@ STATIC const mp_rom_map_elem_t synthio_synthesizer_locals_dict_table[] = {
|
|||
{ MP_ROM_QSTR(MP_QSTR_sample_rate), MP_ROM_PTR(&synthio_synthesizer_sample_rate_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_max_polyphony), MP_ROM_INT(CIRCUITPY_SYNTHIO_MAX_CHANNELS) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_pressed), MP_ROM_PTR(&synthio_synthesizer_pressed_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_note_info), MP_ROM_PTR(&synthio_synthesizer_note_info_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_blocks), MP_ROM_PTR(&synthio_synthesizer_blocks_obj) },
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(synthio_synthesizer_locals_dict, synthio_synthesizer_locals_dict_table);
|
||||
|
|
|
@ -45,3 +45,4 @@ void common_hal_synthio_synthesizer_retrigger(synthio_synthesizer_obj_t *self, m
|
|||
void common_hal_synthio_synthesizer_release_all(synthio_synthesizer_obj_t *self);
|
||||
mp_obj_t common_hal_synthio_synthesizer_get_pressed_notes(synthio_synthesizer_obj_t *self);
|
||||
mp_obj_t common_hal_synthio_synthesizer_get_blocks(synthio_synthesizer_obj_t *self);
|
||||
envelope_state_e common_hal_synthio_synthesizer_note_info(synthio_synthesizer_obj_t *self, mp_obj_t note, mp_float_t *vol_out);
|
||||
|
|
|
@ -45,6 +45,22 @@
|
|||
|
||||
#include "shared-module/synthio/LFO.h"
|
||||
|
||||
MAKE_ENUM_VALUE(synthio_note_state_type, note_state, ATTACK, SYNTHIO_ENVELOPE_STATE_ATTACK);
|
||||
MAKE_ENUM_VALUE(synthio_note_state_type, note_state, DECAY, SYNTHIO_ENVELOPE_STATE_DECAY);
|
||||
MAKE_ENUM_VALUE(synthio_note_state_type, note_state, SUSTAIN, SYNTHIO_ENVELOPE_STATE_SUSTAIN);
|
||||
MAKE_ENUM_VALUE(synthio_note_state_type, note_state, RELEASE, SYNTHIO_ENVELOPE_STATE_RELEASE);
|
||||
|
||||
MAKE_ENUM_MAP(synthio_note_state) {
|
||||
MAKE_ENUM_MAP_ENTRY(note_state, ATTACK),
|
||||
MAKE_ENUM_MAP_ENTRY(note_state, DECAY),
|
||||
MAKE_ENUM_MAP_ENTRY(note_state, SUSTAIN),
|
||||
MAKE_ENUM_MAP_ENTRY(note_state, RELEASE),
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(synthio_note_state_locals_dict, synthio_note_state_locals_table);
|
||||
MAKE_PRINTER(synthio, synthio_note_state);
|
||||
MAKE_ENUM_TYPE(synthio, NoteState, synthio_note_state);
|
||||
|
||||
#define default_attack_time (MICROPY_FLOAT_CONST(0.1))
|
||||
#define default_decay_time (MICROPY_FLOAT_CONST(0.05))
|
||||
#define default_release_time (MICROPY_FLOAT_CONST(0.2))
|
||||
|
@ -316,6 +332,7 @@ STATIC const mp_rom_map_elem_t synthio_module_globals_table[] = {
|
|||
{ 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) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_Note), MP_ROM_PTR(&synthio_note_type) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_NoteState), MP_ROM_PTR(&synthio_note_state_type) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_LFO), MP_ROM_PTR(&synthio_lfo_type) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_Synthesizer), MP_ROM_PTR(&synthio_synthesizer_type) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_from_file), MP_ROM_PTR(&synthio_from_file_obj) },
|
||||
|
|
|
@ -29,10 +29,16 @@
|
|||
#include "py/objnamedtuple.h"
|
||||
#include "py/enum.h"
|
||||
|
||||
typedef enum {
|
||||
SYNTHIO_ENVELOPE_STATE_ATTACK, SYNTHIO_ENVELOPE_STATE_DECAY,
|
||||
SYNTHIO_ENVELOPE_STATE_SUSTAIN, SYNTHIO_ENVELOPE_STATE_RELEASE
|
||||
} envelope_state_e;
|
||||
|
||||
typedef enum synthio_bend_mode_e {
|
||||
SYNTHIO_BEND_MODE_STATIC, SYNTHIO_BEND_MODE_VIBRATO, SYNTHIO_BEND_MODE_SWEEP, SYNTHIO_BEND_MODE_SWEEP_IN
|
||||
} synthio_bend_mode_t;
|
||||
|
||||
extern const mp_obj_type_t synthio_note_state_type;
|
||||
extern const cp_enum_obj_t bend_mode_VIBRATO_obj;
|
||||
extern const mp_obj_type_t synthio_bend_mode_type;
|
||||
typedef struct synthio_synth synthio_synth_t;
|
||||
|
|
|
@ -185,6 +185,17 @@ mp_obj_t common_hal_synthio_synthesizer_get_pressed_notes(synthio_synthesizer_ob
|
|||
return MP_OBJ_FROM_PTR(result);
|
||||
}
|
||||
|
||||
envelope_state_e common_hal_synthio_synthesizer_note_info(synthio_synthesizer_obj_t *self, mp_obj_t note, mp_float_t *vol_out) {
|
||||
for (int chan = 0; chan < CIRCUITPY_SYNTHIO_MAX_CHANNELS; chan++) {
|
||||
if (self->synth.span.note_obj[chan] == note) {
|
||||
*vol_out = self->synth.envelope_state[chan].level / 32767.;
|
||||
return self->synth.envelope_state[chan].state;
|
||||
}
|
||||
}
|
||||
return (envelope_state_e) - 1;
|
||||
}
|
||||
|
||||
|
||||
mp_obj_t common_hal_synthio_synthesizer_get_blocks(synthio_synthesizer_obj_t *self) {
|
||||
return self->blocks;
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#define SYNTHIO_FREQUENCY_SHIFT (16)
|
||||
|
||||
#include "shared-module/audiocore/__init__.h"
|
||||
#include "shared-bindings/synthio/__init__.h"
|
||||
|
||||
typedef struct {
|
||||
uint16_t dur;
|
||||
|
@ -49,11 +50,6 @@ typedef struct {
|
|||
uint16_t attack_level, sustain_level;
|
||||
} synthio_envelope_definition_t;
|
||||
|
||||
typedef enum {
|
||||
SYNTHIO_ENVELOPE_STATE_ATTACK, SYNTHIO_ENVELOPE_STATE_DECAY,
|
||||
SYNTHIO_ENVELOPE_STATE_SUSTAIN, SYNTHIO_ENVELOPE_STATE_RELEASE
|
||||
} envelope_state_e;
|
||||
|
||||
typedef struct {
|
||||
int16_t level;
|
||||
uint16_t substep;
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
from synthio import Synthesizer, Note, Envelope
|
||||
from audiocore import get_buffer
|
||||
|
||||
s = Synthesizer()
|
||||
n = Note(440, envelope=Envelope())
|
||||
print("{} {:.2f}".format(*s.note_info(n)))
|
||||
s.press(n)
|
||||
print("press")
|
||||
for _ in range(9):
|
||||
print("{} {:.2f}".format(*s.note_info(n)))
|
||||
get_buffer(s)
|
||||
|
||||
s.release(n)
|
||||
print("release")
|
||||
for _ in range(11):
|
||||
print("{} {:.2f}".format(*s.note_info(n)))
|
||||
get_buffer(s)
|
|
@ -0,0 +1,23 @@
|
|||
None 0.00
|
||||
press
|
||||
synthio.NoteState.ATTACK 0.23
|
||||
synthio.NoteState.ATTACK 0.46
|
||||
synthio.NoteState.ATTACK 0.70
|
||||
synthio.NoteState.ATTACK 0.93
|
||||
synthio.NoteState.DECAY 1.00
|
||||
synthio.NoteState.DECAY 0.91
|
||||
synthio.NoteState.DECAY 0.81
|
||||
synthio.NoteState.SUSTAIN 0.80
|
||||
synthio.NoteState.SUSTAIN 0.80
|
||||
release
|
||||
synthio.NoteState.RELEASE 0.80
|
||||
synthio.NoteState.RELEASE 0.71
|
||||
synthio.NoteState.RELEASE 0.61
|
||||
synthio.NoteState.RELEASE 0.52
|
||||
synthio.NoteState.RELEASE 0.43
|
||||
synthio.NoteState.RELEASE 0.34
|
||||
synthio.NoteState.RELEASE 0.24
|
||||
synthio.NoteState.RELEASE 0.15
|
||||
synthio.NoteState.RELEASE 0.06
|
||||
synthio.NoteState.RELEASE 0.00
|
||||
None 0.00
|
Loading…
Reference in New Issue