From 2c534f55ad9134c27880e180a0cdabb0e10b7b80 Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 30 Dec 2019 12:12:37 -0600 Subject: [PATCH] Added duration setting options/shortcuts Set Duration was added as a dropdown option under the Edit menu with options to increment, decrement, and set a fixed duration and associated shortcut keys. --- src/id.h | 26 +++++--- src/main.c | 24 +++++-- src/resource.rc | 13 +++- src/tracker.c | 171 ++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 215 insertions(+), 19 deletions(-) diff --git a/src/id.h b/src/id.h index b4e4193..351da33 100644 --- a/src/id.h +++ b/src/id.h @@ -17,15 +17,23 @@ #define ID_TRANSPOSE 117 #define ID_MAKE_SUBROUTINE 118 #define ID_UNMAKE_SUBROUTINE 119 -#define ID_OPTIONS 120 -#define ID_PLAY 121 -#define ID_STOP 122 -#define ID_CLEAR_SONG 123 -#define ID_ZOOM_IN 130 -#define ID_ZOOM_OUT 131 -#define ID_OCTAVE_1 140 -#define ID_HELP 150 -#define ID_ABOUT 151 +#define ID_INCREMENT_DURATION 120 +#define ID_DECREMENT_DURATION 121 +#define ID_SET_DURATION_1 123 +#define ID_SET_DURATION_2 124 +#define ID_SET_DURATION_3 125 +#define ID_SET_DURATION_4 126 +#define ID_SET_DURATION_5 127 +#define ID_SET_DURATION_6 128 +#define ID_OPTIONS 130 +#define ID_PLAY 131 +#define ID_STOP 132 +#define ID_CLEAR_SONG 133 +#define ID_ZOOM_IN 140 +#define ID_ZOOM_OUT 141 +#define ID_OCTAVE_1 150 +#define ID_HELP 160 +#define ID_ABOUT 161 #define IDA_ACCEL 1 diff --git a/src/main.c b/src/main.c index f071527..b3c793f 100644 --- a/src/main.c +++ b/src/main.c @@ -251,10 +251,26 @@ LRESULT CALLBACK MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) DialogBox(hinstance, MAKEINTRESOURCE(IDD_OPTIONS), hWnd, OptionsDlgProc); break; } - case ID_CUT: case ID_COPY: case ID_PASTE: case ID_DELETE: - case ID_SPLIT_PATTERN: case ID_JOIN_PATTERNS: - case ID_MAKE_SUBROUTINE: case ID_UNMAKE_SUBROUTINE: case ID_TRANSPOSE: - case ID_CLEAR_SONG: case ID_ZOOM_OUT: case ID_ZOOM_IN: + case ID_CUT: + case ID_COPY: + case ID_PASTE: + case ID_DELETE: + case ID_SPLIT_PATTERN: + case ID_JOIN_PATTERNS: + case ID_MAKE_SUBROUTINE: + case ID_UNMAKE_SUBROUTINE: + case ID_TRANSPOSE: + case ID_CLEAR_SONG: + case ID_ZOOM_OUT: + case ID_ZOOM_IN: + case ID_INCREMENT_DURATION: + case ID_DECREMENT_DURATION: + case ID_SET_DURATION_1: + case ID_SET_DURATION_2: + case ID_SET_DURATION_3: + case ID_SET_DURATION_4: + case ID_SET_DURATION_5: + case ID_SET_DURATION_6: editor_command(id); break; case ID_PLAY: diff --git a/src/resource.rc b/src/resource.rc index 3a4278c..4353ae1 100644 --- a/src/resource.rc +++ b/src/resource.rc @@ -31,6 +31,17 @@ BEGIN MENUITEM "&Make Subroutine", ID_MAKE_SUBROUTINE, GRAYED MENUITEM "U&nmake Subroutine", ID_UNMAKE_SUBROUTINE, GRAYED MENUITEM "T&ranspose...", ID_TRANSPOSE, GRAYED + POPUP "Set D&uration" + { + MENUITEM "&Increment\tCtrl+.", ID_INCREMENT_DURATION, GRAYED + MENUITEM "&Decrement\tCtrl+,", ID_DECREMENT_DURATION, GRAYED + MENUITEM "&Whole Note\tCtrl+1", ID_SET_DURATION_1, GRAYED + MENUITEM "&Half Note\tCtrl+2", ID_SET_DURATION_2, GRAYED + MENUITEM "&Quarter Note\tCtrl+3", ID_SET_DURATION_3, GRAYED + MENUITEM "&Eighth Note\tCtrl+4", ID_SET_DURATION_4, GRAYED + MENUITEM "&Sixteenth Note\tCtrl+5", ID_SET_DURATION_5, GRAYED + MENUITEM "&Thirty-second Note\tCtrl+6", ID_SET_DURATION_6, GRAYED + } END POPUP "&Options" BEGIN @@ -115,7 +126,7 @@ STYLE 0 CAPTION "About EB Music Editor" FONT 8, "MS Shell Dlg" BEGIN - LTEXT "EarthBound Music Editor v2.204: released 2015-10-31\r\nProgrammed by Goplat\r\nAdditional programming by BlueStone\r\n\r\nDefault BGM titles based on 4F90A.txt by Michael1\r\n", 0, + LTEXT "EarthBound Music Editor v2.3.0: released 2015-10-31\r\nProgrammed by Goplat\r\nAdditional programming by BlueStone\r\n\r\nDefault BGM titles based on 4F90A.txt by Michael1\r\n", 0, 5, 5, 190, 55 DEFPUSHBUTTON "&OK", IDOK, 145, 51, 50, 14 END diff --git a/src/tracker.c b/src/tracker.c index 6002f05..820b4ac 100644 --- a/src/tracker.c +++ b/src/tracker.c @@ -332,7 +332,12 @@ LRESULT CALLBACK EditorWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara ID_SPLIT_PATTERN, ID_JOIN_PATTERNS, ID_MAKE_SUBROUTINE, ID_UNMAKE_SUBROUTINE, ID_TRANSPOSE, ID_CLEAR_SONG, - ID_ZOOM_IN, ID_ZOOM_OUT, 0 + ID_ZOOM_IN, ID_ZOOM_OUT, + ID_INCREMENT_DURATION, ID_DECREMENT_DURATION, + ID_SET_DURATION_1, ID_SET_DURATION_2, + ID_SET_DURATION_3, ID_SET_DURATION_4, + ID_SET_DURATION_5, ID_SET_DURATION_6, + 0 }; switch (uMsg) { case WM_CREATE: @@ -719,6 +724,9 @@ static BOOL cursor_home(BOOL select) { return TRUE; } +/// \brief Attempts to move the cursor back by one control code. +/// \return Returns false if the cursor cannot be moved backwards due +/// to already being at the top of the track, otherwise returns true. static BOOL cursor_back(BOOL select) { int prev_pos; struct parser prev; @@ -940,6 +948,102 @@ static void delete_sel(BOOL cut) { restore_cursor(t, start - t->track); } +static void updateOrInsertDuration(BYTE(*callback)(BYTE, int), int durationOrOffset) +{ + // We cannot insert a duration code before an 0x00 code, + // so ensure that's not the case before proceeding. + if (cursor_track->track != NULL + && *cursor.ptr != 0) + { + BYTE* original_pos = cursor.ptr; + struct track* t = cursor_track; + int off = cursor.ptr - t->track; + if (*cursor.ptr >= 0x01 && *cursor.ptr <= 0x7F) + { + BYTE duration = callback(*cursor.ptr, durationOrOffset); + if (duration != 0) + { + *cursor.ptr = duration; + cur_song.changed = TRUE; + InvalidateRect(hwndTracker, NULL, FALSE); + } + } + else + { + // A duration code isn't selected so + // find the last duration and last note, if any. + BYTE* last_duration_pos = NULL; + BYTE* last_note_pos = NULL; + cursor_home(FALSE); + while (cursor.ptr != original_pos) + { + if (*cursor.ptr >= 0x01 && *cursor.ptr <= 0x7F) + last_duration_pos = cursor.ptr; + else if (*cursor.ptr >= 0x80 && *cursor.ptr <= 0xDF) + last_note_pos = cursor.ptr; + + // Advance the cursor if possible and continue, otherwise break. + if (!cursor_fwd(FALSE)) + break; + } + + // If we found a duration and a note doesn't follow it (meaning + // the selected note follows the duration code), set the duration. + if (last_duration_pos != NULL + && last_duration_pos > last_note_pos) + { + BYTE duration = callback(*last_duration_pos, durationOrOffset); + if (duration != 0) + { + *last_duration_pos = duration; + + // restore the cursor position. + //cursor.ptr = original_pos; + pattern_changed(); + restore_cursor(t, off); + } + } + else if (last_note_pos != NULL) + { + // Else if we found a note position and it comes after the last + // duration code (or there is none), insert a duration. + BYTE duration = callback(last_duration_pos == NULL ? state.chan[cursor_chan].note_len : *last_duration_pos, durationOrOffset); + if (duration != 0) + { + track_insert(1, (BYTE *)&duration); + cursor_fwd(FALSE); + } + } + } + } +} + +static BYTE setDurationOffsetCallback(BYTE originalDuration, int offset) +{ + BYTE newDuration = min(max(0x00, originalDuration + offset), 0xFF); + return (newDuration >= 0x01 && newDuration <= 0x7F) ? newDuration : 0; +} + +static BYTE setDurationCallback(BYTE originalDuration, int duration) +{ + return (duration >= 0x01 && duration <= 0x7F) ? duration : 0; +} + +static void incrementDuration() +{ + updateOrInsertDuration(setDurationOffsetCallback, 1); +} + +static void decrementDuration() +{ + updateOrInsertDuration(setDurationOffsetCallback, -1); +} + +static void setDuration(BYTE duration) +{ + updateOrInsertDuration(setDurationCallback, duration); +} + void editor_command(int id) { switch (id) { case ID_CUT: delete_sel(TRUE); break; @@ -1061,6 +1165,30 @@ void editor_command(int id) { zoom = zoom_levels[++zoom_idx]; InvalidateRect(hwndTracker, NULL, FALSE); break; + case ID_INCREMENT_DURATION: + incrementDuration(); + break; + case ID_DECREMENT_DURATION: + decrementDuration(); + break; + case ID_SET_DURATION_1: + setDuration(0x60); + break; + case ID_SET_DURATION_2: + setDuration(0x30); + break; + case ID_SET_DURATION_3: + setDuration(0x18); + break; + case ID_SET_DURATION_4: + setDuration(0x0C); + break; + case ID_SET_DURATION_5: + setDuration(0x06); + break; + case ID_SET_DURATION_6: + setDuration(0x03); + break; } } @@ -1132,16 +1260,49 @@ static void tracker_keydown(WPARAM wParam) { case VK_DELETE: delete_sel(shift); break; + case VK_ADD: + incrementDuration(); + break; + case VK_SUBTRACT: + decrementDuration(); + break; + case VK_NUMPAD1: + setDuration(0x60); + break; + case VK_NUMPAD2: + setDuration(0x30); + break; + case VK_NUMPAD3: + setDuration(0x18); + break; + case VK_NUMPAD4: + setDuration(0x0C); + break; + case VK_NUMPAD5: + setDuration(0x06); + break; + case VK_NUMPAD6: + setDuration(0x03); + break; default: if (control) { if (wParam == 'C') copy_sel(); else if (wParam == 'V') paste_sel(); else if (wParam == 'X') delete_sel(TRUE); - break; + else if (wParam == VK_OEM_COMMA) decrementDuration(); + else if (wParam == VK_OEM_PERIOD) incrementDuration(); + else if (wParam == '1') setDuration(0x60); + else if (wParam == '2') setDuration(0x30); + else if (wParam == '3') setDuration(0x18); + else if (wParam == '4') setDuration(0x0C); + else if (wParam == '5') setDuration(0x06); + else if (wParam == '6') setDuration(0x03); + } + else + { + int note = note_from_key(wParam, shift); + addOrInsertNote(note); } - - int note = note_from_key(wParam, shift); - addOrInsertNote(note); break; } }