Skip to content

Commit

Permalink
Merge pull request #8186 from jepler/synthio-note-state
Browse files Browse the repository at this point in the history
synthio: Add Synthesizer.note_state
  • Loading branch information
tannewt authored Jul 21, 2023
2 parents 3053039 + 60b233f commit 9e22850
Show file tree
Hide file tree
Showing 10 changed files with 109 additions and 7 deletions.
1 change: 1 addition & 0 deletions ports/atmel-samd/boards/mini_sam_m4/mpconfigboard.mk
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ LONGINT_IMPL = MPZ

# No I2S on SAMD51G
CIRCUITPY_AUDIOBUSIO = 0
CIRCUITPY_SYNTHIO = 0

CIRCUITPY_BITBANG_APA102 = 1

Expand Down
23 changes: 22 additions & 1 deletion shared-bindings/synthio/Synthesizer.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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);
Expand All @@ -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(self, 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.
//|
Expand Down Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions shared-bindings/synthio/Synthesizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
27 changes: 27 additions & 0 deletions shared-bindings/synthio/__init__.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,32 @@

#include "shared-module/synthio/LFO.h"

//| class EnvelopeState:
//| ATTACK: EnvelopeState
//| """The note is in its attack phase"""
//| DECAY: EnvelopeState
//| """The note is in its decay phase"""
//| SUSTAIN: EnvelopeState
//| """The note is in its sustain phase"""
//| RELEASE: EnvelopeState
//| """The note is in its release phase"""
//|
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, EnvelopeState, 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))
Expand Down Expand Up @@ -316,6 +342,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_EnvelopeState), 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) },
Expand Down
6 changes: 6 additions & 0 deletions shared-bindings/synthio/__init__.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
11 changes: 11 additions & 0 deletions shared-module/synthio/Synthesizer.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
6 changes: 1 addition & 5 deletions shared-module/synthio/__init__.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
17 changes: 17 additions & 0 deletions tests/circuitpython/synthio_note_info.py
Original file line number Diff line number Diff line change
@@ -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)
23 changes: 23 additions & 0 deletions tests/circuitpython/synthio_note_info.py.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
None 0.00
press
synthio.EnvelopeState.ATTACK 0.23
synthio.EnvelopeState.ATTACK 0.46
synthio.EnvelopeState.ATTACK 0.70
synthio.EnvelopeState.ATTACK 0.93
synthio.EnvelopeState.DECAY 1.00
synthio.EnvelopeState.DECAY 0.91
synthio.EnvelopeState.DECAY 0.81
synthio.EnvelopeState.SUSTAIN 0.80
synthio.EnvelopeState.SUSTAIN 0.80
release
synthio.EnvelopeState.RELEASE 0.80
synthio.EnvelopeState.RELEASE 0.71
synthio.EnvelopeState.RELEASE 0.61
synthio.EnvelopeState.RELEASE 0.52
synthio.EnvelopeState.RELEASE 0.43
synthio.EnvelopeState.RELEASE 0.34
synthio.EnvelopeState.RELEASE 0.24
synthio.EnvelopeState.RELEASE 0.15
synthio.EnvelopeState.RELEASE 0.06
synthio.EnvelopeState.RELEASE 0.00
None 0.00
1 change: 0 additions & 1 deletion tests/cmdline/cmd_showbc.py.exp
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
File cmdline/cmd_showbc.py, code block '<module>' (descriptor: \.\+, bytecode @\.\+ bytes)
Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+):
########
\.\+63
arg names:
(N_STATE 3)
(N_EXC_STACK 0)
Expand Down

0 comments on commit 9e22850

Please sign in to comment.