From 893cfa91f1a9292980a651a5c89988d29d70fa06 Mon Sep 17 00:00:00 2001 From: IgorTimofeev Date: Sat, 30 Dec 2023 10:01:34 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9D=D0=A3=20=D0=A8=D0=9E=20=D0=96,=20=D0=9D?= =?UTF-8?q?=D0=9E=D0=92=D0=90=D0=AF=20=D0=90=D0=9F=D0=9F=D0=A1=D0=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Applications/Pioneer.app/Icon.pic | Bin 323 -> 304 bytes .../Pioneer.app/Images/Background.pic | Bin 14528 -> 14264 bytes .../Pioneer.app/Images/CuePressed.pic | Bin 373 -> 0 bytes .../Pioneer.app/Images/CueReleased.pic | Bin 377 -> 0 bytes .../Pioneer.app/Images/PlayPressed.pic | Bin 365 -> 0 bytes .../Pioneer.app/Images/PlayReleased.pic | Bin 365 -> 0 bytes .../Pioneer.app/Localizations/Arabic.lang | 3 - .../Pioneer.app/Localizations/Belarusian.lang | 3 - .../Pioneer.app/Localizations/Bengali.lang | 3 - .../Pioneer.app/Localizations/Bulgarian.lang | 3 - .../Pioneer.app/Localizations/Chinese.lang | 3 - .../Pioneer.app/Localizations/Dutch.lang | 3 - .../Pioneer.app/Localizations/English.lang | 3 - .../Pioneer.app/Localizations/Finnish.lang | 3 - .../Pioneer.app/Localizations/French.lang | 3 - .../Pioneer.app/Localizations/German.lang | 3 - .../Pioneer.app/Localizations/Hindi.lang | 3 - .../Pioneer.app/Localizations/Italian.lang | 3 - .../Pioneer.app/Localizations/Japanese.lang | 3 - .../Pioneer.app/Localizations/Korean.lang | 3 - .../Pioneer.app/Localizations/Lolcat.lang | 3 - .../Pioneer.app/Localizations/Polish.lang | 3 - .../Pioneer.app/Localizations/Portuguese.lang | 3 - .../Pioneer.app/Localizations/Russian.lang | 3 - .../Pioneer.app/Localizations/Slovak.lang | 3 - .../Pioneer.app/Localizations/Spanish.lang | 3 - .../Pioneer.app/Localizations/Ukrainian.lang | 3 - Applications/Pioneer.app/Main.lua | 834 ++++++++++++------ 28 files changed, 554 insertions(+), 343 deletions(-) delete mode 100644 Applications/Pioneer.app/Images/CuePressed.pic delete mode 100644 Applications/Pioneer.app/Images/CueReleased.pic delete mode 100644 Applications/Pioneer.app/Images/PlayPressed.pic delete mode 100644 Applications/Pioneer.app/Images/PlayReleased.pic delete mode 100644 Applications/Pioneer.app/Localizations/Arabic.lang delete mode 100644 Applications/Pioneer.app/Localizations/Belarusian.lang delete mode 100644 Applications/Pioneer.app/Localizations/Bengali.lang delete mode 100644 Applications/Pioneer.app/Localizations/Bulgarian.lang delete mode 100644 Applications/Pioneer.app/Localizations/Chinese.lang delete mode 100644 Applications/Pioneer.app/Localizations/Dutch.lang delete mode 100644 Applications/Pioneer.app/Localizations/English.lang delete mode 100644 Applications/Pioneer.app/Localizations/Finnish.lang delete mode 100644 Applications/Pioneer.app/Localizations/French.lang delete mode 100644 Applications/Pioneer.app/Localizations/German.lang delete mode 100644 Applications/Pioneer.app/Localizations/Hindi.lang delete mode 100644 Applications/Pioneer.app/Localizations/Italian.lang delete mode 100644 Applications/Pioneer.app/Localizations/Japanese.lang delete mode 100644 Applications/Pioneer.app/Localizations/Korean.lang delete mode 100644 Applications/Pioneer.app/Localizations/Lolcat.lang delete mode 100644 Applications/Pioneer.app/Localizations/Polish.lang delete mode 100644 Applications/Pioneer.app/Localizations/Portuguese.lang delete mode 100644 Applications/Pioneer.app/Localizations/Russian.lang delete mode 100644 Applications/Pioneer.app/Localizations/Slovak.lang delete mode 100644 Applications/Pioneer.app/Localizations/Spanish.lang delete mode 100644 Applications/Pioneer.app/Localizations/Ukrainian.lang diff --git a/Applications/Pioneer.app/Icon.pic b/Applications/Pioneer.app/Icon.pic index 4ec117454c88a42851bed68ac0618ef5e37255fd..4b9502740f58a9f8dfa3a24493ff8884fd63c399 100644 GIT binary patch literal 304 zcmXw!F$%&!6hvqLCRuwgV436^f?#D~ypV+!Ho?xyO#;>yf`Z^R?0@YYbat}_0*~3u z?8|DtTuhubUs&z0D6s(_?RLbPK`bjVZ=`?u!e-vREJv}5XiU{ zT7?cr9=`cn^;>oV<|RHbMi?Dp$0AqX62UmjXQU=zF{*^ijC!iXD~b11qjsD2Mg23T nf=oRfQBkjmxsb)uc*$kS4uPO|FW45mo!GRb3*$AzgIzz literal 323 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 delta 5840 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 diff --git a/Applications/Pioneer.app/Images/CuePressed.pic b/Applications/Pioneer.app/Images/CuePressed.pic deleted file mode 100644 index f84076e90aa070a65122ba0475d902864f1617b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 373 zcmXv~J5EF~5cACBeR?jiCE^+@AyLu55jX<Q0xcgj0C_K!0~qnQ82!Q+dRPl8xi d76(<{St<`O2jo*LZYJCJpxCde^25k(IJ|xHp!!;-$h8=0uC@Y-~`}g zL+99IdnQ+_%ZnJj{32T3VGY0$rfq{BV#{rVK(O-O2XKnDKma-se`ta&hbEG=^wizK zmix{|TT+9lz7s2-*kFRPAw3dSI;_Z?k#m+Ga86FastIo@GK>m!s&2IOQboq72VZly zGpTf8^w66g=Ib@gpg}F~W#U6I9@~TEJ8bbq(OaHclH*S0AjkeVQ*;!|zZfh%OZ_N_ f8|vbq$~$X?!*Jbe2TQMQ%=a;0Lq0I$n`Q0~XBJB3 diff --git a/Applications/Pioneer.app/Images/PlayPressed.pic b/Applications/Pioneer.app/Images/PlayPressed.pic deleted file mode 100644 index 250cd223f2290dbbfa78d833c6ed1bea8764fb6a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 365 zcmXw#Jx&BM427Q)&rDiwK#8~p5)u^+TxP3`#GgcmXtvrULrFu$A|!4B2k=GUjW;OG z=j8c4JC|n{=P9ZDA|2xj5FVCDwiMPI@XE@gqhhkM?I^a~biriH-cbNs?m8x`JI6?~ z_R*1kM^@jEae{zozA z5i1WJC#&z0T{|9HXxMOy4M!do!OC;5f?~Axi5Wk%i&pQ~2Zs1+8Lu2{u(l7rf#@(Z ZH$o30ej+77WEcZN3?Yu|m#uAr@CR_HQ|SNz diff --git a/Applications/Pioneer.app/Images/PlayReleased.pic b/Applications/Pioneer.app/Images/PlayReleased.pic deleted file mode 100644 index dbdacf3ba997110fee41b3bc4b576b6c26d52b81..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 365 zcmXw#Jx&BM427Q^PbMulphR2)327^u}Y(9IZ1^5`g-wd@9xt~Wz4>w0h`K-as0N$ZPaux9OJ zp!GY^`i6`%FoKpRau*8IwY!PdjE=#K+drD|s|#fa1)}ZCgxT7V)?4iGZL^D(#mj9G zEe``{t?!gw8;>Q{u;IM#izr|%&m&?%)b@!5Kb8zy?|Z^be6@_d4K`^zux~&zE#?~_ WN0A-TBuq3#1_$O4`^kD4f&T!SR!U(2 diff --git a/Applications/Pioneer.app/Localizations/Arabic.lang b/Applications/Pioneer.app/Localizations/Arabic.lang deleted file mode 100644 index 07bd42d7..00000000 --- a/Applications/Pioneer.app/Localizations/Arabic.lang +++ /dev/null @@ -1,3 +0,0 @@ -{ - greeting = "مرحبًا،", -} \ No newline at end of file diff --git a/Applications/Pioneer.app/Localizations/Belarusian.lang b/Applications/Pioneer.app/Localizations/Belarusian.lang deleted file mode 100644 index f8b21f0d..00000000 --- a/Applications/Pioneer.app/Localizations/Belarusian.lang +++ /dev/null @@ -1,3 +0,0 @@ -{ - greeting = "Добры дзень,", -} \ No newline at end of file diff --git a/Applications/Pioneer.app/Localizations/Bengali.lang b/Applications/Pioneer.app/Localizations/Bengali.lang deleted file mode 100644 index 1ca562ce..00000000 --- a/Applications/Pioneer.app/Localizations/Bengali.lang +++ /dev/null @@ -1,3 +0,0 @@ -{ - greeting = "হ্যালো,", -} \ No newline at end of file diff --git a/Applications/Pioneer.app/Localizations/Bulgarian.lang b/Applications/Pioneer.app/Localizations/Bulgarian.lang deleted file mode 100644 index c908fb8e..00000000 --- a/Applications/Pioneer.app/Localizations/Bulgarian.lang +++ /dev/null @@ -1,3 +0,0 @@ -{ - greeting = "Здравейте,", -} \ No newline at end of file diff --git a/Applications/Pioneer.app/Localizations/Chinese.lang b/Applications/Pioneer.app/Localizations/Chinese.lang deleted file mode 100644 index 255f44ee..00000000 --- a/Applications/Pioneer.app/Localizations/Chinese.lang +++ /dev/null @@ -1,3 +0,0 @@ -{ - greeting = "你好, " -} diff --git a/Applications/Pioneer.app/Localizations/Dutch.lang b/Applications/Pioneer.app/Localizations/Dutch.lang deleted file mode 100644 index dc212b43..00000000 --- a/Applications/Pioneer.app/Localizations/Dutch.lang +++ /dev/null @@ -1,3 +0,0 @@ -{ - greeting = "Hallo, " -} diff --git a/Applications/Pioneer.app/Localizations/English.lang b/Applications/Pioneer.app/Localizations/English.lang deleted file mode 100644 index dab7151d..00000000 --- a/Applications/Pioneer.app/Localizations/English.lang +++ /dev/null @@ -1,3 +0,0 @@ -{ - greeting = "Hello, " -} \ No newline at end of file diff --git a/Applications/Pioneer.app/Localizations/Finnish.lang b/Applications/Pioneer.app/Localizations/Finnish.lang deleted file mode 100644 index f5667f85..00000000 --- a/Applications/Pioneer.app/Localizations/Finnish.lang +++ /dev/null @@ -1,3 +0,0 @@ -{ - greeting = "Hei,", -} \ No newline at end of file diff --git a/Applications/Pioneer.app/Localizations/French.lang b/Applications/Pioneer.app/Localizations/French.lang deleted file mode 100644 index cce92ece..00000000 --- a/Applications/Pioneer.app/Localizations/French.lang +++ /dev/null @@ -1,3 +0,0 @@ -{ - greeting = "Bonjour, " -} \ No newline at end of file diff --git a/Applications/Pioneer.app/Localizations/German.lang b/Applications/Pioneer.app/Localizations/German.lang deleted file mode 100644 index 3687ffdc..00000000 --- a/Applications/Pioneer.app/Localizations/German.lang +++ /dev/null @@ -1,3 +0,0 @@ -{ - greeting = "Gruß, " -} \ No newline at end of file diff --git a/Applications/Pioneer.app/Localizations/Hindi.lang b/Applications/Pioneer.app/Localizations/Hindi.lang deleted file mode 100644 index 996afd04..00000000 --- a/Applications/Pioneer.app/Localizations/Hindi.lang +++ /dev/null @@ -1,3 +0,0 @@ -{ - greeting = "नमस्ते,", -} \ No newline at end of file diff --git a/Applications/Pioneer.app/Localizations/Italian.lang b/Applications/Pioneer.app/Localizations/Italian.lang deleted file mode 100644 index 3dd6851c..00000000 --- a/Applications/Pioneer.app/Localizations/Italian.lang +++ /dev/null @@ -1,3 +0,0 @@ -{ - greeting = "Ciao, " -} \ No newline at end of file diff --git a/Applications/Pioneer.app/Localizations/Japanese.lang b/Applications/Pioneer.app/Localizations/Japanese.lang deleted file mode 100644 index 3a8e2f34..00000000 --- a/Applications/Pioneer.app/Localizations/Japanese.lang +++ /dev/null @@ -1,3 +0,0 @@ -{ - greeting = "こんにちは、", -} \ No newline at end of file diff --git a/Applications/Pioneer.app/Localizations/Korean.lang b/Applications/Pioneer.app/Localizations/Korean.lang deleted file mode 100644 index 63b58bad..00000000 --- a/Applications/Pioneer.app/Localizations/Korean.lang +++ /dev/null @@ -1,3 +0,0 @@ -{ - greeting = "여보세요,", -} \ No newline at end of file diff --git a/Applications/Pioneer.app/Localizations/Lolcat.lang b/Applications/Pioneer.app/Localizations/Lolcat.lang deleted file mode 100644 index 0cded96e..00000000 --- a/Applications/Pioneer.app/Localizations/Lolcat.lang +++ /dev/null @@ -1,3 +0,0 @@ -{ - greeting = "Y haol thar, " -} \ No newline at end of file diff --git a/Applications/Pioneer.app/Localizations/Polish.lang b/Applications/Pioneer.app/Localizations/Polish.lang deleted file mode 100644 index b022f96a..00000000 --- a/Applications/Pioneer.app/Localizations/Polish.lang +++ /dev/null @@ -1,3 +0,0 @@ -{ - greeting = "Witam,", -} \ No newline at end of file diff --git a/Applications/Pioneer.app/Localizations/Portuguese.lang b/Applications/Pioneer.app/Localizations/Portuguese.lang deleted file mode 100644 index ea79c637..00000000 --- a/Applications/Pioneer.app/Localizations/Portuguese.lang +++ /dev/null @@ -1,3 +0,0 @@ -{ - greeting = "Olá,", -} \ No newline at end of file diff --git a/Applications/Pioneer.app/Localizations/Russian.lang b/Applications/Pioneer.app/Localizations/Russian.lang deleted file mode 100644 index 42abc380..00000000 --- a/Applications/Pioneer.app/Localizations/Russian.lang +++ /dev/null @@ -1,3 +0,0 @@ -{ - greeting = "Привет, " -} \ No newline at end of file diff --git a/Applications/Pioneer.app/Localizations/Slovak.lang b/Applications/Pioneer.app/Localizations/Slovak.lang deleted file mode 100644 index 12705658..00000000 --- a/Applications/Pioneer.app/Localizations/Slovak.lang +++ /dev/null @@ -1,3 +0,0 @@ -{ - greeting = "Ahoj,", -} \ No newline at end of file diff --git a/Applications/Pioneer.app/Localizations/Spanish.lang b/Applications/Pioneer.app/Localizations/Spanish.lang deleted file mode 100644 index 7618abf4..00000000 --- a/Applications/Pioneer.app/Localizations/Spanish.lang +++ /dev/null @@ -1,3 +0,0 @@ -{ - greeting = "Hola, " -} \ No newline at end of file diff --git a/Applications/Pioneer.app/Localizations/Ukrainian.lang b/Applications/Pioneer.app/Localizations/Ukrainian.lang deleted file mode 100644 index 7a026018..00000000 --- a/Applications/Pioneer.app/Localizations/Ukrainian.lang +++ /dev/null @@ -1,3 +0,0 @@ -{ - greeting = "Привіт, " -} \ No newline at end of file diff --git a/Applications/Pioneer.app/Main.lua b/Applications/Pioneer.app/Main.lua index 74356cb7..45266750 100644 --- a/Applications/Pioneer.app/Main.lua +++ b/Applications/Pioneer.app/Main.lua @@ -14,6 +14,26 @@ local event = require("Event") local currentScriptDirectory = filesystem.path(system.getCurrentScript()) +local configPath = paths.user.applicationData .. "Pioneer/Config.cfg" +local config + +if filesystem.exists(configPath) then + config = filesystem.readTable(configPath) +else + config = { + tapes = { + + }, + lastTape = nil + } +end + +local function saveConfig() + filesystem.writeTable(configPath, config) +end + +-------------------------------------------------------------------------------- + local function loadImage(name) local result, reason = image.load(currentScriptDirectory .. "Images/" .. name .. ".pic") @@ -25,62 +45,92 @@ local function loadImage(name) end local speedSlider -local speedMin = 0.25 -local speedMax = 1.75 -local bpmMin = 40 -local bpmMax = 200 +local blinkState = false +local blinkUptime = 0 +local blinkInterval = 0.5 local powerButton local tapes local tapeIndex -local tape +local tape, tapeConfig local tapeWritingProgress +local function invoke(...) + return component.invoke(tape.address, ...) +end + +local function getCurrentTapeSpeed() + local speed = 2 * speedSlider.value - 1 + + if tapeConfig.speedIndex == 0 then + speed = speed * 0.25 + elseif tapeConfig.speedIndex == 1 then + speed = speed * 0.5 + elseif tapeConfig.speedIndex == 2 then + speed = speed * 0.75 + end + + return speed +end + local function updateCurrentTapeSpeed() - component.invoke(tape.address, "setSpeed", speedMin + tape.speed * (speedMax - speedMin)) + local speed = getCurrentTapeSpeed() + + invoke("setSpeed", 1 + speed * 0.75) end local function updateCurrentTape() tape = tapes[tapeIndex] - speedSlider.value = tape.speed + tapeConfig = config.tapes[tape.address] + speedSlider.value = tapeConfig.speed + config.lastTape = tape.address updateCurrentTapeSpeed() end -local function incrementTape(next) - tapeIndex = tapeIndex + (next and 1 or -1) - - if tapeIndex > #tapes then - tapeIndex = 1 - elseif tapeIndex < 1 then - tapeIndex = #tapes - end - - updateCurrentTape() -end - local function updateTapes() tapes = {} tapeIndex = 1 + tape = nil + tapeConfig = nil + + local counter = 1 for address in component.list("tape_drive") do table.insert(tapes, { address = address, - size = component.invoke(address, "getSize"), - speed = 0.5, - cues = {}, + size = component.invoke(address, "getSize") }) + + if not config.tapes[address] then + config.tapes[address] = { + speed = 0.5, + speedIndex = 0, + cue = 0, + cues = {}, + cueIndex = 1, + hotCues = {} + } + end + + if config.lastTape == address then + tapeIndex = counter + end + + counter = counter + 1 end - updateCurrentTape() + if #tapes > 0 then + updateCurrentTape() + end end -------------------------------- Round mini button ------------------------------------------------ local function roundMiniButtonDraw(button) - local bg, fg = button.animationCurrentBackground, powerButton.pressed and button.animationCurrentText or 0x0 + local bg, fg = button.animationCurrentBackground, (powerButton.pressed or button.ignoresPower) and button.animationCurrentText or 0x0 -- Background screen.drawRectangle(button.x + 1, button.y + 1, button.width - 2, button.height - 2, bg, fg, " ") @@ -119,7 +169,7 @@ end local function roundTinyButtonDraw(button) - local bg, fg = button.animationCurrentBackground, powerButton.pressed and button.animationCurrentText or 0x0 + local bg, fg = button.animationCurrentBackground, (powerButton.pressed or button.ignoresPower) and button.animationCurrentText or 0x0 -- Left screen.drawText(button.x, button.y, bg, "⢰") @@ -133,18 +183,6 @@ local function roundTinyButtonDraw(button) -- Lower screen.drawText(button.x, button.y + 1, bg, "⠈⠛⠛⠁") - - -- -- Left - -- screen.drawText(button.x, button.y, bg, "⣾") - - -- -- Middle - -- screen.set(button.x + 1, button.y, bg, fg, "⠄") - - -- -- Right - -- screen.drawText(button.x + 2, button.y, bg, "⡆") - - -- -- Lower - -- screen.drawText(button.x, button.y + 1, bg, "⠈⠉") end local function newRoundTinyButton(x, y, ...) @@ -156,91 +194,6 @@ local function newRoundTinyButton(x, y, ...) return button end --------------------------------- UpperButtons ------------------------------------------------ - -local function upperButtonDraw(button) - local bg, fg = button.animationCurrentBackground, powerButton.pressed and button.animationCurrentText or 0x0 - - -- Background - screen.drawRectangle(button.x + 1, button.y + 1, button.width - 2, button.height - 2, bg, fg, " ") - - -- Upper - screen.drawText(button.x, button.y, fg, "⢀" .. string.rep("⣀", button.width - 2) .. "⡀") - - -- Left - screen.drawText(button.x, button.y + 1, fg, "⢸") - - -- Middle - screen.drawText(math.floor(button.x + button.width / 2 - unicode.len(button.text) / 2), button.y + 1, fg, button.text) - - -- Right - screen.drawText(button.x + button.width - 1, button.y + 1, fg, "⡇") - - -- Lower - screen.drawText(button.x, button.y + button.height - 1, fg, "⠈" .. string.rep("⠉", button.width - 2) .. "⠁") - -end - -local function upperButtonEventHandler(workspace, button, e1, e2, e3, e4, e5) - if e1 == "touch" and powerButton.pressed then - button:press() - end -end - -local function newUpperButton(x, y, width, ...) - local button = GUI.button(x, y, width, 3, ...) - - button.pressed = false - button.draw = upperButtonDraw - button.eventHandler = upperButtonEventHandler - - return button -end - --------------------------------- Round mini button ------------------------------------------------ - -local function hotCueButtonDraw(button) - local bg, fg = button.animationCurrentBackground, powerButton.pressed and button.animationCurrentText or 0x2D2D2D - - -- Upper - screen.drawText(button.x, button.y, bg, "⢀" .. string.rep("⣀", button.width - 2) .. "⡀") - - -- Left - screen.drawText(button.x, button.y + 1, bg, "⢸") - - -- Middle - screen.set(button.x + 1, button.y + 1, 0x2D2D2D, 0x5A5A5A, "⣤") - screen.set(button.x + 2, button.y + 1, bg, 0x787878, "⠤") - - screen.set(button.x + 3, button.y + 1, bg, fg, button.text) - - screen.set(button.x + 4, button.y + 1, bg, 0x787878, "⠒") - screen.set(button.x + 5, button.y + 1, 0x2D2D2D, 0x5A5A5A, "⠛") - - -- Right - screen.drawText(button.x + button.width - 1, button.y + 1, bg, "⡇") - - -- Lower - screen.drawText(button.x, button.y + button.height - 1, bg, "⠈" .. string.rep("⠉", button.width - 2) .. "⠁") - -end - -local function hotCueButtonEventHandler(workspace, button, e1, e2, e3, e4, e5) - if e1 == "touch" then - button:press() - end -end - -local function newHotCueButton(x, y, defaultForeground, pressedForeground, text) - local button = GUI.button(x, y, 7, 3, 0x1E1E1E, defaultForeground, 0x0, pressedForeground, text) - - button.draw = hotCueButtonDraw - button.eventHandler = hotCueButtonEventHandler - - return button -end - - -------------------------------- Window ------------------------------------------------ local backgroundImage = loadImage("Background") @@ -250,7 +203,6 @@ local workspace, window, menu = system.addWindow(GUI.window(1, 1, 78, 49)) window.drawShadow = false - -------------------------------- Jog ------------------------------------------------ local jogImages = {} @@ -259,17 +211,12 @@ for i = 1, 12 do jogImages[i] = loadImage("Jog" .. i) end +-------------------------------- Overlay ------------------------------------------------ -local function getIsPlaying() - return component.invoke(tape.address, "getState") == "PLAYING" -end - --------------------------------- Background ------------------------------------------------ - -local windowBackground = window:addChild(GUI.object(1, 1, window.width, window.height)) +local overlay = window:addChild(GUI.object(1, 1, window.width, window.height)) local currentJogIndex = 1 -local displayWidth, displayHeight = 33, 9 +local displayWidth, displayHeight = 33, 10 local function displayDrawProgressBar(x, y, width, progress) local progressActiveWidth = math.floor(progress * width) @@ -278,9 +225,8 @@ local function displayDrawProgressBar(x, y, width, progress) screen.drawText(x + progressActiveWidth, y, 0x4B4B4B, string.rep("━", width - progressActiveWidth)) end -windowBackground.draw = function(windowBackground) - -- Background - screen.drawImage(windowBackground.x, windowBackground.y, backgroundImage) +overlay.draw = function(overlay) + screen.drawImage(overlay.x, overlay.y, backgroundImage) -- Ignoring if power is off if not powerButton.pressed then @@ -288,21 +234,18 @@ windowBackground.draw = function(windowBackground) end -- Power indicator - screen.drawText(windowBackground.x + 73, windowBackground.y + 3, 0xFF0000, "●") + screen.drawText(overlay.x + 73, overlay.y + 3, 0xFF0000, "●") -- Speed slider indicator - screen.drawText(windowBackground.x + 68, windowBackground.y + 39, 0xFFDB40, "⠆") + screen.drawText(overlay.x + 68, overlay.y + 39, 0xFFDB40, "⠆") -- Jog - screen.drawImage(windowBackground.x + 33, windowBackground.y + 29, jogImages[currentJogIndex]) + screen.drawImage(overlay.x + 33, overlay.y + 29, jogImages[currentJogIndex]) -- Display - local displayX, displayY = windowBackground.x + 22, windowBackground.y + 3 - local displayUpperText + local displayX, displayY = overlay.x + 22, overlay.y + 3 if tapeWritingProgress then - displayUpperText = "Writing in progress" - local progressWidth = displayWidth - 4 displayDrawProgressBar( @@ -311,38 +254,104 @@ windowBackground.draw = function(windowBackground) progressWidth, tapeWritingProgress ) - else - -- UpperText - displayUpperText = component.invoke(tape.address, "getLabel") - if not displayUpperText or #displayUpperText == 0 then - displayUpperText = "Untitled tape" + -- UpperText + local text = "Writing in progress" + + screen.drawText( + math.floor(displayX + displayWidth / 2 - #text / 2), + displayY + 1, + 0xE1E1E1, + text + ) + else + -- Label + local label = tape and invoke("getLabel") or "No tape" + + if not label or #label == 0 then + label = "Untitled tape" end - -- BPM - local bpmText = tostring(math.floor(bpmMin + speedSlider.value * (bpmMax - bpmMin))) .. " bpm" - local bpmWidth = #bpmText + 4 - - local bpmX = displayX + displayWidth - 2 - bpmWidth - local bpmY = displayY + displayHeight - 5 + screen.drawRectangle(displayX, displayY, displayWidth, 1, 0x004980, 0xE1E1E1, " ") + screen.drawText(displayX + 1, displayY, 0xE1E1E1, text.limit("♪ " .. label, displayWidth - 3)) - screen.drawFrame(bpmX, bpmY, bpmWidth, 3, 0xE1E1E1) - screen.drawText(bpmX + 2, bpmY + 1, 0xE1E1E1, bpmText) + if tape then + -- Stats + local position = invoke("getPosition") + local statsX = displayX + 2 + local statsY = displayY + displayHeight - 5 - -- Lower track - local progressWidth = displayWidth - 4 + -- Track index + screen.drawText(statsX, statsY, 0xE1E1E1, "Track") + screen.drawText(statsX, statsY + 1, 0xE1E1E1, string.format("%02d", tapeIndex)) - displayDrawProgressBar( - math.floor(displayX + displayWidth / 2 - progressWidth / 2), - displayY + displayHeight - 2, - progressWidth, - tape.size == 0 and 0 or component.invoke(tape.address, "getPosition") / tape.size - ) + -- Time + local timeSecondsTotal = position / (1500 * 4) + local timeMinutes = math.floor(timeSecondsTotal / 60) + local timeSeconds, timeMilliseconds = math.modf(timeSecondsTotal - timeMinutes * 60) + screen.drawText(statsX + 10, statsY + 1, 0xE1E1E1, string.format("%02d", timeMinutes) .. "m:" .. string.format("%02d", timeSeconds) .. "s".. string.format("%03d", math.floor(timeMilliseconds * 1000))) + + -- Tempo + screen.drawText(statsX + 24, statsY, 0xE1E1E1, "Tempo") + screen.drawText(statsX + 26, statsY + 1, 0xE1E1E1, string.format("%02d", math.floor(getCurrentTapeSpeed() * 100)) .. "%") + + -- Tempo index + + -- Track + local trackWidth = displayWidth - 4 + local trackHeight = 3 + statsY = statsY + 2 + + screen.drawRectangle( + statsX, + statsY + 1, + trackWidth, + trackHeight - 2, + 0x2D2D2D, + 0xE1E1E1, + " " + ) + + screen.drawText( + math.floor(statsX + (tape.size == 0 and 0 or position / tape.size) * trackWidth), + statsY + 1, + 0xE1E1E1, + "│" + ) + + -- Memory cues + local cueY = statsY + + for i = 1, #tapeConfig.cues do + screen.drawText( + statsX + math.floor(tapeConfig.cues[i] / tape.size * trackWidth), + cueY, + i == tapeConfig.cueIndex and 0xE1E1E1 or 0xCC0000, + "•" + ) + end + + -- Hot cues + for name, position in pairs(tapeConfig.hotCues) do + screen.drawText( + statsX + math.floor(position / tape.size * trackWidth), + cueY, + 0x66FF40, + "•" .. name + ) + end + + -- Current cue + cueY = statsY + trackHeight - 1 + + screen.drawText( + statsX + math.floor(tapeConfig.cue / tape.size * trackWidth), + cueY, + 0xFFB640, + "•" + ) + end end - - -- UpperText - displayUpperText = text.limit(displayUpperText, displayWidth - 2) - screen.drawText(math.floor(displayX + displayWidth / 2 - #displayUpperText / 2), displayY + 1, 0xE1E1E1, displayUpperText) end -------------------------------- Power button ------------------------------------------------ @@ -376,12 +385,8 @@ end -------------------------------- ImageButton ------------------------------------------------ -local imageButtonBlink = false -local imageButtonBlinkUptime = 0 -local imageButtonBlinkInterval = 0.5 - local function imageButtonDraw(button) - screen.drawImage(button.x, button.y, (powerButton.pressed and (not button.blinking or imageButtonBlink)) and button.imageOn or button.imageOff) + screen.drawImage(button.x, button.y, (powerButton.pressed and (not button.blinking or blinkState)) and button.imageOn or button.imageOff) end local function newImageButton(x, y, width, height, name) @@ -400,42 +405,116 @@ end local speedSliderImage = loadImage("SpeedSlider") -speedSlider = window:addChild(GUI.object(71, 33, 5, 14)) +speedSlider = window:addChild(GUI.object(71, 33, 5, 15)) +speedSlider.value = 0.5 speedSlider.draw = function(speedSlider) -- screen.drawRectangle(speedSlider.x, speedSlider.y, speedSlider.width, speedSlider.height, 0xFF0000, 0x0, " ") local x = speedSlider.x - local y = speedSlider.y + math.floor((1 - speedSlider.value) * (speedSlider.height - image.getHeight(speedSliderImage) / 2)) + local y = speedSlider.y + math.floor((1 - speedSlider.value) * speedSlider.height) - math.floor((1 - speedSlider.value) * 3) screen.drawImage(x, y, speedSliderImage) end speedSlider.eventHandler = function(workspace, speedSlider, e1, e2, e3, e4) - if e1 == "touch" or e1 == "drag" then - speedSlider.value = 1 - ((e4 - speedSlider.y) / speedSlider.height) - tape.speed = speedSlider.value + if (e1 == "touch" or e1 == "drag") then + if e4 == speedSlider.y + speedSlider.height - 1 then + speedSlider.value = 0 + elseif e4 == math.floor(speedSlider.y + speedSlider.height / 2) then + speedSlider.value = 0.5 + else + speedSlider.value = 1 - ((e4 - speedSlider.y) / speedSlider.height) + end - updateCurrentTapeSpeed() + if tape and powerButton.pressed then + tapeConfig.speed = speedSlider.value + + updateCurrentTapeSpeed() + end workspace:draw() end end --------------------------------- File/url/label upper buttons ------------------------------------------------ +-------------------------------- Display buttons ------------------------------------------------ -local _ = window:addChild(newUpperButton(14, 1, 7, 0x1E1E1E, 0xF0F0F0, 0x0F0F0F, 0xA5A5A5, "Help")) -local urlUpperButton = window:addChild(newUpperButton(14, 4, 7, 0x1E1E1E, 0x3349FF, 0x0F0F0F, 0x002480, "Url")) -local fileUpperButton = window:addChild(newUpperButton(14, 7, 7, 0x1E1E1E, 0xFFDB40, 0x0F0F0F, 0x996D00, "File")) +local function displayButtonDraw(button) + local bg, fg = button.animationCurrentBackground, (powerButton.pressed or button.ignoresPower) and button.animationCurrentText or 0x4B4B4B -local _ = window:addChild(newRoundTinyButton(14, 12, 0x0F0F0F, 0xFF0000, 0x0F0F0F, 0xFF0000, "⢠⡄")) -local _ = window:addChild(newRoundTinyButton(18, 12, 0x0F0F0F, 0x2D2D2D, 0x0F0F0F, 0x2D2D2D, "⢠⡄")) + -- Background + screen.drawRectangle(button.x + 1, button.y + 1, button.width - 2, button.height - 2, bg, fg, " ") -local labelUpperButton = window:addChild(newUpperButton(23, 1, 9, 0x1E1E1E, 0xFFDB40, 0x0F0F0F, 0x996D00, "Label")) -local _ = window:addChild(newUpperButton(33, 1, 9, 0x1E1E1E, 0xFFDB40, 0x0F0F0F, 0x996D00, " ")) -local _ = window:addChild(newUpperButton(43, 1, 9, 0x1E1E1E, 0xFFDB40, 0x0F0F0F, 0x996D00, " ")) + -- Upper + screen.drawText(button.x, button.y, fg, "⢀" .. string.rep("⣀", button.width - 2) .. "⡀") + + -- Left + screen.drawText(button.x, button.y + 1, fg, "⢸") + + -- Middle + screen.drawText(math.floor(button.x + button.width / 2 - unicode.len(button.text) / 2), button.y + 1, fg, button.text) + + -- Right + screen.drawText(button.x + button.width - 1, button.y + 1, fg, "⡇") + + -- Lower + screen.drawText(button.x, button.y + button.height - 1, fg, "⠈" .. string.rep("⠉", button.width - 2) .. "⠁") + +end + +local function newDisplayButton(x, y, width, ...) + local button = GUI.button(x, y, width, 3, ...) + + button.pressed = false + button.draw = displayButtonDraw + + return button +end + +local helpButton = window:addChild(newDisplayButton(14, 1, 7, 0x0F0F0F, 0xF0F0F0, 0x0, 0xA5A5A5, "Help")) +helpButton.onTouch = function() + if not powerButton.pressed then + return + end + + local container = GUI.addBackgroundContainer(workspace, true, true, "Help") + container.layout:removeChildren() + + local lines = { + "Pioneer CDJ-2000 nexus", + " ", + "Pro-grade digital DJ deck for Computronix", + "tape drives and DFPWM audio codec.", + "To convert your favorite tracks, use", + "https://music.madefor.cc", + " ", + "Designed by Pioneer Corporation in Japan", + " ", + "Developed and adapted for MineOS by", + "Igor Timofeev, vk.com/id7799889", + "Maxim Afonin, @140bpmdubstep" + } + + local textBox = container.layout:addChild(GUI.textBox(1, 1, container.layout.width, #lines, nil, 0xB4B4B4, lines, 1, 0, 0)) + textBox:setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) + textBox.eventHandler = container.panel.eventHandler + + workspace:draw() +end + +local closeButton = window:addChild(newDisplayButton(14, 4, 7, 0x0F0F0F, 0x4B4B4B, 0x0, 0x3349FF, "Close")) +closeButton.onTouch = function() + window:remove() +end + +local wipeButton = window:addChild(newDisplayButton(14, 7, 7, 0x0F0F0F, 0x4B4B4B, 0x0, 0xFFDB40, "Wipe")) + +local fileButton = window:addChild(newDisplayButton(23, 1, 10, 0x0F0F0F, 0x4B4B4B, 0x0, 0xFFDB40, "File")) +fileButton.onTouch = function() + if not tape or not powerButton.pressed then + return + end -fileUpperButton.onTouch = function() local filesystemDialog = GUI.addFilesystemDialog(workspace, true, 50, math.floor(window.height * 0.8), "Confirm", "Cancel", "File name", "/") filesystemDialog:setMode(GUI.IO_MODE_OPEN, GUI.IO_MODE_FILE) @@ -444,7 +523,7 @@ fileUpperButton.onTouch = function() filesystemDialog:show() filesystemDialog.onSubmit = function(path) - local tapeSpaceFree = tape.size - component.invoke(tape.address, "getPosition") + local tapeSpaceFree = tape.size - invoke("getPosition") local fileSize = filesystem.size(path) if fileSize > tapeSpaceFree then @@ -454,7 +533,7 @@ fileUpperButton.onTouch = function() local file = filesystem.open(path, "rb") - component.invoke(tape.address, "stop") + invoke("stop") local bytesWritten, chunk = 0 while true do @@ -464,12 +543,12 @@ fileUpperButton.onTouch = function() break end - if not component.invoke(tape.address, "isReady") then + if not invoke("isReady") then GUI.alert("Tape was removed during writing") break end - component.invoke(tape.address, "write", chunk) + invoke("write", chunk) bytesWritten = bytesWritten + #chunk tapeWritingProgress = bytesWritten / fileSize @@ -477,19 +556,25 @@ fileUpperButton.onTouch = function() end file:close() - component.invoke(tape.address, "seek", -tape.size) + invoke("seek", -tape.size) tapeWritingProgress = nil end end -urlUpperButton.onTouch = function() +local urlButton = window:addChild(newDisplayButton(34, 1, 10, 0x0F0F0F, 0x4B4B4B, 0x0, 0xFFDB40, "Url")) + +local labelButton = window:addChild(newDisplayButton(45, 1, 10, 0x0F0F0F, 0x4B4B4B, 0x0, 0xFFDB40, "Label")) +labelButton.onTouch = function() + if not tape or not powerButton.pressed then + return + end + local container = GUI.addBackgroundContainer(workspace, true, true, title) - local input = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0x969696, 0xE1E1E1, 0x2D2D2D, "", "Url", false)) + local input = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0x969696, 0xE1E1E1, 0x2D2D2D, invoke("getLabel") or "", "New label", false)) input.onInputFinished = function() - - + invoke("setLabel", input.text) workspace:draw() end @@ -499,46 +584,28 @@ urlUpperButton.onTouch = function() end workspace:draw() - - return container end -labelUpperButton.onTouch = function() - local container = GUI.addBackgroundContainer(workspace, true, true, title) - - local input = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0x969696, 0xE1E1E1, 0x2D2D2D, component.invoke(tape.address, "getLabel") or "", "New label", false)) +-------------------------------- Quantize/time buttons ------------------------------------------------ - input.onInputFinished = function() - component.invoke(tape.address, "setLabel", input.text) - workspace:draw() - end - - container.panel.onTouch = function() - container:remove() - workspace:draw() - end - - workspace:draw() - - return container -end +local _ = window:addChild(newRoundTinyButton(14, 12, 0x0F0F0F, 0xFF0000, 0x0F0F0F, 0xFF0000, "⢠⡄")) +local _ = window:addChild(newRoundTinyButton(18, 12, 0x0F0F0F, 0x2D2D2D, 0x0F0F0F, 0x2D2D2D, "⢠⡄")) -------------------------------- Needle search ------------------------------------------------ local needleSearch = window:addChild(GUI.object(25, 15, 29, 2)) needleSearch.draw = function() - -- screen.drawRectangle(needleSearch.x, needleSearch.y, needleSearch.width, needleSearch.height, 0xFF0000, 0x0, " ") - screen.drawText(needleSearch.x, needleSearch.y, powerButton.pressed and 0xE1E1E1 or 0x0, "▲ ╷ ╷ ╷ ╷ ╷ ╷ ╷ ╷ ╷ ╷ ╷ ╷ ╷ ▲") end needleSearch.eventHandler = function(workspace, needleSearch, e1, e2, e3, e4) - if e1 == "touch" and powerButton.pressed and tape then - local position = component.invoke(tape.address, "getPosition") + if (e1 == "touch" or e1 == "drag") and powerButton.pressed and tape then local newPosition = math.floor((e3 - needleSearch.x) / needleSearch.width * tape.size) - component.invoke(tape.address, "seek", newPosition - position) + invoke("seek", newPosition - invoke("getPosition")) + + workspace:draw() end end @@ -547,6 +614,23 @@ end local previousTapeButton = window:addChild(newRoundMiniButton(2, 30, 0x2D2D2D, 0xFFB600, 0x0F0F0F, 0xCC9200, "<<")) local nextTapeButton = window:addChild(newRoundMiniButton(7, 30, 0x2D2D2D, 0xFFB600, 0x0F0F0F, 0xCC9200, ">>")) +local function incrementTape(next) + if not tape or not powerButton.pressed then + return + end + + tapeIndex = tapeIndex + (next and 1 or -1) + + if tapeIndex > #tapes then + tapeIndex = 1 + elseif tapeIndex < 1 then + tapeIndex = #tapes + end + + updateCurrentTape() + saveConfig() +end + previousTapeButton.onTouch = function() incrementTape(false) end @@ -570,13 +654,11 @@ end -------------------------------- Hot cue buttons ------------------------------------------------ -local hotCueButtonA = window:addChild(newHotCueButton(3, 13, 0x66FF40, 0x336D00, "A")) -local hotCueButtonB = window:addChild(newHotCueButton(3, 16, 0xFFB600, 0x664900, "B")) -local hotCueButtonB = window:addChild(newHotCueButton(3, 19, 0xFF2440, 0x660000, "C")) -local hotCueSaveLoad = window:addChild(newHotCueButton(3, 23, 0x0F0F0F, 0x000000, "⠰⠆")) +local hotCueRecCallButton -hotCueSaveLoad.draw = function(button) - local bg, fg = button.animationCurrentBackground, button.animationCurrentText +local function hotCueButtonDraw(button) + local bg = button.animationCurrentBackground + local fg = (powerButton.pressed and tape and (hotCueRecCallButton.rec or tapeConfig.hotCues[button.text])) and button.animationCurrentText or 0x2D2D2D -- Upper screen.drawText(button.x, button.y, bg, "⢀" .. string.rep("⣀", button.width - 2) .. "⡀") @@ -585,22 +667,80 @@ hotCueSaveLoad.draw = function(button) screen.drawText(button.x, button.y + 1, bg, "⢸") -- Middle - screen.set(button.x + 1, button.y + 1, 0x2D2D2D, fg, " ") - screen.set(button.x + 2, button.y + 1, 0x2D2D2D, fg, " ") - screen.set(button.x + 3, button.y + 1, 0x2D2D2D, fg, "⠶") - screen.set(button.x + 4, button.y + 1, 0x2D2D2D, fg, " ") - screen.set(button.x + 5, button.y + 1, 0x2D2D2D, fg, " ") + screen.set(button.x + 1, button.y + 1, 0x2D2D2D, 0x5A5A5A, "⣤") + screen.set(button.x + 2, button.y + 1, bg, 0x787878, "⠤") + + screen.set(button.x + 3, button.y + 1, bg, fg, button.text) + + screen.set(button.x + 4, button.y + 1, bg, 0x787878, "⠒") + screen.set(button.x + 5, button.y + 1, 0x2D2D2D, 0x5A5A5A, "⠛") -- Right screen.drawText(button.x + button.width - 1, button.y + 1, bg, "⡇") -- Lower screen.drawText(button.x, button.y + button.height - 1, bg, "⠈" .. string.rep("⠉", button.width - 2) .. "⠁") + end +local hotCueButtons = {} -hotCueButtonA.onTouch = function() - +local function newHotCueButton(x, y, index, text) + local button = GUI.button(x, y, 7, 3, 0x1E1E1E, 0x66FF40, 0x0, 0x336D00, text) + + button.draw = hotCueButtonDraw + + button.onTouch = function() + if not tape or not powerButton.pressed then + return + end + + local hotCuePosition = tapeConfig.hotCues[button.text] + + if hotCueRecCallButton.rec then + local position = invoke("getPosition") + tapeConfig.hotCues[button.text] = (not hotCuePosition or hotCuePosition ~= position) and position or nil + + workspace:draw() + saveConfig() + + elseif hotCuePosition then + invoke("seek", hotCuePosition - invoke("getPosition")) + workspace:draw() + end + end + + table.insert(hotCueButtons, button) + + return button +end + +-- local hotCueButtonA = window:addChild(newHotCueButton(3, 13, 0x66FF40, 0x336D00, "A")) +-- local hotCueButtonB = window:addChild(newHotCueButton(3, 16, 0xFFB600, 0x664900, "B")) +-- local hotCueButtonB = window:addChild(newHotCueButton(3, 19, 0xFF2440, 0x660000, "C")) + +local hotCueButtonA = window:addChild(newHotCueButton(3, 13, 1, "A")) +local hotCueButtonB = window:addChild(newHotCueButton(3, 16, 2, "B")) +local hotCueButtonB = window:addChild(newHotCueButton(3, 19, 3, "C")) + +hotCueRecCallButton = window:addChild(GUI.button(3, 23, 7, 3, 0x1E1E1E, 0x1E1E1E, 0x0, 0x0, "⠶")) +hotCueRecCallButton.rec = false +hotCueRecCallButton.draw = hotCueButtonDraw +hotCueRecCallButton.onTouch = function() + hotCueRecCallButton.rec = not hotCueRecCallButton.rec + + -- Updating buttons color scheme + local button + + for i = 1, #hotCueButtons do + button = hotCueButtons[i] + + button.colors.default.text = hotCueRecCallButton.rec and 0xFF2440 or 0x66FF40 + button.colors.pressed.text = hotCueRecCallButton.rec and 0x660000 or 0x336D00 + button.animationCurrentText = button.colors.default.text + end + + workspace:draw() end -------------------------------- Loop buttons ------------------------------------------------ @@ -669,36 +809,148 @@ loopButtonIn.onTouch = function() end --------------------------------- Cue button ------------------------------------------------ +-------------------------------- Cue memory buttons ------------------------------------------------ + +local function incrementCueIndex(value) + if not tape or not powerButton.pressed then + return + end + + if #tapeConfig.cues == 0 then + tapeConfig.cueIndex = 1 + return + end + + tapeConfig.cueIndex = tapeConfig.cueIndex + value + + if tapeConfig.cueIndex < 1 then + tapeConfig.cueIndex = #tapeConfig.cues + elseif tapeConfig.cueIndex > #tapeConfig.cues then + tapeConfig.cueIndex = 1 + end + + tapeConfig.cue = tapeConfig.cues[tapeConfig.cueIndex] + + -- invoke("stop") + invoke("seek", tapeConfig.cue - invoke("getPosition")) + + saveConfig() +end + +local cuePrevButton = window:addChild(newRoundTinyButton(50, 18, 0x0F0F0F, 0xFFB640, 0x0, 0x996D00, "⢔ ")) +cuePrevButton.onTouch = function() + incrementCueIndex(-1) +end + +local cueNextButton = window:addChild(newRoundTinyButton(54, 18, 0x0F0F0F, 0xFFB640, 0x0, 0x996D00, " ⡢")) +cueNextButton.onTouch = function() + incrementCueIndex(1) +end + +local cueDelButton = window:addChild(newRoundTinyButton(59, 18, 0x0F0F0F, 0x4B4B4B, 0x0, 0x2D2D2D, " ")) +cueDelButton.onTouch = function() + if not tape or not powerButton.pressed or #tapeConfig.cues == 0 then + return + end + + table.remove(tapeConfig.cues, tapeConfig.cueIndex) + + if tapeConfig.cueIndex > #tapeConfig.cues then + tapeConfig.cueIndex = math.max(tapeConfig.cueIndex - 1, 1) + end + + saveConfig() +end + +local cueMemButton = window:addChild(newRoundTinyButton(63, 18, 0x0F0F0F, 0x4B4B4B, 0x0, 0x2D2D2D, " ")) +cueMemButton.onTouch = function() + if not tape or not powerButton.pressed then + return + end + + local cue + + for i = 1, #tapeConfig.cues do + cue = tapeConfig.cues[i] + + if cue == tapeConfig.cue then + return + end + end + + table.insert(tapeConfig.cues, tapeConfig.cue) + table.sort(tapeConfig.cues) + + saveConfig() +end + +-------------------------------- Cue / play buttons ------------------------------------------------ local cueButton = window:addChild(newImageButton(2, window.height - 11, 9, 5, "Cue")) -cueButton.eventHandler = function(workspace, cueButton, e1) - if e1 == "touch" then - workspace:draw() - end -end - --------------------------------- Play button ------------------------------------------------ - local playButton = window:addChild(newImageButton(2, window.height - 5, 9, 5, "Play")) - playButton.blinking = true +cueButton.eventHandler = function(workspace, cueButton, e1) + if e1 == "touch" and tape and powerButton.pressed then + if playButton.blinking then + tapeConfig.cue = invoke("getPosition") + cueButton.blinking = false + + workspace:draw() + saveConfig() + else + invoke("seek", tapeConfig.cue - invoke("getPosition")) + + workspace:draw() + end + end +end + playButton.eventHandler = function(workspace, playButton, e1) - if e1 == "touch" and powerButton.pressed then + if e1 == "touch" and tape and powerButton.pressed then playButton.blinking = not playButton.blinking - component.invoke(tape.address, playButton.blinking and "stop" or "play") + if not playButton.blinking then + cueButton.blinking = false + end + + invoke(playButton.blinking and "stop" or "play") workspace:draw() end end +-------------------------------- Jog mode ------------------------------------------------ + +local jogModeDisplay = window:addChild(GUI.object(71, 20, 3, 2)) +jogModeDisplay.draw = function(jogModeDisplay) + local vinyl = (powerButton.pressed and not config.jogModeCdj) and 0x00B6FF or 0x002440 + local cdj = (powerButton.pressed and config.jogModeCdj) and 0xFFFF40 or 0x332400 + + screen.drawText(jogModeDisplay.x, jogModeDisplay.y, vinyl, "⢀⣀⡀") + screen.drawRectangle(jogModeDisplay.x, jogModeDisplay.y + 1, 3, 1, vinyl, cdj, "⣤") + screen.drawText(jogModeDisplay.x, jogModeDisplay.y + 2, cdj, "⠈⠉⠁") +end + +local jogModeButton = window:addChild(newRoundMiniButton(74, 20, 0x2D2D2D, 0x696969, 0x1E1E1E, 0x3C3C3C, "JM")) +jogModeButton.ignoresPower = true +jogModeButton.onTouch = function() + if not tape or not powerButton.pressed then + return + end + + config.jogModeCdj = not config.jogModeCdj + workspace:draw() + saveConfig() +end -------------------------------- Right beat buttons ------------------------------------------------ local beatSyncButton = window:addChild(newRoundMiniButton(70, 24, 0xB4B4B4, 0x0F0F0F, 0x787878, 0x0F0F0F, "Sy")) +beatSyncButton.ignoresPower = true + local beatSyncMasterButton = window:addChild(newRoundMiniButton(74, 24, 0xB4B4B4, 0x0F0F0F, 0x787878, 0x0F0F0F, "Ms")) +beatSyncMasterButton.ignoresPower = true -------------------------------- Right tempo buttons ------------------------------------------------ @@ -709,9 +961,15 @@ masterTempoButton.switchMode = true masterTempoButton:press() tempoButton.onTouch = function() - -end + tapeConfig.speedIndex = tapeConfig.speedIndex + 1 + if tapeConfig.speedIndex > 3 then + tapeConfig.speedIndex = 1 + end + + updateCurrentTapeSpeed() + saveConfig() +end -------------------------------- Events ------------------------------------------------ @@ -721,45 +979,61 @@ local jogIncrementUptime = 0 local overrideWindowEventHandler = window.eventHandler -window.eventHandler = function(workspace, window, e1, ...) - overrideWindowEventHandler(workspace, window, e1, ...) +window.eventHandler = function(workspace, window, e1, e2, e3, ...) + if (e1 == "component_added" or e1 == "component_removed") and e3 == "tape_drive" then + updateTapes() + else + overrideWindowEventHandler(workspace, window, e1, e2, e3, ...) - local shouldDraw = false - local isPlaying = getIsPlaying() + if not tape or not powerButton.pressed then + return + end - local uptime = computer.uptime() + local shouldDraw = false + local isPlaying = invoke("getState") == "PLAYING" + local position = invoke("getPosition") + local uptime = computer.uptime() - -- Cheching if play button state was changed - if isPlaying == playButton.blinking then - playButton.blinking = not playButton.blinking - shouldDraw = true - end - - if isPlaying then - if uptime > jogIncrementUptime then - -- Rotating jog - currentJogIndex = currentJogIndex + 1 - - if currentJogIndex > #jogImages then - currentJogIndex = 1 - end - - jogIncrementUptime = uptime + (1 - speedSlider.value) * (jogIncrementSpeedMax - jogIncrementSpeedMin) + -- Cheching if play button state was changed + if isPlaying == playButton.blinking then + playButton.blinking = not playButton.blinking shouldDraw = true end - else - jogIncrementUptime = uptime + (1 - speedSlider.value) * (jogIncrementSpeedMax - jogIncrementSpeedMin) - end - -- Blink - if uptime > imageButtonBlinkUptime then - imageButtonBlinkUptime = uptime + imageButtonBlinkInterval - imageButtonBlink = not imageButtonBlink - shouldDraw = true - end + -- Cue button + local cueButtonBlinking = playButton.blinking and tapeConfig.cue ~= invoke("getPosition") - if shouldDraw then - workspace:draw() + if cueButtonBlinking ~= cueButton.blinking then + cueButton.blinking = cueButtonBlinking + shouldDraw = true + end + + if isPlaying then + -- Rotating jog + if uptime > jogIncrementUptime then + currentJogIndex = currentJogIndex + 1 + + if currentJogIndex > #jogImages then + currentJogIndex = 1 + end + + jogIncrementUptime = uptime + (1 - speedSlider.value) * (jogIncrementSpeedMax - jogIncrementSpeedMin) + shouldDraw = true + end + else + jogIncrementUptime = uptime + (1 - speedSlider.value) * (jogIncrementSpeedMax - jogIncrementSpeedMin) + end + + -- Blink + if uptime > blinkUptime then + blinkUptime = uptime + blinkInterval + blinkState = not blinkState + shouldDraw = true + end + + if shouldDraw then + workspace:draw() + end end end