Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: copy/paste step #17

Merged
merged 6 commits into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions filesystem/keymap.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ type KeyMap struct {
RemoveTrack string `json:"remove_track"`
AddStep string `json:"add_step"`
RemoveStep string `json:"remove_step"`
CopyStep string `json:"copy_step"`
PasteStep string `json:"paste_step"`
PreviousStep string `json:"previous_step"`
NextStep string `json:"next_step"`
PageUp string `json:"page_up"`
Expand Down Expand Up @@ -42,6 +44,8 @@ func NewDefaultAzertyKeyMap() KeyMap {
RemoveTrack: ")",
AddStep: "+",
RemoveStep: "°",
CopyStep: "ctrl+c",
PasteStep: "ctrl+v",
PreviousStep: ",",
NextStep: ";",
PageUp: "p",
Expand Down Expand Up @@ -73,6 +77,8 @@ func NewDefaultAzertyMacKeyMap() KeyMap {
RemoveTrack: ")",
AddStep: "_",
RemoveStep: "°",
CopyStep: "ctrl+c",
PasteStep: "ctrl+v",
PreviousStep: ",",
NextStep: ";",
PageUp: "p",
Expand Down Expand Up @@ -103,6 +109,8 @@ func NewDefaultQwertyKeyMap() KeyMap {
RemoveTrack: "-",
AddStep: "+",
RemoveStep: "_",
CopyStep: "ctrl+c",
PasteStep: "ctrl+v",
PreviousStep: ",",
NextStep: ".",
PageUp: "p",
Expand Down Expand Up @@ -134,6 +142,8 @@ func NewDefaultQwertyMacKeyMap() KeyMap {
RemoveTrack: "-",
AddStep: "+",
RemoveStep: "_",
CopyStep: "ctrl+c",
PasteStep: "ctrl+v",
PreviousStep: ",",
NextStep: ".",
PageUp: "p",
Expand Down
29 changes: 29 additions & 0 deletions sequencer/helper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package sequencer

// Helper functions for deep copying pointers
func copyIntPtr(ptr *int) *int {
if ptr == nil {
return nil
}
copy := *ptr
return &copy
}

func copyUint8Ptr(ptr *uint8) *uint8 {
if ptr == nil {
return nil
}
copy := *ptr
return &copy
}

func copyUint8SlicePtr(ptr *[]uint8) *[]uint8 {
if ptr == nil {
return nil
}
copy := make([]uint8, len(*ptr))
for i, v := range *ptr {
copy[i] = v
}
return &copy
}
66 changes: 66 additions & 0 deletions sequencer/sequencer.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ type Sequencer interface {
AddStep(track int)
RemoveStep(track int)
ToggleStep(track, step int)
CopyStep(track, step int)
PasteStep(track, step int)
Tempo() float64
SetTempo(tempo float64)
Reset()
Expand All @@ -72,6 +74,8 @@ type sequencer struct {
isPlaying bool

isFirstTick bool

stepClipboard step
}

// New creates a new sequencer. It also creates new tracks and calls the
Expand Down Expand Up @@ -245,6 +249,68 @@ func (s *sequencer) ToggleStep(track, step int) {
s.tracks[track].steps[step].clearParameters()
}

// CopyStep copies a step to the 'clipboard' step.
// TODO: Double check this, had some AI help with pointers
ajboni marked this conversation as resolved.
Show resolved Hide resolved
xaviergodart marked this conversation as resolved.
Show resolved Hide resolved
func (s *sequencer) CopyStep(track, srcStep int) {
if track < 0 || track >= len(s.tracks) || srcStep < 0 || srcStep >= len(s.tracks[track].steps) {
return // Out of bounds, do nothing
}

originalStep := s.tracks[track].steps[srcStep]

// Create a deep copy of the step
s.stepClipboard = step{
midi: originalStep.midi, // Assuming midi.Midi is safe to copy directly
ajboni marked this conversation as resolved.
Show resolved Hide resolved
xaviergodart marked this conversation as resolved.
Show resolved Hide resolved
track: nil, // We don't want to keep a reference to the original track
position: originalStep.position,
active: originalStep.active,
triggered: false, // Reset triggered state for the copy
controls: make(map[int]*midi.Control),
length: copyIntPtr(originalStep.length),
chord: copyUint8SlicePtr(originalStep.chord),
velocity: copyUint8Ptr(originalStep.velocity),
probability: copyIntPtr(originalStep.probability),
offset: originalStep.offset,
}

// Deep copy the controls
for k, v := range originalStep.controls {
controlCopy := *v // Assuming midi.Control is safe to copy directly
s.stepClipboard.controls[k] = &controlCopy
}
}

// PasteStep pastes a clipboard step into a destination step
func (s *sequencer) PasteStep(track, dstStep int) {
if track < 0 || track >= len(s.tracks) || dstStep < 0 || dstStep >= len(s.tracks[track].steps) {
return // Out of bounds, do nothing
}

// Create a deep copy of the clipboard
newStep := step{
midi: s.stepClipboard.midi,
track: s.tracks[track],
position: dstStep,
active: s.stepClipboard.active,
triggered: false, // Reset triggered state
controls: make(map[int]*midi.Control),
length: copyIntPtr(s.stepClipboard.length),
chord: copyUint8SlicePtr(s.stepClipboard.chord),
velocity: copyUint8Ptr(s.stepClipboard.velocity),
probability: copyIntPtr(s.stepClipboard.probability),
offset: s.stepClipboard.offset,
}

// Deep copy the controls
for k, v := range s.stepClipboard.controls {
controlCopy := *v
newStep.controls[k] = &controlCopy
}

// Replace the step in the track
s.tracks[track].steps[dstStep] = &newStep
}

func (s *sequencer) start() {
// Each time the clock ticks, we call the sequencer tick method that
// basically makes every track move forward in time.
Expand Down
14 changes: 12 additions & 2 deletions ui/keymap.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ type keyMap struct {

AddStep key.Binding
RemoveStep key.Binding
CopyStep key.Binding
PasteStep key.Binding

NextStep key.Binding
PreviousStep key.Binding
Expand Down Expand Up @@ -101,6 +103,14 @@ func newKeyMap(keys filesystem.KeyMap) keyMap {
key.WithKeys(keys.RemoveStep),
key.WithHelp(keys.RemoveStep, "remove step"),
),
CopyStep: key.NewBinding(
key.WithKeys(keys.CopyStep),
key.WithHelp(keys.CopyStep, "copy active step parameters"),
),
PasteStep: key.NewBinding(
key.WithKeys(keys.PasteStep),
key.WithHelp(keys.PasteStep, "paste copied parameters into active step"),
),
PreviousStep: key.NewBinding(
key.WithKeys(keys.PreviousStep),
key.WithHelp(keys.PreviousStep, "select previous step"),
Expand Down Expand Up @@ -178,8 +188,8 @@ func newKeyMap(keys filesystem.KeyMap) keyMap {
key.WithHelp(keys.Help, "toggle help"),
),
Quit: key.NewBinding(
key.WithKeys("ctrl+c", "esc"),
key.WithHelp("ctrl+c/esc", "quit"),
key.WithKeys("ctrl+q", "esc"),
key.WithHelp("ctrl+q/esc", "quit"),
),
}
for i, k := range keys.Steps {
Expand Down
14 changes: 14 additions & 0 deletions ui/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,20 @@ func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.updateParams()
return m, nil

case key.Matches(msg, m.keymap.CopyStep):
m.seq.CopyStep(m.activeTrack, m.activeStep)
m.mode = stepMode
m.stepModeTimer = 0
m.updateParams()
return m, nil

case key.Matches(msg, m.keymap.PasteStep):
m.seq.PasteStep(m.activeTrack, m.activeStep)
m.mode = stepMode
m.stepModeTimer = 0
m.updateParams()
return m, nil

case key.Matches(msg, m.keymap.StepToggle):
number := m.keymap.StepToggleIndex[msg.String()]
if m.mode == patternMode {
Expand Down
Loading