splicer

splicer

DNA animation.gif
Animation by Brian0918, Public Domain


Welcome to splicer! :dna:

This is an eduscript for sequins and lattice.

DNA shall be our metaphor for this lesson. DNA is a double helix, so we shall have two patterns attached to one instance of the “lattice” core sequencer. (There is also a third “utility” pattern.) DNA pairs adenine with thymine and guanine with cytosine, so we shall have four instances of “Sequins.”

That’s where the metaphor ends!

We’ll use our lattice and Sequins to send some notes to the PolyPerc engine.

Adenine and guanine store note values (pitch). Thymine and cytosine store note triggers (on/off). Open up /home/we/dust/code/splicer/splicer.lua and scan through the code.

Find the comment that says “Lesson 2: Livecode” to continue the lesson.

Happy splicing!

Sequins docs: https://monome.org/docs/crow/reference/#sequins
Lattice docs: https://monome.org/docs/norns/reference/lib/lattice

Thanks

Huge thanks to @Galapagoose for the brilliant Sequins library that came with crow v3. I just copied and pasted their code into the right place on norns and sent in a PR.

Install

;install https://github.com/northern-information/splicer

splicer on GitHub

45 Likes

Thanks, @tyleretters ! Looks like a great starting point for me to understand how to make a sequencer for Norns.

2 Likes

:smiley: let me know if anything’s unclear or you’d like more explanation!

1 Like

there’s a comment on line 102 of this script that says

-- might be fun to turn the divs into Sequins ;)

but when I try to do so by, for example, replacing

at_div = 1/16

with

at_div = s{1/16,1/18}

or

at_div = s{1,2,3,4}

I get the following error from Maiden:

### SCRIPT ERROR: init
/home/we/norns/lua/lib/lattice.lua:145: attempt to perform arithmetic on a table value (field 'division')
stack traceback:
	/home/we/norns/lua/core/norns.lua:146: in metamethod '__mul'
	/home/we/norns/lua/lib/lattice.lua:145: in function 'lattice.new_pattern'
	/home/we/dust/code/splicer/splicer1.lua:57: in global 'init'
	/home/we/norns/lua/core/script.lua:126: in function 'core/script.init'
	[C]: in function 'xpcall'
	/home/we/norns/lua/core/norns.lua:147: in field 'try'
	/home/we/norns/lua/core/engine.lua:91: in function </home/we/norns/lua/core/engine.lua:89>

anyone have any idea why this might be?

for reference, this is the bit that begins at line 57, the line referenced in the error:

  at_pattern = splicer_lattice:new_pattern{
    action = function() at() end,
    division = at_div
  }

and function at() referenced above is

function at()
  -- thymine returns 1 or 0
  if thymine() == 1 then
    -- add the at_octave to the note value adenine() returns
    play_note((at_octave * 12) + adenine())
  end
end
1 Like

the erroring line must become division = at_div.next(). (or, equivalently, the syntax sugar at_div(). )

1 Like

this still throws an error, but the errors are different depending on which version I use. For

division = at_div.next()

I get the error

### SCRIPT ERROR: init
/home/we/dust/code/splicer/splicer1.lua:59: attempt to call a nil value (field 'next')
stack traceback:
	/home/we/norns/lua/core/norns.lua:146: in field 'next'
	/home/we/dust/code/splicer/splicer1.lua:59: in global 'init'
	/home/we/norns/lua/core/script.lua:126: in function 'core/script.init'
	[C]: in function 'xpcall'
	/home/we/norns/lua/core/norns.lua:147: in field 'try'
	/home/we/norns/lua/core/engine.lua:91: in function </home/we/norns/lua/core/engine.lua:89>

and for

division = at_div()

I get the error

lua: /home/we/norns/lua/core/clock.lua:65: /home/we/norns/lua/lib/lattice.lua:108: attempt to perform arithmetic on a table value (field 'division')
stack traceback:
	[C]: in function 'error'
	/home/we/norns/lua/core/clock.lua:65: in function 'core/clock.resume'

The same area seems to be the issue, and the general problem still seems to be the same: division wants to receive a static number, not a value from a table, even if the values from the table are themselves numbers. I’m just not sure how to tell division, “hey, it’s okay for you to receive values from a table; they, too, are numbers, just like the numbers you liked so much before”

1 Like

my bad, i should have written at_div:next() (i’m always forgetting the colon for lua methods.) that’s the first error.

