def pitch_unidir(time, correct): """ Unidirectional pitch-shift into a specified pitch. Starts at most 3 semitones away from the desired pitch. """ trans = ratio(window(1.) + correct) origin = ratio(correct) return interleave([trans, origin], [0., time * 1000], [1, window(.75)])
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 pitch_peak(time, correct): """ A triangle-wave-like pitch shift. Moves up / down from no-transposition and returns to normal. Guaranteed to reach its destination within the first 75% of the time requested. """ ms = lambda t: _ms(t, time) trans = ratio(window(1.) + correct) origin = ratio(correct) attack_time = random() * .75 + .2 decay_time = 1. - attack_time dest = [origin, trans, origin] times = [0., ms(attack_time), ms(decay_time)] curves = [1.] + window([.75, .75]) return interleave(dest, times, curves)
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 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)
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])