Jump to content
  • Sky
  • Blueberry
  • Slate
  • Blackcurrant
  • Watermelon
  • Strawberry
  • Orange
  • Banana
  • Apple
  • Emerald
  • Chocolate
  • Charcoal
Karasuro

OpenMatrix

Recommended Posts

Hello! Karasuro here. I just downloaded OpenComputers recently and have been testing things out. Here is my first program I've written in OC and it needs lots of improvement and optimization but at least it works.
 
Note:
You may need a high-end computer for this to work. I made it using Teir 3 everything. (Creative Mode)
Also you will need to hook up a note block.

 

Now checks the resolution of the system before changing it's local resolution. Returns to the previous resolution upon exit.

 

To-Do:

Add variable tempo.

Switch to Computer.beep() for extra pitch support.

Add variable scales.
 
Code: Optimized to mpmxyz's recommendations.

--[[OpenMatrix v0.7.1 by Karasuro]]--

-- Requirements
local computer = require "computer";
local component = require "component";
local term = require "term";
local event = require "event";
local gpu = component.gpu;
local source = component.note_block;
local note = require "note";
local keyboard = require "keyboard";
local color = require "colors";

-- Variables and Tables
term.clear();
local maxX, maxY = gpu.getResolution();

if (maxX ~= 80) then
  if (maxY ~= 25) then
    gpu.setResolution(80, 25);
  end
end

local w, h = gpu.getResolution();
local UI = {bg=0x000000, fg=0xFFFFFF, txt=0x000000, width=w, height=h};
local UI_btn = {on=0xFFFFFF, off=0xFFAA00, play=0xFFFF55};
local UI_menu = {default=0xFFAA00, hit=0xFFFF55};
local buttons = {};

for Y = 2,16 do
  local row = {};
  table.insert(buttons, row);
  for X = 2,47,3 do
    if (Y == 2) then
      table.insert(row, {state=3, posX=X, posY=Y, pitch=25});
    elseif (Y == 3) then
      table.insert(row, {state=3, posX=X, posY=Y, pitch=24});
    elseif (Y == 4) then
      table.insert(row, {state=3, posX=X, posY=Y, pitch=22});
    elseif (Y == 5) then
      table.insert(row, {state=3, posX=X, posY=Y, pitch=20});
    elseif (Y == 6) then
      table.insert(row, {state=3, posX=X, posY=Y, pitch=18});
    elseif (Y == 7) then
      table.insert(row, {state=3, posX=X, posY=Y, pitch=17});
    elseif (Y == 8) then
      table.insert(row, {state=3, posX=X, posY=Y, pitch=15});
    elseif (Y == 9) then
      table.insert(row, {state=3, posX=X, posY=Y, pitch=13});
    elseif (Y == 10) then
      table.insert(row, {state=3, posX=X, posY=Y, pitch=12});
    elseif (Y == 11) then
      table.insert(row, {state=3, posX=X, posY=Y, pitch=10});
    elseif (Y == 12) then
      table.insert(row, {state=3, posX=X, posY=Y, pitch=8});
    elseif (Y == 13) then
      table.insert(row, {state=3, posX=X, posY=Y, pitch=6});
    elseif (Y == 14) then
      table.insert(row, {state=3, posX=X, posY=Y, pitch=5});
    elseif (Y == 15) then
      table.insert(row, {state=3, posX=X, posY=Y, pitch=3});
    else
      table.insert(row, {state=3, posX=X, posY=Y, pitch=1})
    end
  end
end

local printDebug = false;
local mX = 0;
local mY = 0;
local pitch = 1;
local playing = false;
local tick = 0;
local beat = 16;
local tempo = 40;
local cycle = 0;
local dt = 0;



local menu = {
  {id="play", posX=2, posY=(h - 1), state=1, label="Play", width=6, height=1}
};
local header = {posX=47,posY=18,width=2,height=1,col=0x00AA00};

local myEventHandlers = setmetatable({}, {__index = function() return unknownEvent end});

-- Functions
function unknownEvent()
  -- Do Nothing!
end

