sampswap

sampswap

swap samples within loops to make new loops.

this script is forked from makebreakbeat. makebreakbeat worked by building the audio one piece at a time while sampswap copies and pastes to edit audio in-place. sampswap first repeats the original audio and then copies random regions, adds effects to that copy, and then pastes the effected copy to a random position along the loop (editing the file in-place essentially).

this script works best with loops (see notes below for reasons why). it can be used to generate material (especially “breakbeat” type things) but since it automatically tempolocks and syncs up to four tracks it can be nicely performed.

some may also find this script might be a framework to borrow from or extend (which I happily encourage!). in addition to the normal norns engine, it has a ‘non-realtime’ engine which effectively can be used to resample audio processed by any SuperCollider SynthDef. also there is a embedded lua library that wraps sox for easily creating effects+splices (more info about this on my blog). sox splicing works easily because sox can join with crossfades using wave similarity to find best locations. basically sox+scnrt = daw in a primordial form. this script is not trying to reinvent the daw, though. if anything I want this script to be a “raw” (“random audio workstation”) where all the operations are random and you only can define their probabilities.

demo:

Requirements

  • norns

Documentation

  • E1 selects a track
  • E2 selects a parameter
  • E3 modifies parameter
  • K2 generates track
  • K3 toggles playing

see the parameters menu for all the parameters. most of the parameters are available on the front UI for quick navigation.

rundown of the screen

sampswap

the title bar. this shows the current track loaded, and the current index of the generated track (in parentheses).

right below the title bar on the left is “Xqn Y” where X is the guessed number of quarter notes in the beat and Y is the guessed bpm. you can change the guessed bpm in the parameters menu.

the third line on the left shows also “Xqn Y” but here it is showing that it will generate X beats at tempo Y.

the fourth line that says “off: X” shows X beats from the down be to sync the current track.

the fifth line that says “repitch/stretch/none” is indicating how the re-tempoing will occur. repitch will simply speed it up/down. stretch will perform a timestretch. none will do nothing.

the seven bars specify the probabilities for adding any of the specified effects to the generated track (except the last bar
which controls amplitude in realtime).

each effect is applied in order and can affect effects further down. basically all the editing happens “in place” in the file, so you end up with something along these lines:

howitworks

happy to answer questions and if time permits I can make a little tutorial video.

notes

  • track bpms are “guessed” assuming they are loops of even number beats. if you are not using loops then do not expect the input bpm to be correct (and probably the syncing won’t work).
  • input tracks are trimmed before processing, so if your loop has purposeful silence at the end, it probably not be guessed correctly for the right bpm.
  • if your track name contains “bpmX” then it will skip guessing the beat and assume the source bpm is X
  • this script generates beats slowly. to get around this I suggest generating short beats (16 beats) continuously (beats continue to play when generating).
  • when the script starts it needs another 3 seconds to start the non-realtime server so K2 will not function until then (in case you see K2 not doing anything).

Install

install with

;install https://github.com/schollz/sampswap

once you start the script for the first time it will install sox, and sendosc (~8 MB total).

56 Likes

Wow - this looks like yet another essential @infinitedigits script and no mistake !

5 Likes

You keep this type of activity and release schedule up and people might run out of kind things to things!

:wink:

6 Likes

your demos are such gold :heart_eyes:
great explanation of the script and sounds really beautiful!

5 Likes

Does this require the newest Norns image?

1 Like

no it doesn’t! it should work on the other images. I’m pretty sure I tested it on it but I can’t remember.

thank you @glia!

1 Like

hey man,

I have been following your instagram posts to the point where they have been keeping me up at night…

I legit think you are on to something that its HUGE and very very creative.

As always , my first thought is "how can I use this LIVE as live improv is my domain of choice for all my work…

but I also get the fundamentals of how, with the processing needed, this can never be a standalone realtime device…

