The first roblox song making plugin - Rosong Studio

Welcome to Rosong Studio!

Rosong Studio is a plugin I have made over the course of the past few days with the intention of letting users create and play sounds all in one place. Right now, there is only one instrument (the piano) and only the white keys work for now. I just wanted to sort of make a proof of concept to see if this is something worth working on. Anyways, let’s move on.

Here is what Rosong Studio looks like. It’s very basic for now.

image image
We have RoSong Studio for creating the songs, and RoSong Player for playing them.

Also, here is a very quick video of what Rosong can do. It was pretty difficult to find a way to demonstrate the listening back part of the plugin, so I left it out of the video.

How do I use Rosong Studio?

First of all, you are going to need a recording. To do this, press the record button at the top of the plugin window to start recording. Once you have began, you can start recording your masterpiece! After you feel you have finished, you just click on the same button once again but this time labeled “End Recording”. Automatically, you will start selecting a module script. Using a server script, (local scripts don’t work unfortunately since I was using HttpService, but I can think of workarounds to add in future updates) you can require the module and all you have to do from there, is do module.new() to initialise the new module, and then do module:Play() to play the song in the script. I am planning on adding a player in the plugin too. Maybe. After running the script, your masterpiece can start playing for everyone to hear!

If you ever just need to quickly play a song and can’t be bothered to write a script and run the test, well then you can just use Rosong Studio’s built-in music player! It’s pretty basic, but it gets all of the tasks done.

How does Rosong Studio work?

It’s pretty simple actually. All I did was first find an optimal sound for a piano key, then use the Pitch audio effect to change the octave of the sound. Allowing it to match to multiple notes. Then, for the recording, all I did was track every note pressed and when and put it into a table. Once the recording was over, it could all be used in a simple module.

Any feedback on my plugin would be appreciated!

13 Likes
function CreatePiano()
	print("PianoMode")
	local ScreenGui = Instance.new("ScreenGui")
	ScreenGui.Parent = PlayerGui
	local Frame = Instance.new("Frame")
	Frame.Parent = ScreenGui
	Frame.Size = UDim2.new(0.8, 0,0.19, 0) -- adjust the size as you like
	Frame.Position = UDim2.new(0.087, 0,0.78, 0) -- adjust the position as you like
	Frame.BackgroundColor3 = Color3.new(0.5, 0.5, 0.5) -- adjust the color as you like
	Frame.Name = "Piano" -- name the frame
	local KeyIndex=1
	local KeyIndex2=1
	local KeyIndex3=1
	local TimeSignature = 3/8 -- three eighth notes per measure
	-- Define a variable to store the tempo in beats per minute (BPM)
	local Tempo = 90 -- adjust as you like
	-- Define a variable to store the duration of one beat in seconds
	local BeatDuration = 20 / Tempo
	-- Define a variable to store the duration of one measure in seconds
	local MeasureDuration = BeatDuration * TimeSignature
	-- Define a variable to store the current time in seconds
	local CurrentTime = 0
	local KeyCount = 56 -- the number of keys on the piano
	local KeyWidth = 21/12 / KeyCount -- the width of each key relative to the frame width
	local KeyColors = {Color3.new(1, 1, 1), Color3.new(0, 0, 0)} -- the colors of the keys (white and black)
	local KeyPattern = {1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 2, 1} -- the pattern of the keys (white and black)
	local KeyIndex = 1 -- the index of the current key in the pattern
	-- the pitch multiplier for each semitone
	local zindexc=1
	-- Define the base pitch and the pitch step variables
	local BasePitch = .1 -- the base pitch of the piano (adjust as you like)
	local PitchStep = math.pow(2,1/12) -- the pitch multiplier for each semitone
	print(PitchStep)
	-- Loop through the sheet music and play each note one by one
	local SheetMusic = {
		-- Each entry is a table with multiple tables representing each finger: each with two values: the  note number and the note length.
		-- The note number is the index of the NoteMap table, which corresponds to a key on the piano
		-- The note length is a fraction of a whole note, such as 1/4 for a quarter note or 1/8 for an eighth note
		--Example: 1 Here is an example of 10 fingers on the keyboard. at one time.
		{{10, 1/8},{13, 1/16},{14, 1/16},{2, 1/8},{43, 1/16},{44, 1/8},{43, 1/16},{44, 1/16},{40, 1/8},{42, 1/16}}, 
		--Example: 2 Here is an example of 1 fingers on the keyboard. at one time.
		{{10, 1/8}},
		--Example: 3 Here is an example of a rest for one whole note.
		{{0, 1}},
		--This runs at a maximum of 1/32. this is the minimum time between each section. 
		{{10, 1/32}},
		-- And so on for the rest of the piece...
	}
	local NoteMap = {
		[1] = "A0",
		[2] = "A#0/Bb0",
		[3] = "B0",
		[4] = "C1",
		[5] = "C#1/Db1",
		[6] = "D1",
		[7] = "D#1/Eb1",
		[8] = "E1",
		[9] = "F1",
		[10] = "F#1/Gb1",
		[11] = "G1",
		[12] = "G#1/Ab1",
		[13] = "A1",
		[14] = "A#1/Bb1",
		[15] = "B1",
		[16] = "C2",
		[17] = "C#2/Db2",
		[18] = "D2",
		[19] = "D#2/Eb2",
		[20] = "E2",
		[21] = "F2",
		[22] = "F#2/Gb2",
		[23] = "G2",
		[24] = "G#2/Ab2",
		[25] = "A2",
		[26] = "A#2/Bb2",
		[27] = "B2",
		[28] = "C3",
		[29] = "C#3/Db3",
		[30] = "D3",
		[31] = "D#3/Eb3",
		[32] = "E3",
		[33] = "F3",
		[34] = "F#3/Gb3",
		[35] = "G3",
		[36] = "G#3/Ab3",
		[37] = "A3",
		[38] = "A#3/Bb3",
		[39] = "B3",
		[40] = "C4", -- middle C
		[41] = "C#4/Db4",
		[42] = "D4",
		[43] = "D#4/Eb4",
		[44] = "E4",
		[45] = "F4",
		[46] = "F#4/Gb4",
		[47] = "G4",
		[48] = "G#4/Ab4",
		[49] = "A4", -- 440 Hz
		[50] = "A#4/Bb4",
		[51] = "B4",
		[52] = "C5",
		[53] = "C#5/Db5",
		[54] = "D5",
		[55] = "D#5/Eb5",
		[56] = "E5"
	}
	--Jingle bells
	local Beat = {
		-- Fur Elise by Ludwig van Beethoven
		-- Tempo: 120 bpm
		-- Time signature: 3/8
		-- Key signature: A minor
		-- Each entry is a table with multiple tables representing each finger: each with two values: the note number and the note length.
		-- The note number is the index of the NoteMap table, which corresponds to a key on the piano
		-- The note length is a fraction of a whole note, such as 1/4 for a quarter note or 1/8 for an eighth note
		-- Example: {{10, 1/8},{13, 1/16},{14, 1/16},{2, 1/8},{43, 1/16},{44, 1/8},{43, 1/16},{44, 1/16},{40, 1/8},{42, 1/16}}

		-- Measure 1: Right hand plays E5 (56) for an eighth note, then D#5 (55) for an eighth note, then E5 (56) for an eighth note
		{{56, 1/8}, {55, 1/8}, {56, 1/8}},

		-- Measure 2: Right hand plays B4 (51) for an eighth note, then D5 (54) for an eighth note, then C5 (52) for an eighth note
		{{51, 1/8}, {54, 1/8}, {52, 1/8}},

		-- Measure 3: Right hand plays A4 (49) for a quarter note (two eighth notes), then E4 (44) for an eighth note
		{{49, 2/8}, {44, 1/8}},

		-- Measure 4: Right hand plays A4 (49) for a quarter note (two eighth notes), then B4 (51) for an eighth note
		{{49, 2/8}, {51, 1/8}},

		-- Measure 5: Right hand plays C5 (52) for an eighth note, then B4 (51) for an eighth note, then A4 (49) for an eighth note
		{{52, 1/8}, {51, 1/8}, {49, 1/8}},

		-- And so on for the rest of the piece...
	}
	local FurElise = {
		-- Fur Elise by Ludwig van Beethoven
		-- Tempo: 120 bpm
		-- Time signature: 3/8
		-- Key signature: A minor
		-- Each entry is a table with multiple tables representing each finger: each with two values: the note number and the note length.
		-- The note number is the index of the NoteMap table, which corresponds to a key on the piano
		-- The note length is a fraction of a whole note, such as 1/4 for a quarter note or 1/8 for an eighth note
		-- Example: {{10, 1/8},{13, 1/16},{14, 1/16},{2, 1/8},{41, 1/16},{44, 1/8},{43, 1/16},{44, 1/16},{40, 1/8},{42, 1/16}}

		-- Measure 1: Right hand plays E5 (56) for an eighth note, left hand plays A2 (25) for an eighth note
		{{56, 1/8}, {25, 1/8}},

		-- Measure 2: Right hand plays D#5 (55) for an eighth note, left hand plays E2 (20) for an eighth note
		{{55, 1/8}, {20, 1/8}},

		-- Measure 3: Right hand plays E5 (56) for an eighth note, left hand plays E2 (20) for an eighth note
		{{56, 1/8}, {20, 1/8}},

		-- Measure 4: Right hand plays B4 (51) for an eighth note, left hand plays A2 (25) for an eighth note
		{{51, 1/8}, {25, 1/8}},

		-- Measure 5: Right hand plays D5 (54) for an eighth note, left hand plays C3 (28) for an eighth note
		{{54, 1/8}, {28, 1/8}},

		-- Measure 6: Right hand plays C5 (52) for an eighth note, left hand plays E2 (20) for an eighth note
		{{52, 1/8}, {20, 1/8}},

		-- Measure 7: Right hand plays A4 (49) for a quarter note (two eighth notes), left hand plays A2 (25) for a quarter note (two eighth notes)
		{{49, 2/8}, {25, 2/8}},

		-- Measure 8: Right hand plays E4 (44) for an eighth note
		{{44, 1/8}},
		-- Measure 9: Right hand plays A4 (49) for a quarter note (two eighth notes), left hand plays A2 (25) for a quarter note (two eighth notes)
		{{49, 2/8}, {25, 2/8}},

		-- Measure 10: Right hand plays B4 (51) for an eighth note
		{{51, 1/8}},

		-- Measure 11: Right hand plays C5 (52) for an eighth note
		{{52, 1/8}},

		-- Measure 12: Right hand plays B4 (51) for an eighth note
		{{51, 1/8}},

		-- Measure 13: Right hand plays A4 (49) for an eighth note
		{{49, 1/8}},

		-- Measure 14: Right hand plays E5 (56) for an eighth note
		{{56, 1/8}},

		-- Measure 15: Right hand plays D#5 (55) for an eighth note
		{{55, 1/8}},

		-- Measure 16: Right hand plays E5 (56) for an eighth note
		{{56, 1/8}},

		-- Measure 17: Right hand plays B4 (51) for an eighth note
		{{51, 1/8}},

		-- Measure 18: Right hand plays D5 (54) for an eighth note
		{{54, 1/8}},

		-- Measure 19: Right hand plays C5 (52) for an eighth note
		{{52, 1/8}},

		-- Measure 20: Right hand plays A4 (49) for a quarter note (two eighth notes), left hand plays A2 (25) for a quarter note (two eighth notes)
		{{49, 2/8}, {25, 2/8}},
		-- Measure 21: Right hand plays E4 (44) for an eighth note
		{{44, 1/8}},

		-- Measure 22: Right hand plays A4 (49) for a quarter note (two eighth notes), left hand plays A2 (25) for a quarter note (two eighth notes)
		{{49, 2/8}, {25, 2/8}},

		-- Measure 23: Right hand plays B4 (51) for an eighth note
		{{51, 1/8}},

		-- Measure 24: Right hand plays C5 (52) for an eighth note
		{{52, 1/8}},

		-- Measure 25: Right hand plays B4 (51) for an eighth note
		{{51, 1/8}},

		-- Measure 26: Right hand plays A4 (49) for an eighth note
		{{49, 1/8}},

		-- Measure 27: Right hand plays E5 (56) for an eighth note
		{{56, 1/8}},

		-- Measure 28: Right hand plays D#5 (55) for an eighth note
		{{55, 1/8}},

		-- Measure 29: Right hand plays E5 (56) for an eighth note
		{{56, 1/8}},

		-- Measure 30: Right hand plays B4 (51) for an eighth note
		{{51, 1/8}},

		-- Measure 31: Right hand plays D5 (54) for an eighth note
		{{54, 1/8}},

		-- Measure 32: Right hand plays C5 (52) for an eighth note
		{{52, 1/8}},

		-- And so on for the rest of the piece...
		-- And so on for the rest of the piece...
	}
	local JingleBells = {
		-- Each entry is a table with two values: the note number and the note length
		-- The note number is the index of the NoteMap table, which corresponds to a key on the piano
		-- The note length is a fraction of a whole note, such as 1/4 for a quarter note or 1/8 for an eighth note
		{40, 1/4}, -- C4 quarter note
		{40, 1/4}, -- C4 quarter note
		{40, 1/2}, -- C4 half note
		{40, 1/4}, -- C4 quarter note
		{40, 1/4}, -- C4 quarter note
		{40, 1/2}, -- C4 half note
		{40, 1/4}, -- C4 quarter note
		{42, 1/4}, -- D4 quarter note
		{44, 1/4}, -- E4 quarter note
		{39, 1/4}, -- B3 quarter note
		{39, 1/8}, -- B3 eighth note
		{39, 1/8}, -- B3 eighth note
		{40, 1/4}, -- C4 quarter note
		{42, 1/8}, -- D4 eighth note
		{42, 1/8}, -- D4 eighth note
		{40, 1/2}, -- C4 half note	
		{44, 1/8}, -- E4 eighth note
		{44, 1/8}, -- E4 eighth note
		{44, 3/16}, -- E4 dotted eighth note
		{44, 1/16}, -- E4 sixteenth note
		{44, 1/8}, -- E4 eighth note
		{42, 1/8}, -- D4 eighth note
		{42, 3/16}, -- D4 dotted eighth note
		{42, 1/16}, -- D4 sixteenth note
		{42, 1/8}, -- D4 eighth note
		{40, 1/2} -- C4 half note	
	}
	local TwinkleLittleStar = {
		-- Each entry is a table with two values: the note number and the note length
		-- The note number is the index of the NoteMap table, which corresponds to a key on the piano
		-- The note length is a fraction of a whole note, such as 1/4 for a quarter note or 1/8 for an eighth note
		{44, 1/4}, -- E4 quarter note
		{44, 1/4}, -- E4 quarter note
		{47, 1/4}, -- G4 quarter note
		{47, 1/4}, -- G4 quarter note
		{49, 1/4}, -- A4 quarter note
		{49, 1/4}, -- A4 quarter note
		{47, 1/2}, -- G4 half note
		{46, 1/4}, -- F#4/Gb4 quarter note
		{46, 1/4}, -- F#4/Gb4 quarter note
		{44, 1/4}, -- E4 quarter note
		{44, 1/4}, -- E4 quarter note
		{42, 1/4}, -- D4 quarter note
		{42, 1/4}, -- D4 quarter note
		{40, 1/2}, -- C4 half note	
		{47, 1/4}, -- G4 quarter note
		{47, 1/4}, -- G4 quarter note
		{46, 1/4}, -- F#4/Gb4 quarter note
		{46, 1/4}, -- F#4/Gb4 quarter note
		{44, 1/2}, -- E4 half note	
		{47, 1/4}, -- G4 quarter note
		{47, 1/4}, -- G4 quarter note
		{46, 1/4}, -- F#4/Gb4 quarter note
		{46, 1/4}, -- F#4/Gb4 quarter note
		{44, 1/2} -- E4 half note	
	}
	local Odetojoy = {
		-- Each entry is a table with two values: the note number and the note length
		-- The note number is the index of the NoteMap table, which corresponds to a key on the piano
		-- The note length is a fraction of a whole note, such as 1/4 for a quarter note or 1/8 for an eighth note
		{40, 1/4}, -- C4 quarter note
		{42, 1/4}, -- D4 quarter note
		{44, 1/4}, -- E4 quarter note
		{42, 1/4}, -- D4 quarter note
		{40, 1/4}, -- C4 quarter note
		{39, 1/4}, -- B3 quarter note
		{37, 1/4}, -- A3 quarter note
		{37, 1/2}, -- A3 half note
		{42, 1/4}, -- D4 quarter note
		{42, 1/4}, -- D4 quarter note
		{40, 1/4}, -- C4 quarter note
		{39, 1/4}, -- B3 quarter note
		{37, 1/2}, -- A3 half note	
		{40, 1/4}, -- C4 quarter note
		{44, 1/4}, -- E4 quarter note
		{47, 1/8}, -- G4 eighth note
		{47, 1/8}, -- G4 eighth note
		{46, 1/8}, -- F#4/Gb4 eighth note
		{46, 1/8}, -- F#4/Gb4 eighth note
		{44, 1/8}, -- E4 eighth note
		{44, 1/8}, -- E4 eighth note
		{42, 1/8}, -- D4 eighth note
		{42, 1/8}, -- D4 eighth note
		{40, 1/2} -- C4 half note	
	}
	
	-- Create a Frame

