Follow the CFC coding style

This commit is contained in:
StyledStrike 2022-11-19 23:45:06 -03:00
parent cfaf83bda6
commit d86ccf3922
13 changed files with 1551 additions and 1478 deletions

17
.github/workflows/glualint.yml vendored Normal file
View File

@ -0,0 +1,17 @@
name: GLuaLint
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
# Allows running this workflow from the Actions tab
workflow_dispatch:
jobs:
Lint:
uses: FPtje/GLuaFixer/.github/workflows/glualint.yml@master
with:
config: "./glualint.json"

View File

@ -2,17 +2,27 @@
A playable keyboard for Garry's Mod
[![GLuaLint](https://github.com/StyledStrike/musical-keyboard/actions/workflows/glualint.yml/badge.svg)](https://github.com/FPtje/GLuaFixer)
[![Workshop Page](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-steam-workshop.jross.me%2F2656563609%2Fsubscriptions-text)](https://steamcommunity.com/sharedfiles/filedetails/?id=2656563609)
### Features
- Sheets
- Multiple keyboard layouts
- Many instruments in one entity
- Preserves the key press timings when heard by other players
- MIDI support ([Module installation guide](https://steamcommunity.com/workshop/filedetails/discussion/2656563609/3199240042192880687/))
- Multi-language *(Help needed! Feel free to open a issue/pull request to add/update language files)*
![Demo](https://i.imgur.com/hD7VBq1.gif)
### Notice
All samples used on this project are part of freely available soundfonts.
- **GeneralUser GS:** [Custom License](http://www.schristiancollins.com/generaluser.php)
- **Alex's gm soundfont version 1.3:** [Creative Commons Attribution 3.0](https://musical-artifacts.com/artifacts/1390)
- **LMMS Presets:** (https://docs.lmms.io/user-manual/8-legal/8.1-license)
- **LMMS Presets:** (https://docs.lmms.io/user-manual/8-legal/8.1-license)
## Contributing
Please follow the [CFC style guidelines](https://github.com/CFC-Servers/cfc_glua_style_guidelines) before opening pull requests.

View File

@ -1,37 +1,45 @@
{
"lint_maxScopeDepth": 8,
"lint_syntaxErrors": true,
"lint_syntaxInconsistencies": true,
"lint_deprecated": true,
"lint_trailingWhitespace": true,
"lint_whitespaceStyle": true,
"lint_beginnerMistakes": true,
"lint_emptyBlocks": true,
"lint_shadowing": true,
"lint_gotos": true,
"lint_goto_identifier": true,
"lint_doubleNegations": true,
"lint_redundantIfStatements": true,
"lint_redundantParentheses": true,
"lint_duplicateTableKeys": true,
"lint_profanity": true,
"lint_unusedVars": true,
"lint_unusedParameters": true,
"lint_unusedLoopVars": false,
"lint_inconsistentVariableStyle": false,
"lint_ignoreFiles": [],
"lint_maxScopeDepth": 7,
"lint_syntaxErrors": true,
"lint_syntaxInconsistencies": true,
"lint_deprecated": true,
"lint_trailingWhitespace": true,
"lint_whitespaceStyle": true,
"lint_beginnerMistakes": true,
"lint_emptyBlocks": true,
"lint_shadowing": true,
"lint_gotos": true,
"lint_goto_identifier": true,
"lint_doubleNegations": true,
"lint_redundantIfStatements": true,
"lint_redundantParentheses": true,
"lint_duplicateTableKeys": true,
"lint_profanity": true,
"lint_unusedVars": true,
"lint_unusedParameters": true,
"lint_unusedLoopVars": true,
"lint_inconsistentVariableStyle": false,
"lint_spaceBeforeComma": true,
"lint_spaceAfterComma": true,
"lint_spaceBetweenParens": true,
"lint_spaceBetweenBrackets": true,
"lint_spaceBetweenBraces": true,
"lint_ignoreFiles": ["**.lua"],
"prettyprint_spaceAfterParens": true,
"prettyprint_spaceAfterBrackets": false,
"prettyprint_spaceAfterBraces": false,
"prettyprint_spaceEmptyParens": false,
"prettyprint_spaceEmptyBraces": false,
"prettyprint_spaceAfterLabel": false,
"prettyprint_spaceBeforeComma": false,
"prettyprint_spaceAfterComma": true,
"prettyprint_semicolons": false,
"prettyprint_cStyle": false,
"prettyprint_rejectInvalidCode": false,
"prettyprint_indentation": "\t",
"log_format": "auto"
"prettyprint_spaceBetweenParens": true,
"prettyprint_spaceBetweenBrackets": false,
"prettyprint_spaceBetweenBraces": true,
"prettyprint_spaceAfterLabel": true,
"prettyprint_spaceBeforeComma": false,
"prettyprint_spaceAfterComma": true,
"prettyprint_semicolons": false,
"prettyprint_cStyle": false,
"prettyprint_rejectInvalidCode": false,
"prettyprint_spaceEmptyParens": false,
"prettyprint_spaceEmptyBraces": false,
"prettyprint_removeRedundantParens":true,
"prettyprint_minimizeParens": true,
"prettyprint_assumeOperatorAssociativity": true,
"prettyprint_indentation": " ",
"log_format": "auto"
}

View File

@ -1,34 +1,37 @@
MKeyboard = {
-- max. allowed number of notes per net event
NET_MAX_NOTES = 32,
-- max. allowed number of notes per net event
NET_MAX_NOTES = 32,
-- players need to be within this distance to receive net events
NET_BROADCAST_DISTANCE = 1500,
-- players need to be within this distance to receive net events
NET_BROADCAST_DISTANCE = 1500,
-- name/location of the settings file
SETTINGS_FILE = 'musical_keyboard.json'
-- name/location of the settings file
SETTINGS_FILE = 'musical_keyboard.json',
-- URL for the midi installation guide
URL_MIDI_GUIDE = 'https://steamcommunity.com/workshop/filedetails/discussion/2656563609/3199240042192880687/'
}
if SERVER then
include('mkeyboard/sv_init.lua')
include( 'mkeyboard/sv_init.lua' )
AddCSLuaFile('mkeyboard/cl_init.lua')
AddCSLuaFile('mkeyboard/cl_interface.lua')
AddCSLuaFile('mkeyboard/cl_midi.lua')
AddCSLuaFile( 'mkeyboard/cl_init.lua' )
AddCSLuaFile( 'mkeyboard/cl_interface.lua' )
AddCSLuaFile( 'mkeyboard/cl_midi.lua' )
AddCSLuaFile('mkeyboard/data/instruments.lua')
AddCSLuaFile('mkeyboard/data/layouts.lua')
AddCSLuaFile('mkeyboard/data/sheets.lua')
AddCSLuaFile( 'mkeyboard/data/instruments.lua' )
AddCSLuaFile( 'mkeyboard/data/layouts.lua' )
AddCSLuaFile( 'mkeyboard/data/sheets.lua' )
end
if CLIENT then
include('mkeyboard/cl_init.lua')
include('mkeyboard/cl_interface.lua')
include('mkeyboard/cl_midi.lua')
include( 'mkeyboard/cl_init.lua' )
include( 'mkeyboard/cl_interface.lua' )
include( 'mkeyboard/cl_midi.lua' )
include('mkeyboard/data/instruments.lua')
include('mkeyboard/data/layouts.lua')
include('mkeyboard/data/sheets.lua')
include( 'mkeyboard/data/instruments.lua' )
include( 'mkeyboard/data/layouts.lua' )
include( 'mkeyboard/data/sheets.lua' )
MKeyboard:LoadSettings()
MKeyboard:LoadSettings()
end

View File

@ -1,81 +1,85 @@
include('shared.lua')
include( 'shared.lua' )
local function Fit(val, valMin, valMax, outMin, outMax)
return (val - valMin) * (outMax - outMin) / (valMax - valMin) + outMin
local function Fit( val, valMin, valMax, outMin, outMax )
return ( val - valMin ) * ( outMax - outMin ) / ( valMax - valMin ) + outMin
end
local blackKeys = {
[1] = true, [3] = true, [6] = true, [8] = true, [10] = true
[1] = true, [3] = true, [6] = true, [8] = true, [10] = true
}
local keyColors = {
Color(255, 148, 77),
Color(171, 0, 197)
Color( 255, 148, 77 ),
Color( 171, 0, 197 )
}
local offsetKeys = {
[2] = 0.2,
[4] = 0.4,
[5] = -0.1,
[7] = 0.1,
[9] = 0.4,
[11] = 0.6
[2] = 0.2,
[4] = 0.4,
[5] = -0.1,
[7] = 0.1,
[9] = 0.4,
[11] = 0.6
}
function ENT:Initialize()
self.drawNotes = {}
self.drawNotes = {}
end
function ENT:EmitNote(note, velocity, level, instrument, automated)
local instr = MKeyboard.Instruments[instrument]
function ENT:EmitNote( note, velocity, level, instrument, automated )
local instr = MKeyboard.instruments[instrument]
if not instr then return end
if note < instr.firstNote or note > instr.lastNote then return end
if not instr then return end
if note < instr.firstNote or note > instr.lastNote then return end
-- self:EmitSound(string.format(instr.path, note), level, 100, velocity / 127, CHAN_STATIC, 0)
sound.Play(string.format(instr.path, note), self:GetPos(), level, 100, velocity / 127)
sound.Play( string.format( instr.path, note ), self:GetPos(), level, 100, velocity / 127 )
local idx = note % 12
local len = 8
local height = -0.2
local width = 1.6
local x = -1.1
local idx = note % 12
local len = 8
local height = -0.2
local width = 1.6
local x = -1.1
if blackKeys[idx] then
len = 5
height = 0.1
width = 1
x = -0.6
end
if blackKeys[idx] then
len = 5
height = 0.1
width = 1
x = -0.6
end
if offsetKeys[idx] then
x = x + offsetKeys[idx]
end
if offsetKeys[idx] then
x = x + offsetKeys[idx]
end
self.drawNotes[note] = {
x = Fit(note, 21, 108, -37, 36.7),
t = RealTime() + 0.2,
min = Vector(x, -1.5, -1),
max = Vector(x + width, len, height),
colorIdx = automated and 2 or 1
}
self.drawNotes[note] = {
x = Fit( note, 21, 108, -37, 36.7 ),
t = RealTime() + 0.2,
min = Vector( x, -1.5, -1 ),
max = Vector( x + width, len, height ),
colorIdx = automated and 2 or 1
}
end
function ENT:Draw()
self:DrawModel()
self:DrawModel()
local t = RealTime()
local ang = self:GetAngles()
local t = RealTime()
local ang = self:GetAngles()
render.SetColorMaterial()
render.SetColorMaterial()
for note, p in pairs(self.drawNotes) do
if t > p.t then
self.drawNotes[note] = nil
else
local clr = keyColors[p.colorIdx]
render.DrawBox(self:LocalToWorld( Vector(-p.x,0,0) ), ang, p.min, p.max,
Color(clr.r, clr.g, clr.b, 255 * ((p.t - t) / 0.2)))
end
end
for note, p in pairs( self.drawNotes ) do
if t > p.t then
self.drawNotes[note] = nil
else
local clr = keyColors[p.colorIdx]
local alpha = 255 * ( ( p.t - t ) / 0.2 )
render.DrawBox(
self:LocalToWorld( Vector( -p.x, 0, 0 ) ),
ang, p.min, p.max,
Color( clr.r, clr.g, clr.b, alpha )
)
end
end
end

View File

@ -1,51 +1,51 @@
AddCSLuaFile('cl_init.lua')
AddCSLuaFile('shared.lua')
include('shared.lua')
AddCSLuaFile( 'cl_init.lua' )
AddCSLuaFile( 'shared.lua' )
include( 'shared.lua' )
function ENT:SpawnFunction(ply, tr)
if not tr.Hit then return end
function ENT:SpawnFunction( ply, tr )
if not tr.Hit then return end
local ent = ents.Create(self.ClassName)
ent:SetPos(tr.HitPos)
ent:SetAngles(Angle(0, ply:EyeAngles().y + 90, 0))
ent:Spawn()
ent:Activate()
local ent = ents.Create( self.ClassName )
ent:SetPos( tr.HitPos )
ent:SetAngles( Angle( 0, ply:EyeAngles().y + 90, 0 ) )
ent:Spawn()
ent:Activate()
return ent
return ent
end
function ENT:Initialize()
self:SetModel('models/styledstrike/musical_keyboard.mdl')
self:PhysicsInit(SOLID_VPHYSICS)
self:SetMoveType(MOVETYPE_VPHYSICS)
self:SetSolid(SOLID_VPHYSICS)
self:SetUseType(SIMPLE_USE)
self:DrawShadow(true)
self:SetModel( 'models/styledstrike/musical_keyboard.mdl' )
self:PhysicsInit( SOLID_VPHYSICS )
self:SetMoveType( MOVETYPE_VPHYSICS )
self:SetSolid( SOLID_VPHYSICS )
self:SetUseType( SIMPLE_USE )
self:DrawShadow( true )
local phys = self:GetPhysicsObject()
if IsValid(phys) then phys:Wake() end
local phys = self:GetPhysicsObject()
if IsValid( phys ) then phys:Wake() end
end
function ENT:Use(ply)
self:SetPlayer(ply)
function ENT:Use( ply )
self:SetPlayer( ply )
end
function ENT:SetPlayer(ply)
if not IsValid(self.Ply) then
net.Start('mkeyboard.set_entity', false)
net.WriteEntity(self)
net.Send(ply)
function ENT:SetPlayer( ply )
if not IsValid( self.Ply ) then
net.Start( 'mkeyboard.set_entity', false )
net.WriteEntity( self )
net.Send( ply )
self.Ply = ply
end
self.Ply = ply
end
end
function ENT:RemovePlayer()
if IsValid(self.Ply) then
net.Start('mkeyboard.set_entity', false)
net.WriteEntity(nil)
net.Send(self.Ply)
if IsValid( self.Ply ) then
net.Start( 'mkeyboard.set_entity', false )
net.WriteEntity( nil )
net.Send( self.Ply )
self.Ply = nil
end
self.Ply = nil
end
end

View File

@ -6,350 +6,366 @@ MKeyboard.queueTimer = nil
MKeyboard.queueStart = 0
MKeyboard.shiftMode = false
MKeyboard.noteState = {}
MKeyboard.noteStates = {}
MKeyboard.blockInput = 0
-- settings & persistence
MKeyboard.Settings = {
layout = 1,
instrument = 1,
sheet = 0,
velocity = 127,
octave = 0,
midiTranspose = 0,
MKeyboard.settings = {
layout = 1,
instrument = 1,
sheet = 0,
velocity = 127,
octave = 0,
midiTranspose = 0,
channelInstruments = {},
drawKeyLabels = true
channelInstruments = {},
drawKeyLabels = true
}
local shortcuts = {
[KEY_TAB] = function()
RunConsoleCommand('keyboard_leave', MKeyboard.entity:EntIndex())
end,
[KEY_TAB] = function()
RunConsoleCommand( 'keyboard_leave', MKeyboard.entity:EntIndex() )
end,
[KEY_SPACE] = function()
MKeyboard.HUD:ToggleExpandedBar()
end,
[KEY_SPACE] = function()
MKeyboard.uiHandler:ToggleExpandedBar()
end,
[KEY_LEFT] = function()
MKeyboard.HUD:ChangeInstrument(-1)
end,
[KEY_LEFT] = function()
MKeyboard.uiHandler:ChangeInstrument( -1 )
end,
[KEY_RIGHT] = function()
MKeyboard.HUD:ChangeInstrument(1)
end,
[KEY_RIGHT] = function()
MKeyboard.uiHandler:ChangeInstrument( 1 )
end,
[KEY_UP] = function()
MKeyboard.HUD:AddOctave(1)
end,
[KEY_UP] = function()
MKeyboard.uiHandler:AddOctave( 1 )
end,
[KEY_DOWN] = function()
MKeyboard.HUD:AddOctave(-1)
end
[KEY_DOWN] = function()
MKeyboard.uiHandler:AddOctave( -1 )
end
}
local dontBlockBinds = {
['+attack'] = true,
['+attack2'] = true,
['+duck'] = true
['+attack'] = true,
['+attack2'] = true,
['+duck'] = true
}
local function ValidateInteger(n, min, max)
return math.Round(math.Clamp(tonumber(n), min, max))
local function validateInteger( n, min, max )
return math.Round( math.Clamp( tonumber( n ), min, max ) )
end
function MKeyboard:LoadSettings()
local rawData = file.Read(self.SETTINGS_FILE, 'DATA')
if rawData == nil then return end
local rawData = file.Read( self.SETTINGS_FILE, 'DATA' )
if rawData == nil then return end
local data = util.JSONToTable(rawData) or {}
local nInstruments = #self.Instruments
local data = util.JSONToTable( rawData ) or {}
local instrumentCount = #self.instruments
-- last layout that was used on the keyboard
if data.layout then
self.Settings.layout = ValidateInteger(data.layout, 1, #self.Layouts)
end
-- last layout that was used on the keyboard
if data.layout then
self.settings.layout = validateInteger( data.layout, 1, #self.layouts )
end
-- last instrument that was used on the keyboard
if data.instrument then
self.Settings.instrument = ValidateInteger(data.instrument, 1, nInstruments)
end
-- last instrument that was used on the keyboard
if data.instrument then
self.settings.instrument = validateInteger( data.instrument, 1, instrumentCount )
end
-- last selected sheet
if data.sheet then
self.Settings.sheet = ValidateInteger(data.sheet, 0, #self.Sheets)
end
-- last selected sheet
if data.sheet then
self.settings.sheet = validateInteger( data.sheet, 0, #self.sheets )
end
-- last velocity set by the settings
if data.velocity then
self.Settings.velocity = ValidateInteger(data.velocity, 1, 127)
end
-- last velocity set by the settings
if data.velocity then
self.settings.velocity = validateInteger( data.velocity, 1, 127 )
end
-- last octave that was used on the keyboard
if data.octave then
self.Settings.octave = ValidateInteger(data.octave, -3, 3)
end
-- last octave that was used on the keyboard
if data.octave then
self.settings.octave = validateInteger( data.octave, -3, 3 )
end
-- last transpose that was used with midi
if data.midiTranspose then
self.Settings.midiTranspose = ValidateInteger(data.midiTranspose, -48, 48)
end
-- last transpose that was used with midi
if data.midiTranspose then
self.settings.midiTranspose = validateInteger( data.midiTranspose, -48, 48 )
end
-- links between instruments and MIDI channels
if data.channelInstruments and type(data.channelInstruments) == 'table' then
for c, i in pairs(data.channelInstruments) do
local channel = ValidateInteger(c, 0, 15)
local instrument = ValidateInteger(i, 1, nInstruments)
-- links between instruments and MIDI channels
if data.channelInstruments and type( data.channelInstruments ) == 'table' then
for c, i in pairs( data.channelInstruments ) do
local channel = validateInteger( c, 0, 15 )
local instrument = validateInteger( i, 1, instrumentCount )
self.Settings.channelInstruments[channel] = instrument
end
end
self.settings.channelInstruments[channel] = instrument
end
end
-- draw labels for keys
self.Settings.drawKeyLabels = Either(isbool(data.drawKeyLabels), tobool(data.drawKeyLabels), true)
-- draw labels for keys
self.settings.drawKeyLabels = Either( isbool( data.drawKeyLabels ), tobool( data.drawKeyLabels ), true )
end
function MKeyboard:SaveSettings()
local s = self.Settings
local s = self.settings
file.Write(self.SETTINGS_FILE, util.TableToJSON({
layout = s.layout,
instrument = s.instrument,
sheet = s.sheet,
velocity = s.velocity,
octave = s.octave,
midiTranspose = s.midiTranspose,
channelInstruments = s.channelInstruments,
drawKeyLabels = s.drawKeyLabels
}, true))
file.Write(
self.SETTINGS_FILE,
util.TableToJSON( {
layout = s.layout,
instrument = s.instrument,
sheet = s.sheet,
velocity = s.velocity,
octave = s.octave,
midiTranspose = s.midiTranspose,
channelInstruments = s.channelInstruments,
drawKeyLabels = s.drawKeyLabels
}, true )
)
end
function MKeyboard:Init(ent)
self.entity = ent
self.blockInput = RealTime() + 0.3
function MKeyboard:Init( ent )
self.entity = ent
self.blockInput = RealTime() + 0.3
self.HUD:Init()
self.uiHandler:Init()
hook.Add('Think', 'mkeyboard_Think', function()
self:Think()
end)
hook.Add( 'Think', 'mkeyboard_ProcessLocalKeyboard', function()
self:Think()
end )
hook.Add('PlayerButtonDown', 'mkeyboard_PlayerButtonDown', function(ply, button)
if ply == LocalPlayer() and IsFirstTimePredicted() then
self:OnButton(button, true)
end
end)
hook.Add( 'PlayerButtonDown', 'mkeyboard_LocalButtonPress', function( ply, button )
if ply == LocalPlayer() and IsFirstTimePredicted() then
self:OnButton( button, true )
end
end )
hook.Add('PlayerButtonUp', 'mkeyboard_PlayerButtonUp', function(ply, button)
if ply == LocalPlayer() and IsFirstTimePredicted() then
self:OnButton(button, false)
end
end)
hook.Add( 'PlayerButtonUp', 'mkeyboard_LocalButtonRelease', function( ply, button )
if ply == LocalPlayer() and IsFirstTimePredicted() then
self:OnButton( button, false )
end
end )
hook.Add('PlayerBindPress', 'mkeyboard_PlayerBindPress', function(_, bind)
if not dontBlockBinds[bind] then return true end
end)
hook.Add( 'PlayerBindPress', 'mkeyboard_BlockBinds', function( _, bind )
if not dontBlockBinds[bind] then return true end
end )
-- Custom Chat compatibility
hook.Add('BlockChatInput', 'mkeyboard_BlockChatInput', function()
return true
end)
-- Custom Chat compatibility
hook.Add( 'BlockChatInput', 'mkeyboard_PreventOpeningChat', function()
return true
end )
end
function MKeyboard:Shutdown()
self.entity = nil
self.entity = nil
self.transmitQueue = {}
self.queueTimer = nil
self.transmitQueue = {}
self.queueTimer = nil
self.shiftMode = false
self.noteState = {}
self.shiftMode = false
self.noteStates = {}
self.MIDI:Close()
self.HUD:Shutdown()
self.midiHandler:Close()
self.uiHandler:Shutdown()
hook.Remove('Think', 'mkeyboard_Think')
hook.Remove('PlayerButtonDown', 'mkeyboard_PlayerButtonDown')
hook.Remove('PlayerButtonUp', 'mkeyboard_PlayerButtonUp')
hook.Remove('PlayerBindPress', 'mkeyboard_PlayerBindPress')
hook.Remove('BlockChatInput', 'mkeyboard_BlockChatInput')
hook.Remove( 'Think', 'mkeyboard_ProcessLocalKeyboard' )
hook.Remove( 'PlayerButtonDown', 'mkeyboard_LocalButtonPress' )
hook.Remove( 'PlayerButtonUp', 'mkeyboard_LocalButtonRelease' )
hook.Remove( 'PlayerBindPress', 'mkeyboard_BlockBinds' )
hook.Remove( 'BlockChatInput', 'mkeyboard_PreventOpeningChat' )
end
function MKeyboard:NoteOn(note, velocity, isMidi, midiChannel)
local instrument = self.Settings.instrument
function MKeyboard:NoteOn( note, velocity, isMidi, midiChannel )
local instrument = self.settings.instrument
if midiChannel then
self.HUD.channelState[midiChannel] = 1
instrument = self.Settings.channelInstruments[midiChannel] or instrument
if midiChannel then
self.uiHandler.channelState[midiChannel] = 1
instrument = self.settings.channelInstruments[midiChannel] or instrument
if instrument == 0 then return end
end
if instrument == 0 then return end
end
local instr = self.Instruments[instrument]
if note < instr.firstNote or note > instr.lastNote then return end
local instr = self.instruments[instrument]
if note < instr.firstNote or note > instr.lastNote then return end
self.entity:EmitNote(note, velocity, 80, instrument, isMidi)
self.entity:EmitNote( note, velocity, 80, instrument, isMidi )
self.noteState[note] = isMidi and 4 or 3 -- see themeColors on cl_interface.lua
self.lastNoteWasAutomated = isMidi
self.noteStates[note] = isMidi and 'midi' or 'on'
self.lastNoteWasAutomated = isMidi
-- remember when we started putting notes
-- on the queue, and when we should send them
local t = SysTime()
-- remember when we started putting notes
-- on the queue, and when we should send them
local t = SysTime()
if not self.queueTimer then
self.queueTimer = t + 0.4
self.queueStart = t
end
if not self.queueTimer then
self.queueTimer = t + 0.4
self.queueStart = t
end
-- add notes to the queue unless the limit is was reached
local noteCount = #self.transmitQueue
if noteCount < self.NET_MAX_NOTES then
self.transmitQueue[noteCount + 1] = { note, velocity, instrument, t - self.queueStart }
end
-- add notes to the queue unless the limit is was reached
local noteCount = #self.transmitQueue
if noteCount < self.NET_MAX_NOTES then
self.transmitQueue[noteCount + 1] = { note, velocity, instrument, t - self.queueStart }
end
end
function MKeyboard:NoteOff(note)
self.noteState[note] = nil
function MKeyboard:NoteOff( note )
self.noteStates[note] = nil
end
function MKeyboard:NoteOffAll()
for k, _ in pairs(self.noteState) do
self.noteState[k] = nil
end
for k, _ in pairs( self.noteStates ) do
self.noteStates[k] = nil
end
end
function MKeyboard:ReproduceQueue()
local t = SysTime()
local t = SysTime()
-- play the networked notes, keeping the original timings
for time, data in pairs(self.reproduceQueue) do
if t > time then
if IsValid(data[1]) then
data[1]:EmitNote(data[2], data[3], 80, data[4], data[5])
end
-- play the networked notes, keeping the original timings
for time, data in pairs( self.reproduceQueue ) do
if t > time then
if IsValid( data[1] ) then
data[1]:EmitNote( data[2], data[3], 80, data[4], data[5] )
end
self.reproduceQueue[time] = nil
end
end
self.reproduceQueue[time] = nil
end
end
end
function MKeyboard:TransmitQueue()
net.Start('mkeyboard.notes', false)
net.WriteEntity(self.entity)
net.WriteBool(self.lastNoteWasAutomated)
net.WriteUInt(#self.transmitQueue, 5)
net.Start( 'mkeyboard.notes', false )
net.WriteEntity( self.entity )
net.WriteBool( self.lastNoteWasAutomated )
net.WriteUInt( #self.transmitQueue, 5 )
for _, params in ipairs(self.transmitQueue) do
net.WriteUInt(params[1], 7) -- note
net.WriteUInt(params[2], 7) -- velocity
net.WriteUInt(params[3], 6) -- instrument
net.WriteFloat(params[4]) -- time offset
end
for _, params in ipairs( self.transmitQueue ) do
net.WriteUInt( params[1], 7 ) -- note
net.WriteUInt( params[2], 7 ) -- velocity
net.WriteUInt( params[3], 6 ) -- instrument
net.WriteFloat( params[4] ) -- time offset
end
net.SendToServer()
net.SendToServer()
table.Empty(self.transmitQueue)
self.queueTimer = nil
table.Empty( self.transmitQueue )
self.queueTimer = nil
end
function MKeyboard:Think()
if not IsValid(self.entity) then
self:Shutdown()
return
end
if not IsValid( self.entity ) then
self:Shutdown()
self.MIDI:Think()
return
end
local t = SysTime()
self.midiHandler:Think()
-- if the queued notes are ready to be sent...
if self.queueTimer and t > self.queueTimer then
self:TransmitQueue()
end
local t = SysTime()
-- if the queued notes are ready to be sent...
if self.queueTimer and t > self.queueTimer then
self:TransmitQueue()
end
end
function MKeyboard:OnButton(button, isPressed)
if button == KEY_LSHIFT then
self.shiftMode = isPressed
end
function MKeyboard:OnButton( button, isPressed )
if button == KEY_LSHIFT then
self.shiftMode = isPressed
end
-- process shortcuts
if shortcuts[button] and not isPressed then
shortcuts[button]()
return
end
-- process shortcuts
if shortcuts[button] and not isPressed then
shortcuts[button]()
return
end
if self.blockInput > RealTime() then return end
if self.blockInput > RealTime() then return end
local layoutKeys = self.Layouts[self.Settings.layout].keys
local layoutKeys = self.layouts[self.settings.layout].keys
-- process layout keys
for idx, params in ipairs(layoutKeys) do
-- params: key [1], note [2], type [3], label [4], require SHIFT [5], alternative key [6]
-- process layout keys
for _, params in ipairs( layoutKeys ) do
--[[ params:
key [1],
note [2],
type [3],
label [4],
require SHIFT [5],
alternative key [6]
]]
if params[1] == button or (params[6] and params[6] == button) then
local note = params[2] + self.Settings.octave * 12
-- if either the "main" or "alternative" buttons are pressed for this key...
if params[1] == button or ( params[6] and params[6] == button ) then
local note = params[2] + self.settings.octave * 12
if isPressed then
if params[5] and self.shiftMode then
self:NoteOn(note, self.Settings.velocity, false)
break
end
if isPressed then
-- if this key requires shift and shift is pressed...
if params[5] and self.shiftMode then
self:NoteOn( note, self.settings.velocity, false )
break
end
if not params[5] and not self.shiftMode then
self:NoteOn(note, self.Settings.velocity, false)
break
end
else
self:NoteOff(note)
end
end
end
-- if this key does NOT require shift and shift is NOT pressed...
if not params[5] and not self.shiftMode then
self:NoteOn( note, self.settings.velocity, false )
break
end
else
self:NoteOff( note )
end
end
end
end
hook.Add('Think', 'mkeyboard_OffscreenThink', function()
MKeyboard:ReproduceQueue()
end)
hook.Add( 'Think', 'mkeyboard_ProcessReproductionQueue', function()
MKeyboard:ReproduceQueue()
end )
net.Receive('mkeyboard.set_entity', function()
local ent = net.ReadEntity()
net.Receive( 'mkeyboard.set_entity', function()
local ent = net.ReadEntity()
MKeyboard:Shutdown()
MKeyboard:Shutdown()
if IsValid(ent) then
MKeyboard:Init(ent)
end
end)
if IsValid( ent ) then
MKeyboard:Init( ent )
end
end )
net.Receive('mkeyboard.notes', function()
local ent = net.ReadEntity()
if not IsValid(ent) or not ent.EmitNote then return end
net.Receive( 'mkeyboard.notes', function()
local ent = net.ReadEntity()
if not IsValid( ent ) or not ent.EmitNote then return end
local automated = net.ReadBool()
local noteCount = net.ReadUInt(5)
local note, vel, instr, timeOffset
local automated = net.ReadBool()
local noteCount = net.ReadUInt( 5 )
local note, vel, instr, timeOffset
local t = SysTime()
local t = SysTime()
local i = 1
for i = 1, noteCount do
note = net.ReadUInt(7)
vel = net.ReadUInt(7)
instr = net.ReadUInt(6)
timeOffset = net.ReadFloat()
while i < noteCount do
note = net.ReadUInt( 7 )
vel = net.ReadUInt( 7 )
instr = net.ReadUInt( 6 )
timeOffset = net.ReadFloat()
MKeyboard.reproduceQueue[t + timeOffset] = { ent, note, vel, instr, automated }
end
end)
MKeyboard.reproduceQueue[t + timeOffset] = { ent, note, vel, instr, automated }
i = i + 1
end
end )
-- net event that only runs on single-player
-- key press/release net event that only runs on single-player
if game.SinglePlayer() then
net.Receive('mkeyboard.key', function()
local button = net.ReadUInt(8)
local pressed = net.ReadBool()
net.Receive( 'mkeyboard.key', function()
local button = net.ReadUInt( 8 )
local pressed = net.ReadBool()
if IsValid(MKeyboard.entity) then
MKeyboard:OnButton(button, pressed)
end
end)
if IsValid( MKeyboard.entity ) then
MKeyboard:OnButton( button, pressed )
end
end )
end

File diff suppressed because it is too large Load Diff

View File

@ -1,89 +1,91 @@
-- based on Starfall's SF.Require, for clientside use
local function SafeRequireModule(moduleName)
local osSuffix
local function SafeRequireModule( moduleName )
local osSuffix
if system.IsWindows() then
osSuffix = (jit.arch ~= 'x64' and 'win32' or 'win64')
elseif system.IsLinux() then
osSuffix = (jit.arch ~= 'x64' and 'linux' or 'linux64')
elseif system.IsOSX() then
osSuffix = (jit.arch ~= 'x64' and 'osx' or 'osx64')
else
return
end
if system.IsWindows() then
osSuffix = jit.arch ~= 'x64' and 'win32' or 'win64'
elseif system.IsLinux() then
osSuffix = jit.arch ~= 'x64' and 'linux' or 'linux64'
elseif system.IsOSX() then
osSuffix = jit.arch ~= 'x64' and 'osx' or 'osx64'
else
return
end
if file.Exists('lua/bin/gmcl_' .. moduleName .. '_' .. osSuffix .. '.dll', 'GAME') then
local ok, err = pcall(require, moduleName)
if ok then
return true
else
ErrorNoHalt(err)
return false
end
end
if file.Exists( 'lua/bin/gmcl_' .. moduleName .. '_' .. osSuffix .. '.dll', 'GAME' ) then
local ok, err = pcall( require, moduleName )
if ok then
return true
else
ErrorNoHalt( err )
return false
return false
end
end
return false
end
-- safely require the midi module
SafeRequireModule('midi')
SafeRequireModule( 'midi' )
local MIDI = {
selectedPort = nil,
portTimer = 0
local midiHandler = {
selectedPort = nil,
portTimer = 0
}
MKeyboard.MIDI = MIDI
MKeyboard.midiHandler = midiHandler
function MIDI:Open(port)
self:Close()
function midiHandler:Open( port )
self:Close()
local portName = midi.GetPorts()[port]
if not portName then
print('Could not find MIDI port: ' .. port)
return
end
local portName = midi.GetPorts()[port]
if not portName then
print( 'Could not find MIDI port: ' .. port )
print('Opening MIDI port: ' .. portName)
return
end
local success, err = pcall(midi.Open, port)
if success then
MKeyboard.HUD:SetMidiPortName(portName)
else
print('Failed to open MIDI port: ' .. err)
end
print( 'Opening MIDI port: ' .. portName )
local success, err = pcall( midi.Open, port )
if success then
MKeyboard.uiHandler:SetMidiPortName( portName )
else
print( 'Failed to open MIDI port: ' .. err )
end
end
function MIDI:Close()
MKeyboard.HUD:SetMidiPortName(nil)
function midiHandler:Close()
MKeyboard.uiHandler:SetMidiPortName( nil )
if midi and midi.IsOpened() then
print('Closing MIDI port.')
midi.Close()
end
if midi and midi.IsOpened() then
print( 'Closing MIDI port.' )
midi.Close()
end
end
function MIDI:Think()
-- Try to open the selected midi port
if self.selectedPort and not midi.IsOpened() and RealTime() > self.portTimer then
self.portTimer = RealTime() + 5
self:Open(self.selectedPort)
end
function midiHandler:Think()
-- Try to open the selected midi port
if self.selectedPort and not midi.IsOpened() and RealTime() > self.portTimer then
self.portTimer = RealTime() + 5
self:Open( self.selectedPort )
end
end
-- listen to events from the MIDI module
hook.Add('MIDI', 'mkeyboard_MIDI', function(_, code, p1, p2)
if not IsValid(MKeyboard.entity) then return end
if not code then return end
hook.Add( 'MIDI', 'mkeyboard_CaptureMIDIEvents', function( _, code, p1, p2 )
if not IsValid( MKeyboard.entity ) then return end
if not code then return end
local midiCmd = midi.GetCommandName(code)
local transpose = MKeyboard.Settings.midiTranspose
local midiCmd = midi.GetCommandName( code )
local transpose = MKeyboard.settings.midiTranspose
if midiCmd == 'NOTE_ON' and p2 > 0 then
local midiChannel = midi.GetCommandChannel(code)
MKeyboard:NoteOn(p1 + transpose, p2, true, midiChannel)
if midiCmd == 'NOTE_ON' and p2 > 0 then
local midiChannel = midi.GetCommandChannel( code )
MKeyboard:NoteOn( p1 + transpose, p2, true, midiChannel )
elseif midiCmd == 'NOTE_OFF' then
MKeyboard:NoteOff(p1 + transpose)
end
end)
elseif midiCmd == 'NOTE_OFF' then
MKeyboard:NoteOff( p1 + transpose )
end
end )

View File

@ -1,47 +1,47 @@
MKeyboard.Instruments = {}
MKeyboard.instruments = {}
function MKeyboard:RegisterInstrument(name, path, firstNote, lastNote)
self.Instruments[#self.Instruments + 1] = {
name = name,
path = path,
firstNote = firstNote,
lastNote = lastNote
}
function MKeyboard:RegisterInstrument( name, path, firstNote, lastNote )
self.instruments[#self.instruments + 1] = {
name = name,
path = path,
firstNote = firstNote,
lastNote = lastNote
}
end
-- ##### Default instruments ##### --
-- default instruments
MKeyboard:RegisterInstrument('Grand Piano', 'styledstrike/instruments/grand_piano/%i.mp3', 24, 96)
MKeyboard:RegisterInstrument('Grand Piano (Soft)', 'styledstrike/instruments/grand_piano_2/%i.mp3', 24, 96)
MKeyboard:RegisterInstrument('Electric Piano', 'styledstrike/instruments/electric_piano/%i.mp3', 24, 96)
MKeyboard:RegisterInstrument('Analog Piano', 'styledstrike/instruments/analog_piano/%i.mp3', 24, 108)
MKeyboard:RegisterInstrument('Binary Piano', 'styledstrike/instruments/binary_piano/%i.mp3', 24, 96)
MKeyboard:RegisterInstrument( 'Grand Piano', 'styledstrike/instruments/grand_piano/%i.mp3', 24, 96 )
MKeyboard:RegisterInstrument( 'Grand Piano (Soft)', 'styledstrike/instruments/grand_piano_2/%i.mp3', 24, 96 )
MKeyboard:RegisterInstrument( 'Electric Piano', 'styledstrike/instruments/electric_piano/%i.mp3', 24, 96 )
MKeyboard:RegisterInstrument( 'Analog Piano', 'styledstrike/instruments/analog_piano/%i.mp3', 24, 108 )
MKeyboard:RegisterInstrument( 'Binary Piano', 'styledstrike/instruments/binary_piano/%i.mp3', 24, 96 )
MKeyboard:RegisterInstrument('Music Box', 'styledstrike/instruments/music_box/%i.mp3', 36, 96)
MKeyboard:RegisterInstrument('Harp', 'styledstrike/instruments/harp/%i.mp3', 24, 96)
MKeyboard:RegisterInstrument('Harpsichord', 'styledstrike/instruments/harpsichord/%i.mp3', 24, 96)
MKeyboard:RegisterInstrument('Nylon Guitar', 'styledstrike/instruments/nylon_guitar/%i.mp3', 24, 96)
MKeyboard:RegisterInstrument('Jazz Guitar', 'styledstrike/instruments/jazz_guitar/%i.mp3', 24, 84)
MKeyboard:RegisterInstrument('Picked Bass', 'styledstrike/instruments/picked_bass/%i.mp3', 24, 72)
MKeyboard:RegisterInstrument('Slap Bass', 'styledstrike/instruments/slap_bass/%i.mp3', 24, 72)
MKeyboard:RegisterInstrument('Acoustic Bass', 'styledstrike/instruments/acoustic_bass/%i.mp3', 24, 72)
MKeyboard:RegisterInstrument( 'Music Box', 'styledstrike/instruments/music_box/%i.mp3', 36, 96 )
MKeyboard:RegisterInstrument( 'Harp', 'styledstrike/instruments/harp/%i.mp3', 24, 96 )
MKeyboard:RegisterInstrument( 'Harpsichord', 'styledstrike/instruments/harpsichord/%i.mp3', 24, 96 )
MKeyboard:RegisterInstrument( 'Nylon Guitar', 'styledstrike/instruments/nylon_guitar/%i.mp3', 24, 96 )
MKeyboard:RegisterInstrument( 'Jazz Guitar', 'styledstrike/instruments/jazz_guitar/%i.mp3', 24, 84 )
MKeyboard:RegisterInstrument( 'Picked Bass', 'styledstrike/instruments/picked_bass/%i.mp3', 24, 72 )
MKeyboard:RegisterInstrument( 'Slap Bass', 'styledstrike/instruments/slap_bass/%i.mp3', 24, 72 )
MKeyboard:RegisterInstrument( 'Acoustic Bass', 'styledstrike/instruments/acoustic_bass/%i.mp3', 24, 72 )
MKeyboard:RegisterInstrument('Marimba', 'styledstrike/instruments/marimba/%i.mp3', 36, 96)
MKeyboard:RegisterInstrument('Vibraphone', 'styledstrike/instruments/vibraphone/%i.mp3', 36, 96)
MKeyboard:RegisterInstrument('Piccolo', 'styledstrike/instruments/piccolo/%i.mp3', 48, 96)
MKeyboard:RegisterInstrument('Pizzicato Strings', 'styledstrike/instruments/pizzicato_strings/%i.mp3', 24, 96)
MKeyboard:RegisterInstrument('Choir Oohs', 'styledstrike/instruments/choir_oohs/%i.mp3', 36, 96)
MKeyboard:RegisterInstrument( 'Marimba', 'styledstrike/instruments/marimba/%i.mp3', 36, 96 )
MKeyboard:RegisterInstrument( 'Vibraphone', 'styledstrike/instruments/vibraphone/%i.mp3', 36, 96 )
MKeyboard:RegisterInstrument( 'Piccolo', 'styledstrike/instruments/piccolo/%i.mp3', 48, 96 )
MKeyboard:RegisterInstrument( 'Pizzicato Strings', 'styledstrike/instruments/pizzicato_strings/%i.mp3', 24, 96 )
MKeyboard:RegisterInstrument( 'Choir Oohs', 'styledstrike/instruments/choir_oohs/%i.mp3', 36, 96 )
MKeyboard:RegisterInstrument('New Age', 'styledstrike/instruments/new_age/%i.mp3', 24, 96)
MKeyboard:RegisterInstrument('Polysynth', 'styledstrike/instruments/polysynth/%i.mp3', 24, 96)
MKeyboard:RegisterInstrument('Soft Synth', 'styledstrike/instruments/soft_synth/%i.mp3', 24, 96)
MKeyboard:RegisterInstrument('Space Voice', 'styledstrike/instruments/space_voice/%i.mp3', 36, 96)
MKeyboard:RegisterInstrument('Saw Lead', 'styledstrike/instruments/saw_lead/%i.mp3', 24, 96)
MKeyboard:RegisterInstrument('Square Lead', 'styledstrike/instruments/square_lead/%i.mp3', 24, 96)
MKeyboard:RegisterInstrument( 'New Age', 'styledstrike/instruments/new_age/%i.mp3', 24, 96 )
MKeyboard:RegisterInstrument( 'Polysynth', 'styledstrike/instruments/polysynth/%i.mp3', 24, 96 )
MKeyboard:RegisterInstrument( 'Soft Synth', 'styledstrike/instruments/soft_synth/%i.mp3', 24, 96 )
MKeyboard:RegisterInstrument( 'Space Voice', 'styledstrike/instruments/space_voice/%i.mp3', 36, 96 )
MKeyboard:RegisterInstrument( 'Saw Lead', 'styledstrike/instruments/saw_lead/%i.mp3', 24, 96 )
MKeyboard:RegisterInstrument( 'Square Lead', 'styledstrike/instruments/square_lead/%i.mp3', 24, 96 )
MKeyboard:RegisterInstrument('Drums (Standard)', 'styledstrike/instruments/drums_standard/%i.mp3', 26, 87)
MKeyboard:RegisterInstrument('Drums (Analog)', 'styledstrike/instruments/drums_analog/%i.mp3', 26, 75)
MKeyboard:RegisterInstrument('Drums (Jazz)', 'styledstrike/instruments/drums_jazz/%i.mp3', 26, 75)
MKeyboard:RegisterInstrument('Drums (Brush)', 'styledstrike/instruments/drums_brush/%i.mp3', 26, 75)
MKeyboard:RegisterInstrument( 'Drums (Standard)', 'styledstrike/instruments/drums_standard/%i.mp3', 26, 87 )
MKeyboard:RegisterInstrument( 'Drums (Analog)', 'styledstrike/instruments/drums_analog/%i.mp3', 26, 75 )
MKeyboard:RegisterInstrument( 'Drums (Jazz)', 'styledstrike/instruments/drums_jazz/%i.mp3', 26, 75 )
MKeyboard:RegisterInstrument( 'Drums (Brush)', 'styledstrike/instruments/drums_brush/%i.mp3', 26, 75 )
MKeyboard:RegisterInstrument('Honky Tonk', 'styledstrike/instruments/honky_tonk/%i.mp3', 24, 96)
MKeyboard:RegisterInstrument( 'Honky Tonk', 'styledstrike/instruments/honky_tonk/%i.mp3', 24, 96 )

View File

@ -1,144 +1,144 @@
MKeyboard.Layouts = {}
MKeyboard.layouts = {}
function MKeyboard:RegisterLayout(name, keys, octaveLimits)
self.Layouts[#self.Layouts + 1] = {
name = name,
keys = keys,
octaveLimits = octaveLimits
}
function MKeyboard:RegisterLayout( name, keys, octaveLimits )
self.layouts[#self.layouts + 1] = {
name = name,
keys = keys,
octaveLimits = octaveLimits
}
end
-- ##### Default keyboard layouts ##### --
-- default keyboard layouts
MKeyboard:RegisterLayout('Compact', {
-- key, note, type, label
{KEY_A, 60, 'w', 'a'},
{KEY_W, 61, 'b', 'w'},
{KEY_S, 62, 'w', 's'},
{KEY_E, 63, 'b', 'e'},
{KEY_D, 64, 'w', 'd'},
{KEY_F, 65, 'w', 'f'},
{KEY_T, 66, 'b', 't'},
{KEY_G, 67, 'w', 'g'},
{KEY_Y, 68, 'b', 'y'},
{KEY_H, 69, 'w', 'h'},
{KEY_U, 70, 'b', 'u'},
{KEY_J, 71, 'w', 'j'},
{KEY_K, 72, 'w', 'k'},
{KEY_O, 73, 'b', 'o'},
{KEY_L, 74, 'w', 'l'},
{KEY_P, 75, 'b', 'p'},
{KEY_SEMICOLON, 76, 'w', ';'},
{KEY_APOSTROPHE, 77, 'w', '\''}
MKeyboard:RegisterLayout( 'Compact', {
-- key, note, type, label
{ KEY_A, 60, 'w', 'a' },
{ KEY_W, 61, 'b', 'w' },
{ KEY_S, 62, 'w', 's' },
{ KEY_E, 63, 'b', 'e' },
{ KEY_D, 64, 'w', 'd' },
{ KEY_F, 65, 'w', 'f' },
{ KEY_T, 66, 'b', 't' },
{ KEY_G, 67, 'w', 'g' },
{ KEY_Y, 68, 'b', 'y' },
{ KEY_H, 69, 'w', 'h' },
{ KEY_U, 70, 'b', 'u' },
{ KEY_J, 71, 'w', 'j' },
{ KEY_K, 72, 'w', 'k' },
{ KEY_O, 73, 'b', 'o' },
{ KEY_L, 74, 'w', 'l' },
{ KEY_P, 75, 'b', 'p' },
{ KEY_SEMICOLON, 76, 'w', ';' },
{ KEY_APOSTROPHE, 77, 'w', '\'' }
}, {
min = -3, max = 2
})
min = -3, max = 2
} )
MKeyboard:RegisterLayout('Expanded', {
-- key, note, type, label, require SHIFT
{KEY_1, 36, 'w', '1'},
{KEY_1, 37, 'b', '!', true},
{KEY_2, 38, 'w', '2'},
{KEY_2, 39, 'b', '@', true},
{KEY_3, 40, 'w', '3'},
{KEY_4, 41, 'w', '4'},
{KEY_4, 42, 'b', '$', true},
{KEY_5, 43, 'w', '5'},
{KEY_5, 44, 'b', '%', true},
{KEY_6, 45, 'w', '6'},
{KEY_6, 46, 'b', '^', true},
{KEY_7, 47, 'w', '7'},
{KEY_8, 48, 'w', '8'},
{KEY_8, 49, 'b', '*', true},
{KEY_9, 50, 'w', '9'},
{KEY_9, 51, 'b', '(', true},
{KEY_0, 52, 'w', '0'},
MKeyboard:RegisterLayout( 'Expanded', {
-- key, note, type, label, require SHIFT
{ KEY_1, 36, 'w', '1' },
{ KEY_1, 37, 'b', '!', true },
{ KEY_2, 38, 'w', '2' },
{ KEY_2, 39, 'b', '@', true },
{ KEY_3, 40, 'w', '3' },
{ KEY_4, 41, 'w', '4' },
{ KEY_4, 42, 'b', '$', true },
{ KEY_5, 43, 'w', '5' },
{ KEY_5, 44, 'b', '%', true },
{ KEY_6, 45, 'w', '6' },
{ KEY_6, 46, 'b', '^', true },
{ KEY_7, 47, 'w', '7' },
{ KEY_8, 48, 'w', '8' },
{ KEY_8, 49, 'b', '*', true },
{ KEY_9, 50, 'w', '9' },
{ KEY_9, 51, 'b', '(', true },
{ KEY_0, 52, 'w', '0' },
{KEY_Q, 53, 'w', 'q'},
{KEY_Q, 54, 'b', 'Q', true},
{KEY_W, 55, 'w', 'w'},
{KEY_W, 56, 'b', 'W', true},
{KEY_E, 57, 'w', 'e'},
{KEY_E, 58, 'b', 'E', true},
{KEY_R, 59, 'w', 'r'},
{KEY_T, 60, 'w', 't'},
{KEY_T, 61, 'b', 'T', true},
{KEY_Y, 62, 'w', 'y'},
{KEY_Y, 63, 'b', 'Y', true},
{KEY_U, 64, 'w', 'u'},
{KEY_I, 65, 'w', 'i'},
{KEY_I, 66, 'b', 'I', true},
{KEY_O, 67, 'w', 'o'},
{KEY_O, 68, 'b', 'O', true},
{KEY_P, 69, 'w', 'p'},
{KEY_P, 70, 'b', 'P', true},
{ KEY_Q, 53, 'w', 'q' },
{ KEY_Q, 54, 'b', 'Q', true },
{ KEY_W, 55, 'w', 'w' },
{ KEY_W, 56, 'b', 'W', true },
{ KEY_E, 57, 'w', 'e' },
{ KEY_E, 58, 'b', 'E', true },
{ KEY_R, 59, 'w', 'r' },
{ KEY_T, 60, 'w', 't' },
{ KEY_T, 61, 'b', 'T', true },
{ KEY_Y, 62, 'w', 'y' },
{ KEY_Y, 63, 'b', 'Y', true },
{ KEY_U, 64, 'w', 'u' },
{ KEY_I, 65, 'w', 'i' },
{ KEY_I, 66, 'b', 'I', true },
{ KEY_O, 67, 'w', 'o' },
{ KEY_O, 68, 'b', 'O', true },
{ KEY_P, 69, 'w', 'p' },
{ KEY_P, 70, 'b', 'P', true },
{KEY_A, 71, 'w', 'a'},
{KEY_S, 72, 'w', 's'},
{KEY_S, 73, 'b', 'S', true},
{KEY_D, 74, 'w', 'd'},
{KEY_D, 75, 'b', 'D', true},
{KEY_F, 76, 'w', 'f'},
{KEY_G, 77, 'w', 'g'},
{KEY_G, 78, 'b', 'G', true},
{KEY_H, 79, 'w', 'h'},
{KEY_H, 80, 'b', 'H', true},
{KEY_J, 81, 'w', 'j'},
{KEY_J, 82, 'b', 'J', true},
{KEY_K, 83, 'w', 'k'},
{KEY_L, 84, 'w', 'l'},
{KEY_L, 85, 'b', 'L', true},
{ KEY_A, 71, 'w', 'a' },
{ KEY_S, 72, 'w', 's' },
{ KEY_S, 73, 'b', 'S', true },
{ KEY_D, 74, 'w', 'd' },
{ KEY_D, 75, 'b', 'D', true },
{ KEY_F, 76, 'w', 'f' },
{ KEY_G, 77, 'w', 'g' },
{ KEY_G, 78, 'b', 'G', true },
{ KEY_H, 79, 'w', 'h' },
{ KEY_H, 80, 'b', 'H', true },
{ KEY_J, 81, 'w', 'j' },
{ KEY_J, 82, 'b', 'J', true },
{ KEY_K, 83, 'w', 'k' },
{ KEY_L, 84, 'w', 'l' },
{ KEY_L, 85, 'b', 'L', true },
{KEY_Z, 86, 'w', 'z'},
{KEY_Z, 87, 'b', 'Z', true},
{KEY_X, 88, 'w', 'x'},
{KEY_C, 89, 'w', 'c'},
{KEY_C, 90, 'b', 'C', true},
{KEY_V, 91, 'w', 'v'},
{KEY_V, 92, 'b', 'V', true},
{KEY_B, 93, 'w', 'b'},
{KEY_B, 94, 'b', 'B', true},
{KEY_N, 95, 'w', 'n'},
{KEY_M, 96, 'w', 'm'}
{ KEY_Z, 86, 'w', 'z' },
{ KEY_Z, 87, 'b', 'Z', true },
{ KEY_X, 88, 'w', 'x' },
{ KEY_C, 89, 'w', 'c' },
{ KEY_C, 90, 'b', 'C', true },
{ KEY_V, 91, 'w', 'v' },
{ KEY_V, 92, 'b', 'V', true },
{ KEY_B, 93, 'w', 'b' },
{ KEY_B, 94, 'b', 'B', true },
{ KEY_N, 95, 'w', 'n' },
{ KEY_M, 96, 'w', 'm' }
}, {
min = -2, max = 1
})
min = -2, max = 1
} )
MKeyboard:RegisterLayout('FL Style', {
-- key, note, type, label, require SHIFT, alternative key
{KEY_Z, 24, 'w', 'z'},
{KEY_S, 25, 'b', 's'},
{KEY_X, 26, 'w', 'x'},
{KEY_D, 27, 'b', 'd'},
{KEY_C, 28, 'w', 'c'},
{KEY_V, 29, 'w', 'v'},
{KEY_G, 30, 'b', 'g'},
{KEY_B, 31, 'w', 'b'},
{KEY_H, 32, 'b', 'h'},
{KEY_N, 33, 'w', 'n'},
{KEY_J, 34, 'b', 'j'},
{KEY_M, 35, 'w', 'm'},
MKeyboard:RegisterLayout( 'FL Style', {
-- key, note, type, label, require SHIFT, alternative key
{ KEY_Z, 24, 'w', 'z' },
{ KEY_S, 25, 'b', 's' },
{ KEY_X, 26, 'w', 'x' },
{ KEY_D, 27, 'b', 'd' },
{ KEY_C, 28, 'w', 'c' },
{ KEY_V, 29, 'w', 'v' },
{ KEY_G, 30, 'b', 'g' },
{ KEY_B, 31, 'w', 'b' },
{ KEY_H, 32, 'b', 'h' },
{ KEY_N, 33, 'w', 'n' },
{ KEY_J, 34, 'b', 'j' },
{ KEY_M, 35, 'w', 'm' },
{KEY_Q, 36, 'w', 'q', false, KEY_COMMA, ','},
{KEY_2, 37, 'b', '2', false, KEY_L, 'l'},
{KEY_W, 38, 'w', 'w', false, KEY_PERIOD, '.'},
{KEY_3, 39, 'b', '3', false, KEY_SEMICOLON, ';'},
{KEY_E, 40, 'w', 'e', false, KEY_SLASH, '/'},
{KEY_R, 41, 'w', 'r'},
{KEY_5, 42, 'b', '5'},
{KEY_T, 43, 'w', 't'},
{KEY_6, 44, 'b', '6'},
{KEY_Y, 45, 'w', 'y'},
{KEY_7, 46, 'b', '7'},
{KEY_U, 47, 'w', 'u'},
{KEY_I, 48, 'w', 'i'},
{KEY_9, 49, 'b', '9'},
{KEY_O, 50, 'w', 'o'},
{KEY_0, 51, 'b', '0'},
{KEY_P, 52, 'w', 'p'},
{KEY_LBRACKET, 53, 'w', '['},
{KEY_EQUAL, 54, 'b', '='},
{KEY_RBRACKET, 55, 'w', ']'}
{ KEY_Q, 36, 'w', 'q', false, KEY_COMMA, ',' },
{ KEY_2, 37, 'b', '2', false, KEY_L, 'l' },
{ KEY_W, 38, 'w', 'w', false, KEY_PERIOD, '.' },
{ KEY_3, 39, 'b', '3', false, KEY_SEMICOLON, ';' },
{ KEY_E, 40, 'w', 'e', false, KEY_SLASH, '/' },
{ KEY_R, 41, 'w', 'r' },
{ KEY_5, 42, 'b', '5' },
{ KEY_T, 43, 'w', 't' },
{ KEY_6, 44, 'b', '6' },
{ KEY_Y, 45, 'w', 'y' },
{ KEY_7, 46, 'b', '7' },
{ KEY_U, 47, 'w', 'u' },
{ KEY_I, 48, 'w', 'i' },
{ KEY_9, 49, 'b', '9' },
{ KEY_O, 50, 'w', 'o' },
{ KEY_0, 51, 'b', '0' },
{ KEY_P, 52, 'w', 'p' },
{ KEY_LBRACKET, 53, 'w', '[' },
{ KEY_EQUAL, 54, 'b', '=' },
{ KEY_RBRACKET, 55, 'w', ']' }
}, {
min = 0, max = 3
})
min = 0, max = 3
} )

File diff suppressed because it is too large Load Diff

View File

@ -1,105 +1,105 @@
resource.AddWorkshop('2656563609')
resource.AddWorkshop( '2656563609' )
util.AddNetworkString('mkeyboard.set_entity')
util.AddNetworkString('mkeyboard.notes')
util.AddNetworkString( 'mkeyboard.set_entity' )
util.AddNetworkString( 'mkeyboard.notes' )
local function FindBroadcastTargets(pos, radius, filter)
-- make the radius squared since we're using DistToSqr (faster)
radius = radius * radius
local function FindBroadcastTargets( pos, radius, filter )
-- make the radius squared since we're using DistToSqr (faster)
radius = radius * radius
local found = {}
local found = {}
for _, v in ipairs(player.GetHumans()) do
if v ~= filter and pos:DistToSqr(v:GetPos()) < radius then
table.insert(found, v)
end
end
for _, v in ipairs( player.GetHumans() ) do
if v ~= filter and pos:DistToSqr( v:GetPos() ) < radius then
table.insert( found, v )
end
end
return found
return found
end
function MKeyboard:IsAMusicalKeyboard(ent)
return IsValid(ent) and ent:GetClass() == 'ent_musical_keyboard'
function MKeyboard:IsAMusicalKeyboard( ent )
return IsValid( ent ) and ent:GetClass() == 'ent_musical_keyboard'
end
function MKeyboard:BroadcastNotes(notes, ent, automated, ignoreTarget)
if #notes == 0 then return end
function MKeyboard:BroadcastNotes( notes, ent, automated, ignoreTarget )
if #notes == 0 then return end
-- only broadcast to nearby players, if any
local targets = FindBroadcastTargets(ent:GetPos(), self.NET_BROADCAST_DISTANCE, ignoreTarget)
if #targets == 0 then return end
-- only broadcast to nearby players, if any
local targets = FindBroadcastTargets( ent:GetPos(), self.NET_BROADCAST_DISTANCE, ignoreTarget )
if #targets == 0 then return end
net.Start('mkeyboard.notes', false)
net.WriteEntity(ent)
net.WriteBool(automated)
net.WriteUInt(#notes, 5)
net.Start( 'mkeyboard.notes', false )
net.WriteEntity( ent )
net.WriteBool( automated )
net.WriteUInt( #notes, 5 )
for _, params in ipairs(notes) do
net.WriteUInt(params[1], 7) -- note
net.WriteUInt(params[2], 7) -- velocity
net.WriteUInt(params[3], 6) -- insrument
net.WriteFloat(params[4]) -- time offset
end
for _, params in ipairs( notes ) do
net.WriteUInt( params[1], 7 ) -- note
net.WriteUInt( params[2], 7 ) -- velocity
net.WriteUInt( params[3], 6 ) -- insrument
net.WriteFloat( params[4] ) -- time offset
end
net.Send(targets)
net.Send( targets )
end
concommand.Add('keyboard_leave', function(ply, _, args)
if #args < 1 then return end
local ent = ents.GetByIndex(args[1])
concommand.Add( 'keyboard_leave', function( ply, _, args )
if #args < 1 then return end
local ent = ents.GetByIndex( args[1] )
if MKeyboard:IsAMusicalKeyboard(ent) and ent.Ply == ply then
ent:RemovePlayer()
end
end)
if MKeyboard:IsAMusicalKeyboard( ent ) and ent.Ply == ply then
ent:RemovePlayer()
end
end )
net.Receive('mkeyboard.notes', function(_, ply)
local ent = net.ReadEntity()
net.Receive( 'mkeyboard.notes', function( _, ply )
local ent = net.ReadEntity()
-- make sure the client didnt send the wrong entity
if not MKeyboard:IsAMusicalKeyboard(ent) then return end
-- make sure the client didnt send the wrong entity
if not MKeyboard:IsAMusicalKeyboard( ent ) then return end
local automated = net.ReadBool()
local noteCount = net.ReadUInt(5)
local notes = {}
local automated = net.ReadBool()
local noteCount = net.ReadUInt( 5 )
local notes = {}
-- make sure the client isnt tricking us
noteCount = math.Clamp(noteCount, 1, MKeyboard.NET_MAX_NOTES)
-- make sure the client isnt tricking us
noteCount = math.Clamp( noteCount, 1, MKeyboard.NET_MAX_NOTES )
-- read all notes, to make sure we have as
-- many as the client told us on noteCount
for i = 1, noteCount do
notes[i] = {
net.ReadUInt(7), -- note
net.ReadUInt(7), -- velocity
net.ReadUInt(6), -- instrument
math.Clamp(net.ReadFloat(), 0, 1) -- time offset
}
end
-- read all notes, to make sure we have as
-- many as the client told us on noteCount
for i = 1, noteCount do
notes[i] = {
net.ReadUInt( 7 ), -- note
net.ReadUInt( 7 ), -- velocity
net.ReadUInt( 6 ), -- instrument
math.Clamp( net.ReadFloat(), 0, 1 ) -- time offset
}
end
-- make sure the client is actually using this keyboard
if IsValid(ent.Ply) and ply == ent.Ply then
-- then broadcast the notes
MKeyboard:BroadcastNotes(notes, ent, automated, ply)
end
end)
-- make sure the client is actually using this keyboard
if IsValid( ent.Ply ) and ply == ent.Ply then
-- then broadcast the notes
MKeyboard:BroadcastNotes( notes, ent, automated, ply )
end
end )
-- hooks that only run serverside on single-player
-- TODO: is there a better way to detect these hooks client-side?
if game.SinglePlayer() then
util.AddNetworkString('mkeyboard.key')
util.AddNetworkString( 'mkeyboard.key' )
hook.Add('PlayerButtonDown', 'mkeyboard_SPButtonDown', function(ply, button)
net.Start('mkeyboard.key', true)
net.WriteUInt(button, 8)
net.WriteBool(true)
net.Send(ply)
end)
hook.Add( 'PlayerButtonDown', 'mkeyboard_ButtonDownWorkaround', function( ply, button )
net.Start( 'mkeyboard.key', true )
net.WriteUInt( button, 8 )
net.WriteBool( true )
net.Send( ply )
end )
hook.Add('PlayerButtonUp', 'mkeyboard_SPButtonUp', function(ply, button)
net.Start('mkeyboard.key', true)
net.WriteUInt(button, 8)
net.WriteBool(false)
net.Send(ply)
end)
hook.Add( 'PlayerButtonUp', 'mkeyboard_ButtonUpWorkaround', function( ply, button )
net.Start( 'mkeyboard.key', true )
net.WriteUInt( button, 8 )
net.WriteBool( false )
net.Send( ply )
end )
end