but , I had this thought that I am interested in exploring… my looper of choice is the RC505 …it has USB out that can be made to me mass storage so that when you hit save in a patch, each loop becomes available as an audio wav file that is the loop for that single track , of which in the rc505 there are 5…

I am dreaming up the feasability of making a rc505 loop as “normal” in the first act, and then, instantly remixing those for a second part of the piece…it seems that if your sampswap could be targeted at any USB mass storage, it could be targeted at the RC505s mass strogae mode where it begaves like a big USB stick.

my thoughts by extention is , that if samp swap has midi clock awareness, then as long as the clock (via a midi usb on a different port) is left running, perhaps the transition between the original looping mix and the sampswap remix, could, in theory, be quite seamless… this would be HUGE for me, as I LOVE what sampswap is creating and this could potitially smooth the processing bump between feeding it things to loop and it beginning to do its thing.

anyways, I am just massivley spit balling here… but I really do think you are onto somehting hugely powerful and capable of making beautiful work.

on a slight different note…i would love to see a video that shows what the loops you are going to feed sampswap are, and then how they sound after been thrown through the script… cos I would love to get my ears around what parts the script is doing and what is intrinsic to the original samples.

anyway, thanks once agian for such an amazing script.

4 Likes

yeah this would be cool! I can try to make something along those lines.

I think about that question a lot too - I like to have instruments that are performative but its at odds with my passion for making music that’s generative. sampswap falls mostly into the latter.

this is a cool idea! yeah I think you could do something along those lines. I’m not sure exactly how, but you could have live loops being recorded and then a background program process them and put them into another loop for hotswapping. lots of cool stuff to think about.

1 Like

ah…the loopstation let me down at the first hurdle !!!

All the background logic is in place, each track gets a folder that easy identifies which program and which track it is from ( folder 001_01 would be program 1 track 1, 002_05 would be program 2 track 6 etc ) …so I had though it would be possible to dedicate a program ( say 99 ) to always be the one you would perform on, then have Sampswap look in the folders that relate to that track… but DISASTER…

The RC505’s Mass storage mode currently puts the actual looper into an idling mode so you cannot perform whilst data is available via USB out… that’s such a shame as it kind of nukes this idea

I guess the next nearest possibility would be to try and leverage the live creation of files into sampswap even if these cant be “sampswapped” in realtime, but where they could perhaps be deployed post processing in a way that is tempo aware (i.e. sampswap becomes more like a second turntable to hold a remix of the primary musical devices)

anyways, I will muse on this some more as , like I say, I am honestly beyond facinated with the results of what you are doing here and it gives me that tingle of a new frontier opening up.

2 Likes

A little error from some files. The provided amen_resampled.wav works fine, but some other WAV files e.g. recorded with my phone

(yes hoping to turn a local church into breakbeat did cross my mind)

or from Legowelt’s sample pack

yield an sox FAIL sox: Input files must have the same # channels error message