local NoteMap = {
	[1] = "A0",
	[2] = "A#0/Bb0",
	[3] = "B0",
	[4] = "C1",
	[5] = "C#1/Db1",
	[6] = "D1",
	[7] = "D#1/Eb1",
	[8] = "E1",
	[9] = "F1",
	[10] = "F#1/Gb1",
	[11] = "G1",
	[12] = "G#1/Ab1",
	[13] = "A1",
	[14] = "A#1/Bb1",
	[15] = "B1",
	[16] = "C2",
	[17] = "C#2/Db2",
	[18] = "D2",
	[19] = "D#2/Eb2",
	[20] = "E2",
	[21] = "F2",
	[22] = "F#2/Gb2",
	[23] = "G2",
	[24] = "G#2/Ab2",
	[25] = "A2",
	[26] = "A#2/Bb2",
	[27] = "B2",
	[28] = "C3",
	[29] = "C#3/Db3",
	[30] = "D3",
	[31] = "D#3/Eb3",
	[32] = "E3",
	[33] = "F3",
	[34] = "F#3/Gb3",
	[35] = "G3",
	[36] = "G#3/Ab3",
	[37] = "A3",
	[38] = "A#3/Bb3",
	[39] = "B3",
	[40] = "C4", -- middle C
	[41] = "C#4/Db4",
	[42] = "D4",
	[43] = "D#4/Eb4",
	[44] = "E4",
	[45] = "F4",
	[46] = "F#4/Gb4",
	[47] = "G4",
	[48] = "G#4/Ab4",
	[49] = "A4", -- 440 Hz
	[50] = "A#4/Bb4",
	[51] = "B4",
	[52] = "C5",
	[53] = "C#5/Db5",
	[54] = "D5",
	[55] = "D#5/Eb5",
	[56] = "E5"
}
local inpdeb= (1/64)	

mouse = game.Players.LocalPlayer:GetMouse()
local KeyMap = {
	-- The first row of keys on the keyboard, from ` to =
	[1] = "`",
	[2] = "1",
	[3] = "2",
	[4] = "3",
	[5] = "4",
	[6] = "5",
	[7] = "6",
	[8] = "7",
	[9] = "8",
	[10] = "9",
	[11] = "0",
	[12] = "-",
	[13] = "=",

	-- The second row of keys on the keyboard, from q to ]
	[14] = "q",
	[15] = "w",
	[16] = "e",
	[17] = "r",
	[18] = "t",
	[19] = "y",
	[20] = "u",
	[21] = "i",
	[22] = "o",
	[23] = "p",
	[24] = "[",
	[25] = "]",

	-- The third row of keys on the keyboard, from a to '
	[26] = "a",
	[27] = "s",
	[28] = "d",
	[29] = "f",
	[30] = "g",
	[31] = "h",
	[32] = "j",
	[33] = "k",
	[34] = "l",
	[35] = ";",
	[36] = "'",

	-- The fourth row of keys on the keyboard, from z to /
	[37] = "z",
	[38] = "x",
	[39] = "c",
	[40] = "v",
	[41] = "b",
	[42] = "n",
	[43] = "m", 
	[44] =",", 
	[45]=".", 
	[46]= "/",

	-- The fifth row of keys on the keyboard, from space to right shift
	[47]= " ",
	[48]= "<",
	[49]= "^", -- left arrow
	[50]= ">", -- up arrow
	--[51]= ">" -- right arrow
}
	-- Define some variables for the piano logic
	local KeyCount = 56 -- the number of keys on the piano
	local KeyWidth = 21/12 / KeyCount -- the width of each key relative to the frame width
	local KeyColors = {Color3.new(1, 1, 1), Color3.new(0, 0, 0)} -- the colors of the keys (white and black)
	local KeyPattern = {1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 2, 1} -- the pattern of the keys (white and black)
	local KeyIndex = 1 -- the index of the current key in the pattern
	-- the pitch multiplier for each semitone
	local zindexc=1
	-- Define the base pitch and the pitch step variables
	local BasePitch = .1 -- the base pi
	if experimental==true then
		function TweenSound(guSound)
			spawn(function()	
				local guSound=guSound
				local prevv=guSound.Volume
				local delays2
				local delays	
				--guSound.Volume=0
				guSound.Volume=0
				local goal = {}

				goal.Volume =  1+(mathrandom(1,10)/100)
				--goal.PlaybackSpeed=0
				local tweenInfo = TweenInfo.new(guSound.TimeLength*.005*mathrandom(1,5))
				local tween = TweenService:Create(guSound, tweenInfo, goal)
				tween:Play()
				guSound:Play() 
				tween.Completed:Wait()
				task.wait(guSound.TimeLength*(.75*(mathrandom(1,4)/4)))
				local TweenService = game:GetService("TweenService")
				local goal = {}
				goal.Volume =  0
				--goal.PlaybackSpeed=0
				local tweenInfo = TweenInfo.new(guSound.TimeLength*.25)
				local tween = TweenService:Create(guSound, tweenInfo, goal)
				--tween:Play() 
				tween.Completed:Wait()
				guSound:Destroy()
			end)
		end
		function PlaySound(input)
	-- Get the key that was clicked or touched
spawn(function()	
	local Key = input.Name -- input name is a number that represents its position on the piano.

	-- Get the sound of the key
	local Sound = input:FindFirstChild("Sound"):Clone()
	Sound.Parent=input

	-- If there is a sound, play it with some random variations
	if Sound then
		Sound.PlaybackSpeed = 0 + (Key/28)-(mathrandom(1,10)/100)
		Sound.Pitch = 0 + (Key/28)+(mathrandom(1,3)/100) -- set the pitch according to the key index

		--Sound.Pitch = BasePitch *  Key + 1 -- vary the pitch slightly
		--Sound.PlaybackSpeed = mathrandom(100, 110) / 100 -- vary the playback speed slightly
		--Sound.Volume = mathrandom(Sound.Volume * 70, Sound.Volume * 80) / 100 -- vary the volume slightly
	-- play the sound
		TweenSound(Sound)
			
	end
			end)

