def process_validate(command, obj): """ check command as valid or one of the shortcuts return True on valid """ if command == []: err_mess("No command entered") command = "None" elif command == ['']: err_mess( "Only alphanumeric characters, spaces, and underscores are allowed" ) elif command[0] in ("q", "quit"): p("Save before exiting?") if inpt("yn"): obj.save() info_block("Exiting processing of {0} '{1}'...".format( obj.reltype, obj.name)) raise Cancel(obj) elif command[0] in ("o", "options"): obj.options() elif command[0] in ("h", "help"): pass # callback handled during inpt() else: return True return False
def rhythms_help(self): info_block( "Rhythms are made up of multiple beats. Each beat follows the format of " + \ "<place> <length> <optional: start>, all of which are in beat notation: ", trailing_newline=False ) info_block( "<place>: the time at which this beat starts within the rhythm. For example" +\ " 'qn' would mean this beat starts one quarter-note after the beginning of the rhythm", trailing_newline=False ) info_block( "<length>: how long the snippet of the sample will be. 'qn' would mean it lasts for one quarter-note. " +\ "no entry, or 0, means that the entire sample will be used", trailing_newline=False ) info_block( "<start>: an optional parameter, which selects where in the sample to start playback " +\ "for this one beat, as if trimming it on the front. 'qn' means the beat would begin " +\ "from one quarter-note in to the sample", trailing_newline=False ) print(" 3q 1q") info_block( "will create a beat that start on the third quarter of the rhythm, and" + \ "lasts for one quarter note" ) p("Do you need info on how to create properly formed beats?", o="y/n") if inpt('yn'): beat_options()
def process(obj): """ process an object. 'self.name', 'self.reltype' required. """ section_head("Processing {0} '{1}'".format(obj.reltype, obj.name)) while True: try: obj.process_message() except AttributeError: pass with style("cyan, bold"): print("\n{0} ".format(RelGlobals.get_process_num()), end="") p("What process to run on {0} '{1}'?".format(obj.reltype, obj.name), h=True, o="'o' to view process options", indent=0, hang=4, leading_newline=False) try: command = inpt(mode='split', split_modes='arg', help_callback=processes_help) if process_validate(command, obj): do_command(command, obj) except Cancel: return
def record_live(self): """ record -- but get this -- live! """ section_head("Record mode") device_ind, device_name = sd_select_device() p("Enter recording duration (in seconds)") record_time = inpt('beatsec') p('Choose sample rate to record at, in samples per second. Hit enter to use default 44100') rate = inpt('int', required=False) if rate == '': rate = 44100 self.rate = Units.rate(rate) print(" Press Enter to begin recording, or 'q' to quit: ", end="") inpt("none", required=False) time.sleep(0.05) section_head("Recording at input {0} ({1}) for {2}".format(device_ind, \ device_name, record_time)) sd.default.channels = 2 recording = sd.rec( ind(record_time * self.rate), self.rate.magnitude, device=device_name) sd.wait() info_block("Finished recording") self.arr = recording if len(self.arr.shape) < 2: transpose = self.arr.reshape(-1, 1) self.arr = np.hstack((transpose, transpose)) self.source_block = { 'live recording': "input '{0}'".format(device_name) }
def __init__(self, parent=None, name=None, rel_id=None, mode="load", path=None, rate=None, reltype="Project", children=None, file=None, custom_path=True, **kwargs): super().__init__(rel_id=rel_id, reltype=reltype, name=name, path=path, parent=parent, mode=mode, **kwargs) RelGlobals.set_project_instance(self) if name is None: self.rename() if path is None: p("Select a location for this project (folder will " + \ "be created within selected location to house project files)") self.path = input_dir() os.makedirs(self.get_data_dir(), exist_ok=False) self.children = children if children is not None else [] self.rate = rate self.arr = None if file is not None: self.read_file() if mode == "create": self.save()
def sd_select_device(device_type='in'): """ type: in or out returns [index, name] of device """ info_title("Devices by index ({0} found):".format(len(sd.query_devices()))) for i in str(sd.query_devices()).split('\n'): info_line(i) while True: p("Enter desired device index") device_ind = inpt('int') try: device = sd.query_devices()[device_ind] except IndexError: err_mess("No device with index {0}".format(device_ind)) continue if device_type in ('in', 'input'): if device['max_input_channels'] < 1: err_mess("Device cannot be used as an input") continue elif device_type in ('out', 'output'): if device['max_output_channels'] < 1: err_mess("Device cannot be used as an output") device_name = device['name'] info_block("'{0}' selected".format(device_name)) return [device_ind, device_name]
def process_child(self, child_name=None): """ cat: edit desc: edit a child member of this object args: [child_name: name of child to edit, omit to list children] """ if len(self.children) == 0: err_mess("This Project has no children to process!") if child_name is None: self.list_children() p("Enter the name of the child you wish to edit") child_name = inpt("name") else: child_name = inpt_validate(child_name, "name") try: child_name = autofill(child_name, [i.name for i in self.children]) except AutofillError as e: err_mess("Child name '{0}' not found".format(e.word)) self.process_child() return child = None for i in self.children: if i.name == child_name: if child is not None: raise UnexpectedIssue("Multiple children with name {0}".format(child_name)) child = i process(child) child.save()
def set_length_and_period(self, length=None, period=None): """ cat: edit desc: set the length and period of this rhythm. Give 0 for either argument to keep the current value args: length: length of the rhythm in beats period: standard subdivision of this rhythm, in beats """ if length == 0 and self.length is not None: pass elif length is None: p("Enter a length in beats for this Rhythm") length = inpt('beats') if length != 0: self.length = length elif self.length is None: err_mess( "There is no current value for length, enter a valid value in beats" ) return self.set_length_and_period(None, period) else: self.length = inpt_validate(length, 'beats') if period == 0 and self.period is not None: pass elif period is None: p("Enter the period, or smallest beat that this Rhythm usually lands on", h=True) self.period = inpt('beat') else: self.period = inpt_validate(period, "beat")
def init_mode(self): """ fill in initialization via input get recording by mode call get_help/playback if asked returns rec array """ valid_modes = ( "live Record (R)", "read from File (F)", "relativism project or sampler Object (O)", "Help (H)" ) info_title("Available modes:") info_list(valid_modes) p("Enter desired mode's letter") mode = inpt('letter', allowed='rfh') # Record Mode if mode == "r": self.record_live() # File Mode elif mode == "f": self.read_file() elif mode == "o": raise NotImplementedError # Help elif mode == "h": raise NotImplementedError
def validate_change_type(self, change_type): if change_type is None: return self.change_type_options[0] while change_type not in self.valid_change_types: err_mess("Invalid change type '{0}'".format(change_type)) p("Select one of: {0}".format(", ".join(self.valid_change_types))) change_type = inpt('alphanum') return change_type
def activate(self, act_rhythm=None, act_sample=None): if not isinstance(act_rhythm, Rhythm): p("Choose a rhythm for this active pair") act_rhythm = self.choose('rhythm') if not isinstance(act_sample, Recording): p("Choose a sample for this active pair") act_sample = self.choose('sample') new_active = Active(self, act_rhythm, act_sample) self.active.append(new_active)
def add_child(self, child): """ backend of add_x processes """ self.children.append(child) self.save_metadata() p("Process new {0} '{1}'? [y/n]".format(child.reltype, child.name)) if inpt("yn"): self.process_child(child.name)
def rename(self, name=None): """ cat: meta dev: call this method via super, and implement renaming of files other than datafile and its self.path directory """ old_name = self.name if old_name is not None: old_data_dir = self.get_data_dir() old_datafile_name = self.get_data_filename() if name is None: p("Give this {0} a name".format(self.reltype)) name = inpt("obj") else: name = inpt_validate(name, 'obj') # validate if hasattr(self.parent, "validate_child_name"): if self.parent.validate_child_name(self, name): self.name = name info_block("Named '{0}'".format(name)) else: err_mess("Invalid name") self.rename() else: if Settings.is_debug(): show_error( AttributeError( "Parent obj '{0}' does not have validate_child_name". format(self.parent))) else: try: log_err( "Parent object type {0} has no 'validate_child_name' method" .format(self.parent.reltype)) except AttributeError: log_err( "Parent object '{0}' of '{1}' has no 'validate_child_name' method" .format(self.parent, self)) self.name = name info_block("Named '{0}'".format(name)) # if actual renaming and not an initial naming if old_name is not None: new_data_dir = self.get_data_dir() # rename dir os.rename(old_data_dir, new_data_dir) # rename datafile (which is in newdatadir now) old_datafile = join_path(new_data_dir, old_datafile_name, ext=self.datafile_extension) new_datafile = self.get_datafile_fullpath() os.rename(old_datafile, new_datafile)
def open_proj(self): """ desc: open a project """ section_head("Opening Project") self.list_projects() p("Enter name of project you want to open, or 'see <name>' to see info about that project" ) name_input = inpt("split", "obj") see = False if name_input[0] == "see": try: proj_name = name_input[1] except IndexError: err_mess("No name provided to 'see'") self.open_proj() return see = True else: proj_name = name_input[0] try: proj_path = self.projects[proj_name] except KeyError: try: proj_name = autofill(proj_name, self.projects.keys(), "name") except AutofillError as e: err_mess("No project matches name '{0}'".format(e.word)) return proj_path = self.projects[proj_name] if proj_path == "None": err_mess("Project {0} is missing".format(proj_name)) self.handle_missing_proj(proj_name) return if see: self.see_proj(proj_name, proj_path) self.open_proj() return try: loader = ProjectLoader("{0}.Project".format(proj_name), proj_path, self) self.current_open_proj = loader.get_proj() except FileNotFoundError as e: err_mess("File Not Found: '{0}'".format(e.filename)) self.handle_missing_proj(proj_name) return
def __init__(self, name, val_units, time_units="beats", val_allowed=None, time_allowed=None, start=None, reltype="Controller", rel_id=None, path=None, markers=None, parent=None, mode="create", **kwargs): super().__init__(rel_id=rel_id, reltype=reltype, name=name, path=path, parent=parent, mode=mode, **kwargs) self.start = start self.markers = {} if markers is None else markers # {sample_ind : ControllerMarker} if time_units is None or time_units not in ("beat", "beats", "sec", "secs", "second", "seconds"): p("Select the timing units to be used for this controller, 'b' for beats notation (recommended), 's' for seconds" ) time_mode = inpt("letter", allowed="bs") if time_mode == "b": time_units = "beats" else: time_units = "secs" self.time_units = time_units self.val_units = val_units self.val_allowed = val_allowed self.time_allowed = (Units.beats("0b"), None) desc = get_reldata(self.add, "desc") try: new_desc = desc.format(val_units=self.val_units, time_units=time_units) except: # if format fails, it means add docstring is overridden somewhere, # and will be handled there pass else: add_reldata(self.add, "desc", new_desc)
def add_rhythm(self, new_rhythm=None): if not isinstance(new_rhythm, Rhythm): p("Create new rhythm (C) or Load from another sampler (L)?") source = inpt("letter", allowed="cl") if source == 'c': new_rhythm = Rhythm() else: # load from sampler raise NotImplementedError self.rhythms.append(new_rhythm) self.list_rhythms() p("Process this rhythm? y/n") if inpt("yn"): process(new_rhythm)
def add_sample(self, new_samp=None): """ desc: add a sample from file, project, or another sampler cat: edit """ if isinstance(new_samp, Recording): new_samp.reltype = "Sample" else: new_samp = Recording(reltype="Sample", parent=self) self.smps.append(new_samp) self.list_samples() nl() p("Process this sample? y/n") if inpt("yn"): process(new_samp)
def main_menu(self): while True: section_head("Relativism Main Menu") p("Would you like to edit Projects (P), Settings (S), or get Help (H)?" ) choice = inpt("letter", allowed="psh") try: if choice == "p": self.projects_menu() elif choice == "s": self.settings_menu() else: self.help_menu() except Cancel: pass
def process_child_sample(self, name=None): """ desc: process a sample contained in this group """ while True: if name is None: self.list_samples() p("Enter the name of the Sample to process") name = inpt('name') else: name = inpt_validate(name, 'name') try: self.samples[name] break except KeyError: err_mess("> Sample '{0}' does not exist!".format(name)) process(self.samples[name])
def handle_missing_proj(self, proj_name): """ locate or remove proj, rewrite file """ self.projects[proj_name] = "None" p("Locate '{0}'?".format(proj_name), o="y/n") located = False if inpt("yn"): p("Select the project's folder/directory (should be named '{0}.Project'" .format(proj_name), start="\n") try: directory = input_dir() self.projects[proj_name] = directory located = True except Cancel: pass if not located: p("Failed to locate. Remove this Project from known projects?", o="y/n") if inpt("yn"): del self.projects[proj_name] self.write_proj_file()
def __init__(self, parent, path, act_rhythm, act_sample, reltype=None, name=None, rel_id=None, muted=None): super().__init__(rel_id, reltype, name, path, parent) print("\n* Initializing active pair") self.reltype = "ActivePair" self.rhythm = act_rhythm self.sample = act_sample self.muted = muted self.variability = Active.Variability() if name is None: self.rename() if muted is None: p("Should this active pair begin muted?", o='y/n') self.muted = inpt('yn')
def complete_args(e, method_name, args, obj): """ handle wrong number of args """ if method_name not in str(e): show_error(e) err_mess("Wrong number of arguments: {0}".format(str(e))) info_title("Arguments for {0}: ".format(method_name), indent=4) obj.get_process(method_name)._rel_data.display_args() # too many command_str = "\n " + method_name + " " if "were given" in str(e): p("Enter intended arguments", start=command_str) new_args = inpt('split', 'arg') return [method_name] + new_args # not enough else: if len(args) > 0: command_str += " ".join(args) + " " p("Complete arguments", start=command_str) new_args = inpt('split', 'arg') return [method_name] + args + new_args
def projects_menu(self): """ top menu for opening or creating projects """ while True: section_head("Projects Menu") # choose open type: open or create while self.current_open_proj is None: if bool(self.projects): p("Open existing project (O) or Create new (C)?") open_or_create = inpt("letter", allowed="oc") if open_or_create == 'c': self.create_proj() else: self.open_proj() else: info_block( "No existing projects. Defaulting to create new") self.create_proj() self.process_project()
def maybe_playback(self): p("Playback before analyzing? [y/n]") if inpt("yn"): self.obj.playback()
def set_variability(self, var=None): if var is None: p("Enter the variability (in percentage, 0-100%) of this Rhythm") self.variability = inpt('pcnt') else: self.variability = inpt_validate(var, 'pcnt')