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 sync_recarm(self) -> None: """Synchronize recarm state of all childs with target childs. Raises ------ SessionError If no childs matched to slave. """ if self.target is None: raise SessionError(f'no target for track {self.name} ({self})') if not self._childs_matched_primary: raise SessionError(f'no matched childs for {self.name} ({self})') childs = self._return_matched_childs(TrackChildsSet.both) t_host = self.s_project.last_ip Targets = te.TypedDict('Targets', {'target': Track, 'recarm': bool}) with rpr.inside_reaper(): targets: ty.Dict[Track.ID, Targets] = {} for child in childs.values(): recarm = child.recarm assert child.target, f'no target for child: {child}' target = child.target if target.id not in targets: targets[target.id] = {'target': target, 'recarm': recarm} elif recarm is True: targets[target.id]['recarm'] = True with rpr.connect(t_host): with rpr.inside_reaper(): # with self.target.make_current_project(): for t_dict in targets.values(): target = t_dict['target'] recarm = t_dict['recarm'] target.recarm = recarm
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 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 get_host_midi_ports( host: ty.Optional[str] = None ) -> ty.Tuple[ty.List[_RMidiPort], ty.List[_RMidiPort]]: """Get all MIDI devices of the particular host. Parameters ---------- host : ty.Optional[str], optional 'localhost' or IPV4 address No Longer Returned ------------------ jack_in_ports: _JMidiPort jack_out_ports _JMidiPort """ if host == 'localhost': host = None ins, outs = [], [] with rpr.connect(host): with rpr.inside_reaper(): for i, port in enumerate(rpr.midi.get_input_names()): ins.append(_RMidiPort(idx=i, name=port)) for i, port in enumerate(rpr.midi.get_output_names()): outs.append(_RMidiPort(idx=i, name=port)) return ins, outs
def get_host_jack_ports( host: ty.Optional[str] = None ) -> ty.Tuple[ty.List[_JMidiPort], ty.List[_JMidiPort]]: """Return all Jack midi ports of Reaper on the specified host. Note ---- reascript 'Levitanus: (session_management) get_system_jack_ports' has to be registered in action editor. Parameters ---------- host : ty.Optional[str], optional 'localhost' or IPV4 address No Longer Returned ------------------ jack_in_ports: _JMidiPort jack_out_ports _JMidiPort """ if host == 'localhost': host = None with rpr.connect(host): with rpr.inside_reaper(): a_id: int = RPR.NamedCommandLookup( '_RSc3a0868bee74abaf333ac661af9a4a27257c37c1') rpr.perform_action(a_id) ports = prs.loads('slave_ports') # print(ports) return ports
def in_measure(self, measure): """ Returns a list of Note contained in the specified measure. Parameters ---------- measure : int Returns ------- notes : List[Note] Notes in the measure. """ notes = [] with reapy.inside_reaper(): measure = int(measure) for n in self: n_measure = n.measure if n_measure < measure: continue elif n_measure == measure: notes.insert(0,n) else: break return notes
def _from_name(name): """Return project with corresponding name. Parameters ---------- name : str Project file name. Including the extension ('.rpp') is optional. Returns ------- Project Raises ------ NameError If no project with the corresponding name is open. """ if not name.lower().endswith('.rpp'): name += '.rpp' with reapy.inside_reaper(): for project in reapy.get_projects(): project_name = project.name[:-4] + '.rpp' if project_name == name: return project raise NameError('"{}" is not currently open.'.format(name))
def load_audio(self, reaper_vol: bool = True) -> ty.Iterable[float]: """Get np.array of Item audiodata in mono. Parameters ---------- reaper_vol : bool, optional Default to True Sohuld audio be normalized to the Reaper item*take level or not Returns ------- ty.Iterable[float] """ with rpr.inside_reaper(): source = self.source filename = source.filename sr = self.sr offset, duration = self._get_item_bounds() loaded = lr.load( filename, sr=sr, mono=True, offset=offset, duration=duration, )[0] if reaper_vol: loaded *= self.vol return loaded # type:ignore
def glue_all_items_on_track(project: R_project, track: R_track) -> None: with reapy.inside_reaper(): for i, item in enumerate(track.items): #make unique selection if it's the first item in the loop make_unique = i == 0 project.select_item(item, makeUnique=make_unique) #glue items project.perform_action(41588)
def visible_fx(self): """ Visible FX in FX chain if any, else None. :type: FX or NoneType """ with reapy.inside_reaper(): return self.fxs[RPR.TrackFX_GetChainVisible(self.id)]
def __enter__(self) -> Track: pr = self.track.s_project self.connect = rpr.connect(pr.last_ip) self.connect.__enter__() self.ir = rpr.inside_reaper() self.ir.__enter__() self.curr_proj = pr.make_current_project() self.curr_proj.__enter__() return self.track
def cut_and_fade( self, st_ofst: float, end_ofst: float, crs_length: float = .1, crs_shape: int = 0, ) -> rpr.Region: """Cut and fade items to make loop. Note ---- units in seconds Parameters ---------- st_ofst : float Start offset from time selection end_ofst : float End offset from time selection crs_length : float, optional Crossfade length Returns ------- reapy.Region loop region """ with rpr.inside_reaper(): self._pr.begin_undo_block() region_ofst = 0.1 start, duration = self._handler.item_handlers[ 0].get_item_bounds_within_ts() main_part, tail = self._handler.split(start + duration - end_ofst) tail.position += end_ofst + region_ofst tail.start_offset += end_ofst tail.length -= end_ofst + region_ofst end_part = main_part.make_copy(main_part.position) del_part, end_part = end_part.split(start + st_ofst) del_part.delete() end_part.position += duration - st_ofst - end_ofst end_part.length = crs_length + region_ofst main_part.length += crs_length reg = self._pr.add_region(start + st_ofst + end_part.length, end_part.position + end_part.length, color=0xff0000, name='#') self._pr.loop_points = (start + st_ofst + end_part.length, end_part.position + end_part.length) main_part.fade_out(crs_length, crs_shape) end_part.fade_in(crs_length, crs_shape) self._pr.end_undo_block('cut and fade loop') return reg
def __setitem__(self, i, value): with reapy.inside_reaper(): if isinstance(i, str): i = self._get_param_index(i) n_params = len(self) if i >= n_params: raise IndexError("{} has only {} params".format( self.parent_fx, n_params)) i = i % n_params # Allows for negative values self.functions["SetParam"](self.parent_id, self.fx_index, i, value)
def __getitem__(self, i): with reapy.inside_reaper(): if isinstance(i, str): i = self._get_fx_index(name=i) n_fxs = self.parent.n_fxs if i >= n_fxs: raise IndexError("{} has only {} fxs".format(self.parent, n_fxs)) i = i % n_fxs # Allows for negative values fx = FX(self.parent, i) return fx
def combine_items_from_track_group(group_name: str, tracks: R_tracks) -> R_tracks: unused_tracks = [] with reapy.inside_reaper(): if len(tracks): main_track = tracks.pop() main_track.name = group_name for t in tracks: t.items[0].track = main_track unused_tracks.append(t) return unused_tracks
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 get_nested_tracks(project: R_project, track: R_track) -> R_tracks: with reapy.inside_reaper(): child_tracks = [] n_track_left = project.n_tracks - track.index last_track_id = n_track_left + track.index new_track_id = track.index for track_id in range(last_track_id, new_track_id + 1, -1): track = project.tracks[track_id - 1] if new_track_id == track.parent_track.index: child_tracks.insert(0, track) return child_tracks
def cut_and_fade_deprecated(self, st_ofst: float, end_ofst: float, crs_length: float = .1) -> rpr.Region: """Cut and fade items to make loop. Note ---- units in seconds Parameters ---------- st_ofst : float Start offset from time selection end_ofst : float End offset from time selection crs_length : float, optional Crossfade length Returns ------- reapy.Region loop region """ with rpr.inside_reaper(): self._pr.begin_undo_block() region_ofst = 0.1 start, duration = self._handler.item_handlers[ 0].get_item_bounds_within_ts() itms_hdlr_l, itms_hdlr_r = self._handler.split(start + st_ofst) _, del_part = itms_hdlr_r.split(start + duration - end_ofst) del_part.delete() itms_hdlr_end = itms_hdlr_r.make_copy( itms_hdlr_r.position + itms_hdlr_r.length, length=crs_length + region_ofst) itms_hdlr_r.length += crs_length reg = self._pr.add_region( itms_hdlr_r.position + itms_hdlr_end.length, itms_hdlr_end.position + itms_hdlr_end.length, color=0xff0000, name='#') self._pr.loop_points = (itms_hdlr_r.position + itms_hdlr_end.length, itms_hdlr_end.position + itms_hdlr_end.length) itms_hdlr_r.fade_out(crs_length) itms_hdlr_end.fade_in(crs_length) self._pr.end_undo_block('cut and fade loop') return reg
def make_region_from_selected_items_or_ts( name: str, tracks_in_matrix: ty.Optional[ty.List[rpr.Track]] = None ) -> rpr.Region: with rpr.inside_reaper(): pr = rpr.Project() ts = pr.time_selection # if no time selection start, end = (ts.start, ts.end) if (start, end) == (0, 0): start, end = ItemsHandler().get_bounds(check_for_indentity=False) region = pr.add_region(start, end, name=name) if tracks_in_matrix: region.add_rendered_tracks(tracks_in_matrix) return region
def update_list(layout: GridLayout, itemlist: ty.List[Item]): layout.clear_widgets() try: rpr.Project() except (rpr.errors.DisabledDistAPIError, AttributeError): print('cannot connect') return with rpr.inside_reaper(): for item in itemlist: btn = Button( text=item.name, size_hint_y=None, height=Window.height / 10 ) btn.bind( on_press=lambda instance, time=item.time: on_track(time, instance) ) layout.add_widget(btn)
def get_track_by_CC_pitch(pitch_list: List[int], tracks: R_tracks) -> R_tracks: with reapy.inside_reaper(): pitch_tracks = [] #loop tracks for t in tracks: #skip if there's no item on the track if not len(t.items): continue CC_notes = t.items[0].takes[0].notes #skip if there's no note on the track if not len(CC_notes): continue #get pitch of the first note CC_note = t.items[0].takes[0].notes[0].pitch if CC_note in pitch_list: pitch_tracks.append(t) return pitch_tracks
def buses_unpacked(self) -> bool: """Whether MIDI BUSes are unpacked from one channel or not. Note ---- currently it's made by the custom JSFX Returns ------- bool """ with rpr.inside_reaper(): fxlist = self.fxs try: fx = fxlist[self._BUS_FX_NAME] par = fx.params[self._BUS_PAR_IDX] except KeyError: return False return par == 1 return False
def load_audio(self, mono: bool = True, reaper_vol: bool = True) -> ty.List[ty.Iterable[float]]: """Load audio of items to the np.array. Parameters ---------- mono : bool, optional Default to True reaper_vol : bool, optional Default to True Sohuld audio be normalized to the Reaper item*take level or not Returns ------- ty.Iterable[ty.Iterable[float]] Raises ------ ItemsError If items are not identical """ if mono and self._audio_mono is not None: return self._audio_mono if not mono and self._audios is not None: return self._audios with rpr.inside_reaper(): items_handler = self if not self.are_bounds_identical: items_handler = self.get_longest_items_on_each_track() if not items_handler.are_bounds_identical: raise ItemsError('bounds of items are not identical') audios: ty.List[ty.Iterable[float]] = [] for ih in items_handler.item_handlers: y = ih.load_audio(reaper_vol=reaper_vol) audios.append(y) if mono: self._audio_mono = [np.sum(audios, 0)] return self._audio_mono self._audios = np.column_stack(audios) return self._audios # type:ignore
def get_module_status(self): if not self.is_connected: return DISCONNECTED with reapy.inside_reaper(): project = reapy.Project() if project.name == '': return CONNECTED channels = [] # levels = [] for i in range(RAW_TRACK_COUNT): command = ACTION_TOGGLE_CHANNEL_1 + (FIRST_RAW_TRACK + i) * COMMANDS_PER_CHANNEL channels.append( reapy.reascript_api.GetToggleCommandState(command)) # track_id = project.tracks[i + FIRST_RAW_TRACK].id # levels.append(reapy.reascript_api.Track_GetPeakHoldDB(track_id, 0, False)) # reapy.reascript_api.Track_GetPeakHoldDB(track_id, 0, True) metronome = reapy.reascript_api.GetToggleCommandState(40364) state = 'ready' position = 0 if project.is_recording: state = 'recording' position = project.play_position - project.cursor_position elif project.is_playing: state = 'playing' position = project.play_position - project.time_selection.start return { 'position': position, 'state': state, 'channels': channels, # 'levels': levels, 'metronome': metronome }
def detect_onsets( items_handler: ItemsHandler, pre_max: float, wait: float, fmin: ty.Optional[int] = None, pre_avg: ty.Optional[float] = None, post_max: ty.Optional[float] = None, post_avg: ty.Optional[float] = None, delta: float = 1.0, onset_markers: str = '', backtrack_markers: str = '', units: LengthUnit = LengthUnit.ms, ) -> ty.Tuple[ty.List[float], ty.List[float], ty.List[float]]: """Detect onsets and place markers if needed. Parameters ---------- items_handler : ItemsHandler pre_max : float time to seek peacks before onset wait : float time to skip after detected onset fmin : ty.Optional[int], optional minimum frequency if filtering is needed pre_avg : ty.Optional[float], optional time to seek mean before offset (if None — pre_max is used) post_max : ty.Optional[float], optional time so seek peak after offset (if None — wait or post_avg are used) post_avg : ty.Optional[float], optional time to seek mean after onset (if None — wait of post_max are used) delta : float, optional threshold offset for mean (ambiguos parameter) onset_markers : str, optional If not null string — markers with the same name will be placed backtrack_markers : str, optional If not null string — markers with the same name will be placed units : LengthUnit, optional length units for onsets and backtrack return (ms are default) Returns ------- Tuple[List[float], List[float], List[float]] onsets, backtracks, onset_envelope(in frames) """ with rpr.inside_reaper(): audio = items_handler.load_audio()[0] sr = items_handler.sr if fmin: Spc = lr.feature.mfcc(audio, sr, fmin=fmin) onset_envelope = lr.onset.onset_strength(sr, S=Spc) else: onset_envelope = lr.onset.onset_strength(audio, sr) if post_avg is None and post_max is None: post_max, post_avg = wait, wait if post_avg is None: post_avg = post_max if post_max is None: post_max = post_avg if pre_avg is None: pre_avg = pre_max (pre_max, wait, pre_avg, post_max, post_avg) = lr.time_to_frames( (pre_max, wait, pre_avg, post_max, post_avg), sr) onsets = lr.util.peak_pick( onset_envelope, pre_max, post_max, pre_avg, post_avg, delta, wait, ) backtrack = lr.onset.onset_backtrack(onsets, onset_envelope) if backtrack_markers: for bck in lr.frames_to_time(backtrack, sr=sr): pos = items_handler.get_bounds(count_ts=True)[0] + bck rpr.Project().add_marker(pos, name=backtrack_markers) if onset_markers: for onst in lr.frames_to_time(onsets, sr=sr): pos = items_handler.get_bounds(count_ts=True)[0] + onst rpr.Project().add_marker(pos, name=onset_markers) if units == LengthUnit.ms: onsets, backtrack = (lr.frames_to_time(onsets, sr), lr.frames_to_time(backtrack, sr)) if units == LengthUnit.samples: onsets, backtrack = (lr.frames_to_samples(onsets, sr), lr.frames_to_samples(backtrack, sr)) return onsets, backtrack, onset_envelope
def __enter__(self) -> Host: self._connect = rpr.connect(self.ip) self._connect.__enter__() self._ir = rpr.inside_reaper() self._ir.__enter__() return self
def midi_note_names(self): with reapy.inside_reaper(): names = [ RPR.GetTrackMIDINoteName(self.id, i, 0) for i in range(128) ] return names
def __getitem__(self, key): with reapy.inside_reaper(): if key >= len(self): raise IndexError return self._elements_class(self.parent, key)
print(hostname, 'is up!') else: print(hostname, 'is down!') if __name__ == '__main__': is_connected('8.8.8.8') is_connected('ya.ru') Config.adddefaultsection(SECTION) Config.setdefault(SECTION, 'IP', "192.168.0.1") grid = GridLayout(cols=1, spacing=20) conn_text = TextInput(text=Config.get(SECTION, 'IP')) 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)) itemlist = get_items_list() print(*(item.name for item in itemlist), sep=' | ') scroll, layout = get_layouts() update_list(layout, itemlist) connection_grid = GridLayout(cols=2, spacing=10, size_hint_y=.5) upd_btn = Button(text='update') upd_btn.bind(on_press=lambda instance: update(layout, conn_text, instance)) connection_grid.add_widget(conn_text) connection_grid.add_widget(upd_btn) grid.add_widget(connection_grid)