Error message context after script load, going to params, loop 1, and loading a file
# script init
pset >> read: /home/we/dust/data/sampswap/sampswap-01.pset
ss_index 2
ss_index 0
ss_index 2
ss_index 0
ss_index 0
debounced
mpv: no process found
pset >> write: /home/we/dust/data/sampswap/sampswap-01.pset
cd /home/we/dust/code/sampswap/lib/ && lua sampswap.lua --server-started -filter-in 4 -filter-out 4 -target-tempo 92 -target-beats 16 -input-tempo 174 -output /tmp/making-YR8xKYsu.wav -input-file /home/we/dust/audio/gethsemane_kirke-18.wav --amp 25 --stutter 15 --revreverb 8 --reverb 5 --reverse 5 --jump 20 --pitch 0 --retemporepitch &
rm -rf /tmp/sampswap/
mkdir -p /tmp/sampswap/
echo 0 > /tmp/sampswap/progress
sox -r 48000 /home/we/dust/audio/gethsemane_kirke-18.wav /tmp/sampswap/breaktemp-1c257CDv.wav
sox /tmp/sampswap/breaktemp-1c257CDv.wav /tmp/sampswap/breaktemp-RIundKez.wav silence 1 0.1 0.025% reverse silence 1 0.1 0.025% reverse
bpm: 174
sox -n -r 48000 -c 1 /tmp/sampswap/breaktemp-lsDZ61vP.wav trim 0.0 2
sox /tmp/sampswap/breaktemp-RIundKez.wav /tmp/sampswap/breaktemp-lsDZ61vP.wav /tmp/sampswap/breaktemp-OOOeuNxv.wav
rm -f /tmp/sampswap/breaktemp-lsDZ61vP.wav
sox /tmp/sampswap/breaktemp-OOOeuNxv.wav /tmp/sampswap/breaktemp-zz16CE2e.wav trim 0 27.586206896552
sox /tmp/sampswap/breaktemp-zz16CE2e.wav /tmp/sampswap/breaktemp-XwRAG5A2.wav trim 0 5.5172413793103
16
cp /tmp/sampswap/breaktemp-XwRAG5A2.wav original.wav
echo 11.1 >> /tmp/sampswap/progress
sox /tmp/sampswap/breaktemp-XwRAG5A2.wav /tmp/sampswap/breaktemp-5PXVowtS.wav trim 2.018966 0.617241
sox /tmp/sampswap/breaktemp-XwRAG5A2.wav /tmp/sampswap/breaktemp-qHQlGQnM.wav trim 0 3.153448
sox /tmp/sampswap/breaktemp-XwRAG5A2.wav /tmp/sampswap/breaktemp-d1PHZLjk.wav trim 3.570690
sox /tmp/sampswap/breaktemp-qHQlGQnM.wav /tmp/sampswap/breaktemp-5PXVowtS.wav /tmp/sampswap/breaktemp-ioOXkHzb.wav splice 3.153448,0.050000,0.000000
sox /tmp/sampswap/breaktemp-ioOXkHzb.wav /tmp/sampswap/breaktemp-d1PHZLjk.wav /tmp/sampswap/breaktemp-cpvfoJGt.wav splice 3.670690,0.050000,0.000000
rm -f /tmp/sampswap/breaktemp-5PXVowtS.wav /tmp/sampswap/breaktemp-qHQlGQnM.wav /tmp/sampswap/breaktemp-d1PHZLjk.wav /tmp/sampswap/breaktemp-ioOXkHzb.wav
echo 22.2 >> /tmp/sampswap/progress
sox /tmp/sampswap/breaktemp-cpvfoJGt.wav /tmp/sampswap/breaktemp-pqACUCbm.wav trim 2.363793 0.444828
sox /tmp/sampswap/breaktemp-cpvfoJGt.wav /tmp/sampswap/breaktemp-lxVVaKQF.wav trim 0 2.118966
sox /tmp/sampswap/breaktemp-cpvfoJGt.wav /tmp/sampswap/breaktemp-9kW2TKQ0.wav trim 2.363793
sox /tmp/sampswap/breaktemp-lxVVaKQF.wav /tmp/sampswap/breaktemp-pqACUCbm.wav /tmp/sampswap/breaktemp-HEJlQuXC.wav splice 2.118966,0.050000,0.000000
sox /tmp/sampswap/breaktemp-HEJlQuXC.wav /tmp/sampswap/breaktemp-9kW2TKQ0.wav /tmp/sampswap/breaktemp-5izgHQ36.wav splice 2.463793,0.050000,0.000000
rm -f /tmp/sampswap/breaktemp-pqACUCbm.wav /tmp/sampswap/breaktemp-lxVVaKQF.wav /tmp/sampswap/breaktemp-9kW2TKQ0.wav /tmp/sampswap/breaktemp-HEJlQuXC.wav
echo 33.3 >> /tmp/sampswap/progress
sox /tmp/sampswap/breaktemp-5izgHQ36.wav /tmp/sampswap/breaktemp-TO8iXtFZ.wav trim 0.294828 0.617241
sox /tmp/sampswap/breaktemp-5izgHQ36.wav /tmp/sampswap/breaktemp-MazMfhMM.wav trim 0 3.498276
sox /tmp/sampswap/breaktemp-5izgHQ36.wav /tmp/sampswap/breaktemp-ymQ53eOp.wav trim 3.915517
sox /tmp/sampswap/breaktemp-MazMfhMM.wav /tmp/sampswap/breaktemp-TO8iXtFZ.wav /tmp/sampswap/breaktemp-n7NK0cuN.wav splice 3.498276,0.050000,0.000000
sox /tmp/sampswap/breaktemp-n7NK0cuN.wav /tmp/sampswap/breaktemp-ymQ53eOp.wav /tmp/sampswap/breaktemp-4JMGBPye.wav splice 4.015517,0.050000,0.000000
rm -f /tmp/sampswap/breaktemp-TO8iXtFZ.wav /tmp/sampswap/breaktemp-MazMfhMM.wav /tmp/sampswap/breaktemp-ymQ53eOp.wav /tmp/sampswap/breaktemp-n7NK0cuN.wav
echo 44.4 >> /tmp/sampswap/progress
sox /tmp/sampswap/breaktemp-XwRAG5A2.wav /tmp/sampswap/breaktemp-sb5Earef.wav trim 4.8175862068966 0.10620689655172
/home/we/dust/data/sampswap/sendosc --host 127.0.0.1 --addr "/score" --port 47113 --recv-port 47888 -s /tmp/sampswap/breaktemp-sb5Earef.wav -s /tmp/sampswap/breaktemp-vJ4kNN7e.wav -s reverberate -s 4 -s 47888 -s 0.000000 -s 0.000000 -s 0.000000 -s 0.000000
sox /tmp/sampswap/breaktemp-vJ4kNN7e.wav /tmp/sampswap/breaktemp-ZXdELlbg.wav reverse
sox FAIL sox: Input files must have the same # channels
sox FAIL formats:
can't open input file `/tmp/sampswap/breaktemp-BBaWwBCj.wav': No such file or directory
sox /tmp/sampswap/breaktemp-4JMGBPye.wav /tmp/sampswap/breaktemp-mEa9RTsJ.wav trim 0 3.803103
sox /tmp/sampswap/breaktemp-4JMGBPye.wav /tmp/sampswap/breaktemp-5xYfpCLl.wav trim 4.188436
sox /tmp/sampswap/breaktemp-mEa9RTsJ.wav /tmp/sampswap/breaktemp-ZXdELlbg.wav /tmp/sampswap/breaktemp-BBaWwBCj.wav splice 3.803103,0.010000,0.000000
sox /tmp/sampswap/breaktemp-BBaWwBCj.wav /tmp/sampswap/breaktemp-5xYfpCLl.wav /tmp/sampswap/breaktemp-VPWtmeYM.wav splice 4.228436,0.010000,0.000000
rm -f /tmp/sampswap/breaktemp-mEa9RTsJ.wav /tmp/sampswap/breaktemp-5xYfpCLl.wav /tmp/sampswap/breaktemp-BBaWwBCj.wav
!!!! ERROR !!!!

