Ambient-Mixer Renderer
The snippet can be accessed without any authentication.
Authored by
Kevin Whitaker
Python script to render downloaded Ambient Mixer data(using downloader from https://github.com/Philooz/pyambientmixer) to mp3.
snippetfile1.txt 4.30 KiB
#!/usr/bin/env python
"""Render Ambient-Mix XML to MP3"""
import untangle, pydub, argparse, math, os
from typing import List
__author__ = "Kevin Whitaker"
__license__ = "GPL"
__version__ = "1.0"
human_unit_to_sec = {
"1m": 60,
"10m": 600,
"1h": 3600
}
def convert_volume_to_decibel(volume: int):
return -(math.log10(1+volume/100))
def convert_balance_to_pan(balance: int):
return (balance*2)/100
def load_sample_to_segment(audio_file):
if not os.path.exists(audio_file):
print("Needed audio file "+audio_file+" does not exist.")
exit(1)
return pydub.AudioSegment.from_mp3(audio_file)
class Channel():
base_segment: pydub.AudioSegment
length: int = 3600
local_url: str = ""
name: str = ""
id: int = -1
url: str = ""
muted: bool = False
volume: int = 100 #between 0 and 100
balance: int = 0 #between -50 and +50
random: bool = False
random_counter: int = 1
random_unit: int = human_unit_to_sec["1h"]
crossfade: bool = False
def __init__(self, length: int, source_dir: str, id: int):
self.length = length
self.id = id
self.local_url = source_dir+"/"+str(id)+".mp3"
self.base_segment = pydub.AudioSegment.silent(duration= length*1000)
def render_channel(self):
if not self.muted:
inital_audio = load_sample_to_segment(self.local_url)
gained_audio = inital_audio.apply_gain(convert_volume_to_decibel(self.volume))
panned_audio = gained_audio.pan(convert_balance_to_pan(self.balance))
if self.random:
trigger_every = int(self.random_unit/self.random_counter)
canvas: pydub.AudioSegment = self.base_segment
for sec in range(trigger_every,self.length,trigger_every):
canvas = canvas.overlay(panned_audio,position=sec*1000)
return canvas
elif self.crossfade:
fadein_audio = panned_audio.fade_in(duration=1000)
crossfaded_audio = fadein_audio.fade_out(duration=1000)
return self.base_segment.overlay(crossfaded_audio, loop=True)
else:
return self.base_segment.overlay(panned_audio, loop=True)
else:
return self.base_segment
class Mix():
channels: List[Channel] = []
length: int = 3600
final_segment: pydub.AudioSegment
def __init__(self, xml_file: str, length: int, source_dir: str):
self.length = length
self.final_segment = pydub.AudioSegment.silent(duration=length*1000)
xml = untangle.parse(xml_file)
for channel_num in range(1,9):
channel_xml = getattr(xml.audio_template, "channel"+str(channel_num))
channel = Channel(length, source_dir, channel_xml.id_audio.cdata)
channel.name = channel_xml.name_audio.cdata
channel.url = channel_xml.url_audio.cdata
channel.muted = channel_xml.mute.cdata == "true"
channel.volume = int(channel_xml.volume.cdata)
channel.balance = int(channel_xml.balance.cdata)
channel.random = channel_xml.random.cdata == "true"
channel.random_counter = int(channel_xml.random_counter.cdata)
channel.random_unit = human_unit_to_sec[channel_xml.random_unit.cdata]
channel.crossfade = channel_xml.crossfade.cdata == "true"
self.channels.append(channel)
def render_channels(self):
for channel in self.channels:
self.final_segment = self.final_segment.overlay(channel.render_channel())
def export_final(self, outfile: str):
self.final_segment.export(outfile, format="mp3")
parser = argparse.ArgumentParser(description="Render Ambient-Mix XML to MP3")
parser.add_argument("-l", help="length of final render in seconds", default=3600, dest="length")
parser.add_argument("-s", help="directory containing audio samples", default="sounds", dest="samples")
parser.add_argument("infile", help="Input XML file defining mix.")
parser.add_argument("outfile", help="Output MP3 file of rendered mix.")
args = parser.parse_args()
if not os.path.exists(args.samples):
print("Samples directory does not exist.")
exit(1)
mixer = Mix(args.infile, int(args.length), args.samples)
mixer.render_channels()
mixer.export_final(args.outfile)
Please register or sign in to comment