Using (and abusing) Renoise as a demosequencer

Soledad Penadés

ASSEMBLY 2010

A bit of background about me

Hold on!

What is Renoise?

What is a "demo" and why would I want to sequence it?

Renoise is ...

And also has an amazing community

But more importantly...

Demos

... AKA demonstrations

What's “right”?

Synchronised

Good synchro makes a memorable demo

Some examples: fr-038: thetha by Farbrausch, Wir Sind Einstein by United Force & Digital Dynamite, tokyo by xplsv

Bad synchro ruins everything

Examples: haujobb: prototype 1, fr-062: the cube

So... how do we synchronise stuff?

Tracked modules

Using certain tracker effects to synch demo parts (e.g. S2X in IT modules)

Although it's not very precise

Using pattern, row and order numbers

Way more precise!

But tracked modules sound awful

The musician might prefer to use VSTi, huge samples and what not

Poor man's synchronisation

The mighty FFT trick

Manual synchronisation

Identify every interesting moment in the song and write down its timestamp

Ultimately, it is silly: you're transcribing the song again, albeit in a bad way

Clever synchronisation

Automatically extract timestamps from the song

And use only the values you want, when you want them

Flexibility == Happiness for all

The Renoise XRNS file format

It's a compressed ZIP file containing a Song.xml file with the actual song data

Lots of advantages

Disadvantage

XRNS dissected (I): GlobalSongData

(For brevity's sake, only relevant stuff is shown)
<GlobalSongData>
  <BeatsPerMin>135</BeatsPerMin>
  <LinesPerBeat>4</LinesPerBeat>
  <TicksPerLine>12</TicksPerLine>

XRNS dissected (I): GlobalSongData

GlobalSongData

XRNS dissected (II): Instruments

<Instruments>
  <Instrument>
    <PluginProperties>
      <PluginDevice>
        <PluginIdentifier>YOUR_VSTI_ID</PluginIdentifier>
          <Parameters>
            <Parameter>
              <Value>FLOATING POINT NUMBER (0..1)</Value>
            </Parameter>
            (more parameters)
          </Parameters>
      </PluginDevice>
    </PluginProperties>

XRNS dissected (II): Instruments

InstrumentSettings

XRNS dissected (III): Tracks

<Tracks>
  <SequencerTrack type="SequencerTrack">
    <FilterDevices>
      <Devices>
        <InstrumentAutomationDevice>
          <LinkedInstrument>INSTRUMENT NUMBER</LinkedInstrument>
          <ParameterNumber0>PARAMETER NUMBER (IN THE VSTI)</ParameterNumber0>
          <ParameterValue0>
            <Value>FLOATING POINT NUMBER (0..1)</Value>
          </ParameterValue0>
          .
          .
          .
          <ParameterNumber13>PARAMETER NUMBER (IN THE VSTI)</ParameterNumber13>
          <ParameterValue13>
            <Value>FLOATING POINT NUMBER (0..1)</Value>
          </ParameterValue13>
        </InstrumentAutomationDevice>
      </Devices>
    </FilterDevices>

XRNS dissected (III): Tracks

AutomationDevice

XRNS dissected (IV):
Patterns 1 (Envelopes)

<Patterns>
  <Pattern>
    <NumberOfLines>INTEGER NUMBER</NumberOfLines>
    <Tracks>
      <PatternTrack type="PatternTrack">
        <Automations>
          <Envelopes>
            <Envelope>
              <DeviceIndex>AUTOMATION DEVICE INDEX</DeviceIndex>
              <ParameterIndex>PARAMETER INDEX IN AUTOMATION DEVICE</ParameterIndex>
              <Envelope>
                <PlayMode>Points|Linear|Cubic</PlayMode>
                <Length>Envelope length in lines</Length>
                <Points>
                  <Point>ROW,FLOATING VALUE (0..1)</Point>
                  ...
                  <Point>ROW,FLOATING VALUE (0..1)</Point>
                </Points>
              </Envelope>
            </Envelope>
          <Envelopes>
        </Automations>

XRNS dissected (IV):
Patterns 1 (Envelopes)

AutomationCurve

XRNS dissected (IV):
Patterns 2 (Lines)

<Patterns>
  <Pattern>
    <NumberOfLines>INTEGER NUMBER</NumberOfLines>
    <Tracks>
      <PatternTrack type="PatternTrack">
        <Lines>
          <Line index="ROW NUMBER">
            <NoteColumns>
              <NoteColumn>
                <Note>NOTENAME-OCTAVE, i.e. C-6, A#2...</Note>
                <Instrument>INSTRUMENT INDEX</Instrument>
                <Volume>VOLUME (HEX)</Volume>
              </NoteColumns>
              <EffectColumns>
                <EffectColumn>
                  <Value>VALUE (HEX)</Value>
                  <Number>EFFECT NUMBER (HEX)</Number>
                </EffectColumn>
              </EffectColumns>
            </NoteColumns>
          </Line>
        </Lines>

XRNS dissected (IV):
Patterns 2 (Lines)

Track

XRNS dissected (V): Order list

<PatternSequence>
  <SequenceEntries>
    <SequenceEntry>
      <Pattern>PATTERN NUMBER</Pattern>
    </SequenceEntry>
    <SequenceEntry>
      <Pattern>PATTERN NUMBER</Pattern>
    </SequenceEntry>
    ...

XRNS dissected (V): Order list

OrderList

Feeding XRNS data into our demo/intro

Two cases

  1. Musician writes the song with Renoise, huge samples, CPU eating VSTi's
  2. Musician writes the song using our own software synth

Case 1: Musician writes the song with Renoise, huge samples, CPU eating VSTi's

We get an MP3 render of the song PLUS the original XRNS file

Case 2: Musician writes the song using our own software synth

We get an XRNS file only

The player

Start with the simplest one, add features later

The player: timing

It's just two formulas

We already know linesPerBeat, BPM and ticksPerLine.

If you're not implementing tick-based effects, you can ignore the second formula!

The player: events list

t = 0
for each pattern in the order list:
  add event == pattern change
  for each row in the pattern:
    add event == row change
    for each track in the pattern:
      if cell not empty:
        add event (note on, note off, volume change...)
    t += secondsPerRow

Warning: if you implement BPM change effects, you have to update secondsPerRow and secondsPerTick!

The player: reasons for using an event list

(versus dispatching events on the fly)

Using the events list with an MP3

Just play the song as usual, with FMOD/BASS/etc, and...

Using the events list in your custom demoeditor

Draw it in another layer (synch layer?)

You could then dynamically feed events into your assets/scenes

Some ideas

Using the events list with your software synth

Each time the callback function is called...

  1. process envelopes
  2. process samples until the next event happening in the buffer
  3. process all events happening in that time (send events to soft synth)
  4. repeat until the buffer has been filled

Potential improvement (soundwise): process envelopes per sample (specially for pitch envelopes - human ear notices rough changes!)

In the main thread, synch to the event list using a different 'currentEvent' pointer

Because the audio thread and main thread do not work in the same events at the same time, due to latency

Oldschool synch is alive
once again!

In the main loop:

find out which events do we need to process
(events between last processed event and possible event at currentTime or less)
for each event to process:
  if (
      event.type == ORDER_CHANGE || 
      event.type == PATTERN_CHANGE || 
      event.type == ROW_CHANGE
    ):
    // Change scenes, do whatever

  // Another obvious example, 4x4 flash
  if event.type == ROW_CHANGE && event.row % 4 == 0:
    // FLASH!!
    // But remember, we said this was boring! ;)

Some more non-Renoise
specific advice

Listen to the music

The rhythm

Some more non-Renoise
specific advice (II)

... and experiment with breaking the rules!

Awesome future: Renoise 2.6+

Links & pointers

Renoise

http://renoise.com

Sorollet

My software synth + Renoise exporter + player
http://soledadpenades.com/projects/sorollet/

Thanks for coming!

Any question?