ReaScripts (скрипты для Reaper) - Учимся создавать!!!

Тема в разделе "Reaper", создана пользователем Archchie, 29 дек 2017.

  1. incubator

    incubator Active Member

    Регистрация:
    24 янв 2007
    Сообщения:
    356
    Симпатии:
    138
    Адрес:
    Spb
    @Archchie, Спасибо большое всегда помогаете мне!
     
  2. Archchie

    Archchie Active Member

    Регистрация:
    24 окт 2017
    Сообщения:
    282
    Симпатии:
    125
    Пол:
    Мужской
    Ни это ?
    reaper.ApplyNudge( project, nudgeflag, nudgewhat, nudgeunits, value, reverse, copies )
    -- только эта функция работает для всех выделенных айтемов

    или
    обновил 2-й раз 16.05.18.
    Предыдущая не правильно работала с растянутым play-rate:
    PHP:

    правый край

    reaper
    .SetMediaItemLengthitemlengthtrue )

    ---------------------------------------------------------

    левый край

      local 
    function SetMediaItemLeftTrim(value,item)
        
    local Pos reaper.GetMediaItemInfo_Valueitem'D_POSITION' )
        
    local len reaper.GetMediaItemInfo_Valueitem'D_LENGTH' )
        
    local take reaper.GetActiveTakeitem )
        
    local playrate reaper.GetMediaItemTakeInfo_Valuetake'D_PLAYRATE')
        
    local offset reaper.GetMediaItemTakeInfo_Valuetake'D_STARTOFFS' )
        if 
    len value then
          reaper
    .SetMediaItemLengthitem,  value true )
          
    reaper.SetMediaItemPositionitem,(len-value)+ Postrue )
          
    reaper.SetMediaItemTakeInfo_Valuetake'D_STARTOFFS',offset + (len-value)*playrate )
        else
          if 
    Pos < (value-lenthen return end
          reaper
    .SetMediaItemLengthitemvalue true )
          
    reaper.SetMediaItemPositionitemPos-(value-len), true )
          
    reaper.SetMediaItemTakeInfo_Valuetake'D_STARTOFFS',offset + (len-value)*playrate )
        
    end
      end

     
    Последнее редактирование: 10 окт 2018 в 19:37
    Aleksandr Oleynik нравится это.
  3. Aleksandr Oleynik

    Aleksandr Oleynik Well-Known Member

    Регистрация:
    16 янв 2007
    Сообщения:
    16.620
    Симпатии:
    8.959
    Пол:
    Мужской
    Адрес:
    Киев
    @Archchie, спасибо огромное!
    Очень не хотелось разбираться с офсетом, вы выручили :)
     
  4. PianoIst

    PianoIst Well-Known Member

    Регистрация:
    19 май 2010
    Сообщения:
    2.763
    Симпатии:
    2.260
    Пол:
    Мужской
    Род занятий:
    Аранжировка, солист-пинанист
    Адрес:
    Новосибирск
    А никто не знает, где поискать алгоритм для поиска loop points?
    Я тут пытаюсь немного автоматизировать процес нарезки сэмплов. И самый ад, конечно, происходит при нарезке сустейнов.
    В общем и целом концепция очень простая:
    • вычленяем "чистый" фрагмент сустейна (это лучше делать "человеком")
    • режем в произвольной точке
    • меняем местами начало и конец
    • и делаем так.
    2018-05-25_03-24-40.png

    Но если сделать "просто" так, в лучшем случае будет тембральный, и, наверняка, динамический контраст.
    А в худшем, и это не редкость, а, скорее, очень частая напасть, попадаешь в противофазу, да так, что найти фазу, в силу "живости" источника - та еще задачка.

    Вот идеально с этой задачей, КМК, справляется sample robot. Но он, к сожалению, работает только на стерео, что в нашу много-микрофонную бытность не комильфо.
    Гуглинг засран простыми howto для обыкновенных лупов (драмсы, гитарные рифы), а найти что-то стоящее по закольцовке сэмплов я чет не могу.

    Набрел на такую тему https://forum.cockos.com/showthread.php?t=180087

    Но, во-первых, это слишком жирно, учитывая 6+ исходных треков для каждого сэмпла. А во-вторых, мне кажется, кардинально от моего способа это дело не отличается, все равно точно также можно нарваться на противофазу.
    Есть идеи?
     
  5. PianoIst

    PianoIst Well-Known Member

    Регистрация:
    19 май 2010
    Сообщения:
    2.763
    Симпатии:
    2.260
    Пол:
    Мужской
    Род занятий:
    Аранжировка, солист-пинанист
    Адрес:
    Новосибирск
    попробовал начать с самого очевидного: поиска одинаковых сэмплов в тейке:
    Код:
    def find_loop(take, search_area):
        accessor = RPR_CreateTakeAudioAccessor(take)
        start = RPR_GetAudioAccessorStartTime(accessor)
        end = RPR_GetAudioAccessorEndTime(accessor)
        sample_length_sec = RPR_parse_timestr_len(search_area, 0, 4)
        end -= sample_length_sec
    
        ret = int()
        start_buf = list([0] * 2 * search_area)
        end_buf = list([0] * 2 * search_area)
    
        (ret, start_buf) = RPR_GetAudioAccessorSamples(
            accessor, 44100, 2, start, search_area, start_buf)
        if ret == 1:
            pr('is audio\n')
    
        if ret == 0:
            pr('is not audio\n')
        if ret == -1:
            pr('error\n')
        (ret, end_buf) = RPR_GetAudioAccessorSamples(
            accessor, 44100, 2, end, search_area, end_buf)
    
        # pr('start buffer: %s \n' % start_buf)
    
        points = get_loop_points(start_buf, end_buf, search_area, 5)
    
        start += RPR_parse_timestr_len(points[0], 0, 4)
        end += RPR_parse_timestr_len(points[1], 0, 4)
        pr('start: %s, end: %s\n' % (start, end))
        return (start, end)
    
    
    def get_loop_points(start_buf, end_buf, search_area, presigion):
        starts = list()
        ends = list()
        for idx in range(search_area // 2):
            starts.append(sample_from_index(start_buf, idx, presigion))
            ends.append(sample_from_index(end_buf, idx, presigion))
    
        # pr('\n')
        # pr('starts: %s' % starts)
        # pr('\n')
        # pr('ends: %s' % ends)
        # pr('\n')
        for idx, val in enumerate(starts):
            if val in ends:
                pr(val)
                start = idx
                end = ends.index(val)
                pr('\n')
                return (start, end)
    
    
    def sample_from_index(buf, idx, presigion):
        sample1 = buf[idx * 2]
        sample2 = buf[idx * 2 + 1]
        sample1 = int(float(sample1) * 100000) / 100000
        sample2 = int(float(sample2) * 100000) / 100000
        return (sample1, sample2)
    
    И вывод в консоль вполне себе обнадеживающий:
    Код:
    is audio
    (0.00414, -0.00149)
    start: 0.004854166666666666, end: 1.0648958333333367
    (0.004854166666666666, 1.0648958333333367)
    Но вот когда я пытаюсь обрезать руками итем по полученным временным отметкам, получается, что сэмплы-то нифига не одинаковые...
    2018-05-25_08-58-10.png
    --- добавлено 25 май 2018 ---
    вообще идея следющая: найти в указанном тайи-селекшн две точки, в которых сэмплы максимально близки по значению друг с другом, а потом дополнить условие окном, в котором сравниваются близлешайшие отрезки в одном направлении с меньшим разрешением (допустимой погрешностью)
     
  6. Aleksandr Oleynik

    Aleksandr Oleynik Well-Known Member

    Регистрация:
    16 янв 2007
    Сообщения:
    16.620
    Симпатии:
    8.959
    Пол:
    Мужской
    Адрес:
    Киев
    @PianoIst, честно говоря задачу до конца так и не понял :(
    Пиковые, или любые другие значения уровней, ни как не характеризуют одинаковость сэмплов. Тебе прийдется сравнивать посэмплово.
     
  7. PianoIst

    PianoIst Well-Known Member

    Регистрация:
    19 май 2010
    Сообщения:
    2.763
    Симпатии:
    2.260
    Пол:
    Мужской
    Род занятий:
    Аранжировка, солист-пинанист
    Адрес:
    Новосибирск
    @Aleksandr Oleynik, задача - сделать бесшовный сустейн луп. Грубо говоря (и это самый проблемный вариант), синус режем, перемещаем как в посте выше, и получаем каку.
    Сэмпл я имел ввиду именно точки уровней, которые 44100 в секунду. Вот мне и интересно, почему когда я нахожу время, на котором они одинаковые (дальше уже можно будет усложнять задачу матчинга), и я верю, что нахожу, но режу неправильно(
    --- добавлено 25 май 2018 ---
    Как вообще посэмплово по аудио-ацессору передвигаться? Может я в секунды плохо конвертирую?
     
  8. PianoIst

    PianoIst Well-Known Member

    Регистрация:
    19 май 2010
    Сообщения:
    2.763
    Симпатии:
    2.260
    Пол:
    Мужской
    Род занятий:
    Аранжировка, солист-пинанист
    Адрес:
    Новосибирск
    так, судя по всему, точки я нахожу правильно, но что-то происходит при конвертации в секунды. Потому что стоит чуть-чуть подвинуть конец вперед, и все срастается
     
  9. Aleksandr Oleynik

    Aleksandr Oleynik Well-Known Member

    Регистрация:
    16 янв 2007
    Сообщения:
    16.620
    Симпатии:
    8.959
    Пол:
    Мужской
    Адрес:
    Киев
    @PianoIst, тебе Женя нужен - он на этом собаку съел :)
    AudioAccessor - его любимая функция!
     
    Последнее редактирование: 25 май 2018
    PianoIst нравится это.
  10. PianoIst

    PianoIst Well-Known Member

    Регистрация:
    19 май 2010
    Сообщения:
    2.763
    Симпатии:
    2.260
    Пол:
    Мужской
    Род занятий:
    Аранжировка, солист-пинанист
    Адрес:
    Новосибирск
    от, видимо, где собака порылась
    https://github.com/ReaTeam/ReaScripts-Templates/blob/master/Audio/Working with audio samples.lua
    Код:
    -- Loop through the audio, one block at a time
        local starttime_sec = range_start
        for cur_block = 0, n_blocks do
    
            -- The last iteration will almost never be a full block
            if cur_block == n_blocks then block_size = extra_spls end
           
            samplebuffer.clear()   
           
            -- Loads 'samplebuffer' with the next block
    GetSamples(audio, samplerate, n_channels, starttime_sec, block_size, samplebuffer)
    объясните мне строчку
    Код:
     -- The last iteration will almost never be a full block
            if cur_block == n_blocks then block_size = extra_spls end
    что такое then?)
    Получается, что "дешево" взять конкретную точку с конца нельзя, если я правильно понял. Надо, как с доступом к файлу - читать все
    --- добавлено 25 май 2018 ---
    а, не, вотоно:)
    Код:
    starttime_sec = starttime_sec + ((block_size * n_channels) / samplerate)
    Кароч, я где-то что-то просто пропустил, ща попробуем этот способ)
     
  11. PianoIst

    PianoIst Well-Known Member

    Регистрация:
    19 май 2010
    Сообщения:
    2.763
    Симпатии:
    2.260
    Пол:
    Мужской
    Род занятий:
    Аранжировка, солист-пинанист
    Адрес:
    Новосибирск
    о, чего словил:
    Код:
    ---------------------------
    Microsoft Visual C++ Runtime Library
    ---------------------------
    Runtime Error!
    
    Program: C:\Program Files\REAPER (x64)\reaper.exe
    
    R6025
    
    - pure virtual function call
    
    
    ---------------------------
    ОК 
    ---------------------------
    
    Делал объектный вариант Жениного скрипта для работы с сэмплами:

    Код:
    def find_loop(item, search_samples, precision):
        item = Item(item, block_size=search_samples)
        samples = item.samples.get_all()
    
    
    class Item:
    
        def __init__(self, item, use_ts=True, block_size=1024):
            self.item = item
            self.take = RPR_GetActiveTake(self.item)
            self.playrate = RPR_GetMediaItemTakeInfo_Value(self.take, "D_PLAYRATE")
            self.PCM_source = RPR_GetMediaItemTake_Source(self.take)
            self.samplerate = RPR_GetMediaSourceSampleRate(self.PCM_source)
    
            self._start = RPR_GetMediaItemInfo_Value(self.item, "D_POSITION")
            self._len = RPR_GetMediaItemInfo_Value(self.item, "D_LENGTH")
            self._get_range(use_ts)
    
            self.samples = ItemSamples(self, block_size)
    
        def _get_range(self, use_ts):
            sel_start = sel_end = None
            if use_ts:
                loop_tr = RPR_GetSet_LoopTimeRange(0, 0, 0, 0, 0)
                sel_start, sel_end = loop_tr[2], loop_tr[3]
            if not sel_start or sel_end == sel_start:
                sel_start = self._start
                sel_end = self._start + self._len
    
            sel_start = max(sel_start, self._start)
            sel_end = min(sel_end, self._start + self._len)
            if sel_end - sel_start < 0:
                RPR_ShowMessageBox(
                    "Time selection out of item range!", "Note", 0)
    
            self.open()
    
            self.start = (sel_start - self._start) * self.playrate
            self.len = (sel_end - sel_start) * self.playrate
            self.end = self._start + self._len
            self.len_spls = int(self._len * self.samplerate)
    
            self.close()
    
        def open(self):
            if self.playrate != 1:
                RPR_SetMediaItemTakeInfo_Value(self.take, "D_PLAYRATE", 1)
                RPR_SetMediaItemInfo_Value(
                    self.item, "D_LENGTH", self._len * self.playrate)
    
        def close(self):
            if self.playrate != 1:
                RPR_SetMediaItemTakeInfo_Value(
                    self.take, "D_PLAYRATE", self.playrate)
                RPR_SetMediaItemInfo_Value(self.item, "D_LENGTH", self._len)
    
            RPR_UpdateTimeline()
    
    
    class ItemSamples:
    
        def __init__(self, item, block_size=1024):
            self.channels_amount = RPR_GetMediaSourceNumChannels(item.PCM_source)
            self.audio = RPR_CreateTakeAudioAccessor(item.take)
    
            self._block_size = block_size
            self._n_blocks = int(item.len_spls / block_size)
            self._extra_spls = item.len_spls - block_size * self._n_blocks
            self._buf_preset = list([0] * block_size * self.channels_amount)
            self.item = item
    
        def get_start_time(self, sample_offset):
            self.item.open()
            if sample_offset < 0:
                retval = self.item.end - (
                    (sample_offset * self.channels_amount) / self.item.samplerate)
                self.item.close()
                return retval
            retval = self.item.start + (
                (sample_offset * self.channels_amount) / self.item.samplerate)
            self.item.close()
            return retval
    
        def get_block(self, block=False, sample=False):
            if block is False and sample is False:
                raise Exception('block or sample offset has to be specified.\
                    block = %s, sample = %s' % (block, sample))
            if block is not False:
                block_size = self._block_size
                start = self.get_start_time(block * block_size)
                start_in_spls = block * block_size
                if block == self._n_blocks:
                    block_size = self._extra_spls
            if sample is not False:
                if block:
                    raise Exception(
                        'only block, or sample can be assigned at time')
                if self.item.end - sample < block_size:
                    block_size = self.item.end - sample
                start = self.get_start_time(sample)
                start_in_spls = sample
    
            self.item.open()
            samplebuffer = self._buf_preset
    
            RPR_GetAudioAccessorSamples(self.audio, self.item.samplerate,
                                        self.channels_amount, start,
                                        block_size, samplebuffer)
    
            samples = list()
            for sample in range(block_size):
                channels = list()
                for channel in range(self.channels_amount):
                    pos = sample * self.channels_amount + channel
                    channels.append(samplebuffer[pos])
                idx = start_in_spls + sample
                samples.append(Sample(idx, channels))
    
            RPR_DestroyAudioAccessor(self.audio)
            self.item.close()
            return samples
    
        def get_all(self):
            samples = list()
            for block in range(self._n_blocks):
                samples.append(self.get_block(block=block))
            return samples
    
    
    class Sample:
    
        def __init__(self, idx, channels):
            self.idx = idx
            self.channels_amount = len(channels)
            self.channels = tuple(channels)
    
    причем, простой копипаст с "переводом" на питон работает отлично. Что я тут могу не оттуда запускать?

    P.S. ацессор удалял при каждой итерации, а добавлял в конструкторе...
     
    Последнее редактирование: 25 май 2018
  12. PianoIst

    PianoIst Well-Known Member

    Регистрация:
    19 май 2010
    Сообщения:
    2.763
    Симпатии:
    2.260
    Пол:
    Мужской
    Род занятий:
    Аранжировка, солист-пинанист
    Адрес:
    Новосибирск
    так, все, кастую в тему @EUGEN27771 !

    я стопудово нахожу правильные индексы одинаковых сэмплов, во:
    2018-05-26_02-55-23.png

    если сдвинуть на ~10 сэмплов, получается вот:
    2018-05-26_02-57-34.png


    Я просто время как-то не так конвертирую (вырезка из кода):
    Код:
    def find_loop(item, search_samples, precision):
        item = Item(item, block_size=search_samples)
        pr('length in samples: %s' % item.len_spls)
        samples = item.samples
    
        start_buf = samples.get_block(block=0)
        end_buf = samples.get_block(sample=-search_samples)
    
        for sample in range(search_samples):
            start_spl = start_buf[sample]
            end_spl = end_buf[sample]
            loop = CheckLoop(start_spl, end_spl, precision)
            if loop.check is True:
                time1 = item.get_time_from_sample_idx(loop.idx[0])
                time2 = item.get_time_from_sample_idx(loop.idx[1])
                pr('values %s, %s' % (loop.start_spl_val, loop.end_spl_val))
                pr('time:')
                return time1, time2
    class Item:
    
        def __init__(self, item, use_ts=True, block_size=1024):
            self.item = item
            self.take = RPR_GetActiveTake(self.item)
            self.playrate = RPR_GetMediaItemTakeInfo_Value(self.take, "D_PLAYRATE")
            self.PCM_source = RPR_GetMediaItemTake_Source(self.take)
            self.samplerate = RPR_GetMediaSourceSampleRate(self.PCM_source)
    
            self._start = RPR_GetMediaItemInfo_Value(self.item, "D_POSITION")
            self._len = RPR_GetMediaItemInfo_Value(self.item, "D_LENGTH")
            self._get_range(use_ts)
    
            self.samples = ItemSamples(self, block_size)
    
    def get_time_from_sample_idx(self, idx):
            return self.start + idx / self.samplerate * self.playrate
    --- добавлено 25 май 2018 ---
    но вообще меня беспокоит, что на таком длинном фрагменте он начинает находить лупы только при растягивании окна поиска почти на весь отрезок...
    В прошлом варианте находил больше одинаковых сэмплов.

    P.S. не, время, вроде правильно находит. Значит заматчить не может с нужной точностью...
    Вообще, сэмплы эти орагнные, оказывается, очень противные. Ни zero-ceossing не работает, ни endlesswav...
    Надо какой-то убер-способ придумывать.
    Попробую ща поискать на чем-то еще
     
    Последнее редактирование: 25 май 2018
    Slick нравится это.
  13. Slick

    Slick IDDQD

    Регистрация:
    13 май 2008
    Сообщения:
    1.929
    Симпатии:
    844
    Род занятий:
    Аранжировщик, Саунд Дизайнер
    Адрес:
    Москва, Апрелевка
    @PianoIst, крутую штуку делаешь. постоянно приходится искать эти точки, чтоб без швов получалось...
     
  14. PianoIst

    PianoIst Well-Known Member

    Регистрация:
    19 май 2010
    Сообщения:
    2.763
    Симпатии:
    2.260
    Пол:
    Мужской
    Род занятий:
    Аранжировка, солист-пинанист
    Адрес:
    Новосибирск
    @Slick, да чет нихрена не выходит...
    увеличиваю точность попадания по стыкам (и это еще без учета формы волны), и перестает детектироваь вообще.
    А с точностью до +- километр и смысла нет...
     
  15. @Michael

    @Michael Well-Known Member

    Регистрация:
    14 дек 2010
    Сообщения:
    821
    Симпатии:
    1.238
    Пол:
    Мужской
    Адрес:
    Орёл / Москва
    Кинь RPP с кусками, склеенными руками.
     
    Последнее редактирование: 26 май 2018
    PianoIst нравится это.
  16. PianoIst

    PianoIst Well-Known Member

    Регистрация:
    19 май 2010
    Сообщения:
    2.763
    Симпатии:
    2.260
    Пол:
    Мужской
    Род занятий:
    Аранжировка, солист-пинанист
    Адрес:
    Новосибирск
    @@Michael, а я разобрался. Кой-чего не так обозвал, и индекс сэмпла неправильно вычислялся.
    Ща все норм :)
    Добавлю окошко для проверки сходимости, rms-окно для вычленения паттернов и покажу.
    Просто это предполагается как пакет (чтоб настройки хранить и экспортировать сразу для нескольких скриптов), поэтому и выложу позже, и написать все надо самому
     
    Aleksandr Oleynik нравится это.
  17. PianoIst

    PianoIst Well-Known Member

    Регистрация:
    19 май 2010
    Сообщения:
    2.763
    Симпатии:
    2.260
    Пол:
    Мужской
    Род занятий:
    Аранжировка, солист-пинанист
    Адрес:
    Новосибирск
    пока без rms-окна, но поскольку уже и так время поиска получается достаточно приличное, думаю, сделать выбор: по "точной подгонке", или по rms + кроссфейд перехода.
    Ну и это... тут только алгоритм поиска пока, автоматиацию нарезки позже сделаю.
    Ищет точки с начала и с конца, где все каналы на одинаковом значении,
    проверяет, достаточно ли коррелируют 100 сэмплов назад от этих точек (грубо говоря, похожая волна или нет), нет - ищет новую точку
     
    CerberPic нравится это.
  18. EUGEN27771

    EUGEN27771 Well-Known Member

    Регистрация:
    23 апр 2010
    Сообщения:
    2.298
    Симпатии:
    1.954
    Пол:
    Мужской
    @PianoIst,
    ---------------------
    Этот код изначально был на EEL, кто-то переложил на Lua.
    В EEL можно за раз получить не более 65536 семплов, поэтому время делилось на блоки по 65536. То есть, получается какое-то кол-во полных блоков по 65536.
    И один неполный блок - остаток.
    Если playrate = 1 - акцессор работает в разы быстрее, и в Lua, и в EEL. Поэтому в начале такая манипуляция с playrate, сброс, а в конце - восстановление.
    Семплы, конечно же, можно получить начиная с любой точки
    По поводу акцессора, без привязки к конкретной задаче.
    Если делать на Lua - можно учесть некоторые моменты - про Питон не скажу, наверное, в чем-то похоже.
    В Lua можно за один раз взять очень много семплов, хотя кол-во все равно ограничено - не помню точно, 2^21 - 1 или 2^22 - 1 .
    Если аудио длинное - тоже нужно делать частями. Однако блоки можно сделать большими.
    Есть еще несколько нюансов по производительности - это важно, когда нужно перебрать миллионы или десятки миллионов семплов.
    В Lua итерация сама по себе медленная, а итерация по reaper.array еще намного медленнее, чем по родной таблице.
    Из reaper.array нужно сделать таблицу(есть ф-я в API), и идти уже по таблице, это ускорит процесс.
    Локальные ф-и в Lua работают на 30-50% быстрее глобальных.
    Если некая ф-я вызывается пару миллионов раз - есть смысл сделать ее локальной - относится не только к этой теме, а вообще.
    То есть, тупо написав, например, в начале кода или прямо перед циклом на 100500 итераций:
    local abs = math.abs; local sin = math.sin; -- получаем + 30% - практически на шару ("Lua Performance Tips." - тут же есть и другие рекомендации).
    Можно подкрутить сборщик мусора, тоже немного помогает, что конкретно - не помню.
    Сделав все эти манипуляции можно получить прирост производительности в 2-3 раза, хотя eel в любом случае будет на порядок быстрее.
    К некоторым задачам можно подобрать альтернативные решения. В одном скрипте нужно было находить участки тишины, для каждого трека в проекте.
    То есть, для всех всех айтемов на каждом треке, все аудио, с учетом пересечений, перекрытий и тп, а потом еще и миди добавилось. Что-то типа глобального гейта.
    Единственное - особая точность по времени не требовалась, что и помогло. Через getpeaks этот номер прокатил, даже точность регулируется, проекты с кучей аудио анализируются практически незаметно(кто использовали ничего не сказали - значит, не заметили;)).
    --- добавлено 26 май 2018 ---
    Ага... Значит не зря я долго писал.
     
    Aleksandr Oleynik и PianoIst нравится это.
  19. PianoIst

    PianoIst Well-Known Member

    Регистрация:
    19 май 2010
    Сообщения:
    2.763
    Симпатии:
    2.260
    Пол:
    Мужской
    Род занятий:
    Аранжировка, солист-пинанист
    Адрес:
    Новосибирск
    @EUGEN27771, спасибо! очень полезное дело!
    В питоне, думаю, надо будет в перспективе все расчеты на numpy перевести (он все преобразует в immutable и хреначит на голом С).
    Чет никак не могу побороть свою лень и начать писать на lua... Просто в библиотеке питона уже боль-менее разобрался, а в lua опять ночами читать... Здесь лень списываю на достаточно специфическую задачу, ради которой юзер и может привязать к риперу интерпретатор ;)
     
  20. PianoIst

    PianoIst Well-Known Member

    Регистрация:
    19 май 2010
    Сообщения:
    2.763
    Симпатии:
    2.260
    Пол:
    Мужской
    Род занятий:
    Аранжировка, солист-пинанист
    Адрес:
    Новосибирск
    вроде, работает)
    Правда, в конце хотел похвастаться, что rms отлично находит щипки у домры, хоть и может не попать по форме волны. но что-то пошло не так... Видно, надо окошко для усреднения уменьшить, сейчас 250 сэмплов
    Надо забацать гибрид, с очень щадящим режимом по форме волны + настраиваемым rms

     
    Slick и Aleksandr Oleynik нравится это.
  21. PianoIst

    PianoIst Well-Known Member

    Регистрация:
    19 май 2010
    Сообщения:
    2.763
    Симпатии:
    2.260
    Пол:
    Мужской
    Род занятий:
    Аранжировка, солист-пинанист
    Адрес:
    Новосибирск
    чет не могу найти, как экшн запустить... Раньше же где-то откапывал..
    --- добавлено 27 май 2018 ---
    шит. OnCommand же. А насколько опасно использовать экшны SWS по id?
    --- добавлено 27 май 2018 ---
    и это нашел: RPR_NamedCommandLookup( command_name )
     
    Aleksandr Oleynik нравится это.
  22. PianoIst

    PianoIst Well-Known Member

    Регистрация:
    19 май 2010
    Сообщения:
    2.763
    Симпатии:
    2.260
    Пол:
    Мужской
    Род занятий:
    Аранжировка, солист-пинанист
    Адрес:
    Новосибирск
    А объясните, пожалуйста:
    Код:
    ( retval, proj, extname, key, valOutNeedBig, valOutNeedBig_sz ) = RPR_GetProjExtState(proj, extname, key, valOutNeedBig, valOutNeedBig_sz )
    что такое valOutNeedBig, valOutNeedBig_sz
    насколько я понимаю, первое - это собственно дата, которую сохраняли
    retval, это, видимо, успешность операции
    а valOutNeedBig_sz?
    Или я совсем неправильно думаю?
    --- добавлено 27 май 2018 ---
    чет не въезжаю я...
    Код:
    from reaper_python import *
    from misc import pr
    import pickle
    
    obj = {"one": 123, "two": [1, 2, 3]}
    output = pickle.dumps(obj, 2)
    pr(output)
    
    RPR_SetProjExtState(0, 'Levitanus_sample_editing_pack', 'test', output)
    
    valOutNeedBig = 0
    valOutNeedBig_sz = 0
    retval = RPR_GetProjExtState(0, 'Levitanus_sample_editing_pack', 'test',
                                 valOutNeedBig, valOutNeedBig_sz)
    pr(retval)
    pr(valOutNeedBig)
    pr(valOutNeedBig_sz)
    
    вывод:
    Код:
    b'\x80\x02}q\x00(X\x03\x00\x00\x00oneq\x01K{X\x03\x00\x00\x00twoq\x02]q\x03(K\x01K\x02K\x03eu.'
    (1, 0, 'Levitanus_sample_editing_pack', 'test', '0', 0)
    0
    0
    
    вопрос 1: можно ли так байты паковать?
    вопрос2: почему не могу распаковать?

    Ответ:
    потому что пикл - плохой вариант из-за кодировки стрингов рипера.
    Хороший вариант - Json в ASCII. Просто. Работает.
     
    Последнее редактирование: 28 май 2018
  23. PianoIst

    PianoIst Well-Known Member

    Регистрация:
    19 май 2010
    Сообщения:
    2.763
    Симпатии:
    2.260
    Пол:
    Мужской
    Род занятий:
    Аранжировка, солист-пинанист
    Адрес:
    Новосибирск
    Едем дальше: как получить dB из значения сэмпла?
    Или это только через get peaks?

    UPD: немного неточно, но думаю, хватит:

    Код:
    item = Item(item, block_size=1)
    sample = item.samples.get_block(block=0)
    peak = sample[0].channels[0]
    peak = abs(peak)
    pr(peak)
    peak = 20 * math.log10(peak)
    pr(peak)
    сэмпл:
    2018-05-28_14-29-44.png 2018-05-28_14-31-20.png

    вывод:
    Код:
    0.035736083984375
    -28.937860797747096
     
    Последнее редактирование: 28 май 2018
  24. PianoIst

    PianoIst Well-Known Member

    Регистрация:
    19 май 2010
    Сообщения:
    2.763
    Симпатии:
    2.260
    Пол:
    Мужской
    Род занятий:
    Аранжировка, солист-пинанист
    Адрес:
    Новосибирск
    мне прямо даже интересно, где косяк :D

    2018-05-28_16-51-52.png
     
  25. PianoIst

    PianoIst Well-Known Member

    Регистрация:
    19 май 2010
    Сообщения:
    2.763
    Симпатии:
    2.260
    Пол:
    Мужской
    Род занятий:
    Аранжировка, солист-пинанист
    Адрес:
    Новосибирск
    короче, либо неправильно считаю децибелы, либо время.
    Коррекция, вроде уже адекватна значениям и времени полученным...
    2018-05-28_17-16-06.png
    где ж подстава...
     
  26. EUGEN27771

    EUGEN27771 Well-Known Member

    Регистрация:
    23 апр 2010
    Сообщения:
    2.298
    Симпатии:
    1.954
    Пол:
    Мужской
    Последнее редактирование: 29 май 2018
    PianoIst нравится это.
  27. PianoIst

    PianoIst Well-Known Member

    Регистрация:
    19 май 2010
    Сообщения:
    2.763
    Симпатии:
    2.260
    Пол:
    Мужской
    Род занятий:
    Аранжировка, солист-пинанист
    Адрес:
    Новосибирск
    @EUGEN27771, ух ты! офигительный компрессор!
    А че я тогда парюсь со всем этим делом?) просто ваш компрессор как экшн забиндить можно будет, и все)
     
  28. EUGEN27771

    EUGEN27771 Well-Known Member

    Регистрация:
    23 апр 2010
    Сообщения:
    2.298
    Симпатии:
    1.954
    Пол:
    Мужской
    С лупами вроде бы другая задача(или я невнимательно посмотрел)...
    По поводу компрессора - это действительно компрессор в буквальном смысле.
    Алгоритм очень прост, один в один соответствует ReaComp. Eel-модули можно использовать в скриптах и jsfx одновременно. Если модуль подключить к JS - при одинаковых настройках результаты в противофазе складываются в минус бесконечность. При условии, что в ReaComp установлен режим classic, не используется фильтр и колено уст. в ноль. Сделать точно такой же фильтр, как в ReaComp сложно и в общем-то нет смысла, ибо неизвестно, какой фильтр "лучше". Soft knee по-нормальному не доделал, потому что надоело, ф-я поджирала ресурсы, для скрипта это ощутимо.
    В скрипте, в основном, все сохранено, если установить интервал между точками равным одному-нескольким семплам и использовать моно-файлы - будет практически точно так же. Возможность задавать такой маленький интервал я намеренно убрал, как и анализ каждого канала по отдельности, в скрипте используется сумма, усредненное значение. Тут приходится искать компромиссы. Логика такая - в 90% случаев исходный файл либо моно, либо стерео, в котором оба канала практически одинаковы.
    Пусть даже многоканальное аудио. Во-первых - вероятность кардинального разброса мала, во-вторых - компрессия по среднему значению ничем не хуже компрессии по максимальному - она просто другая. Пример - в варианте по макс. значению - резкий скачок в одном из каналов может угробить звук во всех остальных. Есть предположение, что это вообще оптимальный вариант.
    То же самое относится и к интервалу между точками. Тут тоже сложно сказать. Стандартный компрессор строит огибающую учитывая каждый семпл, это правило полностью соблюдается, огибающая внутри скрипта строится точно также, учитываются все семплы. Стандартный компрессор устанавливает выходной уровень каждого семпла согласно текущему уровню огибающей, в зависимости от настроек - сама огибающая может изменяться в сторону роста/спада с разной скоростью, но в любом случае моментально влияет на выходной уровень. В скрипте ситуация немного другая - каждая точка на кривой громкости может быть установлена не ранее, чем через заданный промежуток времени, если ставить точки на каждый семпл - будет абсолютно такая же огибающая, как внутри компрессора. Но это полная крайность и крайняя глупость, возможно, но только для тестов, на практике это дико нерационально и бессмысленно.
    В случае компрессора с "нормальными" значениями атаки/релиза(больше, чем несколько семплов) - скорость изменения огибающей(внутри прибора) не может быть очень высокой даже при резких перепадах входного сигнала. При этом, не забываем, что сигнал реальный(думаю, искусственно можно создать сигнал, который через фирмовые компрессоры пройдет вопреки настройкам), и значения атаки/релиза свойственные компрессору, а не лимитеру и тп. Наибольшая скорость, вероятно, нарастание на резких атаках у высоких звуков, что-то такое... Поскольку внутри кода я имею такую же огибающую, как в обычном компрессоре - как только она выходит за порог - откатываю назад и ставлю "открывающую" точку на кривой, то есть ловлю атаку и считаю от нее. После этого точки идут согласно указанному в настройках интервалу, через некоторое кол-во семплов, но значение равно значению реальной огибающей на тек. момент. Это значит, что на коротком интервале между точками кривой семплы реальной огибающей могут гулять в плюс/минус, но итоговая кривая на айтеме никогда не уйдет от реальной огибающей, и чем меньше интервал между точками кривой - тем меньше отклонения. Грубо говоря - кривая на айтеме полностью соблюдает общую тенденцию, но меньше уходит в детали, интервал меду точками кривой можно расценивать как смягчение огибающей. Когда огибающая падает ниже порога - ставлю "закрывающую" точку. Короче, точки не плодятся бессмысленно.
    Почему ориентир на ReaComp - в тот момент не было аргументов на вопрос "что это такое? почему это компрессор?..."
     
    Последнее редактирование: 29 май 2018
    PianoIst нравится это.
  29. Aleksandr Oleynik

    Aleksandr Oleynik Well-Known Member

    Регистрация:
    16 янв 2007
    Сообщения:
    16.620
    Симпатии:
    8.959
    Пол:
    Мужской
    Адрес:
    Киев
    @Archchie, логика данной функции очень проста - какое бы кол-во треков вы не выделили и в каком порядке, она их всех перенесёт ПОД трек указанный в функции в первой переменной один за другим, вторая переменная может делать этот указанный трек папкой для перенесенных. Вот и всё. Ни как иначе её заставить работать не получится.
     
    Последнее редактирование: 30 май 2018
  30. CerberPic

    CerberPic Member

    Регистрация:
    17 фев 2017
    Сообщения:
    69
    Симпатии:
    22
    Пол:
    Мужской
    @Archchie, ну так подсчёт же производится до перемещения. Следовательно, если трек 1 мы куда-нибудь переместим, то трек 2 уже не будет треком 2, а станет треком 1.
     
  31. Archchie

    Archchie Active Member

    Регистрация:
    24 окт 2017
    Сообщения:
    282
    Симпатии:
    125
    Пол:
    Мужской
    Все разобрался ! Она просто работает как готовый экшен 'то есть сначала ловит трек который мы указали , а потом под него перемещает ',(На ваше "ПОД" большими буквами не обратил внимания),а я думал- что какой номер мы указали под тот номер и должно переместится(что было бы удобнее). Трек у меня был подписан и покрашен только один выделенный ,а остальные просто новые и не понятно было что происходит.
     
    Последнее редактирование: 30 май 2018
    Aleksandr Oleynik нравится это.
  32. Aleksandr Oleynik

    Aleksandr Oleynik Well-Known Member

    Регистрация:
    16 янв 2007
    Сообщения:
    16.620
    Симпатии:
    8.959
    Пол:
    Мужской
    Адрес:
    Киев
    Это какраз значения не имеет. Функция ВСЕ выделенные треки переносит СРАЗУ, а не в лупе.
     
    CerberPic нравится это.
  33. PianoIst

    PianoIst Well-Known Member

    Регистрация:
    19 май 2010
    Сообщения:
    2.763
    Симпатии:
    2.260
    Пол:
    Мужской
    Род занятий:
    Аранжировка, солист-пинанист
    Адрес:
    Новосибирск
    а можно скриптом добавлять скрипты в экшны?
    Я просто думаю в комбайн встроить немного модифицированные скрипты сообщества, которые бы сохраняли настройки в проект.
    Естессно со всеми упоминаниями на главной странице.
    Просто, взять тот же шикарный енвелоп-компрессор Жени, который позволяет делать неразрывные лупы с компрессией. Но даже для двух разных итемов приходится дважды щелкать: по скрипту и по пресету. А когда их, скажем, 10 000 :D
    Идея в том, чтобы делать "разметку" маркерами, а потом в один проход все резать скриптом. Короче, объяснился, чтоб вопросов не было.

    Теперь к задаче: В папке с пакетом лежат чуть модифицированные скрипты, типа:
    Код:
    //-- mainloop -----------Женин компрессор---------------
    function mainloop()
    local(preset_data)
    (
      GetMouseState(); // Get mouse
      Draw_Controls();
      Presets();
      preset_data = GetPreset();
      RPR_SetProjExtState(0, 'Levitanus_sample_editing_pack', "gen_ebc", preset_data)
    
      RunMain ? (
        MAIN();
        RunMain = 0;
        RunMain_cnt+=1; // Change cnt
      );
    
      SetMouseLastState(); // Update mouse last state
      gfx_update(); // Update gfx window
      gfx_char = gfx_getchar();
      //gfx_char==68 || gfx_char== 194 ? DevMode = !DevMode; // -- Shift + D - don't use!!!
      gfx_char==32 ? Main_OnCommand(40044, 0); //-- play
      gfx_char >= 0 ? defer("mainloop();");    //-- defer
    );
    Как бы их так запускать. Есть луа, есть eel. Не заставлять же добавлять руками все это добро в экшны. Да и Id тогда не вытащить. Или вытащить?
     
  34. Archchie

    Archchie Active Member

    Регистрация:
    24 окт 2017
    Сообщения:
    282
    Симпатии:
    125
    Пол:
    Мужской
    Так же интересует этот вопрос ,
    с недели две назад спрашивал(просил) у @Michael-a в личку сделать такой скрипт, если возможно,
    но к сожалению ответа так и не дождался.
     
  35. @Michael

    @Michael Well-Known Member

    Регистрация:
    14 дек 2010
    Сообщения:
    821
    Симпатии:
    1.238
    Пол:
    Мужской
    Адрес:
    Орёл / Москва
     
    Archchie и PianoIst нравится это.
  36. Archchie

    Archchie Active Member

    Регистрация:
    24 окт 2017
    Сообщения:
    282
    Симпатии:
    125
    Пол:
    Мужской
  37. Archchie

    Archchie Active Member

    Регистрация:
    24 окт 2017
    Сообщения:
    282
    Симпатии:
    125
    Пол:
    Мужской
    @@Michael, эта функция добавляет по имени, а подскажи пожалуйста как просканировать папку что бы автоматически получить имя каждого скрипта в папке
     
  38. PianoIst

    PianoIst Well-Known Member

    Регистрация:
    19 май 2010
    Сообщения:
    2.763
    Симпатии:
    2.260
    Пол:
    Мужской
    Род занятий:
    Аранжировка, солист-пинанист
    Адрес:
    Новосибирск
    @Archchie, на луа пишете?
    https://stackoverflow.com/questions/5303174/how-to-get-list-of-directories-in-lua
    На eel искать не в один поисковый апрос, но тоже можно постараться.
    На питоне тоже один поисковый запрос
    --- добавлено 31 май 2018 ---
    фрагмент из Lokasenna_GUI 2
    Код:
    -- I hate working with 'requires', so I've opted to do it this way.
    -- This also works much more easily with my Script Compiler.
    local function req(file)
      
        if missing_lib then return function () end end
      
        local sep = string.match(reaper.GetOS(), "Win") and "\\" or "/"
    
        local ret, err = loadfile(script_path .."Lokasenna_GUI"..sep.. file)
        if not ret then
            reaper.ShowMessageBox("Couldn't load "..file.."\n\nError: "..tostring(err), "Library error", 0)
            missing_lib = true        
            return function () end
    
        else
            return ret
        end   
    
    end
    
    -- The Core library must be loaded prior to any classes, or the classes will throw up errors
    -- when they look for functions that aren't there.
    local sep = string.match(reaper.GetOS(), "Win") and "\\" or "/"
    --- добавлено 31 май 2018 ---
    упс, я его там уже немного нафаршировал. Но думаю, разберешься
     
    Последнее редактирование: 31 май 2018
    Archchie нравится это.
  39. @Michael

    @Michael Well-Known Member

    Регистрация:
    14 дек 2010
    Сообщения:
    821
    Симпатии:
    1.238
    Пол:
    Мужской
    Адрес:
    Орёл / Москва
    Archchie и PianoIst нравится это.
  40. Aleksandr Oleynik

    Aleksandr Oleynik Well-Known Member

    Регистрация:
    16 янв 2007
    Сообщения:
    16.620
    Симпатии:
    8.959
    Пол:
    Мужской
    Адрес:
    Киев
    Не могу найти, а как будто было -
    Как запомнить размер и положение GUI, чтоб при следующем открытии оно откравалось с этим размером и положением?
     
  41. SuperPups

    SuperPups Active Member

    Регистрация:
    14 ноя 2008
    Сообщения:
    354
    Симпатии:
    141
    Пол:
    Мужской
    Адрес:
    UA
    А можно скриптами миди треки писать. Запустил скрипт, он фанеру написал и инструменты выбрал и всё - можно петь!
     
  42. EUGEN27771

    EUGEN27771 Well-Known Member

    Регистрация:
    23 апр 2010
    Сообщения:
    2.298
    Симпатии:
    1.954
    Пол:
    Мужской
    @Aleksandr Oleynik, нужно запомнить в ext state позицию окна скрипта и текущие настройки gfx_w; gfx_h;
    Позицию окна - для этого коорд. окна скрипта в коорд. окна Рипера. Можно просто от нулей (x=0; y=0), чуть лучше - взять некие постоянные отр. значения по рамкам.
    Но лучше всего правильно посчитать отступы, чтобы окно никогда не съезжало постоянно на +/-. Я где-то выкладывал принцип - был вопрос, как свернуть/развернуть окно скрипта.
     
    Aleksandr Oleynik нравится это.
  43. EUGEN27771

    EUGEN27771 Well-Known Member

    Регистрация:
    23 апр 2010
    Сообщения:
    2.298
    Симпатии:
    1.954
    Пол:
    Мужской
    С опр. набором условий - можно. Уверен на 100%, что через какое-то время вопрос будет не смешным, а реальным.
    лет 10 назад - BB midiout >> Reaper midi input >> какие-то древние библы из Контакт или вообще Hypersonic.





    ----------------------------
    BB построен на паттернах. Воды много утекло, а они закопались в Аудио(наверное, зря).
     

    Вложения:

    Aleksandr Oleynik и keyboarder нравится это.
  44. Archchie

    Archchie Active Member

    Регистрация:
    24 окт 2017
    Сообщения:
    282
    Симпатии:
    125
    Пол:
    Мужской
    Подскажите, кто знает, как получить "ID" скрипта ?
     
    Последнее редактирование: 27 авг 2018
  45. incubator

    incubator Active Member

    Регистрация:
    24 янв 2007
    Сообщения:
    356
    Симпатии:
    138
    Адрес:
    Spb
    Так ведь прямо в экшн листе@Archchie,
     
  46. Aleksandr Oleynik

    Aleksandr Oleynik Well-Known Member

    Регистрация:
    16 янв 2007
    Сообщения:
    16.620
    Симпатии:
    8.959
    Пол:
    Мужской
    Адрес:
    Киев
    Archchie нравится это.
  47. Archchie

    Archchie Active Member

    Регистрация:
    24 окт 2017
    Сообщения:
    282
    Симпатии:
    125
    Пол:
    Мужской
    Это я знаю, Скриптом как получить?
     
  48. Aleksandr Oleynik

    Aleksandr Oleynik Well-Known Member

    Регистрация:
    16 янв 2007
    Сообщения:
    16.620
    Симпатии:
    8.959
    Пол:
    Мужской
    Адрес:
    Киев
    А по какому признаку вы хотите его получать и зачем?
    Если вам известно имя скрипта, то запускайте его по имени.
    А если вам не известно его имя, то как вы собрались по API узнавать его ID?
     
  49. Archchie

    Archchie Active Member

    Регистрация:
    24 окт 2017
    Сообщения:
    282
    Симпатии:
    125
    Пол:
    Мужской
    Имя то известно, а как запустить его по имени?
    По "id" знаю как запустить, а по имени не знаю!
    Не подскажете как?
     
    Последнее редактирование: 28 авг 2018
  50. Aleksandr Oleynik

    Aleksandr Oleynik Well-Known Member

    Регистрация:
    16 янв 2007
    Сообщения:
    16.620
    Симпатии:
    8.959
    Пол:
    Мужской
    Адрес:
    Киев
    @Archchie, та запросто - сейчас к компу подойду и напишу код
     
    Archchie нравится это.

Поделиться этой страницей