a different flavor of audio programming

202

Upload: others

Post on 07-Feb-2022

7 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: A different flavor of audio programming
Page 2: A different flavor of audio programming

A different flavor of audio programming

We won’t talk about

● Real time programming● Signal processing● C++

Page 3: A different flavor of audio programming

A different flavor of audio programming

We won’t talk about

● Real time programming● Signal processing● C++

We will talk about

● Digital Audio Workstation (DAW) internals○ File formats○ Representation of time○ Automation points○ Audio rendering details

● Python

Page 4: A different flavor of audio programming

About me (@offlinemark)

● Previously security engineer○ Reversed binaries, file formats,

network protocols○ Lead developer for Manticore project:

analysis tool for reversing x86/ARM● Music producer & DJ

○ 10 years using DAWs○ REAPER, Garageband, Ableton Live, FL Studio

Page 5: A different flavor of audio programming
Page 6: A different flavor of audio programming

Disclaimer

Page 7: A different flavor of audio programming

Digital Audio Workstations (DAWs)

Page 8: A different flavor of audio programming
Page 9: A different flavor of audio programming

DAW Core

Page 10: A different flavor of audio programming

DAW Core

Reverb Distortion EQ Limiter

Plugins

Page 11: A different flavor of audio programming

DAW Core

Reverb Distortion EQ Limiter

Plugins

Audio Subsystem

Page 12: A different flavor of audio programming

Needs of DAW users

Audio

Page 13: A different flavor of audio programming

AudioPlugins

Needs of DAW users

Page 14: A different flavor of audio programming

Audio

“Workflow”

Plugins

???

Needs of DAW users

Page 15: A different flavor of audio programming

Workflow Problem Example:

Time Marker Export

Page 16: A different flavor of audio programming
Page 17: A different flavor of audio programming
Page 18: A different flavor of audio programming
Page 19: A different flavor of audio programming

DAW Core

Time Marker Export

Page 20: A different flavor of audio programming

DAW Core

Time Marker Export

Audio Subsystem

Time Markers

Page 21: A different flavor of audio programming

DAW Core

Time Marker Export

Audio Subsystem

Time Markers

Page 22: A different flavor of audio programming

DAW Core

.als, .flp

Project File

Time Markers

SaveProject

Page 23: A different flavor of audio programming

.als, .flp

DAW Core

Project File

Time Markers

Time Markers

Page 24: A different flavor of audio programming

.als, .flp

DAW Core

Project File

Time Markers

DAW CoreTime

Markers

LoadProject

Page 25: A different flavor of audio programming

.als, .flp

DAW Core

Third PartyTool

Project File

Time Markers

LoadProject

Time Markers

Page 26: A different flavor of audio programming

.als, .flp

DAW Core

Third PartyTool

Project File

Time Markers

Export

Time Markers

00:00 - A05:22 - B10:59 - C

markers.txt

Page 27: A different flavor of audio programming

DAW Core

???????????????????????????????

Third PartyTool

Project File

Time Markers

LoadProject

Time Markers

???

Page 28: A different flavor of audio programming

ReverseEngineering

Taking things apart to see how they work

● Fix● Learn● Extend

???????????????????????????????

��

Page 29: A different flavor of audio programming

???????????????????????????????

DAW Core

Third PartyTool

Project File

Time Markers

LoadProject???

Page 30: A different flavor of audio programming

DAW Core

Third PartyTool

Project File

Time Markers

LoadProject???

Page 31: A different flavor of audio programming

DAW Core

Third PartyTool

Project File

Time Markers

LoadProject???

Page 32: A different flavor of audio programming

DAW Core

Third PartyTool

Project File

Time Markers

LoadProject

Time Markers

Page 33: A different flavor of audio programming

Prior Art: Solving workflow problems

Page 34: A different flavor of audio programming

Live Enhancement Suite● By @DylanTallchief and @InvertedSilence

● Extends Live via desktop automation (AutoHotkey, Hammerspoon) ● New hotkeys, menus, features● http://enhancementsuite.me/

Page 35: A different flavor of audio programming

@bcrypt’s rekordbox scripts

● ableton-to-cues.py - convert between Ableton Live Warp Markers and Rekordbox cues

● https://github.com/diracdeltas/rekordbox-scripts

Page 36: A different flavor of audio programming

Time Marker Extraction

Page 37: A different flavor of audio programming

Our goal:● Third party tool● Analyze DAW project file● Extract time markers● Output as plain text

○ Marker text○ Marker time (mm:ss format)

Page 38: A different flavor of audio programming

The map:

1. Easy Mode2. Hard Mode3. Unreal Mode

Page 39: A different flavor of audio programming

Easy Mode

No tempo automation(Single static tempo)

Page 40: A different flavor of audio programming

Tempo automation: Dynamic BPM changes

Page 41: A different flavor of audio programming

Tempo automation: Dynamic BPM changes

Page 42: A different flavor of audio programming

Hard Mode

With tempo automation (naive impl, inaccurate)

(Dynamic tempo)

Page 43: A different flavor of audio programming

With tempo automation (fancy impl, accurate)

(DAW rendering engine details)

Unreal Mode

Page 44: A different flavor of audio programming

The map:

1. Easy Mode2. Hard Mode3. Unreal Mode4. Super Unreal Mode 😱

Page 45: A different flavor of audio programming

1. Easy Mode2. Hard Mode3. Unreal Mode4. Super Unreal Mode 😱

The map:

With nonlinear tempo automation

Page 46: A different flavor of audio programming

1. Easy Mode2. Hard Mode3. Unreal Mode4. Super Unreal Mode 😱

The map:

With nonlinear tempo automation

Page 47: A different flavor of audio programming

1. Easy Mode2. Hard Mode3. Unreal Mode4. Super Unreal Mode 😱

The map:

We will only coverlinear tempo automation today

Page 48: A different flavor of audio programming

Easy mode: No tempo automation

Page 49: A different flavor of audio programming

My first guess at the Marker structure:

struct Marker {int time; // millisecondsstring text;

};

Page 50: A different flavor of audio programming

Wrong! Times are stored in beat time

struct Marker {int beat_time; // beat timestring text;

};

Page 51: A different flavor of audio programming
Page 52: A different flavor of audio programming

DAWS store object times in beat time

Page 53: A different flavor of audio programming

Beat time is stable

Page 54: A different flavor of audio programming

Beat time is convertible to real timeusing the BPM (Beats per Minute)

Page 55: A different flavor of audio programming

Pseudocode

// 1. Parse markers and BPM// 2. Print outfor marker in markers { sec_time = marker.beat_time * (60/BPM) print_out(sec_time, marker.text)}

Page 56: A different flavor of audio programming

Pseudocode

// 1. Parse markers and BPM// 2. Print outfor marker in markers { sec_time = marker.beat_time * (60/BPM) print_out(sec_time, marker.text)}

Page 57: A different flavor of audio programming

Pseudocode

// 1. Parse markers and BPM// 2. Print outfor marker in markers { sec_time = marker.beat_time * (60/BPM) print_out(sec_time, marker.text)}

Page 58: A different flavor of audio programming

Easy ModeWhat we need:

● Markers (beat time, text)● Tempo (BPM)

Page 59: A different flavor of audio programming

Ableton Live Format (.als)

$ file demo.alsdemo.als: gzip compressed data...

Page 60: A different flavor of audio programming

Ableton Live Format (.als)

● Compressed XML document● Declarative object hierarchy● Human readable

(but undocumented)

Page 61: A different flavor of audio programming

Ableton Live Format (.als): Locators

Page 62: A different flavor of audio programming

Ableton Live Format (.als): Tempo (BPM)

Page 63: A different flavor of audio programming

FL Studio Format (.flp)

$ file demo.flpdemo.flp: data

Page 64: A different flavor of audio programming

FL Studio Format (.flp)

● Raw binary format● Not human readable,

“undocumented”

Page 65: A different flavor of audio programming

FLP Format Resources: Notes from devs (1999)

Page 66: A different flavor of audio programming

FLP Format Resources

● github.com/LMMS/lmms/ (10 years old)● github.com/andrewrk/PyDaw (10 years old)● github.com/monadgroup/FLParser

Helpful, but missing parts of the format.

Page 67: A different flavor of audio programming

FL Studio Format (.flp)

Header ● Header section: file signature, header len, ...● Data section: array of Event structures● Iterate and interpret events to construct state● Each event carries 1 piece of data (int/string) about

project*

*variable length data can contain multiple pieces of data via an array of structs

Events

Page 68: A different flavor of audio programming

FL Studio Format (.flp)

Header ● Event Structure○ ID field: 1 byte ID (0-255)○ Data field: Depends on ID

■ 0-63: int8/uint_8■ 64-127: int16/uint_16■ 128-191: int32/uint_32■ 192-255: variable length (additional size field)

Event 1

Event 2

Event 3

Event 4

...*variable length data can contain multiple pieces of data via an array of structs

Page 69: A different flavor of audio programming

FL Studio Format (.flp)

Header ● Up to 256 events● Relevant events:

○ MARKER_TIME (0x94) [uint32]: marker beat*○ MARKER_TEXT (0xcd) [UTF-16]: marker text○ TEMPO (0x9c) [uint32]: millibeat per minute

Event 1

Event 2

Event 3

Event 4

...*technically is a “pulse”, which is a beat subdivision

Page 70: A different flavor of audio programming

FL Studio Format (.flp)

Header ● Up to 256 events● Relevant events:

○ MARKER_TIME (0x94) [uint32]: marker beat*○ MARKER_TEXT (0xcd) [UTF-16]: marker text○ TEMPO (0x9c) [uint32]: millibeat per minute

Event 1

Event 2

Event 3

Event 4

...*technically is a “pulse”, which is a beat subdivision

Page 71: A different flavor of audio programming

Easy ModeWhat we need:

● ✅ Markers (beat time, text)● ✅ Tempo (BPM)

Page 72: A different flavor of audio programming

Pseudocode

// parse markers (beat, text) and BPMfor marker in markers { sec_time = marker.beat_time * (60/BPM) print_out(sec_time, marker.text)}

Page 73: A different flavor of audio programming

Hard mode: Tempo automation (naive)

Page 74: A different flavor of audio programming

Tempo automation

Page 75: A different flavor of audio programming

Handling tempo automation

Page 76: A different flavor of audio programming

Abstract Algorithm● Start at beginning of timeline● For each automation segment

○ Compute time elapsed during segment● Continue until we reach marker● Sum all elapsed time segments

Page 77: A different flavor of audio programming

How are automation points stored?

struct Point {int beat_time;int BPM_value;

};

Page 78: A different flavor of audio programming

Hard mode pseudocode

// parse markers (beat, text)// and tempo automation pointsfor marker in markers { sec_time = compute_sec_time(marker.beat_time, points) print_out(sec_time, marker.text)}

Page 79: A different flavor of audio programming

Hard mode pseudocode

// parse markers (beat, text)// and tempo automation pointsfor marker in markers { sec_time = compute_sec_time(marker.beat_time, points) print_out(sec_time, marker.text)}

Page 80: A different flavor of audio programming

Hard mode pseudocode

// parse markers (beat, text)// and tempo automation pointsfor marker in markers { sec_time = compute_sec_time(marker.beat_time, points) print_out(sec_time, marker.text)}