--	Key.BackgroundColor3 = Key.BackgroundColor3:Lerp(Color3.new(0.5, 0.5, 0.5), 0.2)
		end
	else 
		function TweenSound(guSound)
			spawn(function()	
				local guSound=guSound
				local prevv=guSound.Volume
				local delays2
				local delays	
				--guSound.Volume=0
				guSound.Volume=0
				local goal = {}

				goal.Volume =  1+(mathrandom(1,10)/100)
				--goal.PlaybackSpeed=0
				local tweenInfo = TweenInfo.new(guSound.TimeLength*.005*mathrandom(1,5))
				local tween = TweenService:Create(guSound, tweenInfo, goal)
				tween:Play()
				guSound:Play() 
				tween.Completed:Wait()
				task.wait(guSound.TimeLength*(.75*(mathrandom(1,4)/4)))
				local TweenService = game:GetService("TweenService")
				local goal = {}
				goal.Volume =  0
				--goal.PlaybackSpeed=0
				local tweenInfo = TweenInfo.new(guSound.TimeLength*.25)
				local tween = TweenService:Create(guSound, tweenInfo, goal)
				--tween:Play() 
				tween.Completed:Wait()
				--guSound:Destroy()
			end)
		end
		function PlaySound(input,origcolor)
			-- Get the key that was clicked or touched
			spawn(function()	
				local Key = input.Name -- input name is a number that represents its position on the piano.
				--if Key.BackgroundColor3=
				--- Get the sound of the key
				local Sound = input:FindFirstChild("Sound"):Clone()
				Sound.Parent=input

				-- If there is a sound, play it with some random variations
				if Sound then
					Sound.PlaybackSpeed = 1 - ((Key-1)/56)-(mathrandom(1,(Key-1)/8)/100)
					if origcolor~=Color3.new(0,0,0) then
						--and (Key*1)<=28 then
					Sound.Pitch = 0 + ((Key-1)/28)+(mathrandom(1,3)/100)*1.059463 -- set the pitch according to the key index
					--elseif origcolor~=Color3.new(0,0,0) and (Key*1)>28 then

					--	Sound.Pitch = 0.1 + ((Key+1)/28)+(mathrandom(1,5)/100)*1.059463 
					
					elseif (Key*1)>28 then 
						Sound.Pitch = 0 + ((Key-1)/28)-(mathrandom(1,3)/100)*1.059463
							--*1.059463 -- set the pitch according to the key index
					elseif (Key*1)<=28 then 
						Sound.Pitch = 0 + ((Key-1)/28)-(mathrandom(1,3)/100)*1.059463 -- set the pitch according to the key index

					end	
						--Sound.Pitch=2.8
					--Sound.Pitch = BasePitch *  Key + 1 -- vary the pitch slightly
					--Sound.PlaybackSpeed = mathrandom(100, 110) / 100 -- vary the playback speed slightly
					--Sound.Volume = mathrandom(Sound.Volume * 70, Sound.Volume * 80) / 100 -- vary the volume slightly
					-- play the sound
					TweenSound(Sound)

				end
			end)

			--	Key.BackgroundColor3 = Key.BackgroundColor3:Lerp(Color3.new(0.5, 0.5, 0.5), 0.2)
		end		

		local function CreateKey(key)
			-- Create a TextButton
			local Key = Instance.new("TextButton")
			Key.Parent = Frame
			zindexc=zindexc+1
			if KeyPattern[KeyIndex2]==2 then
				Key.Size = UDim2.new(KeyWidth/1.4, 0, .7, 0) -- adjust the size as you like
				Key.Position = UDim2.new((KeyIndex - 1) * KeyWidth, 0, 0, 0) -- place it next to the previous key
				KeyIndex=KeyIndex-1	
				Key.ZIndex=zindexc+3
			else
				Key.Size = UDim2.new(KeyWidth, 0, 1, 0) -- adjust the size as you like
				Key.Position = UDim2.new((KeyIndex - 1) * KeyWidth, 0, 0, 0) -- place it next to the previous key
				Key.ZIndex=zindexc
			end	
			Key.BackgroundColor3 = KeyColors[KeyPattern[KeyIndex2]] -- set the color according to the pattern
			Key.Text = "" -- no text
			Key.Name = KeyIndex3 -- name the key

			-- Create a Sound
			local Sound 
			if KeyIndex3<28 then Sound=script.Piano:Clone()
			else Sound=script.Piano:Clone()
			end
			Sound.Name="Piano"
			Sound.Parent = Key
			--Sound.SoundId = "rbxassetid://123456789" -- replace with your sound sample id
			--Sound.PlaybackSpeed=length
			--	Sound.Pitch = BasePitch * math.pow(PitchStep, KeyIndex3 - 1) -- set the pitch according to the key index
			Sound.Name = "Sound" -- name the sound

			-- Return the key
			return Key
		end
	
-- Define a variable to store the time signature

musicspeed=1
	
			-- The rest of the song is omitted for brevity
		
function PlayerPiano2(SheetMusic)
spawn(function()			
			local Length
			while true do
		for c = 1, #SheetMusic do
			
			for i = 1, #SheetMusic[c] do

			-- Get the current note and its length from the sheet music table
			local Note = SheetMusic[c][i][1]
			Length = SheetMusic[c][i][2]

			-- Get the key and the sound that correspond to the note from the keys table and the notemap table
			local Key = ScreenGui.Piano:FindFirstChild(Note)
			--local Sound = Key.Sound

			-- Highlight the key by changing its color slightly

			PlaySound(Key,Length*20)

			-- Wait for the duration of the note in seconds
			--task.wait(MeasureDuration * Length*20)
			end
			task.wait(MeasureDuration*20/8)--waits for the longest note of each moment to complete before going to next
		end 
				end
	end)			
end
-- Loop through the sheet music and play each note one by one

function PlayerPiano(SheetMusic)
while true do
for i = 1, #SheetMusic do

	-- Get the current note and its length from the sheet music table
	local Note = SheetMusic[i][1]
	local Length = SheetMusic[i][2]

	-- Get the key and the sound that correspond to the note from the keys table and the notemap table
	local Key = ScreenGui.Piano:FindFirstChild(Note)
	local Sound = Key.Sound

	-- Highlight the key by changing its color slightly

	PlaySound(Key,Length*20)

	-- Wait for the duration of the note in seconds
	task.wait(MeasureDuration * Length*20)

	end 
	end
	end
	
	 KeyIndex=1
	 KeyIndex2=1
	 KeyIndex3=1
	
	for i = 1, KeyCount do
		local Key = CreateKey()
		local origcolor=Key.BackgroundColor3
		local keydeb=false
		local function connectkey()
			if keydeb==false then
				keydeb=true	
					Key.BackgroundColor3 = Key.BackgroundColor3:Lerp(Color3.new(0.5, 0.5, 0.5), 0.2) 

				PlaySound(Key,origcolor)	task.wait(.1) Key.BackgroundColor3 = origcolor
				task.wait(inpdeb)
				keydeb=false	
			end
		end
		Key.MouseButton1Click:Connect(connectkey)
		Key.TouchTap:Connect(connectkey)
		local KeyID=KeyIndex3
		mouse.KeyDown:connect(function (key) -- Run function
		

				local boardkey = tostring(string.lower(key))
				--print(boardkey,KeyMap[KeyID])
				if (boardkey) == tostring(KeyMap[KeyID]) then
					keydeb=true	
					Key.BackgroundColor3 = Key.BackgroundColor3:Lerp(Color3.new(0.5, 0.5, 0.5), 0.2) 
					PlaySound(Key)	task.wait(.1) Key.BackgroundColor3 = origcolor
					task.wait(inpdeb)
					keydeb=false	
				end		
			
		end)
		KeyIndex3=KeyIndex3+1
		KeyIndex2=KeyIndex2+1
		KeyIndex=KeyIndex+1
		if KeyIndex2 > #KeyPattern then
			KeyIndex2 = 1
		end

	end
	
	end
	local	FurElise2 = {
		{{56, 1/8}, {32, 1/8}, {39, 1/8}, {44, 1/8}}, {{55, 1/8}}, {{56, 1/8}}, {{55, 1/8}}, {{56, 1/8}, {32, 1/8}, {39, 1/8}, {44, 1/8}}, {{49, 1/8}}, {{52, 1/8}}, {{44, 1/4}, {28, 1/4}},
		{{47, 1/8}}, {{40, 1/4}, {25, 1/4}}, {{44, 1/8}}, {{32, 1/4}, {20, 1/4}}, {{35, 1/8}}, {{28, 3/8}, {16, 3/8}},
		{{56, 1/8}, {32, 1/8}, {39, 1/8}, {44, 1/8}}, {{55, 1/8}}, {{56, 1/8}}, {{55, 1/8}}, {{56, 1/8}, {32, 1/8}, {39, 1/8}, {44, 1/8}}, {{49, 1/8}}, {{52, 1/8}}, {{44, 1/4}, {28, 3/16}},
		{{47 ,3 /16}, {40 ,3 /16}, {44 ,3 /16}},
		{{46 ,3 /16}, {39 ,3 /16}, {43 ,3 /16}},
		{{45 ,3 /16}, {38 ,3 /16}, {42 ,3 /16}},
		{{44 ,3 /16}, {37 ,3 /16}, {41 ,3 /16}},
		{{43 ,3 /16}, {36 ,3 /16}, {40 ,3 /16}},
		{{42 ,3 /16}, {35 ,3 /16}, {39 ,3 /16}},
		{{41 ,5 /32}, {34 ,5 /32}, {38 ,5 /32}},
		{{40 ,5 /32}, {33 ,5 /32}, {37 ,5 /32}},
		{{39 ,5 /32}, {32 ,5 /32}, {36 ,5 /32}},
		{{38 ,5 /32}, {31 ,5 /32}, {35 ,5 /32}},
		{{37 ,5 /32}, {30 ,5 /32}, {34 ,5 /32}},
		{{36 ,5 /32}, {29 ,5 /32}, {33 ,5 /32}},
		{{35 ,7 /64}, {28 ,7 /64}},
		{{36 ,7 /64}, {29 ,7 /64}},
		{{37 ,7 /64}, {30 ,7 /64}},
		{{38 ,7 /64}, {31 ,7 /64}},
		{{39 ,7 /64}, {32 ,7 /64}},
		{{40 ,7 /64}, {33 ,7 /64}},
		{{41 ,7 /64}, {34 ,7 /64}},
		{{42 ,7 /64}, {35 ,7 /64}},
		{{43 ,7 /64}, {36 ,7 /64}},
		{{44 ,7 /64}, {37 ,7 /64}},
		{{45 ,7 /64}, {38 ,7 /64}},
		{{46 ,7 /64}, {39 ,7 /64}},
		{{47 ,7 /64}, {40 ,7 /64}},
		{{48 ,7 /64}, {41 ,7 /64}},
		{{49 ,7 /64}, {42 ,7 /64}},
		{{50 ,7 /64}, {43 ,7 /64}},
		{{51 ,7 /64}, {44 ,7 /64}},
		{{52 ,7 /64}, {45 ,7 /64}},
		{{53 ,7 /64}, {46 ,7 /64}},
		{{54 ,7 /64}, {47 ,7 /64}},
		{{55 ,1/8}, {48, 1/8}}, {{56, 1/8}}, {{55, 1/8}}, {{56, 1/8}, {32, 1/8}, {39, 1/8}, {44, 1/8}}, {{49, 1/8}}, {{52, 1/8}}, {{44, 1/4}, {28, 1/4}},
		{{47, 1/8}}, {{40, 1/4}, {25, 1/4}}, {{44, 1/8}}, {{32, 1/4}, {20, 1/4}}, {{35, 1/8}}, {{28, 3/8}, {16, 3/8}},
		{{56, 1/8}, {32, 1/8}, {39, 1/8}, {44, 1/8}}, {{55, 1/8}}, {{56, 1/8}}, {{55, 1/8}}, {{56, 1/8}, {32, 1/8}, {39, 1/8}, {44, 1/8}}, {{49, 1/8}}, {{52, 1/8}}, {{44 ,3 /16}, {28 ,3 /16}},
		{{47 ,3 /16}, {40 ,3 /16}, {44 ,3 /16}},
		{{46 ,3 /16}, {39 ,3 /16}, {43 ,3 /16}},
		{{45 ,3 /16}, {38 ,3 /16}, {42 ,3 /16}},
		{{44 ,3 /16}, {37 ,3 /16}, {41 ,3 /16}},
		{{43 ,3 /16}, {36 ,3 /16}, {40 ,3 /16}},
		{{42 ,3 /16}, {35 ,3 /16}, {39 ,3 /16}},
		{{41 ,5 /32}, {34 ,5 /32}, {38 ,5 /32}},
		{{43 ,3 /16}, {36 ,3 /16}, {40 ,3 /16}},
		{{46 ,3 /16}, {39 ,3 /16}, {43 ,3 /16}},
		{{45 ,3 /16}, {38 ,3 /16}, {42 ,3 /16}},
		{{44 ,3 /16}, {37 ,3 /16}, {41 ,3 /16}},
		{{43 ,3 /16}, {36 ,3 /16}, {40 ,3 /16}},
		{{42 ,3 /16}, {35 ,3 /16}, {39 ,3 /16}},
{{41 ,5 /32}, {34 ,5 /32}, {38 ,5 /32}},
{{40 ,5 /32}, {33 ,5 /32}, {37 ,5 /32}},
{{39 ,5 /32}, {32 ,5 /32}, {36 ,5 /32}},
{{38 ,5 /32}, {31 ,5 /32}, {35 ,5 /32}},
{{37 ,5 /32}, {30 ,5 /32}, {34 ,5 /32}},
{{36 ,5 /32}, {29 ,5 /32}, {33 ,5 /32}},
{{35, 1/8}, {28, 1/8}}, {{36, 1/8}}, {{37, 1/8}}, {{38, 1/8}}, {{39, 1/8}}, {{40, 1/8}}, {{41, 1/8}}, {{42, 1/8}},
{{43, 1/8}}, {{44, 1/8}}, {{45, 1/8}}, {{46, 1/8}}, {{47, 1/8}}, {{48, 1/8}}, {{49, 1/8}}, {{50, 1/8}},
{{51, 1/4}, {44, 1/4}}, {{52, 1/4}, {45, 1/4}}, {{53, 1/4}, {46, 1/4}}, {{54, 1/4}, {47, 1/4}},
{{55, 3/16}, {48, 3/16}}, {{56, 3/16}, {49, 3/16}}, {{57, 3/16}, {50, 3/16}}, {{58, 3/16}, {51, 3/16}},
{{59, 3/16}, {52, 3/16}}, {{60, 3/16}, {53, 3/16}}, {{61, 3/16}, {54, 3/16}}, {{62, 3/16}, {55, 3/16}},
{{63, 7/64}, {56,7 /64}},
{{64 ,7 /64}, {57 ,7 /64}},
{{65 ,7 /64}, {58 ,7 /64}},
		{{66 ,7 /64}, {59 ,7 /64}},	
		{{42 ,3 /16}, {35 ,3 /16}, {39 ,3 /16}},
		{{41 ,5 /32}, {34 ,5 /32}, {38 ,5 /32}},
		{{40 ,5 /32}, {33 ,5 /32}, {37 ,5 /32}},
		{{39 ,5 /32}, {32 ,5 /32}, {36 ,5 /32}},
		{{38 ,5 /32}, {31 ,5 /32}, {35 ,5 /32}},
		{{37 ,5 /32}, {30 ,5 /32}, {34 ,5 /32}},
		{{36 ,5 /32}, {29 ,5 /32}, {33 ,5 /32}},
		{{35, 1/8}, {28, 1/8}}, {{36, 1/8}}, {{37, 1/8}}, {{38, 1/8}}, {{39, 1/8}}, {{40, 1/8}}, {{41, 1/8}}, {{42, 1/8}},
		{{43, 1/8}}, {{44, 1/8}}, {{45, 1/8}}, {{46, 1/8}}, {{47, 1/8}}, {{48, 1/8}}, {{49, 1/8}}, {{50, 1/8}},
		{{51, 1/4}, {44, 1/4}}, {{52, 1/4}, {45, 1/4}}, {{53, 1/4}, {46, 1/4}}, {{54, 1/4}, {47, 1/4}},
		{{55, 3/16}, {48, 3/16}}, {{56, 3/16}, {49, 3/16}}, {{57, 3/16}, {50, 3/16}}, {{58, 3/16}, {51, 3/16}},
		{{59, 3/16}, {52, 3/16}}, {{60, 3/16}, {53, 3/16}}, {{61, 3/16}, {54, 3/16}}, {{62, 3/16}, {55, 3/16}},
		{{63, 7/64}, {56,7 /64}},
		{{64 ,7 /64}, {57 ,7 /64}},
		{{65 ,7 /64}, {58 ,7 /64}},
		{{66 ,7 /64}, {59 ,7 /64}},
		{{67 ,7 /64}, {60 ,7 /64}},
		{{68 ,7 /64}, {61 ,7 /64}},
		{{69 ,7 /64}, {62 ,7 /64}},
		{{70 ,7 /64}, {63 ,7 /64}},
		{{71 ,1/8}, {64, 1/8}}, {{72, 1/8}}, {{71, 1/8}}, {{72, 1/8}, {48, 1/8}, {55, 1/8}, {60, 1/8}}, {{65, 1/8}}, {{68, 1/8}}, {{60, 1/4}, {44, 1/4}},
		{{63, 1/8}}, {{56, 1/4}, {41, 1/4}}, {{60, 1/8}}, {{48, 1/4}, {36, 1/4}}, {{51, 1/8}}, {{44, 3/8}, {32, 3/8}},
		{{72, 1/8}, {48, 1/8}, {55, 1/8}, {60, 1/8}}, {{71, 1/8}}, {{72, 1/8}}, {{71, 1/8}}, {{72, 1/8}, {48, 1/8}, {55, 1/8}, {60, 1/8}}, {{65, 1/8}}, {{68, 1/8}}, {{60 ,3 /16}, {44 ,3 /16}},
		{{63 ,3 /16}, {56 ,3 /16}, {60 ,3 /16}},
		{{62 ,3 /16}, {55 ,3 /16}, {59 ,3 /16}},
		{{61 ,3 /16}, {54 ,3 /16}, {58 ,3 /16}},
		{{60 ,3 /16}, {53 ,3 /16}, {57 ,3 /16}},
		{{59 ,3 /16}, {52 ,3 /16}, {56 ,3 /16}},
		{{58 ,5 /32}, {51 ,5 /32}, {55 ,5 /32}},
		{{57 ,5 /32}, {50 ,5 /32}, {54 ,5 /32}},
		{{56 ,5 /32}, {49 ,5 /32}, {53 ,5 /32}},
		{{55 ,5 /32}, {48 ,5 /32}, {52 ,5 /32}},
		{{54 ,5 /32}, {47 ,5 /32}, {51 ,5 /32}},
		{{53, 1/8}, {46, 1/8}}, {{54, 1/8}}, {{55, 1/8}}, {{56, 1/8}}, {{57, 1/8}}, {{58, 1/8}}, {{59, 1/8}}, {{60, 1/8}},
		{{61, 1/8}}, {{62, 1/8}}, {{63, 1/8}}, {{64, 1/8}}, {{65, 1/8}}, {{66, 1/8}}, {{67, 1/8}}, {{68, 1/8}},
		{{69, 1/4}, {62, 1/4}}, {{70, 1/4}, {63, 1/4}}, {{71, 1/4}, {64, 1/4}}, {{72, 1/4}, {65, 1/4}},
		{{73, 3/16}, {66, 3/16}}, {{74, 3/16}, {67, 3/16}}, {{75, 3/16}, {68, 3/16}}, {{76, 3/16}, {69, 3/16}},
		{{77, 7/64}, {70 ,7 /64}},
		{{78 ,7 /64}, {71 ,7 /64}},
		{{79 ,7 /64}, {72 ,7 /64}},
		{{80 ,7 /64}, {73 ,7 /64}},
		{{81 ,7 /64}, {74 ,7 /64}},
		{{82 ,7 /64}, {75 ,7 /64}},
		{{83 ,7 /64}, {76 ,7 /64}},
		{{84 ,1/8}, {77, 1/8}}, {{85, 1/8}}, {{84, 1/8}}, {{85, 1/8}, {61, 1/8}, {68, 1/8}, {73, 1/8}}, {{78, 1/8}}, {{81, 1/8}}, {{73, 1/4}, {57, 1/4}},
		{{76, 1/8}}, {{69, 1/4}, {54, 1/4}}, {{73, 1/8}}, {{61, 1/4}, {49, 1/4}}, {{64, 1/8}}, {{57, 3/8}, {45, 3/8}},
		{{85, 1/8}, {61, 1/8}, {68, 1/8}, {73, 1/8}}, {{84, 1/8}}, {{85, 1/8}}, {{84, 1/8}}, {{85, 1/8}, {61, 1/8}, {68, 1/8}, {73, 1/8}}, {{78, 1/8}}, {{81, 1/8}}, {{73 ,3 /16}, {57 ,3 /16}},
		{{76 ,3 /16}, {69 ,3 /16}, {73 ,3 /16}},
		{{75 ,3 /16}, {68 ,3 /16}, {72 ,3 /16}},
	}