function myEventHandlers.key_up(adress, char, code, playerName)
  -- Space Bar
  if (code == 0x39) then
    playing = not playing;
  end
 
  -- Backspace
  if (code == 0x0E) then
    gpu.setResolution(maxX, maxY);
    term.clear();
    os.exit();
  end
 
  -- Grave or ~
  if (code == 0x29) then
    printDebug = not printDebug;
  end
end

function myEventHandlers.touch(screenAdress, x, y, button, playerName)
  mX = x;
  mY = y;
  if (button == 0) then
    -- Buttons Table Handling
    for r in pairs(buttons) do
      for c in pairs(buttons[r]) do
        if (mX >= buttons[r][c].posX) and (mX <= (buttons[r][c].posX + 2)) and (mY == buttons[r][c].posY) then
          if (buttons[r][c].state == 3) then
            buttons[r][c].state = 1;
          else
            buttons[r][c].state = 3;
          end
        end
      end
    end
    
    -- Menu Table Handling
    for i in pairs(menu) do
      if (mX >= menu[i].posX) and (mX <= (menu[i].posX + menu[i].width)) and (mY == menu[i].posY) then
        --play/stop button
        if menu[i].id == "play" then
          playing = not playing;
          menu[i].label = "Stop";
        end
      end
    end
  end
 end

function handleEvent(eventID, ...)
  if (eventID) then
    myEventHandlers[eventID](...);
  end
end

function playTone(pitch)
  if (source ~= nil) then
    source.trigger(pitch);
  else
    computer.beep(pitch);
  end
end
 
  --Draw Instructions ONCE
  gpu.setBackground(UI.bg); --Black
  gpu.setForeground(UI.fg); --White
  gpu.set(2,20,"~ = debug info | space = play/stop | backspace = exit");
  gpu.set(2,21,"Multiple notes on each column = more processing time.")

-- Main Loop
while true do

  --[[UPDATE DATA]]--
  if playing then
    -- Update UI
    menu[1].label = "Stop";
    
    -- Start ticking
    tick = tick + (tempo * 0.1);
    if tick > 1 then
      header.posX = header.posX + 3;
      if header.posX > 47 then
        header.posX = 2;
      end
      tick = 0;
      beat = beat + 1;
    end
    
    if (beat > 16) then
      beat = 1;
    end
    
    -- Get Active Notes
    for r in pairs(buttons) do
      for c in pairs(buttons[r]) do
        if (buttons[r][c].posX == header.posX) then
          if (buttons[r][c].state == 1) then
            buttons[r][c].state = 2;
            pitch = buttons[r][c].pitch;
            playTone(pitch);
          end
        else
          if (buttons[r][c].state == 2) then
            buttons[r][c].state = 1;
          end
        end
      end
    end
  else
    -- Reset UI
    header.posX = 47;
    tick = 0;
    beat = 16;
    menu[1].label = "Play";
    for r in pairs(buttons) do
      for c in pairs(buttons[r]) do
        if (buttons[r][c].state == 2) or (buttons[r][c].state == 1) then
          buttons[r][c].state = 1;
        else
          buttons[r][c].state = 3;
        end
      end
    end
  end
 
  --[[DRAW STUFF]]--
  -- Draw Buttons and their states
  for r in pairs(buttons) do
    for c in pairs(buttons[r]) do
      if (buttons[r][c].state == 3) then
        --Draw Button OFF
        gpu.setBackground(UI_btn.off); --Orange
        gpu.fill(buttons[r][c].posX,buttons[r][c].posY,2,1," ");
        gpu.setBackground(UI.bg); --Black
      end
      
      if (buttons[r][c].state == 2) then
        -- Draw Buttons ON
        gpu.setBackground(UI_btn.on); --White
        gpu.fill(buttons[r][c].posX,buttons[r][c].posY,2,1," ");
        gpu.setBackground(UI.bg); --Black
      end
      
      if (buttons[r][c].state == 1) then
        -- Draw Buttons PLAY
        gpu.setBackground(UI_btn.play); --Yellow
        gpu.fill(buttons[r][c].posX,buttons[r][c].posY,2,1," ");
        gpu.setBackground(UI.bg); --Black
      end
    end
  end
 
  -- Draw Play Head
  gpu.setBackground(header.col); --Green
  gpu.fill(header.posX,header.posY,2,1," ");
  gpu.setBackground(UI.bg); --Black
  if (header.posX ~= 2) then
    gpu.fill((header.posX - 3),header.posY,2,1," ");
  end
  if (header.posX == 2) then
    gpu.fill(47,18,2,1," ");
  end
  if (header.posX == 47) then
    gpu.fill(2,header.posY,45,1," ");
  end
 
  --Draw Menu
  for i in pairs(menu) do
    if (menu[i].state == 1) then
      gpu.setBackground(UI_menu.default) --Orange
      gpu.fill(menu[i].posX,menu[i].posY,menu[i].width,menu[i].height," ");
      gpu.setForeground(UI.txt); --Black
      gpu.set(menu[i].posX,menu[i].posY,menu[i].label);
      gpu.setBackground(UI.bg); --Black
      gpu.setForeground(UI.fg); --White
    end
    
    if (menu[i].state == 2) then
      gpu.setBackground(UI_menu.hit); --Yellow
      gpu.fill(menu[i].posX,menu[i].posY,menu[i].width,menu[i].height," ");
      gpu.setForeground(UI.txt); --Black
      gpu.set(menu[i].posX,menu[i].posY,menu[i].label);
      gpu.setBackground(UI.bg); --Black
      gpu.setForeground(UI.fg); --White
    end
  end
 
  -- print debug information
  if (printDebug) then
    gpu.fill(50,2,80,5," ");
    gpu.setBackground(UI.bg) --Black
    gpu.setForeground(UI.fg) --White
    gpu.set(50,2,tostring(mX)..", "..tostring(mY));
    gpu.set(50,3,"playing: "..tostring(playing));
    gpu.set(50,4,"beat: "..tostring(beat));
    gpu.set(50,5,"pitch: "..tostring(pitch));
  else
    gpu.fill(50,2,80,5," ");
  end
 
  handleEvent(event.pull(0.001));
