peastman / sso

Sonatina Symphonic Orchestra
Other
155 stars 9 forks source link

Created tremolos for viola, cello, and bass #14

Closed peastman closed 6 months ago

peastman commented 6 months ago

We already had tremolo articulations for the violins, but not for the other string sections. I created them by adding together lots of overlapping staccato notes, staggering the times to create a realistic unmeasured tremolo. I believe that's how Mattias created the violin ones.

For posterity, here is the script I used. It reads the sfz file for a staccato articulation, creates the audio files for the tremolo samples, and outputs an sfz file (which requires minor edits afterward). The variables time_scale and amp_scale are customized for each instrument.

import numpy as np
import scipy.io
import soundfile
import librosa
import sys
import os

def createSample(staccato_name):
    sample_rate, stac1 = scipy.io.wavfile.read(staccato_name)
    sample_rate, stac2 = scipy.io.wavfile.read(staccato_name.replace('rr1', 'rr2'))
    stac1 = stac1/32767
    stac2 = stac2/32767
    stac2 = [librosa.effects.pitch_shift(stac2[:,0], sr=sample_rate, n_steps=-1), librosa.effects.pitch_shift(stac2[:,1], sr=sample_rate, n_steps=-1)]
    stac2 = np.array(stac2).T
    stac1[:,0] *= np.linspace(1, 0, stac1.shape[0])
    stac1[:,1] *= np.linspace(1, 0, stac1.shape[0])
    stac2[:,0] *= np.linspace(1, 0, stac2.shape[0])
    stac2[:,1] *= np.linspace(1, 0, stac2.shape[0])
    notes = [(0.0, 0, 1.0)]
    for i in range(60):
        delay = 0.9 + 0.2*np.random.rand()
        amp = 0.9 + 0.2*np.random.rand()
        notes.append((delay, 1-notes[-1][1], amp))
    num_repeated = 30
    for i in range(num_repeated):
        notes.append(notes[i+1])
    time_scale = 0.07*sample_rate
    amp_scale = 0.45
    output = np.zeros((int(time_scale*len(notes)*2), 2), dtype=np.float32)
    start_times = []
    last_start = 0
    for delay, note, amp in notes:
        n = stac1 if note == 0 else stac2
        start = last_start + int(time_scale*delay)
        length = n.shape[0]
        output[start:start+length, 0] += amp_scale*amp*n[:,0]
        output[start:start+length, 1] += amp_scale*amp*n[:,1]
        start_times.append(start)
        last_start = start
    output = output[:start_times[-1]+1,:]
    output_path = staccato_name.replace('stc-rr1', 'trm').replace('wav', 'flac')
    soundfile.write(output_path, output, sample_rate)
    return output_path, start_times[num_repeated], start_times[-1]

group = 0
for line in open(sys.argv[1]):
    line = line.strip()
    if line == '<group>':
        if group == 0:
            group = 1
        else:
            break
    if line.startswith('loop_mode'):
        print('loop_mode=loop_continuous')
    elif line.startswith('sample='):
        line = line.replace('\\', '/')
        output_path, loop_start, loop_end = createSample(line[7:])
        print(f'sample={output_path}')
        print(f'loop_start={loop_start}')
        print(f'loop_end={loop_end}')
    else:
        print(line)