PlayerPiano2(FurElise2)
	end
3 Likes

Looks pretty impressive so far! Can’t wait to see where this goes.

Looks like the keyboard is only one octave so I do believe you meant changes the note/pitch/frequency :slight_smile:

1 Like

Yeah, my bad, I’m not really a music guy haha.

1 Like

I’m sort of confused on what you are trying to tell me here?

1 Like

I has made something similar. But that is a script that generates a complete piano 56 key piano with keybinds for every keyboard key and touch/click input controls.

1 Like

Oh okay, that’s really cool! ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎

1 Like

Yes the only thing you have to define in that script to get it to work is the location of the Piano sample and the screenGui. It generates the rest from scratch.

2 Likes

I also edited the post to remove a undefined nametag variable and defined the PlayerGui
So i think all you need is a audio sample to run the script and create a full 56 key piano that is bound to the keyboard and touch/click input. It also has my attempt to create a playerpiano format to play songs automatically. But it’s a bit tedious. It would be much easier for someone to record a input and play it back. This can be used for a educational tool that would be really amazing and interesting for ROBLOX.
You can use a serverscript with functions like this to save specific data

function SaveDataPairs (Datastorkey,ChatBotData,Querie)
	-- Create an empty table to store the data pairs
	local DataPairs = {}

	-- Iterate over the children of the Queries folder
	for _, child in ipairs (Querie:GetChildren ()) do
		-- Check if the child is a string value object
		if child:IsA ("StringValue") then
			-- Add the child's name and value to the table as a key-value pair
			DataPairs[child.Name] = child.Value
		end
	end

	-- Save the table to the data store with a key of "ChatBotQueries"
	local success, result = pcall (function ()
		ChatBotData:SetAsync (Datastorkey, DataPairs)
	end)

	if success then
		print ("Data pairs saved successfully")
	else
		warn ("Data pairs failed to save: " .. result)
	end
