|
|
|
02-12-2017, 11:32 AM
|
#1
|
Human being with feelings
Join Date: Oct 2013
Location: Moscow, Russia
Posts: 3,960
|
JS basic sampler - any work examples?
Hi guys, for new StepSequencer/DrumRack script I searching solution to manipulate sampler sample paths from script. All samplers I`ve seen hold this info in their binary chunks (RS5K also). So maybe it is possible to code very basic JS, which will run sample by MIDI trigger + ADSR? Any code examples much appreciated.
Main reason for test it is what will be if I encode sample path base64 chunk for this JS manually. RS5K understand it when I apply TrackStateChunk first time, but it crashed after second time focusing in FX Chain (example below). I pinged Justin for dedicated RS5K API functions for manipulating this, but if it is possible to not use RS5K, I`ll be very thankful to you if you can help.
Thanks.
Here is RS5K example
Code:
-- @description Move item active audio take to RS5K
-- @version 1.0alpha
-- @author MPL
-- @website http://forum.cockos.com/member.php?u=70694
-- @changelog
-- + init
local pitch_base = 60
-------------------------------------------------------------------
function m(s) reaper.ShowConsoleMsg(s) end
-------------------------------------------------------------------
function enc(data)
-- character table string
local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
return ((data:gsub('.', function(x)
local r,b='',x:byte()
for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end
return r;
end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
if (#x < 6) then return '' end
local c=0
for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end
return b:sub(c+1,c+1)
end)..({ '', '==', '=' })[#data%3+1])
end
---------------------------------------------------------------------
function InsertRS5K(track, samplepath)
local insert_id
local gen_guid = reaper.genGuid('' )
local sample_path_base64 = enc(samplepath)
local default_data = -- file is C:\\1234567890
[[ BYPASS 0 0 0
<VST "VSTi: ReaSamplOmatic5000 (Cockos)" reasamplomatic.dll 0 "RS5K" 1920167789
bW9zcu5e7f4AAAAAAgAAAAEAAAAAAAAAAgAAAAAAAAAqAQAAAQAAAAAAEIA=
]]..sample_path_base64..[[AAAAAAAAAPA/AAAAAAAA4D8AAAAAAADwPwAAAAAAAAAAAAAAAAAA8D+amZmZmZmxP83M
zMzMzOs/AAAAAAAAAACamZmZmZnJP/yp8dJNYkA//Knx0k1iQD8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwPwAAAAAAAOA/AQAAAAAAAAAAAAAAAAAAAAAA
8D9AAAAAVVVVVVVVxT//////AAAAAAAAAAAAAAAAAADwPwAAAAAAAPA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADOpCEhGmWQPwAAAAAAAPA//Knx0k1i
MD8AAAAAAAAAAA==
AAAQAAAA
>
FLOATPOS 0 0 0 0
FXID ]]..gen_guid..'\n'..[[
WAK 0]]
local def_chain =
[[<FXCHAIN
WNDRECT 24 52 752 466
SHOW 0
LASTSEL 0
DOCKED 0
]]..default_data..'>'
local _, chunk = reaper.GetTrackStateChunk( track, '' )
local t = {}
for line in chunk:gmatch('[^\n\r]+') do
t[#t+1] = line
if line:match('DOCKED') then insert_id = #t end
if line:match('MAINSEND ') then insert_id2 = #t end
end
if insert_id then
table.insert(t,insert_id+1,default_data )
else
table.insert(t,insert_id2+1,def_chain )
end
local out_str = table.concat(t, '\n')
--m(out_str)
reaper.SetTrackStateChunk( track, out_str )
end
---------------------------------------------------------------------
function main()
local item = reaper.GetSelectedMediaItem( 0, 0 )
if not item then return end
local track4insert = reaper.GetMediaItem_Track( item )
t = {}
for i = 1, reaper.CountSelectedMediaItems( 0 ) do
local sel_item = reaper.GetSelectedMediaItem( 0, i-1 )
local act_take = reaper.GetActiveTake( sel_item )
if not reaper.TakeIsMIDI(act_take) then
local guid = reaper.BR_GetMediaItemGUID( sel_item )
local src = reaper.GetMediaItemTake_Source( act_take )
local src_file = reaper.GetMediaSourceFileName( src, '' )
t[#t+1] = {guid =guid,
src_file =src_file}
end
end
if #t < 1 then return end
for i = #t,1,-1 do
local item = reaper.BR_GetMediaItemByGUID( 0, t[i].guid )
local item_tr = reaper.GetMediaItem_Track( item )
reaper.DeleteTrackMediaItem( item_tr, item )
InsertRS5K(track4insert, t[i].src_file)
end
reaper.UpdateArrange()
end
main()
Last edited by mpl; 02-12-2017 at 12:09 PM.
|
|
|
02-12-2017, 03:40 PM
|
#2
|
Human being with feelings
Join Date: Sep 2016
Location: Toronto
Posts: 744
|
Big +1 for RS5K API and Media Explorer API. I'm always excited to see someone working on programming for Reaper as I think it is one of the most undeveloped parts.
I can't help with the coding but on the same topic, here's an idea for easy audio step sequencing (I just refined thanks to me2beats' select next hidden track script.)
http://imgur.com/a/XTrn7
The samples are stored in a hidden, indented track:
Custom: Drum mode prepare
Script: X-Raym_Select only tracks of selected items.lua
Xenakios/SWS: Select first items of selected tracks
Script: X-Raym_Select only tracks of selected items.lua
Track: Insert new track
Item edit: Move items/envelope points down one track/a bit
Item properties: Toggle mute
Xenakios/SWS: Select previous tracks, keeping current selection
SWS: Make folder from selected tracks
SWS: Set selected folder(s) collapsed
SWS: Select only children of selected folders
Script: kawa_MAIN_SelectedTrackHideTCP_Type2.lua
Cycle action mouse modifier:
http://imgur.com/a/Jj8OI
I still need to make an action to open the hidden track back up so the samples can be edited if needed.
|
|
|
02-12-2017, 06:03 PM
|
#3
|
Human being with feelings
Join Date: Aug 2012
Posts: 271
|
Sorry, I'm writing it in Russian
Миша, провозился несколько часов, интересная штука. Давно в lua ничего не делал, но вопрос с RS5000, вроде, решен.
Короче, так запросто подменять имя, конечно же, не получается(хотя можно поморочиться)
Причина - Чанк непрерывный. Раскодируй или весь чанк(то,что base64, естественно), или основную часть, середину(я брал ее).
Функция для декодинга та же с lua org.
Приклей имя(или замени, если уже было), и закодируй обратно. Работает, проверено. Для красоты разбей на строки, как положено, но работает и так.
===================================
It is a working example:
Code:
-- @description Move item active audio take to RS5K
-- @version 1.0alpha
-- @author MPL
-- @website http://forum.cockos.com/member.php?u=70694
-- @changelog
-- + init
local pitch_base = 60
-------------------------------------------------------------------
function m(s) reaper.ShowConsoleMsg(s) end
-------------------------------------------------------------------
-- character table string
local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
-- encoding
function enc(data)
return ((data:gsub('.', function(x)
local r,b='',x:byte()
for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end
return r;
end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
if (#x < 6) then return '' end
local c=0
for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end
return b:sub(c+1,c+1)
end)..({ '', '==', '=' })[#data%3+1])
end
-- decoding
function dec(data)
data = string.gsub(data, '[^'..b..'=]', '')
return (data:gsub('.', function(x)
if (x == '=') then return '' end
local r,f='',(b:find(x)-1)
for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end
return r;
end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
if (#x ~= 8) then return '' end
local c=0
for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end
return string.char(c)
end))
end
---------------------------------------------------------------------
function InsertRS5K(track, samplepath)
local insert_id
local gen_guid = reaper.genGuid('' )
------------------------------------
local default_data = -- file is C:\\1234567890
[[
<VST "VSTi: ReaSamplOmatic5000 (Cockos)" reasamplomatic.dll 0 "RS5K" 1920167789
bW9zcu5e7f4AAAAAAgAAAAEAAAAAAAAAAgAAAAAAAAD9AAAAAQAAAAAAEAA=
]].. enc(samplepath .. dec([[AAAAAAAAAPA/AAAAAAAA4D8AAAAAAADwPwAAAAAAAAAAAAAAAAAA8D+amZmZmZmxP83MzMzMzOs/AAAAAAAAAACamZmZmZnJP/yp8dJNYkA//Knx0k1iQD8AAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwPwAAAAAAAOA/AQAAAAAAAAAAAAAAAAAAAAAA8D9AAAAAVVVVVVVVxT//////AAAAAAAAAAAAAAAAAADwPwAAAAAAAPA/AAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADOpCEhGmWQPwAAAAAAAPA//Knx0k1iMD8AAAAAAAAAAA==]])) ..
[[AAAQAAAA
>
FLOATPOS 0 0 0 0
FXID ]] .. gen_guid .. '\n' .. [[
WAK 0]]
------------------------------------
local def_chain =
[[<FXCHAIN
WNDRECT 341 57 922 742
SHOW 2
LASTSEL 1
DOCKED 0
BYPASS 0 0 0
]] .. default_data .. '>'
------------------------------------
local _, chunk = reaper.GetTrackStateChunk( track, '' )
local t = {}
for line in chunk:gmatch('[^\n\r]+') do
t[#t+1] = line
if line:match('DOCKED') then insert_id = #t end
if line:match('MAINSEND ') then insert_id2 = #t end
end
if insert_id then
table.insert(t,insert_id+1,default_data )
else
table.insert(t,insert_id2+1,def_chain )
end
local out_str = table.concat(t, '\n')
--m(out_str)
reaper.SetTrackStateChunk( track, out_str )
end
---------------------------------------------------------------------
function main()
local item = reaper.GetSelectedMediaItem( 0, 0 )
if not item then return end
local track4insert = reaper.GetMediaItem_Track( item )
t = {}
for i = 1, reaper.CountSelectedMediaItems( 0 ) do
local sel_item = reaper.GetSelectedMediaItem( 0, i-1 )
local act_take = reaper.GetActiveTake( sel_item )
if not reaper.TakeIsMIDI(act_take) then
local guid = reaper.BR_GetMediaItemGUID( sel_item )
local src = reaper.GetMediaItemTake_Source( act_take )
local src_file = reaper.GetMediaSourceFileName( src, '' )
t[#t+1] = {guid =guid,
src_file =src_file}
end
end
if #t < 1 then return end
for i = #t,1,-1 do
local item = reaper.BR_GetMediaItemByGUID( 0, t[i].guid )
local item_tr = reaper.GetMediaItem_Track( item )
reaper.DeleteTrackMediaItem( item_tr, item )
InsertRS5K(track4insert, t[i].src_file)
end
reaper.UpdateArrange()
end
main()
Last edited by eugen2777; 02-12-2017 at 07:11 PM.
|
|
|
02-12-2017, 08:17 PM
|
#4
|
Human being with feelings
Join Date: Oct 2013
Location: Moscow, Russia
Posts: 3,960
|
Question is more about JS sampler implementation.
Since Justin said today related API coming soon so no need to chunk manipulation already (which are anyway painful and CPU hungry).
Last edited by mpl; 02-12-2017 at 09:43 PM.
|
|
|
02-13-2017, 04:44 AM
|
#5
|
Human being with feelings
Join Date: Aug 2012
Posts: 271
|
As far as I know, JSFX can take audio file only from the program directory - REAPER\Data etc.
As an example of the use of samples - there are DrumReaplacer and some jsfx use impulses.
|
|
|
02-13-2017, 08:00 AM
|
#6
|
Human being with feelings
Join Date: Mar 2007
Posts: 3,978
|
Quote:
Originally Posted by eugen2777
As far as I know, JSFX can take audio file only from the program directory - REAPER\Data etc.
As an example of the use of samples - there are DrumReaplacer and some jsfx use impulses.
|
Hi,
! BUT !: If you place JSFX Effect to project folder \Effects\file.js
then you can place the data for it the same way , like \Data\somefolder\*.wav etc...
Then the reaper will use the JS Effect from project directory instead of regular (install) dir and also its "data". It works for midi_drumseq and I believe that it works generally (e.g. ampsim "amp-model" with IRs etc..)
So maybe there is some way then? ;-)
akademie
|
|
|
02-16-2017, 06:28 PM
|
#7
|
Human being with feelings
Join Date: Nov 2009
Location: mostly inside my own head
Posts: 346
|
I don't know if this is relevant or helpful, but I just wrote a basic JSFX sampler that takes incoming audio and stores it for playback on a MIDI trigger. It currently has a fixed zero-length attack and 10ms release (or when the sample runs out, whichever is sooner), but that's simple enough to change.
The code is on GitHub, and it's hopefully fairly readable. To actually run it, you'll need both "ui-lib.jsfx-inc" for the UI and "synth-framework.jsfx-inc" for the polyphonic synth logic (or just install the sampler from ReaPack).
I wrote it because I sometimes found myself hand-arranging drum loops from samples, which ended up with my project being littered with individual hi-hat items. Instead, I wanted to assemble the samples once at the beginning of my project, have a sampler on each track to "learn" all the samples (e.g. one sampler for all the different snare sounds, one for bass drums, etc.), and then play them back later.
The recorded sounds are read/written in @serialise, but it currently doesn't handle sample-rate changes correctly (it will play the samples back at the wrong speed). This isn't a problem for my use-case, because my projects will have the drum loop at the beginning to re-learn. If this becomes an issue, it can be solved by interpolating on playback.
Last edited by geraintluff; 02-17-2017 at 03:57 AM.
|
|
|
01-04-2020, 08:10 AM
|
#8
|
Human being with feelings
Join Date: Mar 2014
Posts: 347
|
Hi Geraint, not sure if you'll see this but was trying to use your sampler...just wanted to ask how to get it working? It's on my track, sample at the beginning but not able to get it to work. Thanks for any time you may have D
Quote:
Originally Posted by geraintluff
I don't know if this is relevant or helpful, but I just wrote a basic JSFX sampler that takes incoming audio and stores it for playback on a MIDI trigger. It currently has a fixed zero-length attack and 10ms release (or when the sample runs out, whichever is sooner), but that's simple enough to change.
The code is on GitHub, and it's hopefully fairly readable. To actually run it, you'll need both "ui-lib.jsfx-inc" for the UI and "synth-framework.jsfx-inc" for the polyphonic synth logic (or just install the sampler from ReaPack).
I wrote it because I sometimes found myself hand-arranging drum loops from samples, which ended up with my project being littered with individual hi-hat items. Instead, I wanted to assemble the samples once at the beginning of my project, have a sampler on each track to "learn" all the samples (e.g. one sampler for all the different snare sounds, one for bass drums, etc.), and then play them back later.
The recorded sounds are read/written in @serialise, but it currently doesn't handle sample-rate changes correctly (it will play the samples back at the wrong speed). This isn't a problem for my use-case, because my projects will have the drum loop at the beginning to re-learn. If this becomes an issue, it can be solved by interpolating on playback.
|
|
|
|
01-06-2020, 04:30 PM
|
#9
|
Human being with feelings
Join Date: Aug 2013
Location: Bowral, Australia
Posts: 1,638
|
I wrote the JSFX 'Time Machine' sampler in the stash. It also stores audio in memory for playback, triggered by MIDI. I Initially wanted to play audio files directly, but as far as I can tell from the documentation there is no way to do this using JSFX. The Time Machine sampler does have the option of a release envelope, for choking. Later versions may eventually get a full ADSR envelope, but that has not been a priority as I wanted it mainly for drum samples.
|
|
|
Thread Tools |
|
Display Modes |
Linear Mode
|
Posting Rules
|
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
HTML code is Off
|
|
|
All times are GMT -7. The time now is 04:53 AM.
|