int compute_sec_time(marker_beat_time, points) { // Accumulation algorithm here}

Page 81: A different flavor of audio programming

Hard ModeWhat we need to do

● ✅ Markers (beat time, text)● 🤔 Automation points● 🤔 How to compute

elapsed time?

Page 82: A different flavor of audio programming

Computing time elapsed between points

Page 83: A different flavor of audio programming

Computing time elapsed = Physics I

🚗💨

Page 84: A different flavor of audio programming

Computing time elapsed = Physics I

Integrate to calculatedistance elapsed

Page 85: A different flavor of audio programming

Computing time elapsed = Physics I

Page 86: A different flavor of audio programming

Can’t integrate the BPM curve directly

Page 87: A different flavor of audio programming

Can’t integrate the BPM curve directly

Page 88: A different flavor of audio programming

Can’t integrate the BPM curve directly

Page 89: A different flavor of audio programming

Must invert BPM to Minutes Per Beat (MPB)

Page 90: A different flavor of audio programming

..or even Seconds Per Beat (SPB)

Page 91: A different flavor of audio programming

Computing Time Elapsed

for a BPM automation

1. Construct BPM function2. Convert to SPB form3. Integrate SPB function

Page 92: A different flavor of audio programming

1. Construct BPM Function

Page 93: A different flavor of audio programming

2. Convert to SPB form

Page 94: A different flavor of audio programming

3. Integrate SPB function

Page 95: A different flavor of audio programming

3. Integrate SPB function

Page 96: A different flavor of audio programming

3. Integrate SPB function

✅Time Elapsed: 2.77 seconds

Page 97: A different flavor of audio programming

Hard ModeWhat we need:

● ✅ Markers (beat time, text)● ✅ How to compute

elapsed time?● 🤔 Automation points

Page 98: A different flavor of audio programming

Ableton Live Format (.als): Tempo Automation Points

Page 99: A different flavor of audio programming

Ableton Live: Tempo Automation Points

Page 100: A different flavor of audio programming

Ableton Live: Tempo Automation Points

Page 101: A different flavor of audio programming

Ableton Live: Tempo Automation Points

Page 102: A different flavor of audio programming

Ableton Live: Tempo Automation Points

Page 103: A different flavor of audio programming

Ableton Live: Tempo Automation Points

Page 104: A different flavor of audio programming

Fl Studio: Tempo Automation Points

Page 105: A different flavor of audio programming

Fl Studio: Tempo Automation Points

Page 106: A different flavor of audio programming

Fl Studio: Tempo Automation Points

Channels

Playlist Items

Page 107: A different flavor of audio programming

Structures

struct Channel { int id; int dest_id; int param_id; Point[] points; // ...};

struct PlaylistItem { int channel_id; int start; int length; // ...};

Page 108: A different flavor of audio programming

Structures

struct Channel { int id; int dest_id; int param_id; Point[] points; // ...};

struct PlaylistItem { int channel_id; int start_beat; int length; // beat time // ...};

Page 109: A different flavor of audio programming

Approach● Parse all channels and playlist items

○ Events: CHANNEL_NEW (0x40), AUTOMATION_CHANNEL (0xe3), AUTOMATION_DATA (0xea), PLAYLIST_ITEMS (0xe9)

Page 110: A different flavor of audio programming

Approach● Filter channels for tempo automation channels

○ Channel dest_id = 0x4000 (Master Track), param_id = 0x5 (Tempo Parameter)

Page 111: A different flavor of audio programming

Approach● Filter playlist items for tempo automation playlist items

○ Using channel IDs of tempo automation channels

Page 112: A different flavor of audio programming

Approach● Render each playlist item’s points

○ Resolve global timeline positions of points

Page 113: A different flavor of audio programming

Approach● Render each playlist item’s points

○ Resolve global timeline positions of points

Channel 1:Point 1: Start + 2Point 2: Start + 10Point 3: Start + 14

Page 114: A different flavor of audio programming

Approach● Render each playlist item’s points

○ Resolve global timeline positions of points

Channel 1:Point 1: Start + 2Point 2: Start + 10Point 3: Start + 14

Item 1: Start: 10 Item 2: Start: 60

Page 115: A different flavor of audio programming

Approach● Render each playlist item’s points

○ Resolve global timeline positions of points

Channel 1:Point 1: Start + 2Point 2: Start + 10Point 3: Start + 14

Item 1: Start: 10Point 1: 12Point 2: 20Point 3: 24

Item 2: Start: 60Point 4: 62Point 5: 80Point 6: 84

Page 116: A different flavor of audio programming

Approach● Render each playlist item

○ Resolve global timeline positions of playlist item’s points

Page 117: A different flavor of audio programming

Approach● Render each playlist item

○ Resolve global timeline positions of playlist item’s points

Page 118: A different flavor of audio programming

Approach● Render each playlist item

○ Resolve global timeline positions of playlist item’s points

Page 119: A different flavor of audio programming

Approach● Merge playlist items

Page 120: A different flavor of audio programming

Hard ModeWhat we need:

● ✅ Markers (beat time, text)● ✅ Automation points● ✅ How to compute

elapsed time?

Page 121: A different flavor of audio programming

One little problem..

Page 122: A different flavor of audio programming

It should take 2.77 seconds

✅Time Elapsed: 2.77 seconds

Page 123: A different flavor of audio programming

What does the DAW say?

Page 124: A different flavor of audio programming

The DAW doesn’t match the theoretical result!

Page 125: A different flavor of audio programming

Lesson 1: Accuracy is relevant!

The quality of implementation will affect our accuracy.

Page 126: A different flavor of audio programming

Lesson 2: Accuracy defined by the DAW

At the end of the day, we must match the DAW.

Page 127: A different flavor of audio programming

Unreal mode: Tempo automation (fancy)

Page 128: A different flavor of audio programming

Enough physics, let’s talk audio rendering

Page 129: A different flavor of audio programming

DAWs can’t truly render audio at changing tempo

Page 130: A different flavor of audio programming

Approximate by sampling automation curve

Page 131: A different flavor of audio programming

Approximate by sampling automation curve

Requires new approach for calculating time elapsed!

Page 132: A different flavor of audio programming

1. Find BPMs at step points

Page 133: A different flavor of audio programming

2. Convert BPMs to SPB

Page 134: A different flavor of audio programming

3. Construct SPB stepwise function

Page 135: A different flavor of audio programming

4. Compute area under SPB stepwise function

Time Elapsed

Page 136: A different flavor of audio programming

One Key Parameter: Tempo Quantization Value

??

Page 137: A different flavor of audio programming

One Key Parameter: Curve Quantization

➔ DAW implementation detail➔ Not documented➔ Not stored in project file

Page 138: A different flavor of audio programming

Unreal ModeWhat we need:

● ✅ Markers (beat time, text)● ✅ Automation points● ✅ How to compute

elapsed time?● 🤔 Quantization value?

Page 139: A different flavor of audio programming

How to find tempoquantization?

???

Page 140: A different flavor of audio programming

How to find tempoquantization?

Give up and ask for help :(

Page 141: A different flavor of audio programming
Page 142: A different flavor of audio programming
Page 143: A different flavor of audio programming
Page 144: A different flavor of audio programming

How to find tempoquantization?

● ✅ Give up● Brute force

Page 145: A different flavor of audio programming

Brute forcing the quantization value

➔ Quantization is happening.➔ Will be a power of 2. (16th, 32rd, 64th note, ...)

➔ Limited possibilities in practice. (214th notes are impractical)

What we know:

Page 146: A different flavor of audio programming

1. Pick a known automation curve (60-120, 4 beats)

Page 147: A different flavor of audio programming

1. Pick a known automation curve (e.g. 60-120, 4 beats)

And remember what the DAW reported

Page 148: A different flavor of audio programming

2. Create experimental test harness

Standalone time calculation, parameterized on quantization

Page 149: A different flavor of audio programming

2. Create experimental test harness

Standalone time calculation, parameterized on quantization

Page 150: A different flavor of audio programming

Input: Start BPM, End BPM, # Beats, Quantization Value

2. Create experimental test harness

Page 151: A different flavor of audio programming

Input: Start BPM, End BPM, # Beats, Quantization Value

Output: Time Elapsed

2. Create experimental test harness

Page 152: A different flavor of audio programming

Output: Time Elapsed

Input: Start BPM, End BPM, # Beats, Quantization Value

2. Create experimental test harness

Page 153: A different flavor of audio programming

3. Try a bunch of values! See which matches the DAW.

Page 154: A different flavor of audio programming

3. Try a bunch of values! See which matches the DAW.

Page 155: A different flavor of audio programming
Page 156: A different flavor of audio programming

Ableton Live’s quantization value

Page 157: A different flavor of audio programming

FL Studio’s quantization value?

Page 158: A different flavor of audio programming

FL Studio’s quantization value?

Page 159: A different flavor of audio programming

How to find tempoquantization?

● ✅ Give up● ✅ Brute force● 👂Observe using

DAW

Page 161: A different flavor of audio programming

Unreal ModeWhat we need:

● ✅ Markers (beat time, text)● ✅ Automation points● ✅ How to compute

elapsed time?● ✅ Quantization value

Page 162: A different flavor of audio programming

.als

Ableton Live Project Export

Time Markers

00:00 - A05:22 - B10:59 - C

markers.txt

.flp

FL Studio Project

Automation Points

Third Party Tool

Page 163: A different flavor of audio programming

.als

Ableton Live Project Export

Time Markers

00:00 - A05:22 - B10:59 - C

markers.txt

.flp

FL Studio Project

Automation Points

Third Party Tool

✅ Third party tool✅ Extracts time markers✅ Robust & high accuracy

Page 164: A different flavor of audio programming

● ✅ Third party tool● ✅ Analyze DAW project file● ✅ Extract time markers● ✅ Output as plain text

○ Marker text○ Marker time (mm:ss format)

Our goal:

Page 165: A different flavor of audio programming

Implementation & Evaluation

Page 166: A different flavor of audio programming

dawtool 🛠

● Python 3● Parsers for DAW formats

○ Ableton Live v8-10○ FL Studio v11-12, 20

● Time marker extraction● Theoretical & DAW modes● No dependencies*

Page 167: A different flavor of audio programming

Evaluation: Speed

Page 168: A different flavor of audio programming

Evaluation: Speed

Page 169: A different flavor of audio programming

Evaluation: Accuracy (Stress Tests)

DAW Max Observed Err.

Ableton Live

Page 170: A different flavor of audio programming

DAW Max Observed Err.

Ableton Live .001 s

Evaluation: Accuracy (Stress Tests)

Page 171: A different flavor of audio programming
Page 172: A different flavor of audio programming
Page 173: A different flavor of audio programming
Page 174: A different flavor of audio programming
Page 175: A different flavor of audio programming
Page 176: A different flavor of audio programming

1 ms error over 16 hour Live set 🤷‍♂

Page 177: A different flavor of audio programming

DAW Max Observed Error % Error

Live .001 s .000002

FL Studio

Evaluation: Accuracy (Stress Tests)

Page 178: A different flavor of audio programming

DAW Max Observed Error % Error

Live .001 s .000002

FL Studio .56 s

Evaluation: Accuracy (Stress Tests)

Page 179: A different flavor of audio programming
Page 180: A different flavor of audio programming
Page 181: A different flavor of audio programming
Page 182: A different flavor of audio programming

DAW Max Observed Error % Error

Live .001 s 0.000002

FL Studio .56 s 0.0029

Evaluation: Accuracy (Stress Tests)

Page 183: A different flavor of audio programming

Demo

Page 184: A different flavor of audio programming

Conclusion

Page 185: A different flavor of audio programming

Audio

“Workflow”

Plugins

Needs of DAW users

Page 186: A different flavor of audio programming

AudioPlugins

Project file manipulation

Needs of DAW users

Page 187: A different flavor of audio programming

AudioPlugins

Project file manipulation

Desktop Automation

Needs of DAW users

Page 188: A different flavor of audio programming

AudioPlugins

Project file manipulation

Time Marker Exporter

Needs of DAW users

Page 189: A different flavor of audio programming

AudioPlugins

Project file manipulation

What else?? 🤔

Needs of DAW users

Page 190: A different flavor of audio programming

Timestamps (https://timestamps.me)

Page 191: A different flavor of audio programming

People actually use it!

Page 192: A different flavor of audio programming
Page 193: A different flavor of audio programming
Page 194: A different flavor of audio programming
Page 195: A different flavor of audio programming
Page 196: A different flavor of audio programming
Page 197: A different flavor of audio programming
Page 198: A different flavor of audio programming
Page 199: A different flavor of audio programming

You are more powerful than you think.

Page 200: A different flavor of audio programming

Acknowledgements

Luke Van Seters

Ableton & Image Line

All Timestamps users

ADC ‘20 Organizers

Page 201: A different flavor of audio programming

Thank you.Mark Mossberg @offlinemarkgithub.com/offlinemark/dawtool

Timestampstimestamps.me

Page 202: A different flavor of audio programming