end
datachange=true
local datachangehandle={}
function LoadDataPairs (Datastorkey,ChatBotData,Querie)
	local Querie=Querie
	local name
	local value
	-- Load the table from the data store with the same key that was used to save it
	local success, result = pcall (function ()
		return ChatBotData:GetAsync (Datastorkey)
	end)

	if success then
		if result then
			print ("Data pairs loaded successfully")

			-- Iterate over the table
			for name, value in pairs (result) do
				-- Create a new string value object in the Queries folder with the name and value from each pair
				if Querie:FindFirstChild(name)==nil then
					local datachanged=true
					local StringValue = Instance.new ("StringValue", Querie)
					StringValue.Name = name	
					StringValue.Value = value
					datachangehandle[Datastorkey] = function() if datachanged==true then datachanged=false return Datastorkey,ChatBotData,Querie else return false end end
					--Learning Function
					StringValue:GetPropertyChangedSignal("Value"):Connect(function()
						datachanged=true
						datachange=true
					end)


				else 
					Querie:FindFirstChild(name).Value=value
					Querie:FindFirstChild(name):GetPropertyChangedSignal("Value"):Connect(function()
						datachanged=true
						datachange=true
					end)
				end	
			end
		else
			print ("No data pairs found for this key")
		end
	else
		warn ("Data pairs failed to load: " .. result)
	end 
end
1 Like

I’ll be sure to check out your script!

1 Like