render gongs

This commit is contained in:
j 2017-10-02 13:37:53 +02:00
parent 2fca6615c2
commit a0650e6475
6 changed files with 180 additions and 3 deletions

8
.gitignore vendored
View file

@ -1,2 +1,10 @@
*.swp *.swp
*.pyc *.pyc
gongs_wav
CLIPS.json
DRONES.json
GONGS.json
MUSIC.json
PATHS.json
VOCALS.json

12
gongs_wav.py Executable file
View file

@ -0,0 +1,12 @@
#!/usr/bin/python3
from glob import glob
import subprocess
import os
for f in glob('gongs/*/*.mp3'):
t = f.replace('gongs/', 'gongs_wav/').replace('.mp3', '.wav')
print(f, t)
folder = os.path.dirname(t)
if not os.path.exists(folder):
os.makedirs(folder)
subprocess.call(['ffmpeg', '-i', f, t])

2
pi.py
View file

@ -6,6 +6,7 @@ class random(object):
PI = str(mp.pi).replace('.', '') PI = str(mp.pi).replace('.', '')
def __init__(self, offset=0): def __init__(self, offset=0):
self.position = offset
self.numbers = list(map(int, self.PI[offset:])) self.numbers = list(map(int, self.PI[offset:]))
def __call__(self): def __call__(self):
@ -14,5 +15,6 @@ class random(object):
mp.dps += 1000 mp.dps += 1000
self.PI = str(mp.pi).replace('.', '') self.PI = str(mp.pi).replace('.', '')
self.numbers = list(map(int, self.PI[offset:])) self.numbers = list(map(int, self.PI[offset:]))
self.position += 1
return self.numbers.pop(0) return self.numbers.pop(0)

View file

@ -419,6 +419,10 @@ def sequence(seq, letter):
tduration = sum([c['duration'] for c in result[track]]) tduration = sum([c['duration'] for c in result[track]])
if not abs(tduration - duration) < 0.000001: if not abs(tduration - duration) < 0.000001:
raise Exception('invalid duration on track: %s %s vs %s %s' % (track, tduration, duration, result[track])) raise Exception('invalid duration on track: %s %s vs %s %s' % (track, tduration, duration, result[track]))
result['gongs'] = {
'tracks': 100 + seq() * 23,
'offset': seq.position,
}
return result return result

132
render_gongs.py Executable file
View file

@ -0,0 +1,132 @@
#!/usr/bin/python3
import os
import sys
import json
import subprocess
from collections import defaultdict
import string
from glob import glob
import nmt
import numpy as np
from pi import random
from keywords import KEYWORDS
import ox
import ox.web.auth
if not os.path.exists('GONGS.json'):
GONGS = defaultdict(list)
prefix = 'gongs_wav'
wavs = glob('%s/*/*.wav' % prefix)
for path in sorted(wavs):
name = os.path.basename(path).split('.')[0]
GONGS[name] = {
'path': path,
'duration': ox.avinfo(path)['duration']
}
with open('GONGS.json', 'w') as fd:
json.dump(GONGS, fd, indent=2, sort_keys=True)
else:
GONGS = json.load(open('GONGS.json'))
def random_choice(seq, items):
n = n_ = len(items) - 1
#print('len', n)
if n == 0:
return items[0]
r = seq()
base = 10
while n > 10:
n /= 10
#print(r)
r += seq()
base += 10
r = int(n_ * r / base)
#print('result', r, items)
return items[r]
def splitint(number, by):
div = int(number/by)
mod = number % by
return [div + 1 if i > (by - 1 - mod) else div for i in range(by)]
def add_blank(track, d):
if track and track[-1].get('blank'):
track[-1]['duration'] += d
else:
blank = {'blank': True, 'duration': d}
track.append(blank)
return d
def get_gongs(seq, result, duration, tracks=42):
for tn in range(tracks):
track = 'gongs%d' % tn
if track not in result:
result[track] = []
position = 0
while position < duration:
n1 = seq()
n2 = seq()
clip = GONGS['%d_%d' % (n1, n2)]
if position + clip['duration'] < duration:
position += clip['duration']
result[track].append(clip)
else:
c = clip.copy()
c['duration'] = duration - position
result[track].append(c)
position += c['duration']
if position < duration:
position += add_blank(result[track], duration - position)
for track in result:
if result[track]:
tduration = sum([c['duration'] for c in result[track]])
if not abs(tduration - duration) < 0.000001:
raise Exception('invalid duration on track: %s %s vs %s %s' % (track, tduration, duration, result[track]))
return result
def mix_gongs(data, output):
mixed = None
for n, clips in enumerate(data.values()):
track = [nmt.sound2np(c['path']) for c in clips]
track = np.vstack(track)
if mixed is None:
mixed = track.astype(np.int)
else:
limit = min(len(track), len(mixed))
mixed = mixed[:limit]
track = track[:limit]
mixed += track.astype(np.int)
print(n)
d = np.sqrt(len(data))
while \
np.max((mixed/d).astype(np.int)) != np.max((mixed/d).astype(np.int16)) \
or np.min((mixed/d).astype(np.int)) != np.min((mixed/d).astype(np.int16)):
d = d + 2
mixed = (mixed/d).astype(np.int16)
nmt.np2sound(mixed, output)
if __name__ == '__main__':
n = int(sys.argv[1])
seq = random(n)
duration = float(sys.argv[2]) + 10
tracks = int(sys.argv[3])
if len(sys.argv) > 1:
tracks = int(sys.argv[1])
else:
tracks = 42
data = get_gongs(seq, {}, duration, tracks)
mix_gongs(data, output)
with open(output + '.json', 'w') as fd:
json.dump(result, fd, indent=4, sort_keys=True)

