JankyTunes: The Contraption
Do you want to play back cute little chiptune songs in your Decker decks? Building on my Janky Sequencer project, I've refactored that code into a contraption that you can use to play tunes you write in it inside your own decks!
You can grab the code for the contraption from inside the deck on the project page along with the instructions and some example stuff: https://micpp.itch.io/jankytunes-the-contraption
Or grab it from below (now updated to Version 4.1)!
%%WGT0{"w":[{"name":"jankytunes1","type":"contraption","size":[193,72],"pos":[159,135],"def":"jankytunes","widgets":{"notes":{},"pitches":{},"synthparams":{},"button1":{},"playbegin":{},"playstop":{},"loop":{},"playing":{},"lastframe":{},"buffer1":{},"buffer2":{},"buffer3":{},"buffer4":{}}}],"d":{"jankytunes":{"name":"jankytunes","size":[193,72],"margin":[0,0,0,0],"description":"a player for jankysequencer format music","version":4.1,"script":"on view do\n if count notes.value\n button1.show:\"none\"\n button1.locked:1\n playbegin.show:\"solid\"\n playbegin.locked:0\n playstop.show:\"solid\"\n playstop.locked:0\n loop.show:\"solid\"\n loop.locked:0\n else\n button1.show:\"solid\"\n button1.locked:0\n end\nend\n\non get_animate do\n animate\nend\n\n#this is called by the view handler on the frame counter\non animate do\n if playing.value\n tempo:(first extract value where param=\"tempo\" from synthparams.value)+0\n if !((lastframe.value[0].text+tempo)>sys.frame)\n lastframe.value:sys.frame\n #play the current row from buffer\n if notes.row = ((count notes.value)-1)\n if loop.value\n notes.row:0\n notes.scroll:0\n playbuffer[]\n card.event[\"playline\" notes.row]\n calcnextrow[]\n else\n stopplayback[]\n card.event[\"reachedend\"]\n end\n else\n notes.row:notes.row+1\n notes.scroll:notes.row\n playbuffer[]\n card.event[\"playline\" notes.row]\n calcnextrow[]\n end\n end\n end\nend\n\non get_row do\n notes.row\nend\n\non set_row x do\n notes.row:x\nend\n\non get_loop do\n loop.value\nend\n\non set_loop x do\n loop.value:x\nend\n\non get_playfromstart do\n playfromstart\nend\n\non get_play do\n startplayback\nend\n\non get_stop do\n stopplayback\nend\n\non get_isplaying do\n playing.value\nend\n\non playfromstart do\n notes.row:0\n startplayback[]\nend\n\n\n\non startplayback do\n playstop.text:\"Stop\"\n #calculate row into buffer\n calcrow[]\n lastframe.value:sys.frame\n playbuffer[]\n card.event[\"playline\" notes.row]\n #calc next row\n calcnextrow[]\n playing.value:1\nend\n\non stopplayback do\n playing.value:0\n playstop.text:\"Play\"\n card.event[\"playstopped\"]\nend\n\non calcnextrow do\n if notes.row = ((count notes.value)-1)\n notes.row:0\n calcrow[]\n notes.row:((count notes.value)-1)\n else\n notes.row:notes.row+1\n calcrow[]\n notes.row:notes.row-1\n end\nend\n\non calcrow do\n #voice1\n if !(notes.rowvalue[\"Voice1\"]=\"\")\n buffer1.value:calcnote[notes.rowvalue[\"Voice1\"]\n notes.rowvalue[\"Len1\"] 1].encoded\n else buffer1.value:\"\"\n end\n #voice2\n if !(notes.rowvalue[\"Voice2\"]=\"\")\n buffer2.value:calcnote[notes.rowvalue[\"Voice2\"]\n notes.rowvalue[\"Len2\"] 2].encoded\n else buffer2.value:\"\"\n end\n #voice3\n if !(notes.rowvalue[\"Voice3\"]=\"\")\n buffer3.value:calcnote[notes.rowvalue[\"Voice3\"]\n notes.rowvalue[\"Len3\"] 3].encoded\n else buffer3.value:\"\"\n end\n #noise\n if !(notes.rowvalue[\"Noise\"]=\"\")\n buffer4.value:calcnoise[notes.rowvalue[\"Noise\"]].encoded\n else buffer4.value:\"\"\n end\nend\n\non playbuffer do\n play[sound[buffer1.value[0].text]]\n play[sound[buffer2.value[0].text]]\n play[sound[buffer3.value[0].text]]\n play[sound[buffer4.value[0].text]]\nend\n\non calcnoise beat do\n #calculate length\n tempo:(first extract value where param=\"tempo\" from synthparams.value)+0\n length:(tempo/60)*8000*beat\n \n v:(first extract value where param=\"volume4\" from synthparams.value)+0\n a:(first extract value where param=\"attack4\" from synthparams.value)+0\n d:(first extract value where param=\"decay4\" from synthparams.value)+0\n s:(first extract value where param=\"sustain4\" from synthparams.value)+0\n r:(first extract value where param=\"release4\" from synthparams.value)+0\n \n l:(length-a)-d\n if l<0 l:0 end\n x:range (length+r)\n \n noise:random[((range 2*v)-v) length+r]\n \n sound[envelope[x a d s/100 r l 1]*noise]\nend\n\non calcnote note beat voice do\n #calculate pitch\n if (count note) = 3\n octave: note[2]\n basenote: \"\" fuse (note[0], note[1])\n else\n octave: note[1]\n basenote: note[0]\n end\n basepitch: (first extract pitch where note=basenote from pitches.value)+0\n pitch:basepitch*2^(octave-4)\n \n #calculate length\n tempo:(first extract value where param=\"tempo\" from synthparams.value)+0\n length:(tempo/60)*8000*beat\n \n #generate waveform + envelope\n \n if voice=1\n wavetype:first extract value where param=\"wavetype1\" from synthparams.value\n v:(first extract value where param=\"volume1\" from synthparams.value)+0\n a:(first extract value where param=\"attack1\" from synthparams.value)+0\n d:(first extract value where param=\"decay1\" from synthparams.value)+0\n s:(first extract value where param=\"sustain1\" from synthparams.value)+0\n r:(first extract value where param=\"release1\" from synthparams.value)+0\n elseif voice=2\n wavetype:first extract value where param=\"wavetype2\" from synthparams.value\n v:(first extract value where param=\"volume2\" from synthparams.value)+0\n a:(first extract value where param=\"attack2\" from synthparams.value)+0\n d:(first extract value where param=\"decay2\" from synthparams.value)+0\n s:(first extract value where param=\"sustain2\" from synthparams.value)+0\n r:(first extract value where param=\"release2\" from synthparams.value)+0\n else\n wavetype:first extract value where param=\"wavetype3\" from synthparams.value\n v:(first extract value where param=\"volume3\" from synthparams.value)+0\n a:(first extract value where param=\"attack3\" from synthparams.value)+0\n d:(first extract value where param=\"decay3\" from synthparams.value)+0\n s:(first extract value where param=\"sustain3\" from synthparams.value)+0\n r:(first extract value where param=\"release3\" from synthparams.value)+0\n end\n \n l:(length-a)-d\n if l<0 l:0 end\n \n if wavetype=\"square\"\n wave:squarewave\n elseif wavetype=\"sawtooth\"\n wave:sawwave\n else\n wave:sinewave\n end\n \n x:range (length+r)\n \n sound[envelope[x a d s/100 r l v]*wave[pitch x]]\n\nend\n\non sinewave pitch x do\n sin (pitch/8000)*2*pi*x\nend\n\non squarewave pitch x do\n sine:sinewave[pitch x]\n (sine>0) - (sine<0) - (sine=0)\nend\n\non sawwave pitch x do\n l:8000/pitch\n floatmod:((l*10000)%(x*10000))/10000\n (((floatmod) / l)*2)-1\nend\n\non envelope x a d s r l v do\n ((x<a) * ((v/a)*x)) + #attack\n ((x=a)*v) +\n (((a<x) & (x<(a+d))) * ((((v*(s-1))/d)*x) + v -(((v*(s-1))/d)*a))) + #decay\n ((x=(a+d)) * (s*v)) +\n ((((a+d)<x) & (x<(a+d+l))) * (s*v)) + #sustain\n ((x=(a+d+l)) * (s*v)) +\n ((((a+d+l)<x) & (x<(a+d+l+r))) * ((((-s*v)/r)*x) + ((s*v)/r)*(a+d+l+r))) #release\nend","template":"on playstopped do\n\nend\n\non reachedend do\n\nend\n\non playline row do\n\nend","attributes":{"name":["loop","row"],"label":["Looping?","Current row"],"type":["bool","number"]},"widgets":{"notes":{"type":"grid","size":[100,50],"pos":[226,-62],"locked":1,"value":{},"row":20},"pitches":{"type":"grid","size":[100,50],"pos":[237,38],"locked":1,"value":{}},"synthparams":{"type":"grid","size":[100,50],"pos":[237,132],"locked":1,"value":{}},"button1":{"type":"button","size":[72,20],"pos":[59,28],"script":"on click do\n alert[\"Select the notes CSV\"]\n notes.value:readcsv[read[\"text\"]]\n alert[\"Select the synth parameters CSV\"]\n synthparams.value:readcsv[read[\"text\"]]\n alert[\"Select the pitches CSV\"]\n pitches.value:readcsv[read[\"text\"]]\n me.show:\"none\"\n me.locked:1\n notes.row:0\n view[]\nend","text":"Load song"},"playbegin":{"type":"button","size":[102,20],"pos":[10,7],"locked":1,"script":"on click do\n playfromstart[]\nend","show":"none","text":"Play from start"},"playstop":{"type":"button","size":[60,20],"pos":[120,7],"locked":1,"script":"on click do\n if !playing.value\n startplayback[]\n else\n stopplayback[]\n end\nend","show":"none","text":"Play"},"loop":{"type":"button","size":[60,20],"pos":[66,44],"locked":1,"show":"none","text":"Loop","style":"check","value":0},"playing":{"type":"button","size":[68,20],"pos":[115,147],"locked":1,"text":"playing","style":"check","value":0},"lastframe":{"type":"field","size":[100,20],"pos":[-42,141],"locked":1,"animated":1,"script":"on change val do\n \nend\n\non view do\n animate[]\nend","value":"0"},"buffer1":{"type":"field","size":[100,20],"pos":[-139,-105],"locked":1},"buffer2":{"type":"field","size":[100,20],"pos":[-15,-105],"locked":1},"buffer3":{"type":"field","size":[100,20],"pos":[110,-106],"locked":1},"buffer4":{"type":"field","size":[100,20],"pos":[232,-104],"locked":1}}}}}
If you use this in a deck, I'd appreciate if you gave me credit - also please let me know if you do as I'd love to see what you can come up with!