:mosquito: Also what is expected to happen if a file name has a space in it? I don’t think spaces are escaped, I the working directory is left with directories referring to truncated file names.

:blossom: What does work is giving me amazing Amen-derivate breakbeats at the official 174 bpm and layers swooshing synths. Wicked!

I think it might be the numbers and/or dashes in the file names. I remember having an issue with either sampswap or makebreakbeats where the file names had dashes and numbers in them and threw out errors. Maybe that will work?

I haven’t managed to get any of my wavs to load at all, tho to be fair many of them are all sorts of odd sample rate / mono etc. And tried one with loads of dashes and numbers in the file name just now and it completely froze Norns shield :-/

1 Like

So I went ahead and downloaded the two files that @xmacex posted and I renamed them something simple with just letters and no numbers and spaces and stuff. I ran them through sampswap and got the same errors as quoted above.

I had to look at the sox forums to see if anything came up and it looks like that error has something to do with the files themselves either being stereo or mono or something to that effect. That’s about as far as I got before I had to run back to work. I’m assuming it has something to do with how sox is interpreting the files you uploaded … But then I guess that seems kind of obvious from the error message :sweat_smile:

I can take a look at these too when I get a spare minute. I haven’t run into these problems with my files so please share your file and the filename so I have something to replicate with. But yeah I’m pretty sure the dash-in-filename problem is because of my flaky Lua argument handler