the second error is something else, but i need to see the whole error trace. (yes, it is probably some other bit of code that is expecting a numbner instead of a sequins object.)

1 Like

yea, building off ezra’s message

attempt to perform arithmetic on a table value means it’s not properly fetching the number from the table - it’s trying to look at the table itself. It’s not a problem with a number being a part of table - if that makes sense.

shouldn’t sequins objects (when invoked with ()) return a number?

1 Like

yes it should. it also advances the sequence. so you would want to use :next() or () the first time in a given clock tick, and then preferably cache and re-use that value. (sequins doesnt hav e a convenient non-mutating value accessor, AFAICT.)

2 Likes
division = at_div:next()

seems to throw a similar error to

division = at_div.next()

specifically, what I’m getting from

division = at_div:next()

is

# script load: /home/we/dust/code/splicer/splicer1.lua
# script run
loading engine: PolyPerc
>> reading PMAP /home/we/dust/data/splicer/splicer1/splicer1.pmap
m.read: /home/we/dust/data/splicer/splicer1/splicer1.pmap not read.
<ok>
Engine.register_commands; count: 7
___ engine commands ___
amp	 	f
cutoff	 	f
gain	 	f
hz	 	f
pan	 	f
pw	 	f
release	 	f
___ polls ___
amp_in_l
amp_in_r
amp_out_l
amp_out_r
cpu_avg
cpu_peak
pitch_in_l
pitch_in_r
# script init
### SCRIPT ERROR: init
/home/we/dust/code/splicer/splicer1.lua:59: attempt to call a nil value (method 'next')
stack traceback:
	/home/we/norns/lua/core/norns.lua:146: in method 'next'
	/home/we/dust/code/splicer/splicer1.lua:59: in global 'init'
	/home/we/norns/lua/core/script.lua:126: in function 'core/script.init'
	[C]: in function 'xpcall'
	/home/we/norns/lua/core/norns.lua:147: in field 'try'
	/home/we/norns/lua/core/engine.lua:91: in function </home/we/norns/lua/core/engine.lua:89>

As for the whole error trace from the second error, when I use

divsion = at_div()

this is the entirety of what gets printed to Maiden (I cleared it out, then ran the script, just to be sure)


norns.script.load("code/splicer/splicer1.lua")
# script clear
# script load: /home/we/dust/code/splicer/splicer1.lua
# script run
loading engine: PolyPerc
>> reading PMAP /home/we/dust/data/splicer/splicer1/splicer1.pmap
m.read: /home/we/dust/data/splicer/splicer1/splicer1.pmap not read.
<ok>
Engine.register_commands; count: 7
___ engine commands ___
amp	 	f
cutoff	 	f
gain	 	f
hz	 	f
pan	 	f
pw	 	f
release	 	f
___ polls ___
amp_in_l
amp_in_r
amp_out_l
amp_out_r
cpu_avg
cpu_peak
pitch_in_l
pitch_in_r
# script init
~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.
Welcome to splicer!
~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.
This is an eduscript for sequins and lattice.
~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.
DNA shall be our metaphor for this lesson.
DNA is a double helix, so we shall have two patterns attached to one instance of the "lattice" core sequencer.
(There is also a third "utility" pattern.)
DNA pairs adenine with thymine and guanine with cytosine, so we shall have four instances of "Sequins."
~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.
That's where the metaphor ends!
We'll use our lattice and Sequins to send some notes to the PolyPerc engine.
Adenine and guanine store note values (pitch).
Thymine and cytosine store note triggers (on/off).
Open up /home/we/dust/code/splicer/splicer.lua and scan through the code.
Find the comment that says "Lesson 2: Livecode" to continue the lesson.
Happy splicing!
~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.
Sequins docs: https://monome.org/docs/crow/reference/#sequins
Lattice docs: https://monome.org/docs/norns/reference/lib/lattice
~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.
lua: 
/home/we/norns/lua/core/clock.lua:65: /home/we/norns/lua/lib/lattice.lua:108: attempt to perform arithmetic on a table value (field 'division')
stack traceback:
	[C]: in function 'error'
	/home/we/norns/lua/core/clock.lua:65: in function 'core/clock.resume'
1 Like

ok, so at_div is used in two places:

  1. when the script is reset, to set the clock divisino of the at lattice pattern:
  1. in this utility update pattern:

so, i’m not sure this variable can do what you want. (i am also not sure what you want.) but if you want the division of the at pattern to change on each reset or on each tick of the utility pattern, here is a diff that makes that work:

diff --git a/splicer.lua b/splicer.lua
index 475914d..b77e756 100644
--- a/splicer.lua
+++ b/splicer.lua
@@ -11,7 +11,7 @@ adenine  = s{0,3,5,7,12,s{3,5,7,12,0},s{2,5,7,9,0}}
 -- thymine is just an "every other" trigger
 thymine  = s{1,0}
 at_octave = 0
-at_div = 1/16
+at_div = s{1/16, 1/8, 1/12, 2/3}
 
 -- guanine is a simple "rising bassline"
 guanine  = s{0,0,0,3,3,3,5,5,5,7,7,7,12,12,12}
@@ -56,7 +56,7 @@ function init()
   -- create the adenine/thymine pattern
   at_pattern = splicer_lattice:new_pattern{
     action = function() at() end,
-    division = at_div
+    division = at_div()
   }
   -- create the guanine/cytosine pattern
   gc_pattern = splicer_lattice:new_pattern{
@@ -100,8 +100,9 @@ function utility()
   -- check for tempo updates
   params:set("clock_tempo", tempo)
   -- might be fun to turn the divs into Sequins ;)
-  if (at_pattern.division ~= at_div) then
-    at_pattern:set_division(at_div)
+  local at_div_current = at_div()
+  if (at_pattern.division ~= at_div_current) then
+    at_pattern:set_division(at_div_current)
   end
   if (at_pattern.division ~= gc_div) then
     gc_pattern:set_division(gc_div)
2 Likes

this works perfectly! Thank you so much. What I wanted to do was play notes at irregular intervals (of time, that is), and this does that.

Ultimately, what I want to do is incorporate this into a script that turns plain text into musical information. Right now, that script works by converting a string of letters (the aforementioned plain text) into a table of numbers and using sequins to work from there. That allows the plain text to determine the pitch of the notes, but sequins alone wouldn’t allow for plain text to determine note event timing in a similar way. That’s what I was hoping to learn how to do from this eduscript, and with your help, I think I’m well on my way. So, again, thank you thank you thank you!

5 Likes

(i’m back from my social media fast!)


@zebra & @zbs thank you so much for helping while i was out. it means a lot to me. <3


@WilliamHazard i’m so happy you got it working. lemme know if i can help with anything.

2 Likes

thank you! Glad you’re back but also glad to hear you took a break. Hope it was restful and helpful.

Generally, the fix above has been working like a charm, including with the text-to-music script I mentioned. I’ve got letters determining clock divisions, which is what I was going for, and it sounds amazing.

One thing I am struggling with is figuring out how to use a clock on crow input 1 instead of norns’s internal clock. I tried changing this bit

-- lattice uses the norns clock
  -- so lets make sure its set to internal
  clock.set_source("internal")
  -- startup with our variable tempo
  params:set("clock_tempo", tempo)
  -- create a default lattice
  splicer_lattice = lattice:new()

to

-- lattice uses the norns clock
  -- so lets make sure its set to internal
  clock.set_source("crow")
  -- startup with our variable tempo
  params:set("clock_tempo", tempo)
  -- create a default lattice
  splicer_lattice = lattice:new()

Nothin doin; it threw an error. I also tried commenting out the params:set part of that and getting rid of tempo =, but that threw an error too. I thought maybe I’d gotten the name of the source wrong (like maybe it was “crw” or “cruh” or “crow_input_1” or something), so I took a look at the relevant bit of the norns api, and I noticed that set_source (source) only accepts internal, midi, or link. Is there a way to work around this to get a clock signal from crow working?

1 Like

what was the error it threw?

i’m away from my modular right now but i think it may be a matter of commenting out the line that sets the clock source and enabling crow from the system menu: control + clock | monome/docs

1 Like

You can just select Crow clock from the params menu!

1 Like

I realized my mistake! I’d tried commenting out clock.set_source("internal") and params:set("clock_tempo", tempo) in the -- lattice uses the norns clock bit, and I’d made sure to get rid of the tempo = line. What I hadn’t done was get rid of params:set("clock_tempo", tempo) the second time it occurs in this script, in the utility function

function utility()
  -- check for tempo updates
  params:set("clock_tempo", tempo)

So it was looking for “tempo,” but “tempo” wasn’t there. That was the source of the error. Now it’s working.

3 Likes

wooo hoo!! awesome job taking your first steps with this!! takes courage

1 Like