View file

@ -50,8 +50,7 @@ def add_clip(playlist, clip, in_, duration):
tractor.plant_filter(volume) tractor.plant_filter(volume)
playlist.append(tractor) playlist.append(tractor)
def add_audio_clip(playlist, file_, duration): def add_audio_clip(playlist, file_, duration, in_=0):
in_ = 0
if not isinstance(file_, str): if not isinstance(file_, str):
file_ = file_.encode('utf-8') file_ = file_.encode('utf-8')
clip = mlt.Producer(profile, file_) clip = mlt.Producer(profile, file_)
@ -109,6 +108,7 @@ target_vocals = source.replace('.json', '.vocals.xml')
target_music = source.replace('.json', '.music.xml') target_music = source.replace('.json', '.music.xml')
target_drones = source.replace('.json', '.drones.xml') target_drones = source.replace('.json', '.drones.xml')
target_source = source.replace('.json', '.source.xml') target_source = source.replace('.json', '.source.xml')
gongs_wav = source.replace('.json', '.gongs.wav')
with open(source) as fd: with open(source) as fd:
data = json.load(fd) data = json.load(fd)
@ -119,12 +119,14 @@ music = mlt.Playlist()
vocals = mlt.Playlist() vocals = mlt.Playlist()
drones0 = mlt.Playlist() drones0 = mlt.Playlist()
drones1 = mlt.Playlist() drones1 = mlt.Playlist()
gongs = mlt.Playlist()
# hide Set to 1 to hide the video (make it an audio-only track), # hide Set to 1 to hide the video (make it an audio-only track),
# 2 to hide the audio (make it a video-only track), # 2 to hide the audio (make it a video-only track),
# or 3 to hide audio and video (hidden track). # or 3 to hide audio and video (hidden track).
drones0.set("hide", 1) drones0.set("hide", 1)
drones1.set("hide", 1) drones1.set("hide", 1)
gongs.set("hide", 1)
vocals.set("hide", 1) vocals.set("hide", 1)
music.set("hide", 1) music.set("hide", 1)
@ -194,8 +196,25 @@ save_xml(drones, target_drones)
save_xml(music, target_music) save_xml(music, target_music)
# render gongs
subprocess.call([
# offset in pi, duration, number of tracks, target
'./render_gongs.py',
str(data['gongs']['offset']),
str(duration),
str(data['gongs']['tracks']),
gongs_wav
])
# load gongs
add_audio_clip(gongs, gongs_wav, int(duration * fps), int(5*fps))
# mix drones + music # mix drones + music
mtractor = mix_audio_tracks(drones, music, 0.3) #mtractor = mix_audio_tracks(drones, music, 0.3)
# mix gongs + music
mtractor = mix_audio_tracks(gongs, music, 0.3)
norm = mlt.Filter(profile, "volume") norm = mlt.Filter(profile, "volume")
# lower volume # lower volume
norm.set("gain", "-12dB") norm.set("gain", "-12dB")