// for finding memory leaks in debug mode with Visual Studio#if defined _DEBUG && defined _MSC_VER#include <crtdbg.h>#endif #include <stdio.h>#include <stdint.h>#include "ft2_header.h"#include "ft2_config.h"#include "ft2_keyboard.h"#include "ft2_audio.h"#include "ft2_midi.h"#include "ft2_pattern_ed.h"#include "ft2_sysreqs.h"#include "ft2_textboxes.h"#include "ft2_tables.h"#include "ft2_structs.h" enum{ KEYTYPE_NUM = 0, KEYTYPE_ALPHA = 1}; static double dVolScaleFK1 = 1.0, dVolScaleFK2 = 1.0; // for block cut/copy/pastestatic bool blockCopied;static int16_t markXSize, markYSize;static uint16_t ptnBufLen, trkBufLen; // for transposing - these are set and tested accordinglystatic int8_t lastTranspVal;static uint8_t lastInsMode, lastTranspMode;static uint32_t transpDelNotes; // count of under-/overflowing notes for warning messagestatic note_t clearNote; static note_t blkCopyBuff[MAX_PATT_LEN * MAX_CHANNELS];static note_t ptnCopyBuff[MAX_PATT_LEN * MAX_CHANNELS];static note_t trackCopyBuff[MAX_PATT_LEN]; static const int8_t tickArr[16] = { 16, 8, 0, 4, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1 }; void recordNote(uint8_t note, int8_t vol); // when the cursor is at the note slotstatic bool testNoteKeys(SDL_Scancode scancode){ const int8_t noteNum = scancodeKeyToNote(scancode); if (noteNum == NOTE_OFF) { // inserts "note off" if editing song if (playMode == PLAYMODE_EDIT || playMode == PLAYMODE_RECPATT || playMode == PLAYMODE_RECSONG) { if (!allocatePattern(editor.editPattern)) return true; // key pressed pattern[editor.editPattern][(editor.row * MAX_CHANNELS) + cursor.ch].note = NOTE_OFF; const uint16_t numRows = patternNumRows[editor.editPattern]; if (playMode == PLAYMODE_EDIT && numRows >= 1) setPos(-1, (editor.row + editor.editRowSkip) % numRows, true); ui.updatePatternEditor = true; setSongModifiedFlag(); } return true; // key pressed } if (noteNum > 0 && noteNum <= 96) { recordNote(noteNum, -1); return true; // note key pressed (and note triggered) } return false; // no note key pressed} // when the cursor is at the note slotvoid testNoteKeysRelease(SDL_Scancode scancode){ const int8_t noteNum = scancodeKeyToNote(scancode); // convert key scancode to note number if (noteNum > 0 && noteNum <= 96) recordNote(noteNum, 0); // release note} static bool testEditKeys(SDL_Scancode scancode, SDL_Keycode keycode){ int8_t i; if (cursor.object == CURSOR_NOTE) { // the edit cursor is at the note slot if (testNoteKeys(scancode)) { keyb.keyRepeat = (playMode == PLAYMODE_EDIT); // repeat keys only if in edit mode return true; // we jammed an instrument } return false; // no note key pressed, test other keys } if (playMode != PLAYMODE_EDIT && playMode != PLAYMODE_RECSONG && playMode != PLAYMODE_RECPATT) return false; // we're not editing, test other keys // convert key to slot data if (cursor.object == CURSOR_VOL1) { // volume column effect type (mixed keys) for (i = 0; i < KEY2VOL_ENTRIES; i++) { if (keycode == key2VolTab[i]) break; } if (i == KEY2VOL_ENTRIES) { // volume column key not found, let's try a hack for '-' and '+' keys first if (scancode == SDL_SCANCODE_MINUS) i = 5; else if (scancode == SDL_SCANCODE_EQUALS) i = 6; else i = -1; // invalid key for slot } } else if (cursor.object == CURSOR_EFX0) { // effect type (mixed keys) for (i = 0; i < KEY2EFX_ENTRIES; i++) { if (keycode == key2EfxTab[i]) break; } if (i == KEY2EFX_ENTRIES) i = -1; // invalid key for slot } else { // all other slots (hex keys) for (i = 0; i < KEY2HEX_ENTRIES; i++) { if (keycode == key2HexTab[i]) break; } if (i == KEY2HEX_ENTRIES) i = -1; // invalid key for slot } if (i == -1 || !allocatePattern(editor.editPattern)) return false; // no edit to be done // insert slot data note_t *p = &pattern[editor.editPattern][(editor.row * MAX_CHANNELS) + cursor.ch]; switch (cursor.object) { case CURSOR_INST1: { uint8_t oldVal = p->instr; p->instr = (p->instr & 0x0F) | (i << 4); if (p->instr > MAX_INST) p->instr = MAX_INST; if (p->instr != oldVal) setSongModifiedFlag(); } break; case CURSOR_INST2: { uint8_t oldVal = p->instr; p->instr = (p->instr & 0xF0) | i; if (p->instr != oldVal) setSongModifiedFlag(); } break; case CURSOR_VOL1: { uint8_t oldVal = p->vol; p->vol = (p->vol & 0x0F) | ((i + 1) << 4); if (p->vol >= 0x51 && p->vol <= 0x5F) p->vol = 0x50; if (p->vol != oldVal) setSongModifiedFlag(); } break; case CURSOR_VOL2: { uint8_t oldVal = p->vol; if (p->vol < 0x10) p->vol = 0x10 + i; else p->vol = (p->vol & 0xF0) | i; if (p->vol >= 0x51 && p->vol <= 0x5F) p->vol = 0x50; if (p->vol != oldVal) setSongModifiedFlag(); } break; case CURSOR_EFX0: { uint8_t oldVal = p->efx; p->efx = i; if (p->efx != oldVal) setSongModifiedFlag(); } break; case CURSOR_EFX1: { uint8_t oldVal = p->efxData; p->efxData = (p->efxData & 0x0F) | (i << 4); if (p->efxData != oldVal) setSongModifiedFlag(); } break; case CURSOR_EFX2: { uint8_t oldVal = p->efxData; p->efxData = (p->efxData & 0xF0) | i; if (p->efxData != oldVal) setSongModifiedFlag(); } break; default: break; } // increase row (only in edit mode) const int16_t numRows = patternNumRows[editor.editPattern]; if (playMode == PLAYMODE_EDIT && numRows >= 1) setPos(-1, (editor.row + editor.editRowSkip) % numRows, true); if (i == 0) // if we inserted a zero, check if pattern is empty, for killing killPatternIfUnused(editor.editPattern); ui.updatePatternEditor = true; return true;} static void evaluateTimeStamp(int16_t *songPos, int16_t *pattNum, int16_t *row, int16_t *tick){ int16_t outSongPos = editor.songPos; int16_t outPattern = editor.editPattern; int16_t outRow = editor.row; int16_t outTick = editor.speed - editor.tick; outTick = CLAMP(outTick, 0, editor.speed - 1); // this is needed, but also breaks quantization on speed>15 if (outTick > 15) outTick = 15; const int16_t numRows = patternNumRows[outPattern]; if (config.recQuant > 0) { if (config.recQuantRes >= 16) { outTick += (editor.speed >> 1) + 1; } else { int16_t r = tickArr[config.recQuantRes-1]; int16_t p = outRow & (r - 1); if (p < (r >> 1)) outRow -= p; else outRow = (outRow + r) - p; outTick = 0; } } if (outTick > editor.speed) { outTick -= editor.speed; outRow++; } if (outRow >= numRows) { outRow = 0; if (playMode == PLAYMODE_RECSONG) outSongPos++; if (outSongPos >= song.songLength) outSongPos = song.songLoopStart; outPattern = song.orders[outSongPos]; } *songPos = outSongPos; *pattNum = outPattern; *row = outRow; *tick = outTick;} void recordNote(uint8_t noteNum, int8_t vol) // directly ported from the original FT2 code - what a mess, but it works...{ int8_t i; int16_t pattNum, songPos, row, tick; int32_t time; note_t *p; const int16_t oldRow = editor.row; if (songPlaying) { // row quantization evaluateTimeStamp(&songPos, &pattNum, &row, &tick); } else { songPos = editor.songPos; pattNum = editor.editPattern; row = editor.row; tick = 0; } bool editmode = (playMode == PLAYMODE_EDIT); bool recmode = (playMode == PLAYMODE_RECSONG) || (playMode == PLAYMODE_RECPATT); if (noteNum == NOTE_OFF) vol = 0; int8_t c = -1; int8_t k = -1; if (editmode || recmode) { // find out what channel is the most suitable in edit/record mode if ((config.multiEdit && editmode) || (config.multiRec && recmode)) { time = 0x7FFFFFFF; for (i = 0; i < song.numChannels; i++) { if (editor.chnMode[i] && config.multiRecChn[i] && editor.keyOffTime[i] < time && editor.keyOnTab[i] == 0) { c = i; time = editor.keyOffTime[i]; } } } else { c = cursor.ch; } for (i = 0; i < song.numChannels; i++) { if (noteNum == editor.keyOnTab[i] && config.multiRecChn[i]) k = i; } } else { // find out what channel is the most suitable in idle/play mode (jamming) if (config.multiKeyJazz) { time = 0x7FFFFFFF; c = 0; if (songPlaying) { for (i = 0; i < song.numChannels; i++) { if (editor.keyOffTime[i] < time && editor.keyOnTab[i] == 0 && config.multiRecChn[i]) { c = i; time = editor.keyOffTime[i]; } } } if (time == 0x7FFFFFFF) { for (i = 0; i < song.numChannels; i++) { if (editor.keyOffTime[i] < time && editor.keyOnTab[i] == 0) { c = i; time = editor.keyOffTime[i]; } } } } else { c = cursor.ch; } for (i = 0; i < song.numChannels; i++) { if (noteNum == editor.keyOnTab[i]) k = i; } } if (vol != 0) { if (c < 0 || (k >= 0 && (config.multiEdit || (recmode || !editmode)))) return; // play note editor.keyOnTab[c] = noteNum; if (row >= oldRow) // non-FT2 fix: only do this if we didn't quantize to next row {#ifdef HAS_MIDI playTone(c, editor.curInstr, noteNum, vol, midi.currMIDIVibDepth, midi.currMIDIPitch);#else playTone(c, editor.curInstr, noteNum, vol, 0, 0);#endif } if (editmode || recmode) { if (allocatePattern(pattNum)) { const int16_t numRows = patternNumRows[pattNum]; p = &pattern[pattNum][(row * MAX_CHANNELS) + c]; // insert data p->note = noteNum; if (editor.curInstr > 0) p->instr = editor.curInstr; if (vol >= 0) p->vol = 0x10 + vol; if (!recmode) { // increase row (only in edit mode) if (numRows >= 1) setPos(-1, (editor.row + editor.editRowSkip) % numRows, true); } else { // apply tick delay for note if quantization is disabled if (!config.recQuant && tick > 0) { p->efx = 0x0E; p->efxData = 0xD0 + (tick & 0x0F); } } ui.updatePatternEditor = true; setSongModifiedFlag(); } } } else { // note off if (k != -1) c = k; if (c < 0) return; editor.keyOffNr++; editor.keyOnTab[c] = 0; editor.keyOffTime[c] = editor.keyOffNr; if (row >= oldRow) // non-FT2 fix: only do this if we didn't quantize to next row {#ifdef HAS_MIDI playTone(c, editor.curInstr, NOTE_OFF, vol, midi.currMIDIVibDepth, midi.currMIDIPitch);#elseLines 1–500 of 1870. Load more
// for finding memory leaks in debug mode with Visual Studio #if defined _DEBUG && defined _MSC_VER #include <crtdbg.h> #endif #include <stdio.h> #include <stdint.h> #include "ft2_header.h" #include "ft2_config.h" #include "ft2_keyboard.h" #include "ft2_audio.h" #include "ft2_midi.h" #include "ft2_pattern_ed.h" #include "ft2_sysreqs.h" #include "ft2_textboxes.h" #include "ft2_tables.h" #include "ft2_structs.h" enum { KEYTYPE_NUM = 0, KEYTYPE_ALPHA = 1 }; static double dVolScaleFK1 = 1.0, dVolScaleFK2 = 1.0; // for block cut/copy/paste static bool blockCopied; static int16_t markXSize, markYSize; static uint16_t ptnBufLen, trkBufLen; // for transposing - these are set and tested accordingly static int8_t lastTranspVal; static uint8_t lastInsMode, lastTranspMode; static uint32_t transpDelNotes; // count of under-/overflowing notes for warning message static note_t clearNote; static note_t blkCopyBuff[MAX_PATT_LEN * MAX_CHANNELS]; static note_t ptnCopyBuff[MAX_PATT_LEN * MAX_CHANNELS]; static note_t trackCopyBuff[MAX_PATT_LEN]; static const int8_t tickArr[16] = { 16, 8, 0, 4, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1 }; void recordNote(uint8_t note, int8_t vol); // when the cursor is at the note slot static bool testNoteKeys(SDL_Scancode scancode) { const int8_t noteNum = scancodeKeyToNote(scancode); if (noteNum == NOTE_OFF) { // inserts "note off" if editing song if (playMode == PLAYMODE_EDIT || playMode == PLAYMODE_RECPATT || playMode == PLAYMODE_RECSONG) { if (!allocatePattern(editor.editPattern)) return true; // key pressed pattern[editor.editPattern][(editor.row * MAX_CHANNELS) + cursor.ch].note = NOTE_OFF; const uint16_t numRows = patternNumRows[editor.editPattern]; if (playMode == PLAYMODE_EDIT && numRows >= 1) setPos(-1, (editor.row + editor.editRowSkip) % numRows, true); ui.updatePatternEditor = true; setSongModifiedFlag(); } return true; // key pressed } if (noteNum > 0 && noteNum <= 96) { recordNote(noteNum, -1); return true; // note key pressed (and note triggered) } return false; // no note key pressed } // when the cursor is at the note slot void testNoteKeysRelease(SDL_Scancode scancode) { const int8_t noteNum = scancodeKeyToNote(scancode); // convert key scancode to note number if (noteNum > 0 && noteNum <= 96) recordNote(noteNum, 0); // release note } static bool testEditKeys(SDL_Scancode scancode, SDL_Keycode keycode) { int8_t i; if (cursor.object == CURSOR_NOTE) { // the edit cursor is at the note slot if (testNoteKeys(scancode)) { keyb.keyRepeat = (playMode == PLAYMODE_EDIT); // repeat keys only if in edit mode return true; // we jammed an instrument } return false; // no note key pressed, test other keys } if (playMode != PLAYMODE_EDIT && playMode != PLAYMODE_RECSONG && playMode != PLAYMODE_RECPATT) return false; // we're not editing, test other keys // convert key to slot data if (cursor.object == CURSOR_VOL1) { // volume column effect type (mixed keys) for (i = 0; i < KEY2VOL_ENTRIES; i++) { if (keycode == key2VolTab[i]) break; } if (i == KEY2VOL_ENTRIES) { // volume column key not found, let's try a hack for '-' and '+' keys first if (scancode == SDL_SCANCODE_MINUS) i = 5; else if (scancode == SDL_SCANCODE_EQUALS) i = 6; else i = -1; // invalid key for slot } } else if (cursor.object == CURSOR_EFX0) { // effect type (mixed keys) for (i = 0; i < KEY2EFX_ENTRIES; i++) { if (keycode == key2EfxTab[i]) break; } if (i == KEY2EFX_ENTRIES) i = -1; // invalid key for slot } else { // all other slots (hex keys) for (i = 0; i < KEY2HEX_ENTRIES; i++) { if (keycode == key2HexTab[i]) break; } if (i == KEY2HEX_ENTRIES) i = -1; // invalid key for slot } if (i == -1 || !allocatePattern(editor.editPattern)) return false; // no edit to be done // insert slot data note_t *p = &pattern[editor.editPattern][(editor.row * MAX_CHANNELS) + cursor.ch]; switch (cursor.object) { case CURSOR_INST1: { uint8_t oldVal = p->instr; p->instr = (p->instr & 0x0F) | (i << 4); if (p->instr > MAX_INST) p->instr = MAX_INST; if (p->instr != oldVal) setSongModifiedFlag(); } break; case CURSOR_INST2: { uint8_t oldVal = p->instr; p->instr = (p->instr & 0xF0) | i; if (p->instr != oldVal) setSongModifiedFlag(); } break; case CURSOR_VOL1: { uint8_t oldVal = p->vol; p->vol = (p->vol & 0x0F) | ((i + 1) << 4); if (p->vol >= 0x51 && p->vol <= 0x5F) p->vol = 0x50; if (p->vol != oldVal) setSongModifiedFlag(); } break; case CURSOR_VOL2: { uint8_t oldVal = p->vol; if (p->vol < 0x10) p->vol = 0x10 + i; else p->vol = (p->vol & 0xF0) | i; if (p->vol >= 0x51 && p->vol <= 0x5F) p->vol = 0x50; if (p->vol != oldVal) setSongModifiedFlag(); } break; case CURSOR_EFX0: { uint8_t oldVal = p->efx; p->efx = i; if (p->efx != oldVal) setSongModifiedFlag(); } break; case CURSOR_EFX1: { uint8_t oldVal = p->efxData; p->efxData = (p->efxData & 0x0F) | (i << 4); if (p->efxData != oldVal) setSongModifiedFlag(); } break; case CURSOR_EFX2: { uint8_t oldVal = p->efxData; p->efxData = (p->efxData & 0xF0) | i; if (p->efxData != oldVal) setSongModifiedFlag(); } break; default: break; } // increase row (only in edit mode) const int16_t numRows = patternNumRows[editor.editPattern]; if (playMode == PLAYMODE_EDIT && numRows >= 1) setPos(-1, (editor.row + editor.editRowSkip) % numRows, true); if (i == 0) // if we inserted a zero, check if pattern is empty, for killing killPatternIfUnused(editor.editPattern); ui.updatePatternEditor = true; return true; } static void evaluateTimeStamp(int16_t *songPos, int16_t *pattNum, int16_t *row, int16_t *tick) { int16_t outSongPos = editor.songPos; int16_t outPattern = editor.editPattern; int16_t outRow = editor.row; int16_t outTick = editor.speed - editor.tick; outTick = CLAMP(outTick, 0, editor.speed - 1); // this is needed, but also breaks quantization on speed>15 if (outTick > 15) outTick = 15; const int16_t numRows = patternNumRows[outPattern]; if (config.recQuant > 0) { if (config.recQuantRes >= 16) { outTick += (editor.speed >> 1) + 1; } else { int16_t r = tickArr[config.recQuantRes-1]; int16_t p = outRow & (r - 1); if (p < (r >> 1)) outRow -= p; else outRow = (outRow + r) - p; outTick = 0; } } if (outTick > editor.speed) { outTick -= editor.speed; outRow++; } if (outRow >= numRows) { outRow = 0; if (playMode == PLAYMODE_RECSONG) outSongPos++; if (outSongPos >= song.songLength) outSongPos = song.songLoopStart; outPattern = song.orders[outSongPos]; } *songPos = outSongPos; *pattNum = outPattern; *row = outRow; *tick = outTick; } void recordNote(uint8_t noteNum, int8_t vol) // directly ported from the original FT2 code - what a mess, but it works... { int8_t i; int16_t pattNum, songPos, row, tick; int32_t time; note_t *p; const int16_t oldRow = editor.row; if (songPlaying) { // row quantization evaluateTimeStamp(&songPos, &pattNum, &row, &tick); } else { songPos = editor.songPos; pattNum = editor.editPattern; row = editor.row; tick = 0; } bool editmode = (playMode == PLAYMODE_EDIT); bool recmode = (playMode == PLAYMODE_RECSONG) || (playMode == PLAYMODE_RECPATT); if (noteNum == NOTE_OFF) vol = 0; int8_t c = -1; int8_t k = -1; if (editmode || recmode) { // find out what channel is the most suitable in edit/record mode if ((config.multiEdit && editmode) || (config.multiRec && recmode)) { time = 0x7FFFFFFF; for (i = 0; i < song.numChannels; i++) { if (editor.chnMode[i] && config.multiRecChn[i] && editor.keyOffTime[i] < time && editor.keyOnTab[i] == 0) { c = i; time = editor.keyOffTime[i]; } } } else { c = cursor.ch; } for (i = 0; i < song.numChannels; i++) { if (noteNum == editor.keyOnTab[i] && config.multiRecChn[i]) k = i; } } else { // find out what channel is the most suitable in idle/play mode (jamming) if (config.multiKeyJazz) { time = 0x7FFFFFFF; c = 0; if (songPlaying) { for (i = 0; i < song.numChannels; i++) { if (editor.keyOffTime[i] < time && editor.keyOnTab[i] == 0 && config.multiRecChn[i]) { c = i; time = editor.keyOffTime[i]; } } } if (time == 0x7FFFFFFF) { for (i = 0; i < song.numChannels; i++) { if (editor.keyOffTime[i] < time && editor.keyOnTab[i] == 0) { c = i; time = editor.keyOffTime[i]; } } } } else { c = cursor.ch; } for (i = 0; i < song.numChannels; i++) { if (noteNum == editor.keyOnTab[i]) k = i; } } if (vol != 0) { if (c < 0 || (k >= 0 && (config.multiEdit || (recmode || !editmode)))) return; // play note editor.keyOnTab[c] = noteNum; if (row >= oldRow) // non-FT2 fix: only do this if we didn't quantize to next row { #ifdef HAS_MIDI playTone(c, editor.curInstr, noteNum, vol, midi.currMIDIVibDepth, midi.currMIDIPitch); #else playTone(c, editor.curInstr, noteNum, vol, 0, 0); #endif } if (editmode || recmode) { if (allocatePattern(pattNum)) { const int16_t numRows = patternNumRows[pattNum]; p = &pattern[pattNum][(row * MAX_CHANNELS) + c]; // insert data p->note = noteNum; if (editor.curInstr > 0) p->instr = editor.curInstr; if (vol >= 0) p->vol = 0x10 + vol; if (!recmode) { // increase row (only in edit mode) if (numRows >= 1) setPos(-1, (editor.row + editor.editRowSkip) % numRows, true); } else { // apply tick delay for note if quantization is disabled if (!config.recQuant && tick > 0) { p->efx = 0x0E; p->efxData = 0xD0 + (tick & 0x0F); } } ui.updatePatternEditor = true; setSongModifiedFlag(); } } } else { // note off if (k != -1) c = k; if (c < 0) return; editor.keyOffNr++; editor.keyOnTab[c] = 0; editor.keyOffTime[c] = editor.keyOffNr; if (row >= oldRow) // non-FT2 fix: only do this if we didn't quantize to next row { #ifdef HAS_MIDI playTone(c, editor.curInstr, NOTE_OFF, vol, midi.currMIDIVibDepth, midi.currMIDIPitch); #else