1 Like


There’s the two files I used, I just downloaded the files that @xmacex posted previously and renamed them in all lowercase with no dashes or numbers.

Interestingly I tried these both in makebreakbeats for comparison and they worked fine, generating some very weird minimal beats :relaxed:

2 Likes

I was hoping you’d also post the breakbeats :wink: I’ll try to work with sox once I’m with norns next week.

2 Likes

Script loads but is kind of frozen : aside from k1 and k3, nothing responds to any other command. Another casualty of the disk image/full disk wipe :roll_eyes:

oh, sorry to hear about the trouble! the script is testing well here in the workshop (eg. all encoders / keys perform corresponding functions), running the fresh image (+ 220321 update) on a standard norns – are there any errors printed to maiden which could help unpack what’s happening when the script runs on your unit?

also, just to confirm, you expanded your filesystem? shield instructions here.

HI Dan!! How are you doing? I read that your recovery is going well. Yes the file system is expanded, I got 23GB of space right now. 192.168.68.112 (not sure what that is, I guess the version).
Righ… I did a fresh install of the script and it’s responded a bit and then froze again.

4 toggle_playing

4 toggle_playing

pset >> write: /home/we/dust/data/sampswap/sampswap-01.pset

script clear

cleaning up script…

canceling lattice clock

finished cleaning

script load: /home/we/dust/code/sampswap/sampswap.lua

including /home/we/dust/code/sampswap/lib/utils.lua

including /home/we/dust/code/sampswap/lib/sample.lua

script run

loading engine: Sampswap

reading PMAP /home/we/dust/data/sampswap/sampswap.pmap

m.read: /home/we/dust/data/sampswap/sampswap.pmap not read.

lua: /home/we/norns/lua/core/clock.lua:59: bad argument #1 to ‘resume’ (thread expected)

stack traceback:

[C]: in function ‘coroutine.resume’

/home/we/norns/lua/core/clock.lua:59: in function ‘core/clock.resume’

Engine.register_commands; count: 7

___ engine commands ___

amp if

load_track isf

lpf if

lpfqr if

tozero1 i

tozero2 ii

tozero3 iii

___ 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

pset >> read: /home/we/dust/data/sampswap/sampswap-01.pset

load_file /home/we/dust/audio/sampswap/amen_resampled/ original.json json

audio.determine_tempo /home/we/dust/audio/sampswap/amen_resampled/original.json

sox FAIL formats: no handler for file extension `json’

lua: /home/we/norns/lua/core/clock.lua:65: /home/we/dust/code/sampswap/lib/utils.lua:45: attempt to perform arithmetic on a nil value (local ‘file_seconds’)

stack traceback:

[C]: in function ‘error’

/home/we/norns/lua/core/clock.lua:65: in function ‘core/clock.resume’

It looks like the data folder is in a weird state. did you copy and paste your old data folder when updating? If there is nothing you need to save, try deleting the data/sampswap folder and the audio/sampswap folder if they exist and then restart the script. If you want to avoid deleting I think the fix is just to verify that the file loaded is an audio file (for some reason it’s loading a json file as audio) - this is something I can do in the code when I get a chance

1 Like