end
Edited by SpiritedDusty
Put code in spoiler
Link to post
Share on other sites

I had some fun playing around with this.

Here some hints for improvement:

 

Did you know you could rewrite the following line

{["state"]=3, ["posX"]=2,  ["posY"]=2, ["pitch"]=25},

to

{state=3, posX=2,  posY=2, pitch=25},

?

It does exactly the same and isn't cluttered with brackets and quotes.

 

And you should definitely try two for loops to create your buttons:

--iterate through posY = 2,3,4 ... 16
for posY = 2,16 do
  local row = {}
  --add table row to the list buttons
  table.insert(buttons, row)
  --iterate through posX = 2,5,8 ... 44 (notice how the third parameter = 3 defines how much posX is changing)
  for posX = 2,44,3 do
    --create a table for the button and add it to the row
    table.insert(row, {state=3, posX=posX,  posY=posY, pitch=25})
  end
end

(That saves ~250 lines of code and makes it easier to move the buttons! :-))

If you have a question about this code: Feel free to ask!

Link to post
Share on other sites

Yeah, I don't know why I went in and added the [""] to the tables. I guess I wasn't really thinking at the time. XP Brain process was string instead of variable.

 

Also, I was looking for a better way to make the buttons array. That helps me out a lot!

However, I'll need to figure out an algorith for pitch.. the scale is F# Major (F#3 - F#5)

 

This may also allow me to implement a setScale() function and instead of using note blocks just use computer.beep(). Larger range and note durration support. ;P

 

The original design had speed variation hence the tempo and dt variables.

Also I need to figure out if doing multiple note.trigger() at once is possible..

 

This project is a port from my Love2D application AudynMatrix.

 

https://dl.dropboxusercontent.com/u/16913227/AudynMatrix.zip

Link to post
Share on other sites

I like this program. I put it on pastebin since copy and paste to edit will cause it to truncate.


pastebin get d4Hhg5PR openMatrix

 

Make sure you have an adapter block with a note_block attached to it connected to the computer and the run "openMatrix"

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use and Privacy Policy.