diff --git a/CHANGELOG.md b/CHANGELOG.md index 59b56adb..574fc76e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ - **NEW**: new Disting EX ops: `EX.CH`, `EX.#`, `EX.N#`, `EX.NO#` - **NEW**: apply VCV Rack compatibility patches, so branches off main can be used in both hardware and software - **FIX**: update Disting EX looper ops to work with Disting EX firmware 1.23+ +- **NEW**: new dual W/ ops: `W/.SEL`, `W/S.POLY`, `W/S.POLY.RESET`, `W/1`, `W/2` ## v4.0.0 diff --git a/docs/ops/wslash_shared.md b/docs/ops/wslash_shared.md new file mode 100644 index 00000000..a68126aa --- /dev/null +++ b/docs/ops/wslash_shared.md @@ -0,0 +1,8 @@ +## W/2.0 + +More extensively covered in the [W/ Documentation](https://www.whimsicalraps.com/pages/w-type). + +There are separate ops for each supported algorithm: delay, synth, tape. Two +units can be connected using a different i2c address (refer to the official +documentation for more details). The following section describes ops that +control which unit is selected. These ops apply to all algorithms. diff --git a/docs/ops/wslash_shared.toml b/docs/ops/wslash_shared.toml new file mode 100644 index 00000000..9d91285d --- /dev/null +++ b/docs/ops/wslash_shared.toml @@ -0,0 +1,11 @@ +["W/.SEL"] +prototype = "W/.SEL x" +short = "Sets target W/2.0 unit (`1` = primary, `2` = secondary)." + +["W/1"] +prototype = "W/1: ..." +short = "Send following W/2.0 OPs to unit 1 ignoring the currently selected unit." + +["W/2"] +prototype = "W/2: ..." +short = "Send following W/2.0 OPs to unit 2 ignoring the currently selected unit." diff --git a/docs/ops/wslashsynth.toml b/docs/ops/wslashsynth.toml index 1b039484..31dcf0af 100644 --- a/docs/ops/wslashsynth.toml +++ b/docs/ops/wslashsynth.toml @@ -14,6 +14,14 @@ short = "set `voice` (s8) to `pitch` (s16V) and strike the vactrol at `velocity` prototype = "W/S.NOTE pitch level" short = "dynamically assign a voice, set to `pitch` (s16V), strike with `velocity`(s16V)" +["W/S.POLY"] +prototype = "W/S.POLY pitch level" +short = "As `W/S.NOTE` but across dual W/. Switches between primary and secondary units every 4 notes or until reset using `W/S.POLY.RESET`." + +["W/S.POLY.RESET"] +prototype = "W/S.POLY.RESET" +short = "Resets `W/S.POLY` note count." + ["W/S.AR.MODE"] prototype = "W/S.AR.MODE is_ar" short = "in attack-release mode, all notes are `plucked` and no `release` is required'" diff --git a/docs/whats_new.md b/docs/whats_new.md index 0a6e69ad..de74dc10 100644 --- a/docs/whats_new.md +++ b/docs/whats_new.md @@ -32,6 +32,7 @@ - **NEW**: new Disting EX ops: `EX.CH`, `EX.#`, `EX.N#`, `EX.NO#` - **NEW**: apply VCV Rack compatibility patches, so branches off main can be used in both hardware and software - **FIX**: update Disting EX looper ops to work with Disting EX firmware 1.23+ +- **NEW**: new dual W/ ops: `W/.SEL`, `W/S.POLY`, `W/S.POLY.RESET`, `W/1`, `W/2` ## v4.0.0 diff --git a/module/config.mk b/module/config.mk index 05a760d5..49a9cac8 100644 --- a/module/config.mk +++ b/module/config.mk @@ -114,6 +114,7 @@ CSRCS = \ ../src/ops/variables.c \ ../src/ops/whitewhale.c \ ../src/ops/wslash.c \ + ../src/ops/wslash_shared.c \ ../src/ops/wslashsynth.c \ ../src/ops/wslashdelay.c \ ../src/ops/wslashtape.c \ diff --git a/simulator/Makefile b/simulator/Makefile index b5a34e18..8820ef22 100644 --- a/simulator/Makefile +++ b/simulator/Makefile @@ -15,7 +15,7 @@ OBJ = tt.o ../src/teletype.o ../src/command.o ../src/helpers.o ../src/drum_helpe ../src/ops/fader.o ../src/ops/matrixarchate.o ../src/ops/wslash.o \ ../src/ops/seed.o ../src/ops/i2c.o ../src/ops/disting.o \ ../src/ops/wslashtape.o ../src/ops/wslashsynth.o ../src/ops/wslashdelay.o \ - ../src/ops/crow.o ../src/ops/i2c2midi.o\ + ../src/ops/wslash_shared.o ../src/ops/crow.o ../src/ops/i2c2midi.o\ ../libavr32/src/euclidean/euclidean.o ../libavr32/src/euclidean/data.o \ ../libavr32/src/music.o ../libavr32/src/util.o ../libavr32/src/random.o \ ../src/ops/midi.o diff --git a/src/match_token.rl b/src/match_token.rl index 55af4579..17ef2c27 100644 --- a/src/match_token.rl +++ b/src/match_token.rl @@ -740,6 +740,7 @@ "WS.REC" => { MATCH_OP(E_OP_WS_REC); }; "WS.CUE" => { MATCH_OP(E_OP_WS_CUE); }; "WS.LOOP" => { MATCH_OP(E_OP_WS_LOOP); }; + "W/.SEL" => { MATCH_OP(E_OP_WS_SEL); }; # disting ex "EX" => { MATCH_OP(E_OP_EX); }; @@ -830,20 +831,22 @@ "EX.ZO2" => { MATCH_OP(E_OP_EX_ZO2); }; # w/s - "W/S.PITCH" => { MATCH_OP(E_OP_WS_S_PITCH); }; - "W/S.VEL" => { MATCH_OP(E_OP_WS_S_VEL); }; - "W/S.VOX" => { MATCH_OP(E_OP_WS_S_VOX); }; - "W/S.NOTE" => { MATCH_OP(E_OP_WS_S_NOTE); }; - "W/S.AR.MODE" => { MATCH_OP(E_OP_WS_S_AR_MODE); }; - "W/S.LPG.TIME" => { MATCH_OP(E_OP_WS_S_LPG_TIME); }; - "W/S.LPG.SYM" => { MATCH_OP(E_OP_WS_S_LPG_SYMMETRY); }; - "W/S.CURVE" => { MATCH_OP(E_OP_WS_S_CURVE); }; - "W/S.RAMP" => { MATCH_OP(E_OP_WS_S_RAMP); }; - "W/S.FM.INDEX" => { MATCH_OP(E_OP_WS_S_FM_INDEX); }; - "W/S.FM.RATIO" => { MATCH_OP(E_OP_WS_S_FM_RATIO); }; - "W/S.FM.ENV" => { MATCH_OP(E_OP_WS_S_FM_ENV); }; - "W/S.PATCH" => { MATCH_OP(E_OP_WS_S_PATCH); }; - "W/S.VOICES" => { MATCH_OP(E_OP_WS_S_VOICES); }; + "W/S.PITCH" => { MATCH_OP(E_OP_WS_S_PITCH); }; + "W/S.VEL" => { MATCH_OP(E_OP_WS_S_VEL); }; + "W/S.VOX" => { MATCH_OP(E_OP_WS_S_VOX); }; + "W/S.NOTE" => { MATCH_OP(E_OP_WS_S_NOTE); }; + "W/S.POLY" => { MATCH_OP(E_OP_WS_S_POLY); }; + "W/S.POLY.RESET" => { MATCH_OP(E_OP_WS_S_POLY_RESET); }; + "W/S.AR.MODE" => { MATCH_OP(E_OP_WS_S_AR_MODE); }; + "W/S.LPG.TIME" => { MATCH_OP(E_OP_WS_S_LPG_TIME); }; + "W/S.LPG.SYM" => { MATCH_OP(E_OP_WS_S_LPG_SYMMETRY); }; + "W/S.CURVE" => { MATCH_OP(E_OP_WS_S_CURVE); }; + "W/S.RAMP" => { MATCH_OP(E_OP_WS_S_RAMP); }; + "W/S.FM.INDEX" => { MATCH_OP(E_OP_WS_S_FM_INDEX); }; + "W/S.FM.RATIO" => { MATCH_OP(E_OP_WS_S_FM_RATIO); }; + "W/S.FM.ENV" => { MATCH_OP(E_OP_WS_S_FM_ENV); }; + "W/S.PATCH" => { MATCH_OP(E_OP_WS_S_PATCH); }; + "W/S.VOICES" => { MATCH_OP(E_OP_WS_S_VOICES); }; # w/d "W/D.FBK" => { MATCH_OP(E_OP_WS_D_FEEDBACK); }; @@ -1069,6 +1072,10 @@ "JF1" => { MATCH_MOD(E_MOD_JF1); }; "JF2" => { MATCH_MOD(E_MOD_JF2); }; + # w/ + "W/1" => { MATCH_MOD(E_MOD_WS1); }; + "W/2" => { MATCH_MOD(E_MOD_WS2); }; + # crow "CROWN" => { MATCH_MOD(E_MOD_CROWN); }; "CROW1" => { MATCH_MOD(E_MOD_CROW1); }; diff --git a/src/ops/op.c b/src/ops/op.c index 69958e73..5c9a1f75 100644 --- a/src/ops/op.c +++ b/src/ops/op.c @@ -32,6 +32,7 @@ #include "ops/variables.h" #include "ops/whitewhale.h" #include "ops/wslash.h" +#include "ops/wslash_shared.h" #include "ops/wslashdelay.h" #include "ops/wslashsynth.h" #include "ops/wslashtape.h" @@ -166,13 +167,13 @@ const tele_op_t *tele_ops[E_OP__LENGTH] = { &op_JF_POLY_RESET, &op_JF_SEL, // W/ - &op_WS_PLAY, &op_WS_REC, &op_WS_CUE, &op_WS_LOOP, + &op_WS_PLAY, &op_WS_REC, &op_WS_CUE, &op_WS_LOOP, &op_WS_SEL, // W/S &op_WS_S_PITCH, &op_WS_S_VEL, &op_WS_S_VOX, &op_WS_S_NOTE, &op_WS_S_AR_MODE, &op_WS_S_LPG_TIME, &op_WS_S_LPG_SYMMETRY, &op_WS_S_CURVE, &op_WS_S_RAMP, &op_WS_S_FM_INDEX, &op_WS_S_FM_RATIO, &op_WS_S_FM_ENV, &op_WS_S_VOICES, - &op_WS_S_PATCH, + &op_WS_S_PATCH, &op_WS_S_POLY, &op_WS_S_POLY_RESET, // W/D &op_WS_D_FEEDBACK, &op_WS_D_MIX, &op_WS_D_LOWPASS, &op_WS_D_FREEZE, @@ -346,6 +347,9 @@ const tele_mod_t *tele_mods[E_MOD__LENGTH] = { // just friends &mod_JF0, &mod_JF1, &mod_JF2, + // w/ + &mod_WS1, &mod_WS2, + // crow &mod_CROWN, &mod_CROW1, &mod_CROW2, &mod_CROW3, &mod_CROW4 }; diff --git a/src/ops/op_enum.h b/src/ops/op_enum.h index 9e8161d5..92c6c75b 100644 --- a/src/ops/op_enum.h +++ b/src/ops/op_enum.h @@ -451,6 +451,7 @@ typedef enum { E_OP_WS_REC, E_OP_WS_CUE, E_OP_WS_LOOP, + E_OP_WS_SEL, E_OP_WS_S_PITCH, E_OP_WS_S_VEL, E_OP_WS_S_VOX, @@ -465,6 +466,8 @@ typedef enum { E_OP_WS_S_FM_ENV, E_OP_WS_S_VOICES, E_OP_WS_S_PATCH, + E_OP_WS_S_POLY, + E_OP_WS_S_POLY_RESET, E_OP_WS_D_FEEDBACK, E_OP_WS_D_MIX, E_OP_WS_D_LOWPASS, @@ -990,6 +993,8 @@ typedef enum { E_MOD_JF0, E_MOD_JF1, E_MOD_JF2, + E_MOD_WS1, + E_MOD_WS2, E_MOD_CROWN, E_MOD_CROW1, E_MOD_CROW2, diff --git a/src/ops/wslash_shared.c b/src/ops/wslash_shared.c new file mode 100644 index 00000000..bf867559 --- /dev/null +++ b/src/ops/wslash_shared.c @@ -0,0 +1,47 @@ +#include "ops/wslash_shared.h" + +#include "helpers.h" +#include "teletype.h" + +static void mod_WS1_func(scene_state_t *ss, exec_state_t *es, + command_state_t *cs, + const tele_command_t *post_command); +static void mod_WS2_func(scene_state_t *ss, exec_state_t *es, + command_state_t *cs, + const tele_command_t *post_command); +static void op_WS_SEL_get(const void *data, scene_state_t *ss, exec_state_t *es, + command_state_t *cs); + +// clang-format off + +const tele_mod_t mod_WS1 = MAKE_MOD(W/1, mod_WS1_func, 0); +const tele_mod_t mod_WS2 = MAKE_MOD(W/2, mod_WS2_func, 0); +const tele_op_t op_WS_SEL = MAKE_GET_OP(W/.SEL, op_WS_SEL_get , 1, false); + +// clang-format on + +uint8_t wslash_unit = 1; + +static void mod_WS1_func(scene_state_t *ss, exec_state_t *es, + command_state_t *cs, + const tele_command_t *post_command) { + u8 u = wslash_unit; + wslash_unit = 1; + process_command(ss, es, post_command); + wslash_unit = u; +} + +static void mod_WS2_func(scene_state_t *ss, exec_state_t *es, + command_state_t *cs, + const tele_command_t *post_command) { + u8 u = wslash_unit; + wslash_unit = 2; + process_command(ss, es, post_command); + wslash_unit = u; +} + +static void op_WS_SEL_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss), + exec_state_t *NOTUSED(es), command_state_t *cs) { + u8 unit = cs_pop(cs); + if (unit > 0 && unit < 3) wslash_unit = unit; +} diff --git a/src/ops/wslash_shared.h b/src/ops/wslash_shared.h new file mode 100644 index 00000000..98541d76 --- /dev/null +++ b/src/ops/wslash_shared.h @@ -0,0 +1,13 @@ +#ifndef _OPS_WSLASH_SHARED_H_ +#define _OPS_WSLASH_SHARED_H_ + +#include "ops/op.h" + +extern const tele_mod_t mod_WS1; +extern const tele_mod_t mod_WS2; + +extern const tele_op_t op_WS_SEL; + +extern uint8_t wslash_unit; + +#endif diff --git a/src/ops/wslashdelay.c b/src/ops/wslashdelay.c index f731194e..31b030e8 100644 --- a/src/ops/wslashdelay.c +++ b/src/ops/wslashdelay.c @@ -3,33 +3,36 @@ #include "helpers.h" #include "i2c.h" #include "ii.h" +#include "ops/wslash_shared.h" #include "teletype_io.h" -I2C_WRITE_16(op_WS_D_FEEDBACK_write, WS_D_ADDR, WS_D_FEEDBACK) -I2C_RECV_16(op_WS_D_FEEDBACK_recv, WS_D_ADDR, WS_D_FEEDBACK) -I2C_WRITE_16(op_WS_D_MIX_write, WS_D_ADDR, WS_D_MIX) -I2C_RECV_16(op_WS_D_MIX_recv, WS_D_ADDR, WS_D_MIX) -I2C_WRITE_16(op_WS_D_LOWPASS_write, WS_D_ADDR, WS_D_LOWPASS) -I2C_RECV_16(op_WS_D_LOWPASS_recv, WS_D_ADDR, WS_D_LOWPASS) -I2C_WRITE_8(op_WS_D_FREEZE_write, WS_D_ADDR, WS_D_FREEZE) -I2C_RECV_8(op_WS_D_FREEZE_recv, WS_D_ADDR, WS_D_FREEZE) -I2C_WRITE_16(op_WS_D_TIME_write, WS_D_ADDR, WS_D_TIME) -I2C_RECV_16(op_WS_D_TIME_recv, WS_D_ADDR, WS_D_TIME) -I2C_WRITE_8_8(op_WS_D_LENGTH_write, WS_D_ADDR, WS_D_LENGTH) -I2C_WRITE_8_8(op_WS_D_POSITION_write, WS_D_ADDR, WS_D_POSITION) -I2C_WRITE_8_8(op_WS_D_CUT_write, WS_D_ADDR, WS_D_CUT) -I2C_WRITE_8(op_WS_D_FREQ_RANGE_write, WS_D_ADDR, WS_D_FREQ_RANGE) -I2C_WRITE_16(op_WS_D_RATE_write, WS_D_ADDR, WS_D_RATE) -I2C_RECV_16(op_WS_D_RATE_recv, WS_D_ADDR, WS_D_RATE) -I2C_WRITE_16(op_WS_D_FREQ_write, WS_D_ADDR, WS_D_FREQ) -I2C_RECV_16(op_WS_D_FREQ_recv, WS_D_ADDR, WS_D_FREQ) -I2C_WRITE_0(op_WS_D_CLK_write, WS_D_ADDR, WS_D_CLK) -I2C_WRITE_8_8(op_WS_D_CLK_RATIO_write, WS_D_ADDR, WS_D_CLK_RATIO) -I2C_WRITE_16(op_WS_D_PLUCK_write, WS_D_ADDR, WS_D_PLUCK) -I2C_WRITE_16(op_WS_D_MOD_RATE_write, WS_D_ADDR, WS_D_MOD_RATE) -I2C_RECV_16(op_WS_D_MOD_RATE_recv, WS_D_ADDR, WS_D_MOD_RATE) -I2C_WRITE_16(op_WS_D_MOD_AMOUNT_write, WS_D_ADDR, WS_D_MOD_AMOUNT) -I2C_RECV_16(op_WS_D_MOD_AMOUNT_recv, WS_D_ADDR, WS_D_MOD_AMOUNT) +#define WS_ADDR (wslash_unit == 2 ? WS_D_ADDR_2 : WS_D_ADDR) + +I2C_WRITE_16(op_WS_D_FEEDBACK_write, WS_ADDR, WS_D_FEEDBACK) +I2C_RECV_16(op_WS_D_FEEDBACK_recv, WS_ADDR, WS_D_FEEDBACK) +I2C_WRITE_16(op_WS_D_MIX_write, WS_ADDR, WS_D_MIX) +I2C_RECV_16(op_WS_D_MIX_recv, WS_ADDR, WS_D_MIX) +I2C_WRITE_16(op_WS_D_LOWPASS_write, WS_ADDR, WS_D_LOWPASS) +I2C_RECV_16(op_WS_D_LOWPASS_recv, WS_ADDR, WS_D_LOWPASS) +I2C_WRITE_8(op_WS_D_FREEZE_write, WS_ADDR, WS_D_FREEZE) +I2C_RECV_8(op_WS_D_FREEZE_recv, WS_ADDR, WS_D_FREEZE) +I2C_WRITE_16(op_WS_D_TIME_write, WS_ADDR, WS_D_TIME) +I2C_RECV_16(op_WS_D_TIME_recv, WS_ADDR, WS_D_TIME) +I2C_WRITE_8_8(op_WS_D_LENGTH_write, WS_ADDR, WS_D_LENGTH) +I2C_WRITE_8_8(op_WS_D_POSITION_write, WS_ADDR, WS_D_POSITION) +I2C_WRITE_8_8(op_WS_D_CUT_write, WS_ADDR, WS_D_CUT) +I2C_WRITE_8(op_WS_D_FREQ_RANGE_write, WS_ADDR, WS_D_FREQ_RANGE) +I2C_WRITE_16(op_WS_D_RATE_write, WS_ADDR, WS_D_RATE) +I2C_RECV_16(op_WS_D_RATE_recv, WS_ADDR, WS_D_RATE) +I2C_WRITE_16(op_WS_D_FREQ_write, WS_ADDR, WS_D_FREQ) +I2C_RECV_16(op_WS_D_FREQ_recv, WS_ADDR, WS_D_FREQ) +I2C_WRITE_0(op_WS_D_CLK_write, WS_ADDR, WS_D_CLK) +I2C_WRITE_8_8(op_WS_D_CLK_RATIO_write, WS_ADDR, WS_D_CLK_RATIO) +I2C_WRITE_16(op_WS_D_PLUCK_write, WS_ADDR, WS_D_PLUCK) +I2C_WRITE_16(op_WS_D_MOD_RATE_write, WS_ADDR, WS_D_MOD_RATE) +I2C_RECV_16(op_WS_D_MOD_RATE_recv, WS_ADDR, WS_D_MOD_RATE) +I2C_WRITE_16(op_WS_D_MOD_AMOUNT_write, WS_ADDR, WS_D_MOD_AMOUNT) +I2C_RECV_16(op_WS_D_MOD_AMOUNT_recv, WS_ADDR, WS_D_MOD_AMOUNT) // clang-format off const tele_op_t op_WS_D_FEEDBACK = MAKE_GET_SET_OP(W/D.FBK, op_WS_D_FEEDBACK_recv, op_WS_D_FEEDBACK_write, 0, true); diff --git a/src/ops/wslashdelay.h b/src/ops/wslashdelay.h index e47e88c3..10ddd3c2 100644 --- a/src/ops/wslashdelay.h +++ b/src/ops/wslashdelay.h @@ -2,6 +2,7 @@ #define _OPS_WSLASH_DELAY_H_ #include "ops/op.h" + extern const tele_op_t op_WS_D_FEEDBACK; extern const tele_op_t op_WS_D_MIX; extern const tele_op_t op_WS_D_LOWPASS; diff --git a/src/ops/wslashsynth.c b/src/ops/wslashsynth.c index 83ed17af..156d6fa1 100644 --- a/src/ops/wslashsynth.c +++ b/src/ops/wslashsynth.c @@ -3,44 +3,56 @@ #include "helpers.h" #include "i2c.h" #include "ii.h" +#include "ops/wslash_shared.h" #include "teletype_io.h" -I2C_WRITE_8_16(op_WS_S_VEL_write, WS_S_ADDR, WS_S_VEL) -I2C_WRITE_8_16(op_WS_S_PITCH_write, WS_S_ADDR, WS_S_PITCH) +#define WS_ADDR (wslash_unit == 2 ? WS_S_ADDR_2 : WS_S_ADDR) -I2C_WRITE_8_16_16(op_WS_S_VOX_write, WS_S_ADDR, WS_S_VOX) -I2C_WRITE_16_16(op_WS_S_NOTE_write, WS_S_ADDR, WS_S_NOTE) +I2C_WRITE_8_16(op_WS_S_VEL_write, WS_ADDR, WS_S_VEL) +I2C_WRITE_8_16(op_WS_S_PITCH_write, WS_ADDR, WS_S_PITCH) -I2C_WRITE_8(op_WS_S_AR_MODE_write, WS_S_ADDR, WS_S_AR_MODE) -I2C_RECV_8(op_WS_S_AR_MODE_recv, WS_S_ADDR, WS_S_AR_MODE) +I2C_WRITE_8_16_16(op_WS_S_VOX_write, WS_ADDR, WS_S_VOX) +I2C_WRITE_16_16(op_WS_S_NOTE_write, WS_ADDR, WS_S_NOTE) -I2C_WRITE_16(op_WS_S_CURVE_write, WS_S_ADDR, WS_S_CURVE) -I2C_RECV_16(op_WS_S_CURVE_recv, WS_S_ADDR, WS_S_CURVE) -I2C_WRITE_16(op_WS_S_RAMP_write, WS_S_ADDR, WS_S_RAMP) -I2C_RECV_16(op_WS_S_RAMP_recv, WS_S_ADDR, WS_S_RAMP) +I2C_WRITE_8(op_WS_S_AR_MODE_write, WS_ADDR, WS_S_AR_MODE) +I2C_RECV_8(op_WS_S_AR_MODE_recv, WS_ADDR, WS_S_AR_MODE) -I2C_WRITE_16(op_WS_S_FM_INDEX_write, WS_S_ADDR, WS_S_FM_INDEX) -I2C_RECV_16(op_WS_S_FM_INDEX_recv, WS_S_ADDR, WS_S_FM_INDEX) -I2C_WRITE_16(op_WS_S_FM_ENV_write, WS_S_ADDR, WS_S_FM_ENV) -I2C_RECV_16(op_WS_S_FM_ENV_recv, WS_S_ADDR, WS_S_FM_ENV) -I2C_WRITE_16_16(op_WS_S_FM_RATIO_write, WS_S_ADDR, WS_S_FM_RATIO) +I2C_WRITE_16(op_WS_S_CURVE_write, WS_ADDR, WS_S_CURVE) +I2C_RECV_16(op_WS_S_CURVE_recv, WS_ADDR, WS_S_CURVE) +I2C_WRITE_16(op_WS_S_RAMP_write, WS_ADDR, WS_S_RAMP) +I2C_RECV_16(op_WS_S_RAMP_recv, WS_ADDR, WS_S_RAMP) -I2C_WRITE_16(op_WS_S_LPG_TIME_write, WS_S_ADDR, WS_S_LPG_TIME) -I2C_RECV_16(op_WS_S_LPG_TIME_recv, WS_S_ADDR, WS_S_LPG_TIME) -I2C_WRITE_16(op_WS_S_LPG_SYMMETRY_write, WS_S_ADDR, WS_S_LPG_SYMMETRY) -I2C_RECV_16(op_WS_S_LPG_SYMMETRY_recv, WS_S_ADDR, WS_S_LPG_SYMMETRY) +I2C_WRITE_16(op_WS_S_FM_INDEX_write, WS_ADDR, WS_S_FM_INDEX) +I2C_RECV_16(op_WS_S_FM_INDEX_recv, WS_ADDR, WS_S_FM_INDEX) +I2C_WRITE_16(op_WS_S_FM_ENV_write, WS_ADDR, WS_S_FM_ENV) +I2C_RECV_16(op_WS_S_FM_ENV_recv, WS_ADDR, WS_S_FM_ENV) +I2C_WRITE_16_16(op_WS_S_FM_RATIO_write, WS_ADDR, WS_S_FM_RATIO) -I2C_WRITE_8_8(op_WS_S_PATCH_write, WS_S_ADDR, WS_S_PATCH) -I2C_WRITE_8(op_WS_S_VOICES_write, WS_S_ADDR, WS_S_VOICES) -I2C_RECV_8(op_WS_S_VOICES_recv, WS_S_ADDR, WS_S_VOICES) +I2C_WRITE_16(op_WS_S_LPG_TIME_write, WS_ADDR, WS_S_LPG_TIME) +I2C_RECV_16(op_WS_S_LPG_TIME_recv, WS_ADDR, WS_S_LPG_TIME) +I2C_WRITE_16(op_WS_S_LPG_SYMMETRY_write, WS_ADDR, WS_S_LPG_SYMMETRY) +I2C_RECV_16(op_WS_S_LPG_SYMMETRY_recv, WS_ADDR, WS_S_LPG_SYMMETRY) + +I2C_WRITE_8_8(op_WS_S_PATCH_write, WS_ADDR, WS_S_PATCH) +I2C_WRITE_8(op_WS_S_VOICES_write, WS_ADDR, WS_S_VOICES) +I2C_RECV_8(op_WS_S_VOICES_recv, WS_ADDR, WS_S_VOICES) + +static void op_WS_S_POLY_get(const void *data, scene_state_t *ss, + exec_state_t *es, command_state_t *cs); +static void op_WS_S_POLY_RESET_get(const void *data, scene_state_t *ss, + exec_state_t *es, command_state_t *cs); // clang-format off + const tele_op_t op_WS_S_PITCH = MAKE_GET_OP(W/S.PITCH, op_WS_S_PITCH_write, 2, false); const tele_op_t op_WS_S_VEL = MAKE_GET_OP(W/S.VEL, op_WS_S_VEL_write, 2, false); const tele_op_t op_WS_S_VOX = MAKE_GET_OP(W/S.VOX, op_WS_S_VOX_write, 3, false); const tele_op_t op_WS_S_NOTE = MAKE_GET_OP(W/S.NOTE, op_WS_S_NOTE_write, 2, false); +const tele_op_t op_WS_S_POLY = MAKE_GET_OP(W/S.POLY , op_WS_S_POLY_get , 2, false); +const tele_op_t op_WS_S_POLY_RESET = MAKE_GET_OP(W/S.POLY.RESET, op_WS_S_POLY_RESET_get, 0, false); + const tele_op_t op_WS_S_AR_MODE = MAKE_GET_SET_OP(W/S.AR.MODE , op_WS_S_AR_MODE_recv, op_WS_S_AR_MODE_write, 0, true); const tele_op_t op_WS_S_CURVE = MAKE_GET_SET_OP(W/S.CURVE , op_WS_S_CURVE_recv, op_WS_S_CURVE_write, 0, true); @@ -57,3 +69,27 @@ const tele_op_t op_WS_S_PATCH = MAKE_GET_OP(W/S.PATCH , op_WS_S_PATCH_write, 2, const tele_op_t op_WS_S_VOICES = MAKE_GET_SET_OP(W/S.VOICES , op_WS_S_VOICES_recv, op_WS_S_VOICES_write, 0, true); // clang-format on + +static u8 note_count = 1; + +static void op_WS_S_POLY_get(const void *NOTUSED(data), + scene_state_t *NOTUSED(ss), + exec_state_t *NOTUSED(es), command_state_t *cs) { + int16_t pitch = cs_pop(cs); + int16_t vel = cs_pop(cs); + uint8_t d[] = { WS_S_NOTE, pitch >> 8, pitch & 0xff, vel >> 8, vel & 0xff }; + + if (note_count < 4) + tele_ii_tx(wslash_unit == 1 ? WS_S_ADDR : WS_S_ADDR_2, d, 5); + else + tele_ii_tx(wslash_unit == 2 ? WS_S_ADDR : WS_S_ADDR_2, d, 5); + + note_count = (note_count + 1) & 7; +} + +static void op_WS_S_POLY_RESET_get(const void *NOTUSED(data), + scene_state_t *NOTUSED(ss), + exec_state_t *NOTUSED(es), + command_state_t *NOTUSED(cs)) { + note_count = 0; +} diff --git a/src/ops/wslashsynth.h b/src/ops/wslashsynth.h index c7079155..0e845bd2 100644 --- a/src/ops/wslashsynth.h +++ b/src/ops/wslashsynth.h @@ -2,11 +2,14 @@ #define _OPS_WSLASH_SYNTH_H_ #include "ops/op.h" + extern const tele_op_t op_WS_S_PITCH; // PITCH extern const tele_op_t op_WS_S_VEL; // VELOCITY extern const tele_op_t op_WS_S_VOX; // PLAY VOICE extern const tele_op_t op_WS_S_NOTE; // PLAY NOTE +extern const tele_op_t op_WS_S_POLY; +extern const tele_op_t op_WS_S_POLY_RESET; extern const tele_op_t op_WS_S_AR_MODE; // AR MODE @@ -22,4 +25,5 @@ extern const tele_op_t op_WS_S_FM_ENV; // FM ENV extern const tele_op_t op_WS_S_PATCH; // PATCH extern const tele_op_t op_WS_S_VOICES; // VOICES + #endif diff --git a/src/ops/wslashtape.c b/src/ops/wslashtape.c index c16e28a4..88794f4c 100644 --- a/src/ops/wslashtape.c +++ b/src/ops/wslashtape.c @@ -3,33 +3,36 @@ #include "helpers.h" #include "i2c.h" #include "ii.h" +#include "ops/wslash_shared.h" #include "teletype_io.h" -I2C_WRITE_8(op_WS_T_RECORD_write, WS_T_ADDR, WS_T_RECORD) -I2C_RECV_8(op_WS_T_RECORD_recv, WS_T_ADDR, WS_T_RECORD) -I2C_WRITE_8(op_WS_T_PLAY_write, WS_T_ADDR, WS_T_PLAY) -I2C_RECV_8(op_WS_T_PLAY_recv, WS_T_ADDR, WS_T_PLAY) -I2C_WRITE_0(op_WS_T_REV_write, WS_T_ADDR, WS_T_REV) -I2C_WRITE_16_16(op_WS_T_SPEED_write, WS_T_ADDR, WS_T_SPEED) -I2C_WRITE_16(op_WS_T_FREQ_write, WS_T_ADDR, WS_T_FREQ) -I2C_RECV_16(op_WS_T_FREQ_recv, WS_T_ADDR, WS_T_FREQ) -I2C_WRITE_16(op_WS_T_PRE_LEVEL_write, WS_T_ADDR, WS_T_PRE_LEVEL) -I2C_RECV_16(op_WS_T_PRE_LEVEL_recv, WS_T_ADDR, WS_T_PRE_LEVEL) -I2C_WRITE_16(op_WS_T_MONITOR_LEVEL_write, WS_T_ADDR, WS_T_MONITOR_LEVEL) -I2C_RECV_16(op_WS_T_MONITOR_LEVEL_recv, WS_T_ADDR, WS_T_MONITOR_LEVEL) -I2C_WRITE_16(op_WS_T_REC_LEVEL_write, WS_T_ADDR, WS_T_REC_LEVEL) -I2C_RECV_16(op_WS_T_REC_LEVEL_recv, WS_T_ADDR, WS_T_REC_LEVEL) -I2C_WRITE_8(op_WS_T_HEAD_ORDER_write, WS_T_ADDR, WS_T_HEAD_ORDER) -I2C_RECV_8(op_WS_T_HEAD_ORDER_recv, WS_T_ADDR, WS_T_HEAD_ORDER) -I2C_WRITE_0(op_WS_T_LOOP_START_write, WS_T_ADDR, WS_T_LOOP_START) -I2C_WRITE_0(op_WS_T_LOOP_END_write, WS_T_ADDR, WS_T_LOOP_END) -I2C_WRITE_8(op_WS_T_LOOP_ACTIVE_write, WS_T_ADDR, WS_T_LOOP_ACTIVE) -I2C_WRITE_8(op_WS_T_LOOP_SCALE_write, WS_T_ADDR, WS_T_LOOP_SCALE) -I2C_RECV_8(op_WS_T_LOOP_SCALE_recv, WS_T_ADDR, WS_T_LOOP_SCALE) -I2C_WRITE_8(op_WS_T_LOOP_NEXT_write, WS_T_ADDR, WS_T_LOOP_NEXT) -I2C_WRITE_16_16(op_WS_T_TIMESTAMP_write, WS_T_ADDR, WS_T_TIMESTAMP) -I2C_WRITE_16_16(op_WS_T_SEEK_write, WS_T_ADDR, WS_T_SEEK) -I2C_WRITE_0(op_WS_T_CLEARTAPE_write, WS_T_ADDR, WS_T_CLEARTAPE) +#define WS_ADDR (wslash_unit == 2 ? WS_T_ADDR_2 : WS_T_ADDR) + +I2C_WRITE_8(op_WS_T_RECORD_write, WS_ADDR, WS_T_RECORD) +I2C_RECV_8(op_WS_T_RECORD_recv, WS_ADDR, WS_T_RECORD) +I2C_WRITE_8(op_WS_T_PLAY_write, WS_ADDR, WS_T_PLAY) +I2C_RECV_8(op_WS_T_PLAY_recv, WS_ADDR, WS_T_PLAY) +I2C_WRITE_0(op_WS_T_REV_write, WS_ADDR, WS_T_REV) +I2C_WRITE_16_16(op_WS_T_SPEED_write, WS_ADDR, WS_T_SPEED) +I2C_WRITE_16(op_WS_T_FREQ_write, WS_ADDR, WS_T_FREQ) +I2C_RECV_16(op_WS_T_FREQ_recv, WS_ADDR, WS_T_FREQ) +I2C_WRITE_16(op_WS_T_PRE_LEVEL_write, WS_ADDR, WS_T_PRE_LEVEL) +I2C_RECV_16(op_WS_T_PRE_LEVEL_recv, WS_ADDR, WS_T_PRE_LEVEL) +I2C_WRITE_16(op_WS_T_MONITOR_LEVEL_write, WS_ADDR, WS_T_MONITOR_LEVEL) +I2C_RECV_16(op_WS_T_MONITOR_LEVEL_recv, WS_ADDR, WS_T_MONITOR_LEVEL) +I2C_WRITE_16(op_WS_T_REC_LEVEL_write, WS_ADDR, WS_T_REC_LEVEL) +I2C_RECV_16(op_WS_T_REC_LEVEL_recv, WS_ADDR, WS_T_REC_LEVEL) +I2C_WRITE_8(op_WS_T_HEAD_ORDER_write, WS_ADDR, WS_T_HEAD_ORDER) +I2C_RECV_8(op_WS_T_HEAD_ORDER_recv, WS_ADDR, WS_T_HEAD_ORDER) +I2C_WRITE_0(op_WS_T_LOOP_START_write, WS_ADDR, WS_T_LOOP_START) +I2C_WRITE_0(op_WS_T_LOOP_END_write, WS_ADDR, WS_T_LOOP_END) +I2C_WRITE_8(op_WS_T_LOOP_ACTIVE_write, WS_ADDR, WS_T_LOOP_ACTIVE) +I2C_WRITE_8(op_WS_T_LOOP_SCALE_write, WS_ADDR, WS_T_LOOP_SCALE) +I2C_RECV_8(op_WS_T_LOOP_SCALE_recv, WS_ADDR, WS_T_LOOP_SCALE) +I2C_WRITE_8(op_WS_T_LOOP_NEXT_write, WS_ADDR, WS_T_LOOP_NEXT) +I2C_WRITE_16_16(op_WS_T_TIMESTAMP_write, WS_ADDR, WS_T_TIMESTAMP) +I2C_WRITE_16_16(op_WS_T_SEEK_write, WS_ADDR, WS_T_SEEK) +I2C_WRITE_0(op_WS_T_CLEARTAPE_write, WS_ADDR, WS_T_CLEARTAPE) // clang-format off const tele_op_t op_WS_T_RECORD = MAKE_GET_SET_OP(W/T.REC , op_WS_T_RECORD_recv , op_WS_T_RECORD_write , 0, true); diff --git a/src/ops/wslashtape.h b/src/ops/wslashtape.h index 432a24df..1790a9c3 100644 --- a/src/ops/wslashtape.h +++ b/src/ops/wslashtape.h @@ -2,6 +2,7 @@ #define _OPS_WSLASH_TAPE_H_ #include "ops/op.h" + extern const tele_op_t op_WS_T_RECORD; extern const tele_op_t op_WS_T_PLAY; extern const tele_op_t op_WS_T_REV; diff --git a/tests/Makefile b/tests/Makefile index ddbac5cc..f292619e 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -24,7 +24,7 @@ tests: main.o \ ../src/ops/matrixarchate.o ../src/ops/wslash.o ../src/ops/seed.o \ ../src/ops/disting.o \ ../src/ops/wslashtape.o ../src/ops/wslashsynth.o ../src/ops/wslashdelay.o \ - ../src/ops/crow.o ../src/ops/i2c2midi.o \ + ../src/ops/wslash_shared.o ../src/ops/crow.o ../src/ops/i2c2midi.o \ ../libavr32/src/euclidean/data.o ../libavr32/src/euclidean/euclidean.o \ ../libavr32/src/music.o ../libavr32/src/util.o ../libavr32/src/random.o diff --git a/utils/cheatsheet.py b/utils/cheatsheet.py index 2556bea0..bd966b3f 100755 --- a/utils/cheatsheet.py +++ b/utils/cheatsheet.py @@ -74,6 +74,7 @@ def inject_latex(value): ("telex_i", "TELEXi", False), ("telex_o", "TELEXo", False), ("disting", "Disting EX", True), + ("wslash_shared", "W/2.0", False), ("wslashdelay", "W/2.0 delay", False), ("wslashsynth", "W/2.0 synth", False), ("wslashtape", "W/2.0 tape", False), diff --git a/utils/docs.py b/utils/docs.py index 1f514ec7..94ac173d 100755 --- a/utils/docs.py +++ b/utils/docs.py @@ -59,6 +59,7 @@ "wslash", "matrixarchate", "disting", + "wslash_shared", "wslashdelay", "wslashsynth", "wslashtape",