def read(self, event: str, values: ValuesType) -> ty.Optional[Exception]: if not event.startswith(self.key_ns): return None if event != self.key_ns + 'make_loop': return None assert isinstance(values, ty.Dict) with rpr.inside_reaper(): rpr.Project().begin_undo_block() try: ih = ItemsHandler(sr=values[self.key_ns + 'samplerate'] # type:ignore ) lf = LoopFinder(ih) st_ofst, end_ofst = lf.get_loop( corr_wind_sec=values[self.key_ns + 'corr_wind'], # type:ignore slide_wind_sec=values[self.key_ns + 'slide_wind'], # type:ignore corr_treshold=values[self.key_ns + 'corr_max_treshold'], # type:ignore corr_min_treshold=values[self.key_ns + # type:ignore 'corr_min_treshold'], ) except (ItemsError, LoopError) as e: return e ls = LoopSlicer(ih, lf) ls.cut_and_fade( st_ofst, end_ofst, crs_length=values[self.key_ns + 'cross_length'], # type:ignore crs_shape=self.cross_shapes[values[ self.key_ns + 'cross_shape'] # type:ignore ]) rpr.Project().end_undo_block('make loop') return None
def open_project(filepath, in_new_tab=False, make_current_project=True): """ Open project and return it. Parameters ---------- filepath : str in_new_tab : bool, optional Whether to open project in new tab (default=`False`). make_current_project : bool, optional Whether to make opened project current project (has no effect if `in_new_tab` is `False`). Returns ------- project : Project Opened project. """ if not make_current_project: current_project = reapy.Project() if in_new_tab: add_project_tab(make_current_project=True) RPR.Main_openProject(filepath) project = reapy.Project() if not make_current_project: current_project.make_current_project() return project
def get_items_list(track_nr: int = 0) -> ty.List[Item]: try: rpr.Project() except (rpr.errors.DisabledDistAPIError, AttributeError): return [] itemlist = [] for item in filter( lambda item: item.track.index == track_nr, rpr.Project().items ): itemlist.append(Item(item)) return itemlist
def get_projects(): """ Return list of all opened projects. Returns ------- projects : list of Project List of all projects. """ i, projects = 0, [reapy.Project(index=0)] while projects[-1]._is_defined: i += 1 projects.append(reapy.Project(index=i)) projects.pop() return projects
def update(_list, conn_text: TextInput, instance) -> None: # ping(conn_text.text) # is_connected(conn_text.text, 2307) try: conn = rpr.connect(conn_text.text) with rpr.inside_reaper(): print(f'connected to {conn_text.text}') except (rpr.errors.DisabledDistAPIError, AttributeError): print('cannot connect to "{}"'.format(conn_text.text)) # conn_text.background_color = (1, 0, 0) return # else: # conn_text.background_color = (1, 1, 1) Config.set(SECTION, 'IP', conn_text.text) Config.write() print(f'SECTION, "IP" to {conn_text.text}') with rpr.inside_reaper(): items_list = get_items_list() pr = rpr.Project() ts = pr.time_selection ts.start = 0 ts.end = pr.length rpr.perform_action(40420) ts.end = 0 for item in items_list: item_ = item.item pr.add_marker(item_.position + item_.length - .1, name='!1016') print(items_list) update_list(_list, items_list)
def get_all_regions( self) -> ty.List[ty.Tuple[rpr.Region, ty.Dict[str, object]]]: """Get all regions with articulation metadata. Returns ------- List[Tuple[reapy.Region, Dict[str, object]]] """ retvals: ty.List[ty.Tuple[rpr.Region, ty.Dict[str, object]]] = [] with rpr.inside_reaper(): regions, keys = self._all_regions_with_keys() metadatas = pickle.loads(rpr.Project().map( 'get_ext_state', { 'key': keys }, defaults={ 'section': GUI_SECTION, 'pickled': True }, pickled_out=True).encode('latin-1')) for reg, metadata in zip(regions, metadatas): if metadata: retvals.append((reg, metadata)) pprint(retvals) return retvals
def read(self, event: str, values: ValuesFilledType, tokens: ty.List[str]) -> ty.Optional[ty.List[RegionContents]]: wildcards: WildcardDict = {} if wildcard_in_tokens(tokens, Wildcard.articulation): wildcards[Wildcard.articulation] = ty.cast( str, values[self.ns + 'art_name']) if wildcard_in_tokens(tokens, Wildcard.part): wildcards[Wildcard.part] = ty.cast(str, values[self.ns + 'art_part']) if wildcard_in_tokens(tokens, Wildcard.dyn): wildcards[Wildcard.dyn] = values[self.ns + 'dyn'] if wildcard_in_tokens(tokens, Wildcard.sul): # type:ignore wildcards[Wildcard.sul] = values[self.ns + 'sul'] # type:ignore try: if event == self.ns + 'cut': self._cut(values) return None if event == self.ns + 'erase_mdata': self.erase_metadata() if event == self.fade_out.key: regions_w_mdata = self.regions_for_part(values) self.fade_out.fade_all(values, regions_w_mdata) return None if event == self.ns + 'regions': rpr.Project().begin_undo_block() return self.make_regions(values, wildcards, tokens) except ItemsError as e: raise ArtError(e) return None
def release_cut( self, silence_level: float, fade_out_shape: str, fade_out_time: float ) -> ty.Optional[ty.Tuple[ItemsHandler, float]]: mdata_ret = self.get_metadata_safe('left') if not mdata_ret: return None reg, metadata = mdata_ret median = ty.cast(float, metadata['median_rms']) ih = ItemsHandler() point = get_last_rms_value_ms( ih, rms_target=median, # want_marker='cut', ) left, ih = ih.split(ih.get_bounds(count_ts=True)[0] + point) point = get_first_rms_value_ms( ih, silence_level, # want_marker='end', below=True, ) ih, right = ih.split(ih.get_bounds(count_ts=True)[0] + point) ts = rpr.Project().time_selection if ts.start == ts.end: left.delete() right.delete() ih.fade_out(length=fade_out_time, shape=FADE_SHAPES[fade_out_shape]) return ih, median
def make_release_region(self, values: ValuesFilledType, wildcards: WildcardDict, tokens: ty.List[str], want_cut: bool) -> ty.Optional[RegionContents]: rpr.Project().begin_undo_block() if want_cut: retval = self.release_cut( db_to_amplitude( ty.cast(float, values[self.ns + 'silence_treshold'])), ty.cast(str, values[self.ns + 'rel_fade_out_shape']), ty.cast(float, values[self.ns + 'rel_fade_out_time'])) if not retval: return None else: cut_handler, median = retval else: cut_handler = ItemsHandler() ret_meta = self.get_metadata_safe('left') if ret_meta is None: return None reg, metadata = ret_meta median = ty.cast(float, metadata['median_rms']) wildcards.update(self.process_wildcards(tokens)) if wildcard_in_tokens(tokens, Wildcard.part): wildcards[Wildcard.part] = 'rls' root = self.get_root(wildcards, cut_handler) metadata = { 'part': 'release', 'root': root, 'median_rms': float(median) } return (wildcards, *cut_handler.get_bounds(count_ts=False), 'trem release region', metadata)
def get_last_rms_value_ms(items_handler: ItemsHandler, rms_target: float, want_marker: ty.Optional[str] = None) -> float: """Get time in ms of the first rms value above target rms. Parameters ---------- items_handler : ItemsHandler rms_target : float want_marker : ty.Optional[str], optional if None — no marker placed, if string — maker with name is placed items_handler : ItemsHandler """ audio = items_handler.load_audio()[0] rms = lr.feature.rms(y=audio)[0] for index, val in enumerate(reversed(rms)): if val >= rms_target: break if index == len(rms): raise ValueError('no rms above target') # print(index, val) ms = ty.cast(float, lr.frames_to_time(len(rms) - index, items_handler.sr)) if want_marker: rpr.Project().add_marker(items_handler.position + ms, name=want_marker, color=0x00ff00) return ms
def save(selected_track = None): """Save. Saves a preset from selected track in Reaper Parameters ---------- presetname: Name of preset to be used Returns ------- return_chunk: chunk that can be stored in preset manager """ #get references project = reapy.Project() if selected_track == None: selected_track = project.get_selected_track(0) else: selected_track = selected_track logging.debug('Storing preset from: ' + selected_track.name + "|" + project.name) #load configuration from selected track vst_track_chunk = reapy.reascript_api.GetTrackStateChunk(selected_track.id,"",10000000,False) #parse track state chnk with RPP vst_track_chunk_parsed = rpp.loads(vst_track_chunk[2]) #search for VST plugin data preset_chunk = vst_track_chunk_parsed.find("FXCHAIN").find("VST") #create new chunk return_chunk = reaper_preset_chunk() return_chunk.from_element(preset_chunk, vst_track_chunk) init_name = selected_track.name return return_chunk, init_name
def _get_enum_index(self): """ Return region index as needed by RPR.EnumProjectMarkers2. """ return next( i for i, r in enumerate(reapy.Project(self.project_id).regions) if r.index == region.index)
def get_closest_region( self, direction: str) -> ty.Optional[ty.Tuple[rpr.Region, object]]: """Find closest Region with articulation metadata. Parameters ---------- direction : str 'left' or 'right' Returns ------- Optional[Tuple[reapy.Region, object]] region and metadata if any """ pr = rpr.Project() assert direction in ( 'left', 'right'), "direction can be only 'left' or 'right'" if direction == 'left': start = .0 else: start = pr.length closest: ty.Optional[ty.Tuple[rpr.Region, object]] = None for reg in pr.regions: key = f'{REGION_KEY}_{reg.index}_{self.name}' if metadata := pr.get_ext_state(GUI_SECTION, key, pickled=True): if (direction == 'left' and pr.cursor_position > reg.start > start): start = reg.start closest = reg, metadata continue if (direction == 'right' and pr.cursor_position < reg.start < start): start = reg.start closest = reg, metadata continue
def __init__( self, sr: int = 22050, item_handlers: ty.Optional[ty.List[ItemHandler]] = None, split_at_ts: bool = False ) -> None: """ Parameters ---------- sr : int, optional Samplerate item_handlers : Optional[List[ItemHandler]] If None — any items in time selection are Used split_at_ts : bool, optional If True — items are split at time selection """ if split_at_ts: # Item: Split item_handlers at time selection rpr.perform_action(40061) self.sr = sr self.pr = rpr.Project() self.item_handlers = self._get_items( ) if item_handlers is None else item_handlers self._audios: ty.Optional[ty.List[ty.Iterable[float]]] = None self._audio_mono: ty.Optional[ty.List[ty.Iterable[float]]] = None
def project(self): """ Item parent project. :type: reapy.Project """ return reapy.Project(RPR.GetItemProjectContext(self.id))
def stop_recording(self): project = reapy.Project() project.stop() item = next((i for i in project.selected_items if i.track.index == CONSOLE_MIX_TRACK), None) if item: return item.length, item.active_take.source.filename
def test_slave_track(mConnect): host = '192.168.2.1' pr = ss.SlaveProject(id=SLAVE_PROJECT_NAME, ip=host) # with pr.make_current_project(): tr = ss.SlaveInTrack(id='in', project=pr) rpr.Project(MASTER_PROJ_NAME).make_current_project() with tr.connect(): mConnect.assert_called_with(host) assert tr.name == 'in'
def load_vst_chunk(pluginname:str, chunk_string:str, new_track=False): """load. Loading preset into Reaper. """ new_chunk = chunk_string #get ref to project project = reapy.Project() if project.n_selected_tracks == 0 or new_track == True: selected_track = project.add_track(project.n_tracks + 1, "New Track") found, plugin = vsti_list.lookup(pluginname) if found: selected_track.add_fx(plugin.reaper_name) else: selected_track = project.get_selected_track(0) #load chunk from reaper and decode it chunk_from_reaper = save(selected_track)[0] decoded_chunk_from_reaper = chunk_from_reaper.decode_vst_chunk() selected_track.instrument.delete() selected_track.add_fx(chunk_from_reaper.plugin_dll) #convert chunk into list with length 210 length = 210 presetdata = [new_chunk[i:i+length] for i in range(0, len(new_chunk), length)] #insert reaper specific header and footer """This is used to calculate the number of list elements for the header of the fx state chunk it is built up the following: 4 Bytes: VST ID 4 Bytes: Reaper Magix Number 4 Bytes: Number of Inputs 8 Bytes by Number of inputs: Input Mask 4 Bytes: Number of outputs 8 Bytes by Number of outputs: output Mask 4 Bytes: Something I don't know 8 Bytes: seems to be the end 01 00 00 00 00 00 10 00 """ instrument_description_lenth = 4 + 4 + 4 + (selected_track.instrument.n_inputs * 8) + 4 + (selected_track.instrument.n_outputs * 8) + 4 + 8 # calculate number of list elements needed for length. Per element its 210 bytes no_list_elements = int(instrument_description_lenth / 210) + (instrument_description_lenth % 210 > 0) if no_list_elements == 1: progchunk = [decoded_chunk_from_reaper[0]] + presetdata else: progchunk = decoded_chunk_from_reaper[0:no_list_elements-1] + presetdata pgm_name = b'\x00' + "test".encode() + b'\x00\x10\x00\x00\x00' progchunk.append(pgm_name) #load new chunk into reaper chunk_from_reaper.encode_vst_chunk(progchunk) load(chunk_from_reaper, selected_track)
def available() -> bool: available = False project = reapy.Project() if project.n_selected_tracks > 0: selected_track = project.get_selected_track(0) if selected_track.instrument != None: available = True return available
def erase_metadata(self) -> None: with rpr.inside_reaper(): regions, keys = self._all_regions_with_keys() keys = list(keys) pprint(('erasing metadata for keys:', keys)) rpr.Project().map('set_ext_state', {'key': keys}, defaults={ 'section': GUI_SECTION, 'pickled': False, 'value': '', })
def validate_path(path, ext=None, isFolder=False): """ Check if a file or folder exists Parameters ---------- path : str Path to the file or folder (relative to the REAPER project) ext : str || [] The file extension required. If it doesn't match the actual file, it will raise isFolder : bool, optional Search for a folder instead of a file Returns ------- fullPath_str : str The path to the file or folder. """ curProject = reapy.Project() curProjectFolder = Path(curProject.path) fullPath = curProjectFolder / path fullPath_str = fullPath.as_posix() valid = False #check if folder exists if isFolder: valid = fullPath.is_dir() if not valid: raise NotADirectoryError(fullPath_str) #check if file exists else: valid = fullPath.is_file() if not valid: raise IsADirectoryError(fullPath_str) file_ext = fullPath.suffix.lower() if file_ext not in READABLE_FORMATS: raise TypeError( f'"{fullPath_str}" file type "{file_ext}" is not supported by REAPER' ) #validate extension if ext is not None: if type(ext) is str: valid = True if file_ext == ext.lower() else False if type(ext) is list: valid = True if file_ext in ext else False ext = " or ".join(ext) if not valid: raise TypeError( f'"{fullPath_str}" file extension must be "{ext}" ') pass if not valid: raise FileNotFoundError(fullPath_str) return fullPath_str
def load(self): """load. Loading preset into Reaper. """ #load chunk from reaper and decode it chunk_from_reaper = rpre.save() decoded_chunk_from_reaper = chunk_from_reaper.decode_vst_chunk() project = reapy.Project() selected_track = project.get_selected_track(0) selected_track.instrument.delete() selected_track.add_fx(chunk_from_reaper.plugin_dll) #read nks file and get chunk preset_to_convert = nksf.NKSF(self.filepath, True, True, True) new_chunk = preset_to_convert._NKSF__chunks["PCHK"].data #convert chunk into list with length 210 length = 210 presetdata = [ new_chunk[i:i + length] for i in range(0, len(new_chunk), length) ] #insert reaper specific header and footer """This is used to calculate the number of list elements for the header of the fx state chunk it is built up the following: 4 Bytes: VST ID 4 Bytes: Reaper Magix Number 4 Bytes: Number of Inputs 8 Bytes by Number of inputs: Input Mask 4 Bytes: Number of outputs 8 Bytes by Number of outputs: output Mask 4 Bytes: Something I don't know 8 Bytes: seems to be the end 01 00 00 00 00 00 10 00 """ instrument_description_lenth = 4 + 4 + 4 + ( selected_track.instrument.n_inputs * 8) + 4 + (selected_track.instrument.n_outputs * 8) + 4 + 8 # calculate number of list elements needed for length. Per element its 210 bytes no_list_elements = int(instrument_description_lenth / 210) + (instrument_description_lenth % 210 > 0) if no_list_elements == 1: progchunk = [decoded_chunk_from_reaper[0]] + presetdata else: progchunk = decoded_chunk_from_reaper[0:no_list_elements - 1] + presetdata pgm_name = b'\x00' + "test".encode() + b'\x00\x10\x00\x00\x00' progchunk.append(pgm_name) #load new chunk into reaper chunk_from_reaper.encode_vst_chunk(progchunk) rpre.load(chunk_from_reaper)
def test_connect( set_track_midi_out, set_track_midi_in, monkey_connect, MonkeyTrack, MonkeyProject ): track = rpr.Track( id='slave_track_2', project=rpr.Project(id='slave_project_2') ) task = ( [ dict( host='192.168.2.2', track=track, port=dict(idx=2, name='system:midi_capture_3'), ), ], [] ) bck.connect_by_task(task) monkey_connect.assert_called_with('192.168.2.2') track.project.make_current_project.assert_called() set_track_midi_in.assert_called_with(track, 2) track = rpr.Track( id='master_track_1', project=rpr.Project(id='master_project') ) task = ( [], [ dict( host='localhost', track=rpr.Track( id='master_track_1', project=rpr.Project(id='master_project') ), port=dict(idx=3, name='MIDI Output 4'), ), ] ) bck.connect_by_task(task) monkey_connect.assert_called_with('localhost') track.project.make_current_project.assert_called() set_track_midi_out.assert_called_with(track, 3)
def open_project(filepath): """ Open project and return it. Returns ------- project : Project Opened project. """ RPR.Main_openProject(filepath) project = reapy.Project() return project
def fade_all( self, values: ValuesFilledType, regions_w_metadata: ty.Iterable[ty.Tuple[rpr.Region, object]]) -> None: with rpr.undo_block( 'set all {name}s {d_t}s to {time}'.format( name=self.name, d_t=self.direction_text, time=values[self.ns + 'fade_time']), -1): print('fade_all') pr = rpr.Project() pr.select_all_items(False) all_items: ty.Iterator[rpr.Item] = pr.items # type:ignore for_fade: ty.Iterator[rpr.Item] = [] # type:ignore def is_in_bounds(item_bounds: ty.Tuple[float, float], region_bounds: ty.Tuple[float, float]) -> bool: # print(item_bounds, region_bounds) if (item_bounds[0] >= region_bounds[0] and item_bounds[1] <= region_bounds[1]): return True return False def item_for_selection( item: rpr.Item, regions_bounds: ty.Iterable[ty.Tuple[float, float]]) -> bool: start = item.position end = start + item.length if len( list( filter(lambda r_b: is_in_bounds((start, end), r_b), regions_bounds))): return True return False regions_w_metadata = list(regions_w_metadata) pprint(list(reg[0].name for reg in regions_w_metadata)) regs_bounds = list([(reg[0].start, reg[0].end) for reg in regions_w_metadata]) for_fade = filter( lambda item: item_for_selection(item, regs_bounds), all_items) handlers = [ ItemHandler(sr=self.sr, item=item) for item in for_fade ] print(list(handl.item.position for handl in handlers)) ih = ItemsHandler(sr=self.sr, item_handlers=handlers) ih.fade_out( ty.cast(float, values[self.ns + 'fade_time']), FADE_SHAPES[ty.cast(str, values[self.ns + 'fade_shape'])], )
def project(self): """ Return item parent project. Returns ------- project : Project Item parent project. """ project_id = RPR.GetItemProjectContext(self.id) project = reapy.Project(project_id) return project
def add_project_tab(make_current_project=True): """Open new project tab and return it. Parameters ---------- make_current_project : bool Whether to select new project as current project (default=`True`). Returns ------- project : Project New project. """ if not make_current_project: current_project = reapy.Project() project = add_project_tab(make_current_project=True) current_project.make_current_project() return project perform_action(40859) return reapy.Project()
def __init__(self, parent_project=None, index=None, parent_project_id=None): if parent_project_id is None: message = ( "One of `parent_project` or `parent_project_id` must be " "specified.") assert parent_project is not None, message parent_project_id = parent_project.id self.project = reapy.Project(parent_project_id) self.project_id = parent_project_id self.index = index
def render_audio(folder: str, preset_name: str, chunk) -> str: """Render Audio. Renders audio inside Reaper Parameters ---------- folder: folder where to store audio preset_name: name of audio file Returns ------- renderpath: full path to rendered audio """ #open new tab reapy.perform_action(40859) #set renderpath in project file logging.debug('Rendering: ' + preset_name) renderpath = folder + "\\" + preset_name + ".mp3" set_output_path(renderpath) #set name of track,as rendering takes trackname for filename project = reapy.Project() vst_track = project.tracks[0] vst_track.name = preset_name #load preset to track 1 project.select_all_tracks() rp.load(chunk) time.sleep(2) #save project so changes get updated project.save() project.select_all_tracks() #call render action by ID reapy.perform_action(42230) logging.debug("Finished Rendering") #remove VSTI to not get issues when loading next time vst_track.instrument.delete() project.save() #close tab reapy.perform_action(40860) return renderpath
def read( self, event: str, values: ValuesType ) -> ty.Optional[ty.Union[Exception, ty.Tuple[LayoutType, ty.List[BaseArt]]]]: # check if arts can do something with event and need for region try: for art in self.arts_instances: if not isinstance(values, ty.Dict): raise TypeError(f'values are of bad type: {type(values)}') retval = art.read(event, values, self.region_tokens) if retval is not None: for contents in retval: self.make_region(*contents, art) undo_name = contents[3] rpr.Project().end_undo_block(undo_name) return None except ArtError as e: return e if event == self.key_ns + 'rendered_tracks_button': # mark tracks for use in render matrix line = ','.join( [track.GUID for track in rpr.Project().selected_tracks]) if not line: self.rendered_tracks_text.Update('master') else: self.rendered_tracks_text.Update(line) if event == self.key_ns + 'wildcards_spin': # add wildcard from Spin to Input self.region_mask.Update( value=values[self.key_ns + 'region_mask'] + # type:ignore values[self.key_ns + 'wildcards_spin'] # type:ignore ) if event == self.key_ns + 'arts_file': return self.load_arts(values) return None