render gongs
This commit is contained in:
parent
2fca6615c2
commit
a0650e6475
6 changed files with 180 additions and 3 deletions
8
.gitignore
vendored
8
.gitignore
vendored
|
@ -1,2 +1,10 @@
|
|||
*.swp
|
||||
*.pyc
|
||||
gongs_wav
|
||||
CLIPS.json
|
||||
DRONES.json
|
||||
GONGS.json
|
||||
MUSIC.json
|
||||
PATHS.json
|
||||
VOCALS.json
|
||||
|
||||
|
|
12
gongs_wav.py
Executable file
12
gongs_wav.py
Executable 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
2
pi.py
|
@ -6,6 +6,7 @@ class random(object):
|
|||
PI = str(mp.pi).replace('.', '')
|
||||
|
||||
def __init__(self, offset=0):
|
||||
self.position = offset
|
||||
self.numbers = list(map(int, self.PI[offset:]))
|
||||
|
||||
def __call__(self):
|
||||
|
@ -14,5 +15,6 @@ class random(object):
|
|||
mp.dps += 1000
|
||||
self.PI = str(mp.pi).replace('.', '')
|
||||
self.numbers = list(map(int, self.PI[offset:]))
|
||||
self.position += 1
|
||||
return self.numbers.pop(0)
|
||||
|
||||
|
|
|
@ -419,6 +419,10 @@ def sequence(seq, letter):
|
|||
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]))
|
||||
result['gongs'] = {
|
||||
'tracks': 100 + seq() * 23,
|
||||
'offset': seq.position,
|
||||
}
|
||||
return result
|
||||
|
||||
|
||||
|
|
132
render_gongs.py
Executable file
132
render_gongs.py
Executable 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)
|
|
@ -50,8 +50,7 @@ def add_clip(playlist, clip, in_, duration):
|
|||
tractor.plant_filter(volume)
|
||||
playlist.append(tractor)
|
||||
|
||||
def add_audio_clip(playlist, file_, duration):
|
||||
in_ = 0
|
||||
def add_audio_clip(playlist, file_, duration, in_=0):
|
||||
if not isinstance(file_, str):
|
||||
file_ = file_.encode('utf-8')
|
||||
clip = mlt.Producer(profile, file_)
|
||||
|
@ -109,6 +108,7 @@ target_vocals = source.replace('.json', '.vocals.xml')
|
|||
target_music = source.replace('.json', '.music.xml')
|
||||
target_drones = source.replace('.json', '.drones.xml')
|
||||
target_source = source.replace('.json', '.source.xml')
|
||||
gongs_wav = source.replace('.json', '.gongs.wav')
|
||||
|
||||
with open(source) as fd:
|
||||
data = json.load(fd)
|
||||
|
@ -119,12 +119,14 @@ music = mlt.Playlist()
|
|||
vocals = mlt.Playlist()
|
||||
drones0 = mlt.Playlist()
|
||||
drones1 = mlt.Playlist()
|
||||
gongs = mlt.Playlist()
|
||||
|
||||
# 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),
|
||||
# or 3 to hide audio and video (hidden track).
|
||||
drones0.set("hide", 1)
|
||||
drones1.set("hide", 1)
|
||||
gongs.set("hide", 1)
|
||||
vocals.set("hide", 1)
|
||||
music.set("hide", 1)
|
||||
|
||||
|
@ -194,8 +196,25 @@ save_xml(drones, target_drones)
|
|||
|
||||
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
|
||||
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")
|
||||
# lower volume
|
||||
norm.set("gain", "-12dB")
|
||||
|
|
Loading…
Reference in a new issue