Tutorial - Tonn API: Automating Multitrack Mixing with Tonn API and DAWProject: A Guide for Developers¶
Introduction¶
In modern music production, automation is key. Whether you’re developing a DAW-integrated tool, running a music tech startup, or streamlining workflows for independent producers, automating the mixing process can save countless hours. This tutorial demonstrates how to use the Tonn API to retrieve a mix preview, download stems, apply processing, and format them for Bitwig Studio and PreSonus Studio One—all programmatically.
What makes this particularly powerful is how Tonn API integrates seamlessly with DAWProject-py, an open-source project that allows for structured export to DAWs. This means that, in just a few steps, you can take your audio, apply mix settings like EQ, Compression, Gain, and Panning, and generate a DAW session file that loads everything into your preferred environment.
This is a simple example, but it highlights the potential for fully automated, API-driven mixing workflows—something that can revolutionize audio production tools. For a more detailed example check out the GitHub repository.
What You’ll Learn¶
- How to retrieve a mix preview from the Tonn API
- How to download and process multitrack audio files
- How to apply gain, EQ, and compression settings programmatically
- How to export directly to DAWs using DAWProject-py
- How to handle network errors and retries gracefully
Prerequisites¶
Before diving into the code, ensure you have:
- Python 3 installed
- The following dependencies installed via
pip:sh pip install requests soundfile numpy - An API key from Tonn Portal
- Bitwig Studio or PreSonus Studio One (optional, but recommended for testing project files)
- Access to a cloud storage bucket to store demo audio files (Tonn API requires audio files to be stored in a bucket before processing)
- Audio files to upload (WAV, MP3, or FLAC)
Alternatively, to simplify API interaction, you can use our Python client library:
pip install roex-python
Find more details on the PyPI project page.
Alternatively, to simplify API interaction, you can use our Python client library:
pip install roex-python
Find more details on the PyPI project page.
1. Setting Up API Communication and File Upload¶
First, let's set up our API communication and file upload functionality:
import time
import requests
import json
import os
import soundfile as sf
# Base API endpoint for Tonn
BASE_URL = "https://tonn.roexaudio.com"
API_KEY = "YOUR_API_KEY_HERE"
def upload_track(filename):
"""Upload a track to Tonn's temporary storage"""
# Get upload URL
response = requests.post(
f"{BASE_URL}/upload",
headers={'X-API-Key': API_KEY},
json={
'filename': filename,
'contentType': 'audio/wav' # or 'audio/mpeg' for MP3
}
)
result = response.json()
# Upload the file
with open(filename, 'rb') as f:
upload_response = requests.put(
result['signed_url'],
data=f,
headers={'Content-Type': 'audio/wav'}
)
return result['readable_url'] if upload_response.status_code == 200 else None
# Example: Upload all tracks in a directory
def upload_all_tracks(directory):
"""Upload all audio files in a directory"""
track_urls = {}
for filename in os.listdir(directory):
if filename.endswith(('.wav', '.mp3', '.flac')):
filepath = os.path.join(directory, filename)
url = upload_track(filepath)
if url:
track_urls[filename] = url
return track_urls
# Retry settings for API calls
MAX_RETRIES = 3
RETRY_DELAY = 2 # seconds
# ... rest of the code remains the same ...
We’ll use requests to interact with the API, ensuring that we handle failures gracefully with retry mechanisms.
2. Downloading Multitrack Audio Files¶
Before processing, we need to fetch the original stems that will be used for the mix. Our function downloads these files while handling errors and retries.
def download_audio(file_name, url, output_dir="audio_in"):
"""
Downloads an audio file from the given URL and saves it locally.
"""
os.makedirs(output_dir, exist_ok=True)
local_filename = os.path.join(output_dir, file_name)
for attempt in range(MAX_RETRIES):
try:
print(f"Downloading {file_name} (Attempt {attempt + 1})...")
response = requests.get(url, stream=True, timeout=10)
response.raise_for_status()
with open(local_filename, "wb") as file:
for chunk in response.iter_content(chunk_size=8192):
file.write(chunk)
print(f"Successfully downloaded {file_name}")
return local_filename
except requests.exceptions.RequestException as e:
print(f"Error downloading {file_name}: {e}")
if attempt < MAX_RETRIES - 1:
time.sleep(RETRY_DELAY ** (attempt + 1))
else:
print(f"Failed to download {file_name} after {MAX_RETRIES} attempts.")
return None
We apply exponential backoff (RETRY_DELAY ** (attempt + 1)) to avoid overloading the API with repeated requests.
3. Retrieving and Polling the Mix Preview¶
Once we submit our multitrack audio to the Tonn API, we must poll for the preview mix until it’s ready. The following function repeatedly checks the API and retrieves the processed mix when available:
def poll_preview_mix(task_id, headers, max_attempts=30, poll_interval=5):
"""
Polls the API until the preview mix is ready.
"""
retrieve_url = f"{BASE_URL}/retrievepreviewmix"
retrieve_payload = {"multitrackData": {"multitrackTaskId": task_id, "retrieveFXSettings": True}}
print("Polling for the preview mix...")
for attempt in range(max_attempts):
response = requests.post(retrieve_url, json=retrieve_payload, headers=headers)
if response.status_code == 200:
results = response.json().get("previewMixTaskResults", {})
if results.get("status") == "MIX_TASK_PREVIEW_COMPLETED":
print("Preview mix is ready!")
return results
elif response.status_code == 202:
print(f"Attempt {attempt + 1}: Still processing...")
else:
print("Unexpected error while polling.")
return None
time.sleep(poll_interval)
print("Mix preview was not available after polling. Try again later.")
return None
4. Processing Audio: Gain, EQ, and Compression¶
Once we have the mix output settings, we apply gain adjustments, equalization, and compression.
def format_audio_tracks_for_daw(mix_output_settings, downloaded_files):
"""
Applies mix settings (gain, EQ, compression) to each track.
"""
audio_tracks = []
for track_name, settings in mix_output_settings.items():
file_path = downloaded_files.get(track_name)
if not file_path:
continue
audio_x, sr = sf.read(file_path, dtype="float32")
audio_x *= settings["gain_settings"]["initial_gain"]
sf.write(file_path, audio_x, sr)
audio_tracks.append({
"file_path": file_path,
"gain": settings["gain_settings"]["gain_amplitude"],
"eq_settings": settings.get("eq_settings", []),
"compressor_settings": settings.get("drc_settings", {})
})
return audio_tracks
5. Exporting to Bitwig Studio and PreSonus Studio One¶
We format our processed tracks and automatically create a DAW project using DAWProject-py.
import createBitwigProject
def create_bitwig_project(audio_tracks):
createBitwigProject.create_project_with_audio_tracks(audio_tracks)
This step ensures seamless DAW integration, making our automated mixing process immediately usable for further production and tweaking.
6. Additional Resources¶
To help you get started, we've provided a demo repository containing the full source code and example payloads. You can find it here: [Demo Repository - Link TBD]
For more on DAW session exports, check out DAWProject-py.
Conclusion¶
By leveraging the Tonn API, we’ve automated a core part of music production: mixing multitrack audio programmatically.
🚀 Ready to build the future of music production? Get your API key from Tonn Portal and start automating today! Also, be sure to check out the [Demo Repository - Link TBD] for example implementations!