ref: a1d153b3ea809d6032483d629d0681bb4743e786
dir: /src/pt2_edit.c/
// for finding memory leaks in debug mode with Visual Studio
#if defined _DEBUG && defined _MSC_VER
#include <crtdbg.h>
#endif
#include <stdint.h>
#include <stdbool.h>
#include <ctype.h> // tolower()
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef _WIN32
#include <direct.h>
#else
#include <unistd.h>
#endif
#include "pt2_header.h"
#include "pt2_helpers.h"
#include "pt2_textout.h"
#include "pt2_tables.h"
#include "pt2_audio.h"
#include "pt2_helpers.h"
#include "pt2_palette.h"
#include "pt2_diskop.h"
#include "pt2_mouse.h"
#include "pt2_sampler.h"
#include "pt2_visuals.h"
#include "pt2_keyboard.h"
#include "pt2_scopes.h"
const int8_t scancode2NoteLo[52] = // "USB usage page standard" order
{
7, 4, 3, 16, -1, 6, 8, 24,
10, -1, 13, 11, 9, 26, 28, 12,
17, 1, 19, 23, 5, 14, 2, 21,
0, -1, 13, 15, -1, 18, 20, 22,
-1, 25, 27, -1, -1, -1, -1, -1,
-1, 30, 29, 31, 30, -1, 15, -1,
-1, 12, 14, 16
};
const int8_t scancode2NoteHi[52] = // "USB usage page standard" order
{
19, 16, 15, 28, -1, 18, 20, -2,
22, -1, 25, 23, 21, -2, -2, 24,
29, 13, 31, 35, 17, 26, 14, 33,
12, -1, 25, 27, -1, 30, 32, 34,
-1, -2, -2, -1, -1, -1, -1, -1,
-1, -2, -2, -2, -2, -1, 27, -1,
-1, 24, 26, 28
};
void setPattern(int16_t pattern); // pt_modplayer.c
void jamAndPlaceSample(SDL_Scancode scancode, bool normalMode);
uint8_t quantizeCheck(uint8_t row);
bool handleSpecialKeys(SDL_Scancode scancode);
int8_t keyToNote(SDL_Scancode scancode);
// used for re-rendering text object while editing it
void updateTextObject(int16_t editObject)
{
switch (editObject)
{
default: break;
case PTB_SONGNAME: editor.ui.updateSongName = true; break;
case PTB_SAMPLENAME: editor.ui.updateCurrSampleName = true; break;
case PTB_PE_PATT: editor.ui.updatePosEd = true; break;
case PTB_EO_QUANTIZE: editor.ui.updateQuantizeText = true; break;
case PTB_EO_METRO_1: editor.ui.updateMetro1Text = true; break;
case PTB_EO_METRO_2: editor.ui.updateMetro2Text = true; break;
case PTB_EO_FROM_NUM: editor.ui.updateFromText = true; break;
case PTB_EO_TO_NUM: editor.ui.updateToText = true; break;
case PTB_EO_MIX: editor.ui.updateMixText = true; break;
case PTB_EO_POS_NUM: editor.ui.updatePosText = true; break;
case PTB_EO_MOD_NUM: editor.ui.updateModText = true; break;
case PTB_EO_VOL_NUM: editor.ui.updateVolText = true; break;
case PTB_DO_DATAPATH: editor.ui.updateDiskOpPathText = true; break;
case PTB_POSS: editor.ui.updateSongPos = true; break;
case PTB_PATTERNS: editor.ui.updateSongPattern = true; break;
case PTB_LENGTHS: editor.ui.updateSongLength = true; break;
case PTB_SAMPLES: editor.ui.updateCurrSampleNum = true; break;
case PTB_SVOLUMES: editor.ui.updateCurrSampleVolume = true; break;
case PTB_SLENGTHS: editor.ui.updateCurrSampleLength = true; break;
case PTB_SREPEATS: editor.ui.updateCurrSampleRepeat = true; break;
case PTB_SREPLENS: editor.ui.updateCurrSampleReplen = true; break;
case PTB_PATTDATA: editor.ui.updateCurrPattText = true; break;
case PTB_SA_VOL_FROM_NUM: editor.ui.updateVolFromText = true; break;
case PTB_SA_VOL_TO_NUM: editor.ui.updateVolToText = true; break;
case PTB_SA_FIL_LP_CUTOFF: editor.ui.updateLPText = true; break;
case PTB_SA_FIL_HP_CUTOFF: editor.ui.updateHPText = true; break;
}
}
void exitGetTextLine(bool updateValue)
{
int8_t tmp8;
uint8_t i;
int16_t posEdPos, tmp16;
int32_t tmp32;
UNICHAR *pathU;
moduleSample_t *s;
SDL_StopTextInput();
// if user updated the disk op path text
if (editor.ui.diskOpScreenShown && editor.ui.editObject == PTB_DO_DATAPATH)
{
pathU = (UNICHAR *)calloc(PATH_MAX + 2, sizeof (UNICHAR));
if (pathU != NULL)
{
#ifdef _WIN32
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, editor.currPath, -1, pathU, PATH_MAX);
#else
strcpy(pathU, editor.currPath);
#endif
diskOpSetPath(pathU, DISKOP_CACHE);
free(pathU);
}
}
if (editor.ui.editTextType != TEXT_EDIT_STRING)
{
if (editor.ui.dstPos != editor.ui.numLen)
removeTextEditMarker();
updateTextObject(editor.ui.editObject);
}
else
{
removeTextEditMarker();
// yet another kludge...
if (editor.ui.editObject == PTB_PE_PATT)
editor.ui.updatePosEd = true;
}
editor.ui.editTextFlag = false;
editor.ui.lineCurX = 0;
editor.ui.lineCurY = 0;
editor.ui.editPos = NULL;
editor.ui.dstPos = 0;
if (editor.ui.editTextType == TEXT_EDIT_STRING)
{
if (editor.ui.dstOffset != NULL)
*editor.ui.dstOffset = '\0';
if (editor.ui.editObject == PTB_SONGNAME)
{
for (i = 0; i < 20; i++)
modEntry->head.moduleTitle[i] = (char)tolower(modEntry->head.moduleTitle[i]);
}
pointerSetPreviousMode();
if (!editor.mixFlag)
updateWindowTitle(MOD_IS_MODIFIED);
}
else
{
// set back GUI text pointers and update values (if requested)
s = &modEntry->samples[editor.currSample];
switch (editor.ui.editObject)
{
case PTB_SA_FIL_LP_CUTOFF:
{
editor.lpCutOffDisp = &editor.lpCutOff;
if (updateValue)
{
editor.lpCutOff = editor.ui.tmpDisp16;
if (editor.lpCutOff > (uint16_t)(FILTERS_BASE_FREQ/2))
editor.lpCutOff = (uint16_t)(FILTERS_BASE_FREQ/2);
editor.ui.updateLPText = true;
}
}
break;
case PTB_SA_FIL_HP_CUTOFF:
{
editor.hpCutOffDisp = &editor.hpCutOff;
if (updateValue)
{
editor.hpCutOff = editor.ui.tmpDisp16;
if (editor.hpCutOff > (uint16_t)(FILTERS_BASE_FREQ/2))
editor.hpCutOff = (uint16_t)(FILTERS_BASE_FREQ/2);
editor.ui.updateHPText = true;
}
}
break;
case PTB_SA_VOL_FROM_NUM:
{
editor.vol1Disp = &editor.vol1;
if (updateValue)
{
editor.vol1 = editor.ui.tmpDisp16;
if (editor.vol1 > 200)
editor.vol1 = 200;
editor.ui.updateVolFromText = true;
showVolFromSlider();
}
}
break;
case PTB_SA_VOL_TO_NUM:
{
editor.vol2Disp = &editor.vol2;
if (updateValue)
{
editor.vol2 = editor.ui.tmpDisp16;
if (editor.vol2 > 200)
editor.vol2 = 200;
editor.ui.updateVolToText = true;
showVolToSlider();
}
}
break;
case PTB_EO_VOL_NUM:
{
editor.sampleVolDisp = &editor.sampleVol;
if (updateValue)
{
editor.sampleVol = editor.ui.tmpDisp16;
editor.ui.updateVolText = true;
}
}
break;
case PTB_EO_POS_NUM:
{
editor.samplePosDisp = &editor.samplePos;
if (updateValue)
{
editor.samplePos = editor.ui.tmpDisp16;
if (editor.samplePos > modEntry->samples[editor.currSample].length)
editor.samplePos = modEntry->samples[editor.currSample].length;
editor.ui.updatePosText = true;
}
}
break;
case PTB_EO_QUANTIZE:
{
editor.quantizeValueDisp = &ptConfig.quantizeValue;
if (updateValue)
{
if (editor.ui.tmpDisp16 > 63)
editor.ui.tmpDisp16 = 63;
ptConfig.quantizeValue = editor.ui.tmpDisp16;
editor.ui.updateQuantizeText = true;
}
}
break;
case PTB_EO_METRO_1: // metronome speed
{
editor.metroSpeedDisp = &editor.metroSpeed;
if (updateValue)
{
if (editor.ui.tmpDisp16 > 64)
editor.ui.tmpDisp16 = 64;
editor.metroSpeed = editor.ui.tmpDisp16;
editor.ui.updateMetro1Text = true;
}
}
break;
case PTB_EO_METRO_2: // metronome channel
{
editor.metroChannelDisp = &editor.metroChannel;
if (updateValue)
{
if (editor.ui.tmpDisp16 > 4)
editor.ui.tmpDisp16 = 4;
editor.metroChannel = editor.ui.tmpDisp16;
editor.ui.updateMetro2Text = true;
}
}
break;
case PTB_EO_FROM_NUM:
{
editor.sampleFromDisp = &editor.sampleFrom;
if (updateValue)
{
editor.sampleFrom = editor.ui.tmpDisp8;
// signed check + normal check
if (editor.sampleFrom < 0x00 || editor.sampleFrom > 0x1F)
editor.sampleFrom = 0x1F;
editor.ui.updateFromText = true;
}
}
break;
case PTB_EO_TO_NUM:
{
editor.sampleToDisp = &editor.sampleTo;
if (updateValue)
{
editor.sampleTo = editor.ui.tmpDisp8;
// signed check + normal check
if (editor.sampleTo < 0x00 || editor.sampleTo > 0x1F)
editor.sampleTo = 0x1F;
editor.ui.updateToText = true;
}
}
break;
case PTB_PE_PATT:
{
posEdPos = modEntry->currOrder;
if (posEdPos > modEntry->head.orderCount-1)
posEdPos = modEntry->head.orderCount-1;
editor.currPosEdPattDisp = &modEntry->head.order[posEdPos];
if (updateValue)
{
if (editor.ui.tmpDisp16 > MAX_PATTERNS-1)
editor.ui.tmpDisp16 = MAX_PATTERNS-1;
modEntry->head.order[posEdPos] = editor.ui.tmpDisp16;
updateWindowTitle(MOD_IS_MODIFIED);
if (editor.ui.posEdScreenShown)
editor.ui.updatePosEd = true;
editor.ui.updateSongPattern = true;
editor.ui.updateSongSize = true;
}
}
break;
case PTB_POSS:
{
editor.currPosDisp = &modEntry->currOrder;
if (updateValue)
{
tmp16 = editor.ui.tmpDisp16;
if (tmp16 > 126)
tmp16 = 126;
if (modEntry->currOrder != tmp16)
{
modEntry->currOrder = tmp16;
editor.currPatternDisp = &modEntry->head.order[modEntry->currOrder];
if (editor.ui.posEdScreenShown)
editor.ui.updatePosEd = true;
editor.ui.updateSongPos = true;
editor.ui.updatePatternData = true;
}
}
}
break;
case PTB_PATTERNS:
{
editor.currPatternDisp = &modEntry->head.order[modEntry->currOrder];
if (updateValue)
{
tmp16 = editor.ui.tmpDisp16;
if (tmp16 > MAX_PATTERNS-1)
tmp16 = MAX_PATTERNS-1;
if (modEntry->head.order[modEntry->currOrder] != tmp16)
{
modEntry->head.order[modEntry->currOrder] = tmp16;
updateWindowTitle(MOD_IS_MODIFIED);
if (editor.ui.posEdScreenShown)
editor.ui.updatePosEd = true;
editor.ui.updateSongPattern = true;
editor.ui.updateSongSize = true;
}
}
}
break;
case PTB_LENGTHS:
{
editor.currLengthDisp = &modEntry->head.orderCount;
if (updateValue)
{
tmp16 = CLAMP(editor.ui.tmpDisp16, 1, 127);
if (modEntry->head.orderCount != tmp16)
{
modEntry->head.orderCount = tmp16;
posEdPos = modEntry->currOrder;
if (posEdPos > modEntry->head.orderCount-1)
posEdPos = modEntry->head.orderCount-1;
editor.currPosEdPattDisp = &modEntry->head.order[posEdPos];
if (editor.ui.posEdScreenShown)
editor.ui.updatePosEd = true;
editor.ui.updateSongLength = true;
editor.ui.updateSongSize = true;
updateWindowTitle(MOD_IS_MODIFIED);
}
}
}
break;
case PTB_PATTDATA:
{
editor.currEditPatternDisp = &modEntry->currPattern;
if (updateValue)
{
if (modEntry->currPattern != editor.ui.tmpDisp16)
{
setPattern(editor.ui.tmpDisp16);
editor.ui.updatePatternData = true;
editor.ui.updateCurrPattText = true;
}
}
}
break;
case PTB_SAMPLES:
{
editor.currSampleDisp = &editor.currSample;
if (updateValue)
{
tmp8 = editor.ui.tmpDisp8;
if (tmp8 < 0x00) // (signed) if >0x7F was entered, clamp to 0x1F
tmp8 = 0x1F;
tmp8 = CLAMP(tmp8, 0x01, 0x1F) - 1;
if (tmp8 != editor.currSample)
{
editor.currSample = tmp8;
updateCurrSample();
}
}
}
break;
case PTB_SVOLUMES:
{
s->volumeDisp = &s->volume;
if (updateValue)
{
tmp8 = editor.ui.tmpDisp8;
// signed check + normal check
if (tmp8 < 0x00 || tmp8 > 0x40)
tmp8 = 0x40;
if (s->volume != tmp8)
{
s->volume = tmp8;
editor.ui.updateCurrSampleVolume = true;
updateWindowTitle(MOD_IS_MODIFIED);
}
}
}
break;
case PTB_SLENGTHS:
{
s->lengthDisp = &s->length;
if (updateValue)
{
tmp32 = editor.ui.tmpDisp16 & 0xFFFE; // even'ify
if (s->loopStart+s->loopLength > 2)
{
if (tmp32 < s->loopStart+s->loopLength)
tmp32 = s->loopStart+s->loopLength;
}
tmp32 &= 0xFFFE;
if (s->length != tmp32)
{
turnOffVoices();
s->length = tmp32;
editor.ui.updateCurrSampleLength = true;
editor.ui.updateSongSize = true;
updateSamplePos();
if (editor.ui.samplerScreenShown)
redrawSample();
recalcChordLength();
updateWindowTitle(MOD_IS_MODIFIED);
}
}
}
break;
case PTB_SREPEATS:
{
s->loopStartDisp = &s->loopStart;
if (updateValue)
{
tmp32 = editor.ui.tmpDisp16 & 0xFFFE; // even'ify
if (s->length >= s->loopLength)
{
if (tmp32+s->loopLength > s->length)
tmp32 = s->length - s->loopLength;
}
else
{
tmp32 = 0;
}
tmp32 &= 0xFFFE;
if (s->loopStart != tmp32)
{
turnOffVoices();
s->loopStart = tmp32;
mixerUpdateLoops();
editor.ui.updateCurrSampleRepeat = true;
if (editor.ui.editOpScreenShown && editor.ui.editOpScreen == 3)
editor.ui.updateLengthText = true;
if (editor.ui.samplerScreenShown)
setLoopSprites();
updateWindowTitle(MOD_IS_MODIFIED);
}
}
}
break;
case PTB_SREPLENS:
{
s->loopLengthDisp = &s->loopLength;
if (updateValue)
{
tmp32 = editor.ui.tmpDisp16 & 0xFFFE; // even'ify
if (s->length >= s->loopStart)
{
if (s->loopStart+tmp32 > s->length)
tmp32 = s->length - s->loopStart;
}
else
{
tmp32 = 2;
}
tmp32 &= 0xFFFE;
if (tmp32 < 2)
tmp32 = 2;
if (s->loopLength != tmp32)
{
turnOffVoices();
s->loopLength = tmp32;
mixerUpdateLoops();
editor.ui.updateCurrSampleReplen = true;
if (editor.ui.editOpScreenShown && editor.ui.editOpScreen == 3)
editor.ui.updateLengthText = true;
if (editor.ui.samplerScreenShown)
setLoopSprites();
updateWindowTitle(MOD_IS_MODIFIED);
}
}
}
break;
default: break;
}
pointerSetPreviousMode();
}
editor.ui.editTextType = 0;
}
void getTextLine(int16_t editObject)
{
pointerSetMode(POINTER_MODE_MSG1, NO_CARRY);
editor.ui.lineCurY = (editor.ui.editTextPos / 40) + 5;
editor.ui.lineCurX = ((editor.ui.editTextPos % 40) * FONT_CHAR_W) + 4;
editor.ui.dstPtr = editor.ui.showTextPtr;
editor.ui.editPos = editor.ui.showTextPtr;
editor.ui.dstPos = 0;
editor.ui.editTextFlag = true;
editor.ui.editTextType = TEXT_EDIT_STRING;
editor.ui.editObject = editObject;
if (editor.ui.dstOffset != NULL)
*editor.ui.dstOffset = '\0';
// kludge
if (editor.mixFlag)
{
textCharNext();
textCharNext();
textCharNext();
textCharNext();
}
renderTextEditMarker();
SDL_StartTextInput();
}
void getNumLine(uint8_t type, int16_t editObject)
{
pointerSetMode(POINTER_MODE_MSG1, NO_CARRY);
editor.ui.lineCurY = (editor.ui.editTextPos / 40) + 5;
editor.ui.lineCurX = ((editor.ui.editTextPos % 40) * FONT_CHAR_W) + 4;
editor.ui.dstPos = 0;
editor.ui.editTextFlag = true;
editor.ui.editTextType = type;
editor.ui.editObject = editObject;
renderTextEditMarker();
SDL_StartTextInput();
}
void handleEditKeys(SDL_Scancode scancode, bool normalMode)
{
int8_t key, hexKey, numberKey;
note_t *note;
if (editor.ui.editTextFlag)
return;
if (editor.ui.samplerScreenShown || (editor.currMode == MODE_IDLE || editor.currMode == MODE_PLAY))
{
// at this point it will only jam, not place it
if (!input.keyb.leftAltPressed && !input.keyb.leftAmigaPressed && !input.keyb.leftCtrlPressed && !input.keyb.shiftPressed)
jamAndPlaceSample(scancode, normalMode);
return;
}
// handle modified (ALT/CTRL/SHIFT etc) keys for editing
if (editor.currMode == MODE_EDIT || editor.currMode == MODE_RECORD)
{
if (handleSpecialKeys(scancode))
{
if (editor.currMode != MODE_RECORD)
modSetPos(DONT_SET_ORDER, (modEntry->currRow + editor.editMoveAdd) & 0x3F);
return;
}
}
// are we editing a note, or other stuff?
if (editor.cursor.mode != CURSOR_NOTE)
{
// if we held down any key modifier at this point, then do nothing
if (input.keyb.leftAltPressed || input.keyb.leftAmigaPressed || input.keyb.leftCtrlPressed || input.keyb.shiftPressed)
return;
if (editor.currMode == MODE_EDIT || editor.currMode == MODE_RECORD)
{
if (scancode == SDL_SCANCODE_0)
numberKey = 0;
else if (scancode >= SDL_SCANCODE_1 && scancode <= SDL_SCANCODE_9)
numberKey = (int8_t)scancode - (SDL_SCANCODE_1-1);
else
numberKey = -1;
if (scancode >= SDL_SCANCODE_A && scancode <= SDL_SCANCODE_F)
hexKey = 10 + ((int8_t)scancode - SDL_SCANCODE_A);
else
hexKey = -1;
key = -1;
if (numberKey != -1)
{
if (key == -1)
key = 0;
key += numberKey;
}
if (hexKey != -1)
{
if (key == -1)
key = 0;
key += hexKey;
}
note = &modEntry->patterns[modEntry->currPattern][(modEntry->currRow * AMIGA_VOICES) + editor.cursor.channel];
switch (editor.cursor.mode)
{
case CURSOR_SAMPLE1:
{
if (key != -1 && key < 2)
{
note->sample = (uint8_t)((note->sample % 0x10) | (key << 4));
if (editor.currMode != MODE_RECORD)
modSetPos(DONT_SET_ORDER, (modEntry->currRow + editor.editMoveAdd) & 0x3F);
updateWindowTitle(MOD_IS_MODIFIED);
}
}
break;
case CURSOR_SAMPLE2:
{
if (key != -1 && key < 16)
{
note->sample = (uint8_t)((note->sample & 16) | key);
if (editor.currMode != MODE_RECORD)
modSetPos(DONT_SET_ORDER, (modEntry->currRow + editor.editMoveAdd) & 0x3F);
updateWindowTitle(MOD_IS_MODIFIED);
}
}
break;
case CURSOR_CMD:
{
if (key != -1 && key < 16)
{
note->command = (uint8_t)key;
if (editor.currMode != MODE_RECORD)
modSetPos(DONT_SET_ORDER, (modEntry->currRow + editor.editMoveAdd) & 0x3F);
updateWindowTitle(MOD_IS_MODIFIED);
}
}
break;
case CURSOR_PARAM1:
{
if (key != -1 && key < 16)
{
note->param = (uint8_t)((note->param % 0x10) | (key << 4));
if (editor.currMode != MODE_RECORD)
modSetPos(DONT_SET_ORDER, (modEntry->currRow + editor.editMoveAdd) & 0x3F);
updateWindowTitle(MOD_IS_MODIFIED);
}
}
break;
case CURSOR_PARAM2:
{
if (key != -1 && key < 16)
{
note->param = (uint8_t)((note->param & 0xF0) | key);
if (editor.currMode != MODE_RECORD)
modSetPos(DONT_SET_ORDER, (modEntry->currRow + editor.editMoveAdd) & 0x3F);
updateWindowTitle(MOD_IS_MODIFIED);
}
}
break;
default: break;
}
}
}
else
{
if (scancode == SDL_SCANCODE_DELETE)
{
if (editor.currMode == MODE_EDIT || editor.currMode == MODE_RECORD)
{
note = &modEntry->patterns[modEntry->currPattern][(modEntry->currRow * AMIGA_VOICES) + editor.cursor.channel];
if (!input.keyb.leftAltPressed)
{
note->sample = 0;
note->period = 0;
}
if (input.keyb.shiftPressed || input.keyb.leftAltPressed)
{
note->command = 0;
note->param = 0;
}
if (editor.currMode != MODE_RECORD)
modSetPos(DONT_SET_ORDER, (modEntry->currRow + editor.editMoveAdd) & 0x3F);
updateWindowTitle(MOD_IS_MODIFIED);
}
}
else
{
// if we held down any key modifier at this point, then do nothing
if (input.keyb.leftAltPressed || input.keyb.leftAmigaPressed || input.keyb.leftCtrlPressed || input.keyb.shiftPressed)
return;
jamAndPlaceSample(scancode, normalMode);
}
}
}
bool handleSpecialKeys(SDL_Scancode scancode)
{
note_t *patt, *note, *prevNote;
if (!input.keyb.leftAltPressed)
return false;
patt = modEntry->patterns[modEntry->currPattern];
note = &patt[(modEntry->currRow * AMIGA_VOICES) + editor.cursor.channel];
prevNote = &patt[(((modEntry->currRow - 1) & 0x3F) * AMIGA_VOICES) + editor.cursor.channel];
if (scancode >= SDL_SCANCODE_1 && scancode <= SDL_SCANCODE_0)
{
// insert stored effect (buffer[0..8])
note->command = editor.effectMacros[scancode - SDL_SCANCODE_1] >> 8;
note->param = editor.effectMacros[scancode - SDL_SCANCODE_1] & 0xFF;
updateWindowTitle(MOD_IS_MODIFIED);
return true;
}
// copy command+effect from above into current command+effect
if (scancode == SDL_SCANCODE_BACKSLASH)
{
note->command = prevNote->command;
note->param = prevNote->param;
updateWindowTitle(MOD_IS_MODIFIED);
return true;
}
// copy command+(effect + 1) from above into current command+effect
if (scancode == SDL_SCANCODE_EQUALS)
{
note->command = prevNote->command;
note->param = prevNote->param + 1; // wraps 0x00..0xFF
updateWindowTitle(MOD_IS_MODIFIED);
return true;
}
// copy command+(effect - 1) from above into current command+effect
if (scancode == SDL_SCANCODE_MINUS)
{
note->command = prevNote->command;
note->param = prevNote->param - 1; // wraps 0x00..0xFF
updateWindowTitle(MOD_IS_MODIFIED);
return true;
}
return false;
}
void jamAndPlaceSample(SDL_Scancode scancode, bool normalMode)
{
int8_t noteVal;
uint8_t ch;
int16_t tempPeriod;
uint16_t cleanPeriod;
moduleChannel_t *chn;
moduleSample_t *s;
note_t *note;
ch = editor.cursor.channel;
assert(ch < AMIGA_VOICES);
chn = &modEntry->channels[ch];
note = &modEntry->patterns[modEntry->currPattern][(quantizeCheck(modEntry->currRow) * AMIGA_VOICES) + ch];
noteVal = normalMode ? keyToNote(scancode) : pNoteTable[editor.currSample];
if (noteVal >= 0)
{
s = &modEntry->samples[editor.currSample];
tempPeriod = periodTable[((s->fineTune & 0xF) * 37) + noteVal];
cleanPeriod = periodTable[noteVal];
editor.currPlayNote = noteVal;
// play current sample
// don't play sample if we quantized to another row (will be played in modplayer instead)
if (editor.currMode != MODE_RECORD || !editor.didQuantize)
{
chn->n_samplenum = editor.currSample;
chn->n_volume = s->volume;
chn->n_period = tempPeriod;
chn->n_start = &modEntry->sampleData[s->offset];
chn->n_length = (s->loopStart > 0) ? (uint32_t)(s->loopStart + s->loopLength) / 2 : s->length / 2;
chn->n_loopstart = &modEntry->sampleData[s->offset + s->loopStart];
chn->n_replen = s->loopLength / 2;
if (chn->n_length == 0)
chn->n_length = 1;
paulaSetVolume(ch, chn->n_volume);
paulaSetPeriod(ch, chn->n_period);
paulaSetData(ch, chn->n_start);
paulaSetLength(ch, chn->n_length);
if (!editor.muted[ch])
paulaStartDMA(ch);
else
paulaStopDMA(ch);
// these take effect after the current DMA cycle is done
paulaSetData(ch, chn->n_loopstart);
paulaSetLength(ch, chn->n_replen);
}
// normalMode = normal keys, or else keypad keys (in jam mode)
if (normalMode || editor.pNoteFlag != 0)
{
if (normalMode || editor.pNoteFlag == 2)
{
// insert note and sample number
if (!editor.ui.samplerScreenShown && (editor.currMode == MODE_EDIT || editor.currMode == MODE_RECORD))
{
note->sample = editor.sampleZero ? 0 : (editor.currSample + 1);
note->period = cleanPeriod;
if (editor.autoInsFlag)
{
note->command = editor.effectMacros[editor.autoInsSlot] >> 8;
note->param = editor.effectMacros[editor.autoInsSlot] & 0xFF;
}
if (editor.currMode != MODE_RECORD)
modSetPos(DONT_SET_ORDER, (modEntry->currRow + editor.editMoveAdd) & 0x3F);
updateWindowTitle(MOD_IS_MODIFIED);
}
}
if (editor.multiFlag)
gotoNextMulti();
}
updateSpectrumAnalyzer(s->volume, tempPeriod);
}
else if (noteVal == -2)
{
// delete note and sample if illegal note (= -2, -1 = ignore) key was entered
if (normalMode || editor.pNoteFlag == 2)
{
if (!editor.ui.samplerScreenShown && (editor.currMode == MODE_EDIT || editor.currMode == MODE_RECORD))
{
note->period = 0;
note->sample = 0;
if (editor.currMode != MODE_RECORD)
modSetPos(DONT_SET_ORDER, (modEntry->currRow + editor.editMoveAdd) & 0x3F);
updateWindowTitle(MOD_IS_MODIFIED);
}
}
}
}
uint8_t quantizeCheck(uint8_t row)
{
uint8_t tempRow, quantize;
quantize = (uint8_t)ptConfig.quantizeValue;
editor.didQuantize = false;
if (editor.currMode == MODE_RECORD)
{
if (quantize == 0)
{
return row;
}
else if (quantize == 1)
{
if (editor.modTick > editor.modSpeed/2)
{
row = (row + 1) & 0x3F;
editor.didQuantize = true;
}
}
else
{
tempRow = ((((quantize / 2) + row) & 0x3F) / quantize) * quantize;
if (tempRow > row)
editor.didQuantize = true;
return tempRow;
}
}
return row;
}
void saveUndo(void)
{
memcpy(editor.undoBuffer, modEntry->patterns[modEntry->currPattern], sizeof (note_t) * (AMIGA_VOICES * MOD_ROWS));
}
void undoLastChange(void)
{
note_t data;
for (uint16_t i = 0; i < MOD_ROWS*AMIGA_VOICES; i++)
{
data = editor.undoBuffer[i];
editor.undoBuffer[i] = modEntry->patterns[modEntry->currPattern][i];
modEntry->patterns[modEntry->currPattern][i] = data;
}
updateWindowTitle(MOD_IS_MODIFIED);
editor.ui.updatePatternData = true;
}
void copySampleTrack(void)
{
uint8_t i;
uint32_t tmpOffset;
note_t *noteSrc;
moduleSample_t *smpFrom, *smpTo;
if (editor.trackPattFlag == 2)
{
// copy from one sample slot to another
// never attempt to swap if from and/or to is 0
if (editor.sampleFrom == 0 || editor.sampleTo == 0)
{
displayErrorMsg("FROM/TO = 0 !");
return;
}
smpTo = &modEntry->samples[editor.sampleTo - 1];
smpFrom = &modEntry->samples[editor.sampleFrom - 1];
turnOffVoices();
// copy
tmpOffset = smpTo->offset;
*smpTo = *smpFrom;
smpTo->offset = tmpOffset;
// update the copied sample's GUI text pointers
smpTo->volumeDisp = &smpTo->volume;
smpTo->lengthDisp = &smpTo->length;
smpTo->loopStartDisp = &smpTo->loopStart;
smpTo->loopLengthDisp = &smpTo->loopLength;
// copy sample data
memcpy(&modEntry->sampleData[smpTo->offset], &modEntry->sampleData[smpFrom->offset], MAX_SAMPLE_LEN);
updateCurrSample();
editor.ui.updateSongSize = true;
}
else
{
// copy sample number in track/pattern
if (editor.trackPattFlag == 0)
{
for (i = 0; i < MOD_ROWS; i++)
{
noteSrc = &modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + editor.cursor.channel];
if (noteSrc->sample == editor.sampleFrom)
noteSrc->sample = editor.sampleTo;
}
}
else
{
for (i = 0; i < AMIGA_VOICES; i++)
{
for (uint8_t j = 0; j < MOD_ROWS; j++)
{
noteSrc = &modEntry->patterns[modEntry->currPattern][(j * AMIGA_VOICES) + i];
if (noteSrc->sample == editor.sampleFrom)
noteSrc->sample = editor.sampleTo;
}
}
}
editor.ui.updatePatternData = true;
}
editor.samplePos = 0;
updateSamplePos();
updateWindowTitle(MOD_IS_MODIFIED);
}
void exchSampleTrack(void)
{
int8_t smp;
uint32_t i, tmpOffset;
moduleSample_t *smpFrom, *smpTo, smpTmp;
note_t *noteSrc;
if (editor.trackPattFlag == 2)
{
// exchange sample slots
// never attempt to swap if from and/or to is 0
if (editor.sampleFrom == 0 || editor.sampleTo == 0)
{
displayErrorMsg("FROM/TO = 0 !");
return;
}
smpTo = &modEntry->samples[editor.sampleTo-1];
smpFrom = &modEntry->samples[editor.sampleFrom-1];
turnOffVoices();
// swap offsets first so that the next swap will leave offsets intact
tmpOffset = smpFrom->offset;
smpFrom->offset = smpTo->offset;
smpTo->offset = tmpOffset;
// swap sample (now offsets are left as before)
smpTmp = *smpFrom;
*smpFrom = *smpTo;
*smpTo = smpTmp;
// update the swapped sample's GUI text pointers
smpFrom->volumeDisp = &smpFrom->volume;
smpFrom->lengthDisp = &smpFrom->length;
smpFrom->loopStartDisp = &smpFrom->loopStart;
smpFrom->loopLengthDisp = &smpFrom->loopLength;
smpTo->volumeDisp = &smpTo->volume;
smpTo->lengthDisp = &smpTo->length;
smpTo->loopStartDisp = &smpTo->loopStart;
smpTo->loopLengthDisp = &smpTo->loopLength;
// swap sample data
for (i = 0; i < MAX_SAMPLE_LEN; i++)
{
smp = modEntry->sampleData[smpFrom->offset+i];
modEntry->sampleData[smpFrom->offset+i] = modEntry->sampleData[smpTo->offset+i];
modEntry->sampleData[smpTo->offset+i] = smp;
}
editor.sampleZero = false;
updateCurrSample();
}
else
{
// exchange sample number in track/pattern
if (editor.trackPattFlag == 0)
{
for (i = 0; i < MOD_ROWS; i++)
{
noteSrc = &modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + editor.cursor.channel];
if (noteSrc->sample == editor.sampleFrom) noteSrc->sample = editor.sampleTo;
else if (noteSrc->sample == editor.sampleTo) noteSrc->sample = editor.sampleFrom;
}
}
else
{
for (i = 0; i < AMIGA_VOICES; i++)
{
for (uint8_t j = 0; j < MOD_ROWS; j++)
{
noteSrc = &modEntry->patterns[modEntry->currPattern][(j * AMIGA_VOICES) + i];
if (noteSrc->sample == editor.sampleFrom) noteSrc->sample = editor.sampleTo;
else if (noteSrc->sample == editor.sampleTo) noteSrc->sample = editor.sampleFrom;
}
}
}
editor.ui.updatePatternData = true;
}
editor.samplePos = 0;
updateSamplePos();
updateWindowTitle(MOD_IS_MODIFIED);
}
void delSampleTrack(void)
{
uint8_t i;
note_t *noteSrc;
saveUndo();
if (editor.trackPattFlag == 0)
{
for (i = 0; i < MOD_ROWS; i++)
{
noteSrc = &modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + editor.cursor.channel];
if (noteSrc->sample == editor.currSample+1)
{
noteSrc->period = 0;
noteSrc->sample = 0;
noteSrc->command = 0;
noteSrc->param = 0;
}
}
}
else
{
for (i = 0; i < AMIGA_VOICES; i++)
{
for (uint8_t j = 0; j < MOD_ROWS; j++)
{
noteSrc = &modEntry->patterns[modEntry->currPattern][(j * AMIGA_VOICES) + i];
if (noteSrc->sample == editor.currSample+1)
{
noteSrc->period = 0;
noteSrc->sample = 0;
noteSrc->command = 0;
noteSrc->param = 0;
}
}
}
}
updateWindowTitle(MOD_IS_MODIFIED);
editor.ui.updatePatternData = true;
}
void trackNoteUp(bool sampleAllFlag, uint8_t from, uint8_t to)
{
bool noteDeleted;
uint8_t j;
note_t *noteSrc;
if (from > to)
{
j = from;
from = to;
to = j;
}
saveUndo();
for (uint8_t i = from; i <= to; i++)
{
noteSrc = &modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + editor.cursor.channel];
if (!sampleAllFlag && noteSrc->sample != editor.currSample+1)
continue;
if (noteSrc->period)
{
// period -> note
for (j = 0; j < 36; j++)
{
if (noteSrc->period >= periodTable[j])
break;
}
noteDeleted = false;
if (++j > 35)
{
j = 35;
if (ptConfig.transDel)
{
noteSrc->period = 0;
noteSrc->sample = 0;
noteDeleted = true;
}
}
if (!noteDeleted)
noteSrc->period = periodTable[j];
}
}
updateWindowTitle(MOD_IS_MODIFIED);
editor.ui.updatePatternData = true;
}
void trackNoteDown(bool sampleAllFlag, uint8_t from, uint8_t to)
{
bool noteDeleted;
int8_t j;
note_t *noteSrc;
if (from > to)
{
j = from;
from = to;
to = j;
}
saveUndo();
for (uint8_t i = from; i <= to; i++)
{
noteSrc = &modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + editor.cursor.channel];
if (!sampleAllFlag && noteSrc->sample != editor.currSample+1)
continue;
if (noteSrc->period)
{
// period -> note
for (j = 0; j < 36; j++)
{
if (noteSrc->period >= periodTable[j])
break;
}
noteDeleted = false;
if (--j < 0)
{
j = 0;
if (ptConfig.transDel)
{
noteSrc->period = 0;
noteSrc->sample = 0;
noteDeleted = true;
}
}
if (!noteDeleted)
noteSrc->period = periodTable[j];
}
}
updateWindowTitle(MOD_IS_MODIFIED);
editor.ui.updatePatternData = true;
}
void trackOctaUp(bool sampleAllFlag, uint8_t from, uint8_t to)
{
bool noteDeleted;
uint8_t j;
note_t *noteSrc;
if (from > to)
{
j = from;
from = to;
to = j;
}
saveUndo();
for (uint8_t i = from; i <= to; i++)
{
noteSrc = &modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + editor.cursor.channel];
if (!sampleAllFlag && noteSrc->sample != editor.currSample+1)
continue;
if (noteSrc->period)
{
// period -> note
for (j = 0; j < 36; j++)
{
if (noteSrc->period >= periodTable[j])
break;
}
noteDeleted = false;
if (j+12 > 35 && ptConfig.transDel)
{
noteSrc->period = 0;
noteSrc->sample = 0;
noteDeleted = true;
}
if (j <= 23)
j += 12;
if (!noteDeleted)
noteSrc->period = periodTable[j];
}
}
updateWindowTitle(MOD_IS_MODIFIED);
editor.ui.updatePatternData = true;
}
void trackOctaDown(bool sampleAllFlag, uint8_t from, uint8_t to)
{
bool noteDeleted;
int8_t j;
note_t *noteSrc;
if (from > to)
{
j = from;
from = to;
to = j;
}
saveUndo();
for (uint8_t i = from; i <= to; i++)
{
noteSrc = &modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + editor.cursor.channel];
if (!sampleAllFlag && noteSrc->sample != editor.currSample+1)
continue;
if (noteSrc->period)
{
// period -> note
for (j = 0; j < 36; j++)
{
if (noteSrc->period >= periodTable[j])
break;
}
noteDeleted = false;
if (j-12 < 0 && ptConfig.transDel)
{
noteSrc->period = 0;
noteSrc->sample = 0;
noteDeleted = true;
}
if (j >= 12)
j -= 12;
if (!noteDeleted)
noteSrc->period = periodTable[j];
}
}
updateWindowTitle(MOD_IS_MODIFIED);
editor.ui.updatePatternData = true;
}
void pattNoteUp(bool sampleAllFlag)
{
bool noteDeleted;
uint8_t k;
note_t *noteSrc;
saveUndo();
for (uint8_t i = 0; i < AMIGA_VOICES; i++)
{
for (uint8_t j = 0; j < MOD_ROWS; j++)
{
noteSrc = &modEntry->patterns[modEntry->currPattern][(j * AMIGA_VOICES) + i];
if (!sampleAllFlag && noteSrc->sample != editor.currSample+1)
continue;
if (noteSrc->period)
{
// period -> note
for (k = 0; k < 36; k++)
{
if (noteSrc->period >= periodTable[k])
break;
}
noteDeleted = false;
if (++k > 35)
{
k = 35;
if (ptConfig.transDel)
{
noteSrc->period = 0;
noteSrc->sample = 0;
noteDeleted = true;
}
}
if (!noteDeleted)
noteSrc->period = periodTable[k];
}
}
}
updateWindowTitle(MOD_IS_MODIFIED);
editor.ui.updatePatternData = true;
}
void pattNoteDown(bool sampleAllFlag)
{
bool noteDeleted;
int8_t k;
note_t *noteSrc;
saveUndo();
for (uint8_t i = 0; i < AMIGA_VOICES; i++)
{
for (uint8_t j = 0; j < MOD_ROWS; j++)
{
noteSrc = &modEntry->patterns[modEntry->currPattern][(j * AMIGA_VOICES) + i];
if (!sampleAllFlag && noteSrc->sample != editor.currSample+1)
continue;
if (noteSrc->period)
{
// period -> note
for (k = 0; k < 36; k++)
{
if (noteSrc->period >= periodTable[k])
break;
}
noteDeleted = false;
if (--k < 0)
{
k = 0;
if (ptConfig.transDel)
{
noteSrc->period = 0;
noteSrc->sample = 0;
noteDeleted = true;
}
}
if (!noteDeleted)
noteSrc->period = periodTable[k];
}
}
}
updateWindowTitle(MOD_IS_MODIFIED);
editor.ui.updatePatternData = true;
}
void pattOctaUp(bool sampleAllFlag)
{
bool noteDeleted;
uint8_t k;
note_t *noteSrc;
saveUndo();
for (uint8_t i = 0; i < AMIGA_VOICES; i++)
{
for (uint8_t j = 0; j < MOD_ROWS; j++)
{
noteSrc = &modEntry->patterns[modEntry->currPattern][(j * AMIGA_VOICES) + i];
if (!sampleAllFlag && noteSrc->sample != editor.currSample+1)
continue;
if (noteSrc->period)
{
// period -> note
for (k = 0; k < 36; k++)
{
if (noteSrc->period >= periodTable[k])
break;
}
noteDeleted = false;
if (k+12 > 35 && ptConfig.transDel)
{
noteSrc->period = 0;
noteSrc->sample = 0;
noteDeleted = true;
}
if (k <= 23)
k += 12;
if (!noteDeleted)
noteSrc->period = periodTable[k];
}
}
}
updateWindowTitle(MOD_IS_MODIFIED);
editor.ui.updatePatternData = true;
}
void pattOctaDown(bool sampleAllFlag)
{
bool noteDeleted;
int8_t k;
note_t *noteSrc;
saveUndo();
for (uint8_t i = 0; i < AMIGA_VOICES; i++)
{
for (uint8_t j = 0; j < MOD_ROWS; j++)
{
noteSrc = &modEntry->patterns[modEntry->currPattern][(j * AMIGA_VOICES) + i];
if (!sampleAllFlag && noteSrc->sample != editor.currSample+1)
continue;
if (noteSrc->period)
{
// period -> note
for (k = 0; k < 36; k++)
{
if (noteSrc->period >= periodTable[k])
break;
}
noteDeleted = false;
if (k-12 < 0 && ptConfig.transDel)
{
noteSrc->period = 0;
noteSrc->sample = 0;
noteDeleted = true;
}
if (k >= 12)
k -= 12;
if (!noteDeleted)
noteSrc->period = periodTable[k];
}
}
}
updateWindowTitle(MOD_IS_MODIFIED);
editor.ui.updatePatternData = true;
}
int8_t keyToNote(SDL_Scancode scancode)
{
int8_t note;
int32_t lookUpKey;
if (scancode < SDL_SCANCODE_B || scancode > SDL_SCANCODE_SLASH)
return -1; // not a note key
lookUpKey = (int32_t)scancode - SDL_SCANCODE_B;
if (lookUpKey < 0 || lookUpKey >= 52)
return -1; // just in case
if (editor.keyOctave == OCTAVE_LOW)
note = scancode2NoteLo[lookUpKey];
else
note = scancode2NoteHi[lookUpKey];
return note;
}