-- MusicalNotes V6
--[[
Original concept by dave1707:
Allows you to move cursor forward or backward thru notes. You can also jump to the start or end of the notes, as well as delete notes.
Stuff by Scotty:
Notes are now selected via piano-style keyboard (three octives only) using a KeyBoard button class.
Music staff size can be scaled up or down (to a limit) via simple slider class control.
G- and F-clef symbols and keyboard can be had at pixabay.com
--]]
supportedOrientations(LANDSCAPE_ANY)
displayMode(FULLSCREEN)
function setup()
print("Set testing == true to:")
print("View keyboard touch areas,\
Display note names.")
testing = false
-- Screen center
X = WIDTH/2
Y = HEIGHT/2
-- To locate and size piano image.
wP = 250
hP = 100
xP = X-wP/2
yP = Y-200
diaP = wP/7 -- diaP = width of keyboard sprite divided by 7 whole-note keys
keySp = diaP
whole = .5
sharp = 1.3
-- Musical Note: The notes "B" and "E" do not have an associated sharp.
nameTab={"C3","C3#","D3","D3#","E3","F3","F3#","G3","G3#","A3","A3#","B3", -- C3 octive
"C4","C4#","D4","D4#","E4","F4","F4#","G4","G4#","A4","A4#","B4", -- C4 octive
"C5","C5#","D5","D5#","E5","F5","F5#","G5","G5#","A5","A5#","B5" -- C5 octive
}
-- C3 keyboard notes
note_C3 = KBbutton(xP-wP+2+keySp*0.5,yP+diaP*whole, diaP/2,"C3", 1)
note_C3s = KBbutton(xP-wP+2+keySp*1.0,yP+diaP*sharp, diaP/2,"C3#", 2)
note_D3 = KBbutton(xP-wP+2+keySp*1.5,yP+diaP*whole, diaP/2,"D3", 3)
note_D3s = KBbutton(xP-wP+2+keySp*2.0,yP+diaP*sharp, diaP/2,"D3#", 4)
note_E3 = KBbutton(xP-wP+2+keySp*2.5,yP+diaP*whole, diaP/2,"E3", 5)
note_F3 = KBbutton(xP-wP+2+keySp*3.5,yP+diaP*whole, diaP/2,"F3", 6)
note_F3s = KBbutton(xP-wP+2+keySp*4.0,yP+diaP*sharp, diaP/2,"F3#", 7)
note_G3 = KBbutton(xP-wP+2+keySp*4.5,yP+diaP*whole, diaP/2,"G3", 8)
note_G3s = KBbutton(xP-wP+2+keySp*5.0,yP+diaP*sharp, diaP/2,"G3#", 9)
note_A3 = KBbutton(xP-wP+2+keySp*5.5,yP+diaP*whole, diaP/2,"A3", 10)
note_A3s = KBbutton(xP-wP+2+keySp*6.0,yP+diaP*sharp, diaP/2,"A3#", 11)
note_B3 = KBbutton(xP-wP+2+keySp*6.5,yP+diaP*whole, diaP/2,"B3", 12)
-- C4 keyboard notes
note_C4 = KBbutton(xP+keySp*0.5, yP+diaP*whole, diaP/2,"C4", 13)
note_C4s = KBbutton(xP+keySp*1.0, yP+diaP*sharp, diaP/2,"C4#", 14)
note_D4 = KBbutton(xP+keySp*1.5, yP+diaP*whole, diaP/2,"D4", 15)
note_D4s = KBbutton(xP+keySp*2.0, yP+diaP*sharp, diaP/2,"D4#", 16)
note_E4 = KBbutton(xP+keySp*2.5, yP+diaP*whole, diaP/2,"E4", 17)
note_F4 = KBbutton(xP+keySp*3.5, yP+diaP*whole, diaP/2,"F4", 18)
note_F4s = KBbutton(xP+keySp*4.0, yP+diaP*sharp, diaP/2,"F4#", 19)
note_G4 = KBbutton(xP+keySp*4.5, yP+diaP*whole, diaP/2,"G4", 20)
note_G4s = KBbutton(xP+keySp*5.0, yP+diaP*sharp, diaP/2,"G4#", 21)
note_A4 = KBbutton(xP+keySp*5.5, yP+diaP*whole, diaP/2,"A4", 22)
note_A4s = KBbutton(xP+keySp*6.0, yP+diaP*sharp, diaP/2,"A4#", 23)
note_B4 = KBbutton(xP+keySp*6.5, yP+diaP*whole, diaP/2,"B4", 24)
-- C5 keyboard notes
note_C5 = KBbutton(xP+wP-2+keySp*0.5,yP+diaP*whole, diaP/2,"C5", 25)
note_C5s = KBbutton(xP+wP-2+keySp*1.0,yP+diaP*sharp, diaP/2,"C5#", 26)
note_D5 = KBbutton(xP+wP-2+keySp*1.5,yP+diaP*whole, diaP/2,"D5", 27)
note_D5s = KBbutton(xP+wP-2+keySp*2.0,yP+diaP*sharp, diaP/2,"D5#", 28)
note_E5 = KBbutton(xP+wP-2+keySp*2.5,yP+diaP*whole, diaP/2,"E5", 29)
note_F5 = KBbutton(xP+wP-2+keySp*3.5,yP+diaP*whole, diaP/2,"F5", 30)
note_F5s = KBbutton(xP+wP-2+keySp*4.0,yP+diaP*sharp, diaP/2,"F5#", 31)
note_G5 = KBbutton(xP+wP-2+keySp*4.5,yP+diaP*whole, diaP/2,"G5", 32)
note_G5s = KBbutton(xP+wP-2+keySp*5.0,yP+diaP*sharp, diaP/2,"G5#", 33)
note_A5 = KBbutton(xP+wP-2+keySp*5.5,yP+diaP*whole, diaP/2,"A5", 34)
note_A5s = KBbutton(xP+wP-2+keySp*6.0,yP+diaP*sharp, diaP/2,"A5#", 35)
note_B5 = KBbutton(xP+wP-2+keySp*6.5,yP+diaP*whole, diaP/2,"B5", 36)
kbTab={}
table.insert(kbTab,note_C3)
table.insert(kbTab,note_C3s)
table.insert(kbTab,note_D3)
table.insert(kbTab,note_D3s)
table.insert(kbTab,note_E3)
table.insert(kbTab,note_F3)
table.insert(kbTab,note_F3s)
table.insert(kbTab,note_G3)
table.insert(kbTab,note_G3s)
table.insert(kbTab,note_A3)
table.insert(kbTab,note_A3s)
table.insert(kbTab,note_B3)
table.insert(kbTab,note_C4)
table.insert(kbTab,note_C4s)
table.insert(kbTab,note_D4)
table.insert(kbTab,note_D4s)
table.insert(kbTab,note_E4)
table.insert(kbTab,note_F4)
table.insert(kbTab,note_F4s)
table.insert(kbTab,note_G4)
table.insert(kbTab,note_G4s)
table.insert(kbTab,note_A4)
table.insert(kbTab,note_A4s)
table.insert(kbTab,note_B4)
table.insert(kbTab,note_C5)
table.insert(kbTab,note_C5s)
table.insert(kbTab,note_D5)
table.insert(kbTab,note_D5s)
table.insert(kbTab,note_E5)
table.insert(kbTab,note_F5)
table.insert(kbTab,note_F5s)
table.insert(kbTab,note_G5)
table.insert(kbTab,note_G5s)
table.insert(kbTab,note_A5)
table.insert(kbTab,note_A5s)
table.insert(kbTab,note_B5)
keyTab={}
js=button(X-250,80,"<<",20)
bs=button(X-125,80,"<", 20)
de=button(X,80,"Delete",20)
fs=button(X+125,80,">", 20)
je=button(X+250,80,">>",20)
spacing = 16 -- initial line-spacing
indent = 50
keyPos = 1
dx = 0
cY = 30
-- slider related things
sX = WIDTH-75
sY = Y-150
sW = wP/7
sH = 100
sLo = 8
sHi = 32.5
spacingSlider = Slider("Line-Spacing\
Slider",sX,sY,sW,sH,sLo,sHi)
end -- end of setup()
function draw()
background(171, 171, 171, 255)
noteSp = spacing*2.5
spacingSlider:draw()
if testing == true then -- for testing
-- origin of musical staff
stroke(255, 0, 0, 128)
strokeWidth(2)
line(indent,HEIGHT,indent,HEIGHT-400)
line(0,500+cY,WIDTH,500+cY)
-- musical staff slide zone
line(0,HEIGHT-400, WIDTH,HEIGHT-400)
-- touch locs
fill(255, 0, 0, 128)
pushStyle()
textMode(CORNER)
text("Red circles illustrate proper touch location on keyboard.", xP-wP,yP-20)
popStyle()
end
pushMatrix()
translate(0,500+spacing+cY)
musicalStaff("G") -- 5-line musical staff with treble clef (G)
translate(0,-spacing*6)
musicalStaff("F") -- 5-line musical staff with bass clef (F)
line(dx+50,spacing*4, dx+50,spacing*6) -- line that ties the two staffs together
translate(50,-spacing*6.5)
musicalNotes()
popMatrix()
popStyle()
fill(0, 0, 0, 255)
fontSize(16)
text(string.format("Musical note %d of %d notes.",keyPos,#keyTab),X,125)
popStyle()
bs:draw() -- back one space
fs:draw() -- forward one space
de:draw() -- delete a note
js:draw() -- jump to start
je:draw() -- jump to end
spriteMode(CORNER)
sprite("Dropbox:Piano",xP-wP+2,yP, wP,hP) -- C3
sprite("Dropbox:Piano", xP,yP, wP,hP) -- C4
sprite("Dropbox:Piano",xP+wP-2,yP, wP,hP) -- C5
for z=1,#nameTab do
kbTab[z]:draw()
end
popMatrix()
pushStyle()
fill(0, 0, 0, 255)
fontSize(12)
textMode(RIGHT)
translate(xP+keySp/2,yP+hP/2)
rotate(90)
text("Middle C (C4)",0,0)
text("(C5)",0,-wP)
text("(C3)",0,wP)
popStyle()
popMatrix()
end -- end of draw()
function touched(t)
if t.y>HEIGHT-400 then
if t.state==MOVING then
dx=dx+t.deltaX
if dx > 0 then dx = 0 end -- added to limit rightward movement of staff
end
end
if t.state==BEGAN then
bs:touched(t)
fs:touched(t)
de:touched(t)
js:touched(t)
je:touched(t)
end
if t.state==MOVING then
spacingSlider:touched(t)
end
-- change "ENDED" to "MOVING" then slide your finger across the keyboard for a cool effect.
if t.state==ENDED then
for z=1,#nameTab do
kbTab[z]:touched(t)
end
end
end -- end of touched()
-- Original button class for notes and functions with minor mods
button = class()
function button:init(x,y,n,k)
self.x=x
self.y=y
self.name=n
self.key=k
end
function button:draw()
sprite("Cargo Bot:Dialogue Button",self.x,self.y)
fill(0, 0, 0, 255)
text(self.name,self.x,self.y)
end
function button:touched(t)
if t.x>self.x-50 and t.x<self.x+50 and t.y>self.y-25 and t.y<self.y+25 then
-- function buttons
if self.name=="<" and keyPos>1 then
keyPos=keyPos-1
end
if self.name==">" and keyPos<=#keyTab then
keyPos=keyPos+1
end
if self.name=="Delete" and keyPos<=#keyTab and keyPos>0 then
table.remove(keyTab,keyPos)
if keyPos>#keyTab then
keyPos=#keyTab
end
if keyPos == 0 then keyPos = 1 end -- added by Scotty
end
if self.name=="<<" then
keyPos=1
dx=0
end
if self.name==">>" then
keyPos=#keyTab+1
end
if keyPos*(spacing*2.5) > WIDTH-(spacing*factor) then
dx=WIDTH-(spacing*factor)-keyPos*(spacing*2.5)
end
end
end
-- Used for piano keyboard buttons. i.e., where on the piano key to touch.
-- By Scotty
KBbutton = class()
function KBbutton:init(x,y,rad,n,k)
self.x = x
self.y = y
self.rad = rad
self.name = n
self.key = k
factor = 9
end
function KBbutton:draw()
--display ellipse for testing
if testing == true then
stroke(255, 0, 0, 255)
strokeWidth(1)
noFill()
ellipse(self.x,self.y,self.rad) -- this highlights proper touch location, nothing more.
end
end
function KBbutton:touched(t)
if vec2(t.x,t.y):dist(vec2(self.x,self.y)) <= self.rad then
-- individual notes
if self.key < 37 then -- total number of keys on the keyboard plus 1
if keyPos < #keyTab then
table.insert(keyTab,keyPos,self.key)
keyPos = keyPos+1
else
keyTab[keyPos] = self.key
keyPos = keyPos+1
end
if keyPos*(spacing*2.5) > WIDTH-(spacing*factor) then
dx=WIDTH-(spacing*factor)-keyPos*(spacing*2.5)
end
end
end
end
-- A simple slider to select a single spacing value within a specific range.
-- By Scotty
Slider = class()
function Slider:init(title,x,y,w,h,lo,hi)
self.title = title
self.x = x
self.y = y
self.w = w
self.h = h
self.lo = lo
self.hi = hi
end
function Slider:draw()
rectMode(CENTER)
fill(255, 255, 255, 255)
stroke(0, 0, 0, 255)
strokeWidth(2)
rect(self.x,self.y,self.w,self.h)
fill(0, 0, 0, 255)
fontSize(16)
textAlign(CENTER)
text(self.title,self.x,self.y-self.h/2-25)
text(string.format("%.1f",spacing),self.x,self.y-self.h/2-50)
end
function Slider:touched(t)
if t.x>self.x-self.w/2 and t.x<self.x+self.w/2 and
t.y>self.y-self.h/2 and t.y<self.y+self.h/2 then
if t.state==MOVING then
spacing = spacing+t.deltaY
-- limit the range of spacing to the defined hi and lo values
if spacing > self.hi then spacing = self.hi end
if spacing < self.lo then spacing = self.lo end
end
end
end
-- Misc functions
function musicalStaff(clef)
-- Draw 5-line staff and etc
clef = clef -- G or F clef
--[[
"spacing" refers to the vertical space between musical staff lines (controlled by the Slider)
--]]
-- Locate and size G-Clef (treble clef) symbol relative to line spacing.
xGClef = 2.2*spacing
yGClef = 1.8*spacing
wGClef = 4.5*spacing--375 orig size
hGClef = 7.5*spacing--640 orig size
-- Locate and size F-Clef (bass clef) symbol relative to line spacing (formerly "size").
xFClef = 2.2*spacing
yFClef = 2.2*spacing
wFClef = 2.5*spacing--539 orig size
hFClef = 3.5*spacing--640 orig size
fill(0)
strokeWidth(2)
pushMatrix()
translate(indent ,0)
for d=0,4 do
stroke(0, 0, 0, 255)
line(dx, d*spacing, WIDTH, d*spacing) -- staff lines
for d=0,3 do
line(dx, d*spacing, dx, d*spacing+spacing) -- vertical line
end
end
spriteMode(CENTER)
if clef == "G" then
sprite("Dropbox:G_Clef", dx+xGClef, yGClef, wGClef, hGClef)
elseif clef == "F" then
sprite("Dropbox:F_Clef", dx+xFClef, yFClef, wFClef, hFClef)
end
popMatrix()
end
function musicalNotes()
-- Draw the musical notes
--[[
"spacing" refers to the vertical space between musical staff lines (controlled by the Slider)
"noteSp" refers to the horizantal space between notes.
--]]
pushStyle()
pushMatrix()
--translate(indent,0)
translate(spacing*3,0)
for z=1,#keyTab do
s=keyTab[z]
pushMatrix()
translate(z*noteSp+dx, s*spacing/2+spacing*5)
stroke(0, 0, 0, 255)
-- The following is to locate the notes properly on musical staff.
-- I'm sure there is a better way to do this.
-- C3 octive
if nameTab[s] == "C3" then
translate(0,spacing*2.5)
elseif nameTab[s] == "C3#" then
translate(0,spacing*2)
sharpSymbol()
elseif nameTab[s] == "D3" then
translate(0,spacing*2)
elseif nameTab[s] == "D3#" then
translate(0,spacing*1.5)
sharpSymbol()
elseif nameTab[s] == "E3" then
translate(0,spacing*1.5)
elseif nameTab[s] == "F3" then
translate(0,spacing*1.5)
elseif nameTab[s] == "F3#" then
translate(0,spacing)
sharpSymbol()
elseif nameTab[s] == "G3" then
translate(0,spacing)
elseif nameTab[s] == "G3#" then
translate(0,spacing*.5)
sharpSymbol()
elseif nameTab[s] == "A3" then
translate(0,spacing*.5)
elseif nameTab[s] == "A3#" then
translate(0,0)
sharpSymbol()
elseif nameTab[s] == "B3" then
translate(0,0)
-- C4 octive
elseif nameTab[s] == "C4" then -- this is what's called "Middle C"
translate(0,0)
pushStyle()
stroke(0, 0, 255, 255)
line(-spacing,0, spacing,0)
popStyle()
elseif nameTab[s] == "C4#" then
translate(0,-spacing*.5)
sharpSymbol()
pushStyle()
stroke(0, 0, 255, 255)
line(-spacing,0, spacing,0)
popStyle()
elseif nameTab[s] == "D4" then
translate(0,-spacing*.5)
elseif nameTab[s] == "D4#" then
translate(0,-spacing)
sharpSymbol()
elseif nameTab[s] == "E4" then
translate(0,-spacing)
elseif nameTab[s] == "F4" then
translate(0,-spacing)
elseif nameTab[s] == "F4#" then
translate(0,-spacing*1.5)
sharpSymbol()
elseif nameTab[s] == "G4" then
translate(0,-spacing*1.5)
elseif nameTab[s] == "G4#" then
translate(0,-spacing*2)
sharpSymbol()
elseif nameTab[s] == "A4" then
translate(0,-spacing*2)
elseif nameTab[s] == "A4#" then
translate(0,-spacing*2.5)
sharpSymbol()
elseif nameTab[s] == "B4" then
translate(0,-spacing*2.5)
-- C5 octive
elseif nameTab[s] == "C5" then
translate(0,-spacing*2.5)
elseif nameTab[s] == "C5#" then
translate(0,-spacing*3)
sharpSymbol()
elseif nameTab[s] == "D5" then
translate(0,-spacing*3)
elseif nameTab[s] == "D5#" then
translate(0,-spacing*3.5)
sharpSymbol()
elseif nameTab[s] == "E5" then
translate(0,-spacing*3.5)
elseif nameTab[s] == "F5" then
translate(0,-spacing*3.5)
elseif nameTab[s] == "F5#" then
translate(0,-spacing*4)
sharpSymbol()
elseif nameTab[s] == "G5" then
translate(0,-spacing*4)
elseif nameTab[s] == "G5#" then
translate(0,-spacing*4.5)
sharpSymbol()
elseif nameTab[s] == "A5" then
translate(0,-spacing*4.5)
line(-spacing,0, spacing,0)
elseif nameTab[s] == "A5#" then
translate(0,-spacing*5)
sharpSymbol()
line(-spacing,0, spacing,0)
elseif nameTab[s] == "B5" then
translate(0,-spacing*5)
line(-spacing,0, spacing,0)
end
-- draw the note with it's stem
line(-spacing/2, 0, -spacing/2, -spacing*3)
rotate(30) -- for style points
fill(0, 0, 0, 255)
ellipse(0,0,spacing*1.25,spacing)
-- display the notes's name if testing
if testing == true then
rotate(-30)
fontSize(spacing*.5)
fill(255, 255, 255, 255)
text(nameTab[s],0,0)
end
popMatrix()
end -- end of for loop
popMatrix()
-- cursor
pushMatrix()
pushStyle()
strokeWidth(spacing*1.25)
lineCapMode(SQUARE)
stroke(255, 0, 0, 50)
translate(spacing*3,0)
line(keyPos*noteSp+dx,spacing*4.5, keyPos*noteSp+dx,spacing*19)
popStyle()
popMatrix()
end
function sharpSymbol()
-- Build a sharp symbol from scratch since I could not find one to my liking online.
-- Truth be told, I didn't spend much time looking.
stroke(0, 0, 0, 255)
strokeWidth(2)
pushMatrix()
pushStyle()
scale(spacing *.075) -- quick and dirty way to adjust size according to line spacing
translate(-16,-4)
lineCapMode(SQUARE)
line( 4,-10, 3,18)
line(-3,-12,-3,16)
strokeWidth(4)
line(-6,-5,6, 0)
line(-6, 5,6,10)
popStyle()
popMatrix()
end
That’s all there is, main plus four tabs.
This started out as a question answered by @dave1707 that morphed into what you see here. I started a new thread because the original is 2 1/2 years old.
Select notes via a piano-style keyboard and the notes pop up on a five-line staff. Two staffs really, bass and treble.
You can scale the staff up of down using the crude slider on the right. Setting the “testing” variable to true allows you to see the note names on the musical notes and the desired touch location on the keyboard.
The keyboard is limited to three octives due to time and space constraints. Maybe I’ll do a complete 88-key keyboard by dividing it two at middle C.
Note: The treble- and bass-clef symbols can be had at pixabay.com. Sorry that I could not provide then directly.
Enjoy.
@Scotty Looks good. That’s a lot of keying you did and it works great. You might want to specify where the keyboard is. I did a search for piano and looked for a small keyboard. I just happened to luck out and selected the correct one. At least I think it was the correct one. I don’t have time to play with it now, but I’ll do it later. Below is the keyboard I tried.
Thanks, @dave1707. The keyboard is also at pixabay. I’ll see if I can re-find it.
I think this is the keyboard I use. Follow this link: https://pixabay.com/illustrations/piano-vector-music-keys-802531/
I guess in the future I should save the URL in my source code.
@Scotty you should be able to export the project as a zip then attach it to the forum. I’ve added the files as part of the project assets and they should come across. Include any license files/terms and you should be ok
@West , I made minor mods (comments regarding links to the images I used) and figured out how to zip the project up. I’m not sure how to make it available to the forum. I’m not even sure what happens when I click on your zip file.
Thanks for your help on this. Zip looks like an easy way to share larger, multi-tab projects. All I have to do if figure out how to use it.
Thanks again.
@Scotty once you have the zip file (long hold and export from codea project selection page) then you can attach it to a forum post with the last button on the right along the menu bar (looks like a page with the corner folded over)
Tapping on the zip file in the forum on the iPad should take you to a page with a link to “Open in Codea” which will launch Codea and ask if you want to import it
Here is a Zip file for the above project.
@West, let me know if my link works because when I click on your link it goes nowhere. Might be user error on my part.
Thanks.
@West Your link worked for me. @Scotty Your link worked for me but there was no keyboard or clefs. Also when I load the code on my iPad Pro, the keyboard overlays the lines for the notes.
@dave1707, I made sure the three images were in my projects folder and redid the zip file. Hopefully they will come through this time.
As for the iPad Pro issue I’m not sure what is going on. I’m running a 3rd Gen iPad so my iPad has not seen an OS update nor a Codea update in forever. That’s the price I pay for running old technology.
Let me know if the new Zip changes anything.
Thanks.
@Scotty I didn’t look thru your code, but you might be positioning some things base on height or width and some other thing with a hard coded value. That has nothing to do with the iPad version, or OS version. So depending on the screen size, things can show up in different relations to other things. Normally if you want your programs to run on any device, you should position things based on width and height instead of hard coding values.
PS. Are you going to add a new zip file or update the one above.
@dave170, I try to locate things relative to screen center. I define that as X and Y (WIDTH/2 and HEIGHT/2). Also, location is relative to an image’s width and height. The piano is located relative to screen center. I modified the code so that the five blue nav/action buttons are also located relative to screen center. That little change may make some difference.
I also sized the treble clef, bass clef and notes based on line spacing and that is controlled using the slider on the right side of the screen.
I have re-Zipped the code so please try the new files. Also, please find the images I used. Since I’m not sure if they get automatically zipped if they are in the project assets folder I included them below.
If there is a specific graphics issue please let me know. I’m still learning.
Thanks.
@Scotty Your zip file didn’t include the keyboard or clefs. Those had to be loaded by themselves.
@dave1707, in my previous post I included the images as separate downloads because I was unsure that all works. I hope you found them and they were in the correct format.
Also, I hope the code edits made some sort of difference.