Skip to content

Instantly share code, notes, and snippets.

@dndrks
Last active November 26, 2021 14:24
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dndrks/ff326eb2f7bdf30955987b41e630fe71 to your computer and use it in GitHub Desktop.
Save dndrks/ff326eb2f7bdf30955987b41e630fe71 to your computer and use it in GitHub Desktop.
norns USB TAPE copy
-- USB tape copy
--
-- * attach a USB drive
-- * choose a TAPE, press K3:
-- * copy
-- * copy+delete
-- * delete
--
-- if you see "NO USB DETECTED",
-- reconnect and wait a minute
-- then try again
local UI = require "ui"
function build_filelist()
local filepath = _path.audio.."tape/"
audio_filepaths = {}
for i in io.popen("ls "..filepath):lines() do
if string.find(i,"%.wav$") then table.insert(audio_filepaths,filepath..i) end
end
tape_names = {}
tape_sizes = {}
for i = 1,#audio_filepaths do
tape_names[i] = string.gsub(audio_filepaths[i],"/home/we/dust/audio/tape/","")
local temp_sizer = io.open(audio_filepaths[i])
tape_sizes[i] = util.round(fsize(temp_sizer)/1000000,0.01)
io.close(temp_sizer)
end
end
function build_ui_lists()
tape_list = UI.ScrollingList.new(0,5,1,tape_names)
tape_list.num_above_selected = 0
tape_list.num_visible = 5
size_list = UI.ScrollingList.new(60,5,1,tape_sizes)
size_list.num_above_selected = 0
size_list.num_visible = 5
end
function fsize (file)
local current = file:seek() -- get current position
local size = file:seek("end") -- get file size
file:seek("set", current) -- restore position
return size
end
function check_usb_for_tape_folder()
if not util.file_exists("/media/usb0/norns_tapes") then
os.execute("sudo mkdir /media/usb0/norns_tapes")
end
end
function check_usb_for_tape(id)
if util.file_exists("/media/usb0/norns_tapes/"..id) then
return true
else
return false
end
end
function usb_actions(state)
if state == "copy" or state == "copy and delete" then
_ui.focus = "copying"
redraw()
local name_id = string.gsub(tape_names[tape_list.index],".wav","")
local name_index = 1
while check_usb_for_tape(name_id..'.wav') do
if name_id:match("(.+)-(.+)") ~= nil then
name_id = name_id:match("(.+)-(.+)")
end
name_id = name_id.."-"..name_index
name_index = name_index+1
end
os.execute('sudo cp -r ~/dust/audio/tape/'..tape_names[tape_list.index]..' /media/usb0/norns_tapes/'..name_id..'.wav')
local checked = check_sum(_path.audio.."/tape/"..tape_names[tape_list.index], "/media/usb0/norns_tapes/"..name_id..".wav")
if checked and state == "copy and delete" then
clock.run(function() clock.sleep(1) _ui.focus = "delete confirmation" redraw() end)
elseif checked and state == "copy" then
clock.run(function() clock.sleep(1) _ui.focus = "tape_scroll" redraw() end)
elseif not checked and (state == "copy and delete" or state == "copy") then
clock.run(function()
_ui.focus = "unsuccessful copy"
redraw()
drive_connected = false
clock.sleep(2)
_ui.focus = "manage"
redraw()
end)
end
elseif state == "delete" then
_ui.focus = "delete confirmation"
redraw()
end
end
function usb_delete(file)
os.execute('sudo rm ~/dust/audio/tape/'..file)
build_filelist()
local old_index = tape_list.index
build_ui_lists()
tape_list.index = util.clamp(old_index,1,#tape_list.entries)
size_list.index = util.clamp(old_index,1,#size_list.entries)
_ui.focus = "tape_scroll"
redraw()
end
function CRC(data, length)
sum = 65535
local d
for i = 1, length do
if data ~= nil then
d = string.byte(data, i) -- get i-th element, like data[i] in C
sum = ByteCRC(sum, d)
else
break
end
end
return sum
end
function ByteCRC(sum, data)
sum = sum ~ data
for i = 0, 7 do -- lua for loop includes upper bound, so 7, not 8
if ((sum & 1) == 0) then
sum = sum >> 1
else
sum = (sum >> 1) ~ 0xA001 -- it is integer, no need for string func
end
end
return sum
end
function check_sum(first,second)
local source = io.open(first,"rb")
local copy = io.open(second,"rb")
local flag;
while true do
if copy ~= nil then
local source_chunk = source:read(16*1024) -- 16kB at a time
local copy_chunk = copy:read(16*1024) -- 16kB at a time
if not source_chunk and not copy_chunk then break end
local source_hash = CRC(source_chunk,3)
local copy_hash = CRC(copy_chunk,3)
if source_hash == copy_hash then flag = true else flag = false end
else
flag = false
break
print("no copy")
end
end
if flag == true then
_ui.focus = "successful copy"
elseif flag == false then
_ui.focus = "unsuccessful copy"
end
io.close(source)
io.close(copy)
redraw()
return flag
end
function tape_delete()
_ui.focus = "deleting"
redraw()
local name_id = string.gsub(tape_names[tape_list.index],".wav","")
local name_index = 1
while check_usb_for_tape(name_id..'.wav') do
if name_id:match("(.+)-(.+)") ~= nil then
name_id = name_id:match("(.+)-(.+)")
end
name_id = name_id.."-"..name_index
name_index = name_index+1
end
os.execute('sudo cp -r ~/dust/audio/tape/'..tape_names[tape_list.index]..' /media/usb0/norns_tapes/'..name_id..'.wav')
_ui.focus = "tape_scroll"
redraw()
end
function init()
key1_hold = false
drive_connected = os.execute("mount | grep sda1")
drive_connected = drive_connected and true or false
check_usb_for_tape_folder()
build_filelist()
print("there are "..#audio_filepaths.." tapes in your TAPE folder")
build_ui_lists()
_ui = {}
_ui.focus = "tape_scroll"
_ui.manage_index = 1
end
function enc(n,d)
if n == 2 then
if _ui.focus == "tape_scroll" then
tape_list:set_index_delta(d)
size_list:set_index_delta(d)
elseif _ui.focus == "manage" then
_ui.manage_index = util.clamp(_ui.manage_index+d,1,3)
end
redraw()
end
end
function key(n,z)
if n == 1 then
key1_hold = z == 1 and true or false
elseif n == 3 and z == 1 then
if _ui.focus == "tape_scroll" and not key1_hold then
_ui.focus = "manage"
elseif _ui.focus == "tape_scroll" and key1_hold then
build_filelist()
local old_index = tape_list.index
build_ui_lists()
tape_list.index = util.clamp(old_index,1,#tape_list.entries)
size_list.index = util.clamp(old_index,1,#size_list.entries)
elseif _ui.focus == "manage" then
if drive_connected then
local actions = {"copy","copy and delete","delete"}
usb_actions(actions[_ui.manage_index])
end
elseif _ui.focus == "delete confirmation" then
usb_delete(tape_names[tape_list.index])
end
elseif n == 2 and z == 1 then
if _ui.focus == "manage" then
_ui.focus = "tape_scroll"
elseif _ui.focus == "delete confirmation" then
_ui.focus = "tape_scroll"
end
end
redraw()
end
function redraw()
if not drive_connected then
drive_connected = os.execute("mount | grep sda1")
drive_connected = drive_connected and true or false
end
screen.clear()
if _ui.focus == "tape_scroll" then
screen.font_size(8)
tape_list:redraw()
size_list:redraw()
elseif _ui.focus == "manage" then
screen.font_size(8)
screen.level(15)
local y_pos;
if tape_list.index == #tape_list.entries-1 then
y_pos = 21
elseif tape_list.index == #tape_list.entries then
y_pos = 32
else
y_pos = 10
end
screen.move(0,y_pos)
screen.text(tape_names[tape_list.index])
screen.move(60,y_pos)
screen.text(tape_sizes[tape_list.index])
local edit_options = {"copy to USB","copy to USB + delete","delete"}
if drive_connected then
for i = 1,3 do
screen.move(30,y_pos+(i*10))
screen.level(_ui.focus == "tape_scroll" and 3 or (_ui.manage_index == i and 15 or 3))
screen.text(edit_options[i])
end
else
screen.move(0,y_pos+10)
screen.text("NO USB DRIVE DETECTED")
end
elseif _ui.focus == "copying" then
screen.level(15)
screen.font_size(8)
screen.move(64,27)
screen.text_center("copying")
screen.move(64,37)
screen.text_center(tape_list.entries[tape_list.index])
screen.font_size(8)
screen.move(64,50)
if tape_sizes[tape_list.index] > 20 and tape_sizes[tape_list.index] < 40 then
screen.text_center("may take a minute")
elseif tape_sizes[tape_list.index] >= 40 and tape_sizes[tape_list.index] < 100 then
screen.text_center("may take several minutes")
elseif tape_sizes[tape_list.index] >= 100 and tape_sizes[tape_list.index] < 250 then
screen.text_center("will take several minutes")
elseif tape_sizes[tape_list.index] >= 250 then
screen.text_center("will take 10+ minutes")
elseif tape_sizes[tape_list.index] < 20 then
end
elseif _ui.focus == "successful copy" then
screen.level(15)
screen.font_size(8)
screen.move(64,32)
screen.text_center("success!")
elseif _ui.focus == "unsuccessful copy" then
screen.level(15)
screen.font_size(20)
screen.move(64,32)
screen.text_center("error!")
elseif _ui.focus == "delete confirmation" then
screen.level(15)
screen.font_size(8)
screen.move(64,27)
screen.text_center("K3: delete "..tape_list.entries[tape_list.index])
screen.move(64,37)
screen.text_center("K2: do not delete")
end
screen.update()
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment