diff --git a/sound_algo/data_only/matrix.scd b/sound_algo/data_only/matrix.scd index e357b96..0c559c8 100644 --- a/sound_algo/data_only/matrix.scd +++ b/sound_algo/data_only/matrix.scd @@ -41,16 +41,19 @@ \perc->1260, \acid->700, \arp->3000, \lead->4000, \stab->2200, \pad->1500, \reese->1100, \melody->3500, \chord->2000 ]; +// voices whose SynthDef has a resonant filter (res param). rev (reverb send) +// is accepted by all lp_/classic synths, so it is not gated by voice. +~matResVoices = ~matResVoices ? [\sub, \acid, \arp, \lead, \stab, \pad, \reese, \bells, \melody, \chord]; // -- default color defs reproduce the historical global variation table -- ~matDefaultColorDefs = { [ nil, - (stretch: 1.0, octave: 0, amp: 1.0, inst: nil, cutoff: nil, pan: nil, steps: Array.fill(16, nil), mod: nil, pose: Array.new), - (stretch: 0.5, octave: 0, amp: 1.0, inst: nil, cutoff: nil, pan: nil, steps: Array.fill(16, nil), mod: nil, pose: Array.new), - (stretch: 1.0, octave: 1, amp: 1.0, inst: nil, cutoff: nil, pan: nil, steps: Array.fill(16, nil), mod: nil, pose: Array.new), - (stretch: 2.0, octave: 0, amp: 1.0, inst: nil, cutoff: nil, pan: nil, steps: Array.fill(16, nil), mod: nil, pose: Array.new), - (stretch: 1.0, octave: -1, amp: 1.05, inst: nil, cutoff: nil, pan: nil, steps: Array.fill(16, nil), mod: nil, pose: Array.new), - (stretch: 0.5, octave: 0, amp: 1.2, inst: nil, cutoff: nil, pan: nil, steps: Array.fill(16, nil), mod: nil, pose: Array.new) ] + (stretch: 1.0, octave: 0, amp: 1.0, inst: nil, cutoff: nil, pan: nil, res: nil, rev: nil, steps: Array.fill(16, nil), mod: nil, pose: Array.new), + (stretch: 0.5, octave: 0, amp: 1.0, inst: nil, cutoff: nil, pan: nil, res: nil, rev: nil, steps: Array.fill(16, nil), mod: nil, pose: Array.new), + (stretch: 1.0, octave: 1, amp: 1.0, inst: nil, cutoff: nil, pan: nil, res: nil, rev: nil, steps: Array.fill(16, nil), mod: nil, pose: Array.new), + (stretch: 2.0, octave: 0, amp: 1.0, inst: nil, cutoff: nil, pan: nil, res: nil, rev: nil, steps: Array.fill(16, nil), mod: nil, pose: Array.new), + (stretch: 1.0, octave: -1, amp: 1.05, inst: nil, cutoff: nil, pan: nil, res: nil, rev: nil, steps: Array.fill(16, nil), mod: nil, pose: Array.new), + (stretch: 0.5, octave: 0, amp: 1.2, inst: nil, cutoff: nil, pan: nil, res: nil, rev: nil, steps: Array.fill(16, nil), mod: nil, pose: Array.new) ] }; ~matColorDefs = ~matColorDefs ? Array.fill(~matVoices.size, { ~matDefaultColorDefs.value }); @@ -241,6 +244,10 @@ File.exists(~matDir).not.if({ ("mkdir -p " ++ ~matDir.quote).systemCmd }); cdPairs = cdPairs ++ [\cutoff, spec[\cutoff]] }); (spec[\pan].notNil and: { (~matModTargets[name] ? []).includes(\pan) }).if({ cdPairs = cdPairs ++ [\pan, spec[\pan]] }); + (spec[\res].notNil and: { ~matResVoices.includes(name) }).if({ + cdPairs = cdPairs ++ [\res, spec[\res]] }); + (spec[\rev].notNil).if({ + cdPairs = cdPairs ++ [\rev, spec[\rev]] }); (~matStepsEmpty.(spec[\steps]).not).if({ ~matColorStepPattern.(name, vi, color, spec, volOf, instPair, cdPairs) }, { @@ -435,7 +442,7 @@ File.exists(~matDir).not.if({ ("mkdir -p " ++ ~matDir.quote).systemCmd }); // -- ~matSetColorDef : set one field of one voice/color, validate, re-source, echo -- ~matSetColorDef = { |vi, color, field, value| - var fields = [\stretch, \octave, \amp, \inst, \cutoff, \pan]; + var fields = [\stretch, \octave, \amp, \inst, \cutoff, \pan, \res, \rev]; ((vi >= 0 and: { vi < ~matVoices.size }) and: { color >= 1 and: { color <= 6 } } and: { fields.includes(field.asSymbol) }).if({ var name = ~matVoices[vi]; @@ -449,6 +456,14 @@ File.exists(~matDir).not.if({ ("mkdir -p " ++ ~matDir.quote).systemCmd }); ok = (~matModTargets[name] ? []).includes(field.asSymbol); v = value.asFloat; }); + (field.asSymbol == \res).if({ + ok = ~matResVoices.includes(name); + v = value.asFloat; + }); + (field.asSymbol == \rev).if({ + v = value.asFloat; + // rev accepted for all voices (ungated) + }); ((field.asSymbol == \stretch) or: { field.asSymbol == \octave } or: { field.asSymbol == \amp }).if({ v = (field.asSymbol == \octave).if({ value.asInteger }, { value.asFloat }); @@ -463,7 +478,8 @@ File.exists(~matDir).not.if({ ("mkdir -p " ++ ~matDir.quote).systemCmd }); }; // -- ~matColorDefPush : send one voice's 6 color defs flattened to surfaces -- -// flat layout per color (1..6): stretch, octave, amp, inst(str), cutoff(-1=nil), pan(-2=nil) +// flat layout per color (1..6) — stride 8: +// stretch, octave, amp, inst(str), cutoff(-1=nil), pan(-2=nil), res(-3=nil), rev(-4=nil) // Distinct OUTBOUND address (plural) so it never collides with the 4-arg inbound // /matrix/colordef set (mirrors /matrix/instrument vs /matrix/instruments). ~matColorDefPush = { |vi| @@ -471,7 +487,7 @@ File.exists(~matDir).not.if({ ("mkdir -p " ++ ~matDir.quote).systemCmd }); var flat = (1..6).collect({ |c| var d = ~matColorDefs[vi][c]; [ d[\stretch], d[\octave], d[\amp], (d[\inst] ? \default).asString, - d[\cutoff] ? -1, d[\pan] ? -2 ] + d[\cutoff] ? -1, d[\pan] ? -2, d[\res] ? -3, d[\rev] ? -4 ] }).flatten; ~toscSend.valueArray(["/matrix/colordefs", vi] ++ flat) } @@ -574,7 +590,7 @@ OSCdef(\mat_steps_get, { |msg, time, addr| (voiceDefs.isArray and: { vi < ~matVoices.size }).if({ voiceDefs.do { |d, ci| (d.isKindOf(Event)).if({ - [\stretch, \octave, \amp, \inst, \cutoff, \pan].do { |f| + [\stretch, \octave, \amp, \inst, \cutoff, \pan, \res, \rev].do { |f| d[f].notNil.if({ ~matColorDefs[vi][ci + 1][f] = d[f] }) }; (d[\steps].notNil and: { d[\steps].isArray }).if({ ~matColorDefs[vi][ci + 1][\steps] = d[\steps] }); (d[\mod].notNil).if({ ~matColorDefs[vi][ci + 1][\mod] = d[\mod] }); diff --git a/sound_algo/data_only/test/test_matrix.scd b/sound_algo/data_only/test/test_matrix.scd index bd79314..8f949f9 100644 --- a/sound_algo/data_only/test/test_matrix.scd +++ b/sound_algo/data_only/test/test_matrix.scd @@ -285,16 +285,16 @@ pass = pass and: { ~matSetColorDef.notNil and: { ~matColorDefPush.notNil } }; ~matSetColorDef.(5, 3, \octave, -2); // acid, color 3 pass = pass and: { ~matColorDefs[5][3][\octave] == -2 }; pass = pass and: { ~trigLog[0] == "/matrix/colordefs" and: { ~trigLog[1] == 5 } }; -pass = pass and: { ~trigLog.size == 38 }; // path + vi + 36 values +pass = pass and: { ~trigLog.size == 50 }; // path + vi + 48 values (6 colors x 8 fields) -// direct test of ~matColorDefPush: address, vi, 38 items total, nil pan=-2, nil cutoff=-1 +// direct test of ~matColorDefPush: address, vi, 50 items total, nil pan=-2, nil cutoff=-1, nil res=-3, nil rev=-4 ~trigLog = nil; ~matColorDefPush.(5); pass = pass and: { ~trigLog[0] == "/matrix/colordefs" }; pass = pass and: { ~trigLog[1] == 5 }; -pass = pass and: { ~trigLog.size == 38 }; -pass = pass and: { ~trigLog[(2 + ((1-1)*6) + 5)] == -2 }; // color 1 pan nil -> -2 -pass = pass and: { ~trigLog[(2 + ((1-1)*6) + 4)] == -1 }; // color 1 cutoff nil -> -1 +pass = pass and: { ~trigLog.size == 50 }; +pass = pass and: { ~trigLog[(2 + ((1-1)*8) + 5)] == -2 }; // color 1 pan nil -> -2 +pass = pass and: { ~trigLog[(2 + ((1-1)*8) + 4)] == -1 }; // color 1 cutoff nil -> -1 // invalid field is rejected (state unchanged) ~matSetColorDef.(5, 3, \bogus, 9); @@ -336,6 +336,17 @@ pass = pass and: { ~matColorDefs[6][2][\inst] == \do_strike }; pass = pass and: { ~matVariation.(\acid, 1, 5).asStream.next(()).notNil }; +// res/rev round-trip through a colour def +~matColorDefs[5][1][\res] = 0.7; // acid, colour 1 +~matColorDefs[5][1][\rev] = 0.3; +variation = ~matVariation.(\acid, 1, 5); +pass = pass and: { variation.notNil and: { variation.isKindOf(Pattern) } }; +// stream one event, must not raise +try { variation.asStream.next(()) } { |e| pass = false; + ("EXCEPTION res/rev stream: " ++ e.errorString).postln }; +~matColorDefs[5][1][\res] = nil; // restore +~matColorDefs[5][1][\rev] = nil; + // --- Step sequencer state (Task 1) --- pass = pass and: { ~matStepsEmpty.notNil and: { ~matSetStep.notNil and: { ~matStepsPush.notNil } } }; // default: every color's steps is a 16-slot all-nil array -> empty