def moans(pitch, time, instrument): """ Engine Main. Returns an odot bundle for the moans engine. API: /engine : "moan" /samples : (string) -- a sample to use /suffix : (string) -- filename suffix /time : (float) -- total time in seconds /pitch : (list) -- curve~ list for transposition ratios /amp : (list) -- curve~ list for amplitude envelope Upon receipt, engine queues /samples & executes envelopes """ categories = filterCategories(pitch, instrument, autocat[instrument]) if len(categories) is 0: return o.bundle() query = one(getSamples(pitch, instrument, categories)) correct = pitch - query['midi'] sample = query['files'] oengine = o.message('/engine', 'moan') osamp = o.message('/samples', sample) ofilename = o.message('/suffix', '_' + instrument + '_moan.wav') otime = o.message('/time', time) opitch = o.message('/pitch', one([pitch_peak, pitch_unidir, pitch_cross])(time, correct)) oamps = o.message('/amp', one([amp_in_out, amp_in, amp_out])(time)) return o.bundle(messages = [oengine, osamp, ofilename, otime, opitch, oamps])
def perc(pitch, time, instrument): """ Engine Main. Returns an odot bundle for the perc engine. API: /engine : "perc" /samples: (string) -- a sample to use /suffix : (string) -- filename suffix /time : (float) -- total time in seconds /pitch : (float) -- transposition factor /amp : (list) -- line~ list for amplitude envelope """ categories = autocat[instrument] categories = filterCategories(pitch, instrument, categories) if len(categories) is 0: return o.bundle() query = one(getSamples(pitch, instrument, categories)) correct = pitch - query["midi"] sample = query["files"] oengine = o.message("/engine", "perc") osamp = o.message("/samples", sample) ofilename = o.message("/suffix", "_" + instrument + "_perc.wav") otime = o.message("/time", time) opitch = o.message("/pitch", ratio(correct)) oamps = o.message("/amp", amp_env(time)) return o.bundle(messages=[oengine, osamp, ofilename, otime, opitch, oamps])
def reartic(pitch, time, instrument): """ Rearticulation event generator. Returns an odot bundle for the rearticulation engine. API: /engine : "reartic" /samples : (list) -- a list of samples for polybuffer~ /suffix : (string) -- filename suffix /time : (float) -- total time in seconds /events : (list of bundles) -- time-tagged bundles containing all of the articulations & envelopes This engine is different from others due to the fact that it plays multiple sounds, arranged in time. (For this reason, the current implementation does not generalize engines into a helper structure yet, which violates DRY and will be fixed in time.) """ # event_times = subdivide(time) # for o.schedule event_times, event_lengths = reartic_time(time) categories = [] for event in event_lengths: categories.append(filterCategories(pitch, instrument, autocat[instrument])) soundqueries = [{'pitch': pitch, 'artic': category} for category in categories if not (not category)] # discard impossible notes seqfiles = [one(getSamples(sq['pitch'], instrument, sq['artic'])) for sq in soundqueries] correct = [ratio(pitch - sound['midi']) for sound in seqfiles] filenames = [e for e in set([sample['files'] for sample in seqfiles])] fileindices = [filenames.index(filename) + 1 for filename in [sq['files'] for sq in seqfiles]] event_times = [e + .05 for e in event_times] oengine = o.message('/engine', 'reartic') oinstr = o.message('/instrument', instrument) osamp = o.message('/samples', filenames) osuffix = o.message('/suffix', '_' + instrument + '_reartic.wav') otime = o.message('/time', time) events = [] envfuncs = [env_uniform, env_jagged, amp_in_out] for index, value in enumerate(fileindices): opitch = o.message('/pitch', correct[index]) obuffer = o.message('/buffer', instrument + '.reartic.' + str(value)) oeventtime = o.message('/event/time', event_times[index]) oamp = o.message('/envelope', one(envfuncs)(event_lengths[index])) events.append(o.bundle(messages = [opitch, obuffer, oeventtime, oamp])) oevents = o.message('/events', events) return o.bundle(messages = [oengine, oinstr, osamp, osuffix, otime, oevents])
def rapids(chord, time, instrument): """ Rapids event generator. Returns an odot bundle for the rapids engine. API: /engine : "rapids" /instrument : (string) -- instrument used /samples : (list) -- a list of samples for polybuffer~ /suffix : (string) -- filename suffix /time : (float) -- total time in seconds /events : (list of bundles) -- bundles containing all of the information to play back a list of samples, rapidly. """ # Figure out event times and update time if necessary: event_times, time = rapids_time(chord, time) # Figure out useful articulation categories: categories = [] for pitch in chord: categories.append(filterCategories(pitch, instrument, autocat[instrument])) # create a dictionary of sound queries to be resolved by getSamples: soundqueries = [{'pitch' : elem[0], 'artic': elem[1]} for elem in collect(chord, categories) if not(not elem[1])] # discard impossible notes (empty category) # execute the query, giving us the samples we'd need: seqfiles = [one(getSamples(sq['pitch'], instrument, sq['artic'])) for sq in soundqueries] print('-' * 100) print(len(soundqueries) == len(seqfiles)) print('-' * 100) # determine transposition ratios for sample playback: #correct = [pitch - ratio(sample['midi']) for sample in seqfiles] correct = [ratio(pair[0]['pitch'] - pair[1]['midi']) for pair in collect(soundqueries, seqfiles)] # filter redundant files: filenames = [e for e in set([sample['files'] for sample in seqfiles])] # determine the index of sample playback in polybuffer~: fileindices = [filenames.index(filename) + 1 for filename in [sq['files'] for sq in seqfiles]] # construct bundle: oengine = o.message('/engine', 'rapids') oinstr = o.message('/insturment', instrument) osamp = o.message('/samples', filenames) osuffix = o.message('/suffix', '_' + instrument + '_rapids.wav') otime = o.message('/time', time) events = [] for index, value in enumerate(fileindices): opitch = o.message('/pitch', correct[index]) obuffer = o.message('/buffer', instrument + '.rapids.' + str(value)) oeventtime = o.message('/event/time', event_times[index] + 0.02) events.append(o.bundle(messages = [opitch, obuffer, oeventtime])) oevents = o.message('/events', events) return o.bundle(messages = [oengine, oinstr, osamp, osuffix, otime, oevents])
def rapids_time(chord, time): """ For rapid play, we create a rapid passage based on a number of notes in the chord. """ bpm = random() * 40. + 60. tuplet = one([5, 6, 7, 4, 5, 6, 7]) time_between_events = 60. / (bpm * tuplet) time_total = tuplet * time_between_events if time_total > time: time = time_total event_times = frange(0., time_total, time_between_events) return (time_between_events, time)
def pitch_cross(time, correct): """ Shifts up then down (or down then up), before returning to the original pitch of the sample. """ ms = lambda t: _ms(t, time) _ts = lambda: random() * .33 + .1 shifts = window([1., 1.]) if (shifts[0] * shifts[1]) > 0: shifts[one([0, 1])] *= -1 trans = [] for s in shifts: trans.append(s + correct) trans = ratio(trans) first = _ts() second = 1 - _ts() - first decay = 1. - (first + second) origin = ratio(correct) dest = [origin] + trans + [origin] times = [0., ms(first), ms(second), ms(decay)] curves = [1.] + window([.75, .75, .75]) return interleave(dest, times, curves)