This commit is contained in:
lifestorm
2024-08-04 23:54:45 +03:00
parent 0e770b2b49
commit df294d03aa
7526 changed files with 4011945 additions and 15 deletions

View File

@@ -0,0 +1,813 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local shortcuts = {
[KEY_TAB] = function()
if IsValid( MKeyboard.entity ) then
RunConsoleCommand( "musical_keyboard_leave", MKeyboard.entity:EntIndex() )
end
end,
[KEY_SPACE] = function()
MKeyboard:ToggleMenu()
end,
[KEY_LEFT] = function()
MKeyboard:SwitchInstrument( -1 )
end,
[KEY_RIGHT] = function()
MKeyboard:SwitchInstrument( 1 )
end,
[KEY_UP] = function()
MKeyboard:SetTranspose( MKeyboard.settings.transpose + 1 )
end,
[KEY_DOWN] = function()
MKeyboard:SetTranspose( MKeyboard.settings.transpose - 1 )
end
}
local dontBlockBinds = {
["+attack"] = true,
["+attack2"] = true,
["+duck"] = true
}
local transmitQueue = {}
local queueTimer, queueStart
MKeyboard.layoutName = ""
MKeyboard.isMenuOpen = false
MKeyboard.lastNoteWasAutomated = false
function MKeyboard:Init( ent )
self.entity = ent
self.blockInputTimer = RealTime() + 0.3
self:LoadSettings()
self:OpenInterface()
self:SetInstrument()
self:SetTranspose()
self:SetLayoutByIndex()
self.piano.OnNoteOn = function( note, velocity, instrument, automated )
instrument = instrument or self.settings.instrument
local data = self.instruments[instrument] or self.instruments[1]
if note < data.noteMin or note > data.noteMax then return end
self.entity:EmitNote( note, velocity, instrument, automated )
self.lastNoteWasAutomated = automated
-- Remember when we started putting notes
-- on the queue, and when we should send them
local t = SysTime()
if not queueTimer then
queueTimer = t + 0.4
queueStart = t
end
-- Add notes to the queue unless the limit was reached
local noteCount = #transmitQueue
if noteCount < self.NET_MAX_NOTES then
transmitQueue[noteCount + 1] = {
note, velocity, instrument, t - queueStart
}
end
end
hook.Add( "Think", "MKeyboard.ProcessLocalKeyboard", function()
if not IsValid( self.entity ) then
self:Shutdown()
return
end
local t = SysTime()
-- If the queued notes are ready to be sent...
if queueTimer and t > queueTimer then
net.Start( "mkeyboard.notes", false )
net.WriteEntity( self.entity )
net.WriteBool( self.lastNoteWasAutomated )
net.WriteUInt( #transmitQueue, 5 )
for _, params in ipairs( 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()
table.Empty( transmitQueue )
queueTimer = nil
end
end )
hook.Add( "PlayerButtonDown", "MKeyboard.DetectButtonPress", function( ply, button )
if ply == LocalPlayer() and IsFirstTimePredicted() then
self:OnButtonPress( button )
end
end )
hook.Add( "PlayerButtonUp", "MKeyboard.DetectButtonRelease", function( ply, button )
if ply == LocalPlayer() and IsFirstTimePredicted() then
self:OnButtonRelease( button )
end
end )
hook.Add( "PlayerBindPress", "MKeyboard.BlockBinds", function( _, bind )
if not dontBlockBinds[bind] then return true end
end )
-- Custom Chat compatibility
hook.Add( "CustomChatBlockInput", "MKeyboard.PreventOpeningChat", function()
return true
end )
if not self.isMIDIAvailable then return end
timer.Create( "MKeyboard.CheckMIDI", 1, 0, function()
self:CheckMIDI()
end )
end
function MKeyboard:Shutdown()
self.entity = nil
transmitQueue = {}
queueTimer = nil
self:CloseInterface()
timer.Remove( "MKeyboard.CheckMIDI" )
if self.isMIDIAvailable then
self:CloseMIDI()
end
hook.Remove( "Think", "MKeyboard.ProcessLocalKeyboard" )
hook.Remove( "PlayerButtonDown", "MKeyboard.DetectButtonPress" )
hook.Remove( "PlayerButtonUp", "MKeyboard.DetectButtonRelease" )
hook.Remove( "PlayerBindPress", "MKeyboard.BlockBinds" )
hook.Remove( "CustomChatBlockInput", "MKeyboard.PreventOpeningChat" )
end
local settings = MKeyboard.settings
local colors = MKeyboard.colors
function MKeyboard:OnButtonPress( button )
if RealTime() > self.blockInputTimer then
self.lastNoteWasAutomated = false
self.piano:PressButton( button, settings.velocity, settings.instrument, colors.manual )
end
end
function MKeyboard:OnButtonRelease( button )
if shortcuts[button] then
shortcuts[button]()
return
end
self.piano:ReleaseButton( button )
end
function MKeyboard:SetInstrument( index )
index = index or settings.instrument
local instrument = self.instruments[index]
if instrument then
self.piano:SetNoteRange( instrument.noteMin, instrument.noteMax )
settings.instrument = index
end
end
function MKeyboard:SetTranspose( transpose )
transpose = transpose or settings.transpose
self.piano:ReleaseAllNotes()
self.piano.transpose = transpose
settings.transpose = transpose
end
function MKeyboard:SetLayoutByIndex( index )
index = index or settings.layout
if self.layouts[index] then
settings.layout = index
local layout = self.layouts[index]
self.piano:SetLayout( layout.keys )
self.layoutName = language.GetPhrase( layout.label )
self:UpdateSheetsList()
end
end
function MKeyboard:SwitchInstrument( to )
local newInstrument = settings.instrument + to
if newInstrument < 1 then
newInstrument = #self.instruments
elseif newInstrument > #self.instruments then
newInstrument = 1
end
self:SetInstrument( newInstrument )
local line = self.instrumentList:GetLine( newInstrument )
self.instrumentList:ClearSelection()
self.instrumentList:SelectItem( line )
self.instrumentList.VBar:AnimateTo( line:GetY() - self.instrumentList:GetTall() * 0.5, 0.25, 0, -1 )
self:SaveSettings()
end
function MKeyboard:CloseInterface()
self.isMenuOpen = false
if IsValid( self.frame ) then
self.frame:Close()
end
if IsValid( self.frameDevices ) then
self.frameDevices:Close()
end
if IsValid( self.frameChannels ) then
self.frameChannels:Close()
end
end
function MKeyboard:ToggleMenu()
self.isMenuOpen = not self.isMenuOpen
-- I might have found a bug here. Basically, cant get the panel position
-- after calling LerpPositions without playing the animation first
-- local x = self.frame:GetX()
local x = ( ScrW() - ScrW() * 0.7 ) * 0.5
local tall = self.frame:GetTall()
if self.isMenuOpen then
self.frame:SetPos( x, ScrH() - tall )
self.frame:MakePopup()
else
self.frame:SetPos( x, ScrH() - tall * 0.49 )
self.frame:SetMouseInputEnabled( false )
self.frame:SetKeyboardInputEnabled( false )
end
end
local function CreatePanel( title, parent, dock, wide, help )
local panel = vgui.Create( "DPanel", parent )
panel:DockPadding( 8, 8, 8, 8 )
panel:DockMargin( 4, 0, 4, 0 )
panel:Dock( dock )
panel:SetBackgroundColor( colors.panel )
if wide then
panel:SetWide( wide )
end
local panelHeader = vgui.Create( "DPanel", panel )
panelHeader:Dock( TOP )
panelHeader:SetPaintBackground( false )
local labelTitle = vgui.Create( "DLabel", panelHeader )
labelTitle:SetFont( "MKeyboard_Key" )
labelTitle:SetText( language.GetPhrase( title ) )
labelTitle:SizeToContents()
labelTitle:Dock( LEFT )
labelTitle:SetTextColor( colors.white )
if help then
local labelHelp = vgui.Create( "DLabel", panelHeader )
labelHelp:SetFont( "MKeyboard_Key" )
labelHelp:SetText( language.GetPhrase( help ) )
labelHelp:SizeToContents()
labelHelp:Dock( RIGHT )
labelHelp:SetTextColor( colors.white )
end
return panel
end
local SetColor = surface.SetDrawColor
local DrawRect = surface.DrawRect
local DrawText = draw.SimpleText
local Ceil = math.ceil
local function DrawSheet( index, x, y )
local data = MKeyboard.sheets[index]
if not data then return end
local borderSize = Ceil( ScrW() * 0.002 )
local titleBarSize = ScrH() * 0.028
surface.SetFont( "MKeyboard_Sheet" )
local sheetW, sheetH = surface.GetTextSize( data.sequence )
local w, h = math.max( ScrW() * 0.3, sheetW + borderSize * 2 ), sheetH + titleBarSize * 0.5
local oldClipping = DisableClipping( true )
x = x - w * 0.5
y = y - h
SetColor( 0, 0, 0, 254 )
DrawRect( x, y, w, h )
draw.DrawText( data.sequence, "MKeyboard_Sheet", x + w * 0.5, y + titleBarSize, nil, TEXT_ALIGN_CENTER )
DrawText( data.title, "MKeyboard_Title", x + w * 0.5, y + borderSize, colors.white, TEXT_ALIGN_CENTER )
DisableClipping( oldClipping )
end
function MKeyboard:OpenInterface()
if not self.selectedMIDIPort then
self:ShowDevicesDialog()
end
local wide = ScrW() * 0.7
local tall = ScrH() * 0.5
self.frame = vgui.Create( "DFrame" )
self.frame:SetPos( ( ScrW() - wide ) * 0.5, ScrH() - tall * 0.5 )
self.frame:SetSize( wide, tall )
self.frame:SetTitle( "" )
self.frame:SetDeleteOnClose( true )
self.frame:SetDraggable( false )
self.frame:SetSizable( false )
self.frame:ShowCloseButton( false )
self.frame:LerpPositions( 1, true )
-- Passthrough button events while this panel is focused
self.frame.OnKeyCodePressed = function( _, key )
self:OnButtonPress( key )
end
self.frame.OnKeyCodeReleased = function( _, key )
self:OnButtonRelease( key )
end
local helpMessage = {
[false] = language.GetPhrase( "musicalk.help.open" ),
[true] = language.GetPhrase( "musicalk.help.close" )
}
self.frame.Paint = function( _, w, h )
draw.RoundedBoxEx( 8, 0, h * 0.4, w, h * 0.6, colors.frame, true, true, false, false )
if self.midiPortName then
DrawText( self.midiPortName, "MKeyboard_Title", w - ( w * 0.01 ), h * 0.43,
colors.white, TEXT_ALIGN_RIGHT, TEXT_ALIGN_TOP )
DrawText( helpMessage[self.isMenuOpen], "MKeyboard_Title", w * 0.01, h * 0.43,
colors.white, TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP )
else
DrawText( helpMessage[self.isMenuOpen], "MKeyboard_Title", w * 0.5, h * 0.43,
colors.white, TEXT_ALIGN_CENTER, TEXT_ALIGN_TOP )
end
if settings.sheet > 0 then
DrawSheet( settings.sheet, w * 0.5, 0 )
end
local pianoW = self.piano.w
local pianoH = h * 0.3
local x = ( w * 0.5 ) - ( pianoW * 0.5 )
local y = 6
draw.RoundedBox( 4, x - 6, y - 6, pianoW + 12, pianoH + 12, colors.panel )
-- Draw layout, instrument name & transpose
local transpose = settings.transpose
local offset = 0
DrawText( self.layoutName, "MKeyboard_Key", x, y, colors.white )
if transpose ~= 0 then
offset = DrawText(
transpose > 0 and "+" .. transpose or transpose,
"MKeyboard_Key",
x + pianoW, y, colors.manual,
TEXT_ALIGN_RIGHT
)
offset = offset + 6
end
local data = self.instruments[settings.instrument]
local _, th = DrawText( data.name, "MKeyboard_Key", x + pianoW - offset, y, colors.white, TEXT_ALIGN_RIGHT )
th = th + 4
self.piano:Draw( x, y + th, pianoH - th )
end
local mainPanel = vgui.Create( "DPanel", self.frame )
mainPanel:SetPos( wide * 0.01, tall * 0.52 )
mainPanel:SetSize( wide * 0.98, tall * 0.45 )
mainPanel:DockPadding( 0, 0, 0, 0 )
mainPanel:SetPaintBackground( false )
---------- Settings panel ----------
local panelSettings = CreatePanel( "musicalk.settings", mainPanel, LEFT, wide * 0.3 )
local settingsScroll = vgui.Create( "DScrollPanel", panelSettings )
settingsScroll:Dock( FILL )
settingsScroll:DockMargin( 0, 8, 0, 0 )
local function AddRow( text, propertyClass )
local parent = vgui.Create( "DPanel", settingsScroll )
parent:SetWide( wide * 0.3 )
parent:SetTall( 28 )
parent:DockPadding( 4, 4, 4, 4 )
parent:DockMargin( 0, 0, 0, 2 )
parent:Dock( TOP )
parent:SetBackgroundColor( colors.white )
local label = vgui.Create( "DLabel", parent )
label:SetText( language.GetPhrase( text ) )
label:SetTextColor( colors.black )
label:SetWide( parent:GetWide() )
label:Dock( FILL )
if not propertyClass then
label:SetTextColor( colors.white )
label:SetContentAlignment( 5 )
parent:SetBackgroundColor( colors.black )
parent:SetTall( 28 )
return
end
local property = vgui.Create( propertyClass, parent )
if propertyClass == "DNumSlider" then
property.TextArea:SetWide( 30 )
function property:PerformLayout()
self.Label:SetWide( 0 )
end
end
property:Dock( RIGHT )
property:SetWide( 150 )
return property
end
AddRow( "musicalk.vkeys" )
-- Layouts list
local rowLayouts = AddRow( "musicalk.layout", "DComboBox" )
for i, v in ipairs( self.layouts ) do
rowLayouts:AddChoice( language.GetPhrase( v.label ), nil, i == settings.layout )
end
rowLayouts.OnSelect = function( _, index )
settings.sheet = 0
self:SetLayoutByIndex( index )
self:SaveSettings()
end
-- Should draw labels?
local rowDrawLabels = AddRow( "musicalk.vkeys.labels", "DImageButton" )
rowDrawLabels:SetImage( settings.drawKeyLabels and "icon16/accept.png" or "icon16/cancel.png" )
rowDrawLabels:SetStretchToFit( false )
rowDrawLabels:SetPaintBackground( true )
rowDrawLabels.DoClick = function()
settings.drawKeyLabels = not settings.drawKeyLabels
self:SaveSettings()
rowDrawLabels:SetIcon( settings.drawKeyLabels and "icon16/accept.png" or "icon16/cancel.png" )
end
-- Velocity
local rowVelocity = AddRow( "musicalk.vkeys.velocity", "DNumSlider" )
rowVelocity:SetMin( 0 )
rowVelocity:SetMax( 127 )
rowVelocity:SetDecimals( 0 )
rowVelocity:SetValue( settings.velocity )
rowVelocity.Label:SetTextColor( colors.black )
rowVelocity.OnValueChanged = function( _, value )
settings.velocity = Ceil( value )
end
-- Transpose
local rowTranspose = AddRow( "musicalk.vkeys.transpose", "DNumSlider" )
rowTranspose:SetMin( -24 )
rowTranspose:SetMax( 42 )
rowTranspose:SetDecimals( 0 )
rowTranspose:SetValue( settings.transpose )
rowTranspose.Label:SetTextColor( colors.black )
rowTranspose.Label:SetWide( 30 )
rowTranspose.Slider:SetWide( 300 )
self.rowTranspose = rowTranspose
rowTranspose.OnValueChanged = function( _, value )
if value < 0 then
value = Ceil( value )
else
value = math.floor( value )
end
self:SetTranspose( value )
self:SaveSettings()
end
AddRow( "MIDI" )
-- MIDI devices
local rowDevices = AddRow( "musicalk.midi.device", "DButton" )
rowDevices:SetText( language.GetPhrase( "musicalk.midi.device.choose" ) )
if self.isMIDIAvailable then
if table.Count( midi.GetPorts() ) == 0 then
rowDevices:SetText( language.GetPhrase( "musicalk.midi.nodevices" ) )
rowDevices:SetEnabled( false )
end
rowDevices.DoClick = function()
self:ShowDevicesDialog()
end
local rowChannels = AddRow( "musicalk.midi.channels", "DButton" )
rowChannels:SetText( language.GetPhrase( "musicalk.midi.channels.setup" ) )
rowChannels.DoClick = function()
self:ShowChannelsDialog()
end
local rowMIDITranspose = AddRow( "musicalk.vkeys.transpose", "DNumSlider" )
rowMIDITranspose:SetMin( -48 )
rowMIDITranspose:SetMax( 48 )
rowMIDITranspose:SetDecimals( 0 )
rowMIDITranspose:SetValue( settings.midiTranspose )
rowMIDITranspose.Label:SetTextColor( colors.black )
rowMIDITranspose.Label:SetWide( 30 )
rowMIDITranspose.Slider:SetWide( 300 )
rowMIDITranspose.OnValueChanged = function( _, value )
if value < 0 then
settings.midiTranspose = Ceil( value )
else
settings.midiTranspose = math.floor( value )
end
self:SaveSettings()
self.piano:ReleaseAllNotes()
end
else
rowDevices:SetText( language.GetPhrase( "musicalk.midi.nomodule" ) )
rowDevices:SetEnabled( false )
local rowInstallHelp = AddRow( "musicalk.midi.guide", "DButton" )
rowInstallHelp:SetText( language.GetPhrase( "musicalk.midi.guide" ) )
rowInstallHelp.DoClick = function()
gui.OpenURL( self.URL_MIDI_GUIDE )
self:ToggleMenu()
end
end
---------- Instruments panel ----------
local panelInstruments = CreatePanel( "musicalk.instruments", mainPanel, FILL, nil, "musicalk.instruments.help" )
self.instrumentList = vgui.Create( "DListView", panelInstruments )
self.instrumentList:Dock( FILL )
self.instrumentList:DockMargin( 0, 8, 0, 0 )
self.instrumentList:AddColumn( language.GetPhrase( "musicalk.instruments" ) )
self.instrumentList:SetMultiSelect( false )
self.instrumentList:SetHideHeaders( true )
self.instrumentList:SetSortable( false )
for i, v in ipairs( self.instruments ) do
self.instrumentList:AddLine( i .. " - " .. v.name )
end
self.instrumentList:SelectItem( self.instrumentList:GetLine( settings.instrument ) )
self.instrumentList.OnRowSelected = function( _, index )
self:SetInstrument( index )
self:SaveSettings()
end
---------- Layouts Panel ----------
local panelSheets = CreatePanel( "musicalk.sheets", mainPanel, RIGHT, wide * 0.3 )
self.sheetList = vgui.Create( "DListView", panelSheets )
self.sheetList:Dock( FILL )
self.sheetList:DockMargin( 0, 8, 0, 0 )
self.sheetList:AddColumn( language.GetPhrase( "musicalk.sheets" ) )
self.sheetList:SetMultiSelect( false )
self.sheetList:SetHideHeaders( true )
self.sheetList:SetSortable( false )
self:UpdateSheetsList()
self.sheetList.OnRowSelected = function( _, _, line )
settings.sheet = line._sheetIndex
self:SaveSettings()
end
end
function MKeyboard:UpdateSheetsList()
local layoutId = self.layouts[settings.layout].id
self.sheetList:Clear()
local selectedItem = self.sheetList:AddLine( language.GetPhrase( "musicalk.sheets.hidden" ) )
selectedItem._sheetIndex = 0
for i, v in ipairs( self.sheets ) do
if v.layout == layoutId then
local line = self.sheetList:AddLine( v.title )
line._sheetIndex = i
if i == settings.sheet then
selectedItem = line
end
end
end
self.sheetList:SelectItem( selectedItem )
end
function MKeyboard:ShowDevicesDialog()
if IsValid( self.frameDevices ) then
self.frameDevices:Close()
end
if not self.isMIDIAvailable then return end
local midiPorts = midi.GetPorts()
if table.Count( midiPorts ) == 0 then return end
if midi.IsOpened() then
self:CloseMIDI()
end
self.selectedMIDIPort = nil
self.frameDevices = vgui.Create( "DFrame" )
self.frameDevices:SetSize( 300, 130 )
self.frameDevices:SetTitle( language.GetPhrase( "musicalk.midi.device.choose" ) )
self.frameDevices:SetVisible( true )
self.frameDevices:SetDraggable( true )
self.frameDevices:ShowCloseButton( true )
self.frameDevices:SetDeleteOnClose( true )
self.frameDevices:Center()
self.frameDevices:MakePopup()
local startTime = SysTime()
local oldPaint = self.frameDevices.Paint
self.frameDevices.Paint = function( s, w, h )
Derma_DrawBackgroundBlur( s, startTime )
oldPaint( s, w, h )
end
local labelHelp = vgui.Create( "DLabel", self.frameDevices )
labelHelp:SetPos( 10, 40 )
labelHelp:SetSize( 280, 40 )
labelHelp:SetText( string.format(
language.GetPhrase( "musicalk.midi.found" ),
tostring( table.Count( midiPorts ) )
) )
local comboDevices = vgui.Create( "DComboBox", self.frameDevices )
comboDevices:SetPos( 10, 90 )
comboDevices:SetSize( 280, 20 )
comboDevices:SetValue( language.GetPhrase( "musicalk.midi.select" ) )
for k, v in pairs( midiPorts ) do
comboDevices:AddChoice( "[" .. k .. "] " .. v )
end
comboDevices.OnSelect = function( _, index )
self.selectedMIDIPort = index - 1
self.frameDevices:Close()
end
end
function MKeyboard:ShowChannelsDialog()
if IsValid( self.frameChannels ) then
self.frameChannels:Close()
return
end
local tall = math.min( 610, ScrH() * 0.6 )
self.frameChannels = vgui.Create( "DFrame" )
self.frameChannels:SetSize( 400, tall )
self.frameChannels:SetTitle( language.GetPhrase( "musicalk.channels" ) )
self.frameChannels:SetVisible( true )
self.frameChannels:SetSizable( true )
self.frameChannels:SetDraggable( true )
self.frameChannels:ShowCloseButton( true )
self.frameChannels:SetDeleteOnClose( true )
self.frameChannels:SetPos( ScrW() - self.frameChannels:GetWide(), 0 )
self.frameChannels:MakePopup()
-- Passthrough button events while this panel is focused
self.frameChannels.OnKeyCodePressed = function( _, key )
self:OnButtonPress( key )
end
self.frameChannels.OnKeyCodeReleased = function( _, key )
self:OnButtonRelease( key )
end
local scrollChannels = vgui.Create( "DScrollPanel", self.frameChannels )
scrollChannels:Dock( FILL )
local function PaintChannel( s, w, h )
SetColor( 0, 0, 0, 255 )
DrawRect( 0, 0, w, h )
SetColor( 50, 50, 50, 255 )
DrawRect( 4, 4, 8, h - 8 )
self.channelState[s.channel] = Lerp( FrameTime() * 8, self.channelState[s.channel], 0 )
SetColor( colors.automated.r, colors.automated.g, colors.automated.b, 255 * self.channelState[s.channel] )
DrawRect( 4, 4, 8, h - 8 )
end
local function OnSelectInstrument( s, _, _, idx )
if idx == -1 then
settings.channelInstruments[s.channel] = nil
else
settings.channelInstruments[s.channel] = idx
end
self:SaveSettings()
end
for i = 0, 15 do
self.channelState[i] = 0
local panelChannel = vgui.Create( "DPanel", scrollChannels )
panelChannel:SetSize( wide, 32 )
panelChannel:Dock( TOP )
panelChannel:DockMargin( 0, 0, 0, 4 )
panelChannel.channel = i
panelChannel.Paint = PaintChannel
local labelIndex = vgui.Create( "DLabel", panelChannel )
labelIndex:SetFont( "Trebuchet24" )
labelIndex:SetTextColor( colors.white )
labelIndex:SetText( "#" .. ( i + 1 ) )
labelIndex:SetWide( 50 )
labelIndex:Dock( LEFT )
labelIndex:DockMargin( 20, 0, 0, 0 )
local combo = vgui.Create( "DComboBox", panelChannel )
combo:SetSortItems( false )
combo:AddChoice( language.GetPhrase( "musicalk.channels.usecurrent" ), -1, true )
combo:AddChoice( language.GetPhrase( "musicalk.channels.mute" ), 0 )
combo:AddSpacer()
combo:Dock( FILL )
local myInstrument = settings.channelInstruments[i] or -1
for idx, v in ipairs( self.instruments ) do
combo:AddChoice( v.name, idx, idx == myInstrument )
end
combo.channel = i
combo.OnSelect = OnSelectInstrument
end
end