Norman

Norman

Simple audio utility for Norns

This script is very basic and my first attempt at creating anything for Norns, so you can lower your very high expectations now :slight_smile:

Currently all it does is let you to take any audio file and normalize, auto trim silence from the beginning, and save over the original file, or as a new file with “_norman” appended to the end. It uses SoX for processing (thanks to @zebra for the suggestion) .

NOTE: Use caution with the “trim silence” option - especially when “overwrite” is set to “yes.” You may lose some of the attack of the initial transient. If you are working with very quiet recordings and using “trim silence”, I recommend also using “normalize”.

My motivation was to provide a way to prepare tape samples for loading into other scripts. I have some direct guitar recordings that are way too quiet, and I wanted to easily boost the volume without having to import them into a DAW.

I have a number of features I would like to add, but I may not be smart enough to implement all of them. They include:

  1. Steal @markeats waveform visualization from Timber so you can manually trim start and end points.
  2. Auto chop samples by time division or transient detection
  3. Create “sample chains” by concatenating all files in a directory into a single audio file. This is popular among Octatrack users, and could be interesting for loading into MLR or another sampler.

Any help/feedback/suggestions are more than welcome!

Requirements

Norns, SoX

to install SoX, SSH into your Norns and run

sudo apt-get update
sudo apt-get install sox

I was not able to install it without running apt-get update first.

Documentation

Use encoders 2 and 3 to select and change parameters. Use key 3 to open and process a file.

Download

v1.0.0 - https://github.com/crimclark/norman/archive/master.zip

22 Likes

very useful idea - looking forward to trying it out :slight_smile:

2 Likes

Love the name! Thanks for this, also can’t wait to try it. Great idea. Perhaps it should even have some sort of batch processing functionality added.

1 Like

Thanks. Batch processing should be fairly easy to implement. I think first I’d like to come up with a better way to trim silence.

I’m working on a different script at the moment (felt like doing something more “musical” now that I know my way around Norns a bit more) but I’ll return to this if people are interested in using it.

1 Like

Definitely interested in the addition of the sample trimmer. This would really open Norns

I’m curious about this feature too. I probably know enough to make it after a little review reading. Anyone know if supercollider has some kind of onset detection class? IIRC puredata has one called [bonk~]

Update: Answered my own question. It’s called Onsets and it’s in the core class library.

http://doc.sccode.org/Classes/Onsets.html

3 Likes

Awesome! If you’re able to get the onset times with supercollider, we could potentially use SoX to split and write the files. I’m not sure whether writing files can be done easily with supercollider?

Maybe you have some other ideas on how to implement it. Either way, I’m happy to help on the Lua or SoX side if you need it!

yes.

Onsets is realtime though.

this seems to at least sort of work, if realtime onset cutting is of interest:

Routine {

	// input file. must be mono
	~input = "path_to_input_file";
	// load the input file into a buffer
	b = Buffer.read(s, ~input);

	// list of onset positions (seconds in buffer)
	~onsets = List.new;
	// estimate of analysis latency (FIXME: guess smarter)
	~latency = 0.005;

	// SendTrig will send OSC, here we capture it
	o = OSCFunc({
		arg msg;
		var pos;
		pos = msg[3] - ~latency;
		~onsets.add(pos);
	}, '\tr');


	postln("analyzing...");
	~syn = {
		// play the buffer, and perform FFT required for Onsets
		var chain = FFT(LocalBuf(2048), PlayBuf.ar(1, b));
		// on each onset, send the time since synth was started
		SendTrig.kr(Onsets.kr(chain), value: Sweep.kr(0, 1));
	}.play(s);

	~syn.postln;

	// wait til we're done playing
        /// FIXME: i'm getting some weird error if i try to use the actual buf duration. 
        /// could instead auto-free the synth and use NodeWatcher, or tevuse PlayBuf to send a second trigger, or whatever.
	//~dur = b.duration + 1.0;
	//~dur.wait; 
/// for now, pretend the buffer is 2s long, sorry
	2.0.wait;
	~syn.free;
	postln("done analyzing.");

	postln("chopping...");
	// current start position in file
	t = 0;
	~onsets.do({ |x, i|
		[x,i].postln;
/// write a new file from the buffer, with appropriate name / offset / duration
		b.write( path: ~input ++ ".chop-" ++ i ++ ".aiff",
			startFrame: t * b.sampleRate,
			numFrames: (x - t) * b.sampleRate);
		t = x;
	});
	postln("done chopping.");
}.play;

you could add DetectSilence and a second tr.igger and stuff

you could also adapt this to non-realtime by launching an NRT supercollider server and doing some other junk to control it. there are probably better options out there for NRT sample analysis, though i don’t know them off the top of my head.

1 Like

This might be more complicated then I bargained for. The Onset class detects an onset and emits a 1 when detected. It doesn’t track time. OnsetStatistics does track time, and SoundFile can write sound files.

1 Like

Thanks for the suggestions. I’m new to supercollider so this is a bit over my head at the moment, but this is still helpful and gives me something new to research.