1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279 |
- /*
- * modcommand.cpp
- * --------------
- * Purpose: Various functions for writing effects to patterns, converting ModCommands, etc.
- * Notes : (currently none)
- * Authors: OpenMPT Devs
- * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
- */
- #include "stdafx.h"
- #include "Sndfile.h"
- #include "mod_specifications.h"
- #include "Tables.h"
- OPENMPT_NAMESPACE_BEGIN
- const EffectType effectTypes[] =
- {
- EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL, EFFECT_TYPE_PITCH, EFFECT_TYPE_PITCH,
- EFFECT_TYPE_PITCH, EFFECT_TYPE_PITCH, EFFECT_TYPE_VOLUME, EFFECT_TYPE_VOLUME,
- EFFECT_TYPE_VOLUME, EFFECT_TYPE_PANNING, EFFECT_TYPE_NORMAL, EFFECT_TYPE_VOLUME,
- EFFECT_TYPE_GLOBAL, EFFECT_TYPE_VOLUME, EFFECT_TYPE_GLOBAL, EFFECT_TYPE_NORMAL,
- EFFECT_TYPE_GLOBAL, EFFECT_TYPE_GLOBAL, EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL,
- EFFECT_TYPE_NORMAL, EFFECT_TYPE_VOLUME, EFFECT_TYPE_VOLUME, EFFECT_TYPE_GLOBAL,
- EFFECT_TYPE_GLOBAL, EFFECT_TYPE_NORMAL, EFFECT_TYPE_PITCH, EFFECT_TYPE_PANNING,
- EFFECT_TYPE_PITCH, EFFECT_TYPE_PANNING, EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL,
- EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL, EFFECT_TYPE_PITCH,
- EFFECT_TYPE_PITCH, EFFECT_TYPE_NORMAL, EFFECT_TYPE_PITCH, EFFECT_TYPE_PITCH,
- EFFECT_TYPE_PITCH, EFFECT_TYPE_PITCH, EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL,
- EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL,
- };
- static_assert(std::size(effectTypes) == MAX_EFFECTS);
- const EffectType volumeEffectTypes[] =
- {
- EFFECT_TYPE_NORMAL, EFFECT_TYPE_VOLUME, EFFECT_TYPE_PANNING, EFFECT_TYPE_VOLUME,
- EFFECT_TYPE_VOLUME, EFFECT_TYPE_VOLUME, EFFECT_TYPE_VOLUME, EFFECT_TYPE_PITCH,
- EFFECT_TYPE_PITCH, EFFECT_TYPE_PANNING, EFFECT_TYPE_PANNING, EFFECT_TYPE_PITCH,
- EFFECT_TYPE_PITCH, EFFECT_TYPE_PITCH, EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL,
- };
- static_assert(std::size(volumeEffectTypes) == MAX_VOLCMDS);
- EffectType ModCommand::GetEffectType(COMMAND cmd)
- {
- if(cmd < std::size(effectTypes))
- return effectTypes[cmd];
- else
- return EFFECT_TYPE_NORMAL;
- }
- EffectType ModCommand::GetVolumeEffectType(VOLCMD volcmd)
- {
- if(volcmd < std::size(volumeEffectTypes))
- return volumeEffectTypes[volcmd];
- else
- return EFFECT_TYPE_NORMAL;
- }
- // Convert an Exx command (MOD) to Sxx command (S3M)
- void ModCommand::ExtendedMODtoS3MEffect()
- {
- if(command != CMD_MODCMDEX)
- return;
- command = CMD_S3MCMDEX;
- switch(param & 0xF0)
- {
- case 0x00: command = CMD_NONE; break; // No filter control
- case 0x10: command = CMD_PORTAMENTOUP; param |= 0xF0; break;
- case 0x20: command = CMD_PORTAMENTODOWN; param |= 0xF0; break;
- case 0x30: param = (param & 0x0F) | 0x10; break;
- case 0x40: param = (param & 0x03) | 0x30; break;
- case 0x50: param = (param & 0x0F) | 0x20; break;
- case 0x60: param = (param & 0x0F) | 0xB0; break;
- case 0x70: param = (param & 0x03) | 0x40; break;
- case 0x90: command = CMD_RETRIG; param = (param & 0x0F); break;
- case 0xA0: if(param & 0x0F) { command = CMD_VOLUMESLIDE; param = (param << 4) | 0x0F; } else command = CMD_NONE; break;
- case 0xB0: if(param & 0x0F) { command = CMD_VOLUMESLIDE; param = 0xF0 | static_cast<PARAM>(std::min(param & 0x0F, 0x0E)); } else command = CMD_NONE; break;
- case 0xC0: if(param == 0xC0) { command = CMD_NONE; note = NOTE_NOTECUT; } break; // this does different things in IT and ST3
- case 0xD0: if(param == 0xD0) { command = CMD_NONE; } break; // ditto
- // rest are the same or handled elsewhere
- }
- }
- // Convert an Sxx command (S3M) to Exx command (MOD)
- void ModCommand::ExtendedS3MtoMODEffect()
- {
- if(command != CMD_S3MCMDEX)
- return;
- command = CMD_MODCMDEX;
- switch(param & 0xF0)
- {
- case 0x10: param = (param & 0x0F) | 0x30; break;
- case 0x20: param = (param & 0x0F) | 0x50; break;
- case 0x30: param = (param & 0x0F) | 0x40; break;
- case 0x40: param = (param & 0x0F) | 0x70; break;
- case 0x50: command = CMD_XFINEPORTAUPDOWN; break; // map to unused X5x
- case 0x60: command = CMD_XFINEPORTAUPDOWN; break; // map to unused X6x
- case 0x80: command = CMD_PANNING8; param = (param & 0x0F) * 0x11; break; // FT2 does actually not support E8x
- case 0x90: command = CMD_XFINEPORTAUPDOWN; break; // map to unused X9x
- case 0xA0: command = CMD_XFINEPORTAUPDOWN; break; // map to unused XAx
- case 0xB0: param = (param & 0x0F) | 0x60; break;
- case 0x70: command = CMD_NONE; break; // No NNA / envelope control in MOD/XM format
- // rest are the same or handled elsewhere
- }
- }
- // Convert a mod command from one format to another.
- void ModCommand::Convert(MODTYPE fromType, MODTYPE toType, const CSoundFile &sndFile)
- {
- if(fromType == toType)
- {
- return;
- }
- if(fromType == MOD_TYPE_MTM)
- {
- // Special MTM fixups.
- // Retrigger with param 0
- if(command == CMD_MODCMDEX && param == 0x90)
- {
- command = CMD_NONE;
- } else if(command == CMD_VIBRATO)
- {
- // Vibrato is approximately half as deep compared to MOD/S3M.
- uint8 speed = (param & 0xF0);
- uint8 depth = (param & 0x0F) >> 1;
- param = speed | depth;
- }
- // Apart from these special fixups, do a regular conversion from MOD.
- fromType = MOD_TYPE_MOD;
- }
- if(command == CMD_DIGIREVERSESAMPLE && toType != MOD_TYPE_DIGI)
- {
- command = CMD_S3MCMDEX;
- param = 0x9F;
- }
- // helper variables
- const bool oldTypeIsMOD = (fromType == MOD_TYPE_MOD), oldTypeIsXM = (fromType == MOD_TYPE_XM),
- oldTypeIsS3M = (fromType == MOD_TYPE_S3M), oldTypeIsIT = (fromType == MOD_TYPE_IT),
- oldTypeIsMPT = (fromType == MOD_TYPE_MPT), oldTypeIsMOD_XM = (oldTypeIsMOD || oldTypeIsXM),
- oldTypeIsS3M_IT_MPT = (oldTypeIsS3M || oldTypeIsIT || oldTypeIsMPT),
- oldTypeIsIT_MPT = (oldTypeIsIT || oldTypeIsMPT);
- const bool newTypeIsMOD = (toType == MOD_TYPE_MOD), newTypeIsXM = (toType == MOD_TYPE_XM),
- newTypeIsS3M = (toType == MOD_TYPE_S3M), newTypeIsIT = (toType == MOD_TYPE_IT),
- newTypeIsMPT = (toType == MOD_TYPE_MPT), newTypeIsMOD_XM = (newTypeIsMOD || newTypeIsXM),
- newTypeIsS3M_IT_MPT = (newTypeIsS3M || newTypeIsIT || newTypeIsMPT),
- newTypeIsIT_MPT = (newTypeIsIT || newTypeIsMPT);
- const CModSpecifications &newSpecs = CSoundFile::GetModSpecifications(toType);
- //////////////////////////
- // Convert 8-bit Panning
- if(command == CMD_PANNING8)
- {
- if(newTypeIsS3M)
- {
- param = (param + 1) >> 1;
- } else if(oldTypeIsS3M)
- {
- if(param == 0xA4)
- {
- // surround remap
- command = static_cast<COMMAND>((toType & (MOD_TYPE_IT | MOD_TYPE_MPT)) ? CMD_S3MCMDEX : CMD_XFINEPORTAUPDOWN);
- param = 0x91;
- } else
- {
- param = mpt::saturate_cast<PARAM>(param * 2u);
- }
- }
- } // End if(command == CMD_PANNING8)
- // Re-map \xx to Zxx if the new format only knows the latter command.
- if(command == CMD_SMOOTHMIDI && !newSpecs.HasCommand(CMD_SMOOTHMIDI) && newSpecs.HasCommand(CMD_MIDI))
- {
- command = CMD_MIDI;
- }
- ///////////////////////////////////////////////////////////////////////////////////////
- // MPTM to anything: Convert param control, extended envelope control, note delay+cut
- if(oldTypeIsMPT)
- {
- if(IsPcNote())
- {
- COMMAND newCmd = static_cast<COMMAND>(note == NOTE_PC ? CMD_MIDI : CMD_SMOOTHMIDI);
- if(!newSpecs.HasCommand(newCmd))
- {
- newCmd = CMD_MIDI; // assuming that this was CMD_SMOOTHMIDI
- if(!newSpecs.HasCommand(newCmd))
- {
- newCmd = CMD_NONE;
- }
- }
- param = static_cast<PARAM>(std::min(static_cast<uint16>(maxColumnValue), GetValueEffectCol()) * 0x7F / maxColumnValue);
- command = newCmd; // might be removed later
- volcmd = VOLCMD_NONE;
- note = NOTE_NONE;
- instr = 0;
- }
- if((command == CMD_S3MCMDEX) && ((param & 0xF0) == 0x70) && ((param & 0x0F) > 0x0C))
- {
- // Extended pitch envelope control commands
- param = 0x7C;
- } else if(command == CMD_DELAYCUT)
- {
- command = CMD_S3MCMDEX; // When converting to MOD/XM, this will be converted to CMD_MODCMDEX later
- param = 0xD0 | (param >> 4); // Preserve delay nibble
- } else if(command == CMD_FINETUNE || command == CMD_FINETUNE_SMOOTH)
- {
- // Convert finetune from +/-128th of a semitone to (extra-)fine portamento (assumes linear slides, plus we're missing the actual pitch wheel depth of the instrument)
- if(param < 0x80)
- {
- command = CMD_PORTAMENTODOWN;
- param = 0x80 - param;
- } else if(param > 0x80)
- {
- command = CMD_PORTAMENTOUP;
- param -= 0x80;
- }
- if(param <= 30)
- param = 0xE0 | ((param + 1u) / 2u);
- else
- param = 0xF0 | std::min(static_cast<PARAM>((param + 7u) / 8u), PARAM(15));
- }
- } // End if(oldTypeIsMPT)
- /////////////////////////////////////////
- // Convert MOD / XM to S3M / IT / MPTM
- if(oldTypeIsMOD_XM && newTypeIsS3M_IT_MPT)
- {
- switch(command)
- {
- case CMD_ARPEGGIO:
- if(!param) command = CMD_NONE; // 000 does nothing in MOD/XM
- break;
- case CMD_MODCMDEX:
- ExtendedMODtoS3MEffect();
- break;
- case CMD_VOLUME:
- // Effect column volume command overrides the volume column in XM.
- if(volcmd == VOLCMD_NONE || volcmd == VOLCMD_VOLUME)
- {
- volcmd = VOLCMD_VOLUME;
- vol = param;
- if(vol > 64) vol = 64;
- command = CMD_NONE;
- param = 0;
- } else if(volcmd == VOLCMD_PANNING)
- {
- std::swap(vol, param);
- volcmd = VOLCMD_VOLUME;
- if(vol > 64) vol = 64;
- command = CMD_S3MCMDEX;
- param = 0x80 | (param / 4); // XM volcol panning is actually 4-Bit, so we can use 4-Bit panning here.
- }
- break;
- case CMD_PORTAMENTOUP:
- if(param > 0xDF) param = 0xDF;
- break;
- case CMD_PORTAMENTODOWN:
- if(param > 0xDF) param = 0xDF;
- break;
- case CMD_XFINEPORTAUPDOWN:
- switch(param & 0xF0)
- {
- case 0x10: command = CMD_PORTAMENTOUP; param = (param & 0x0F) | 0xE0; break;
- case 0x20: command = CMD_PORTAMENTODOWN; param = (param & 0x0F) | 0xE0; break;
- case 0x50:
- case 0x60:
- case 0x70:
- case 0x90:
- case 0xA0:
- command = CMD_S3MCMDEX;
- // Surround remap (this is the "official" command)
- if(toType & MOD_TYPE_S3M && param == 0x91)
- {
- command = CMD_PANNING8;
- param = 0xA4;
- }
- break;
- }
- break;
- case CMD_KEYOFF:
- if(note == NOTE_NONE)
- {
- note = newTypeIsS3M ? NOTE_NOTECUT : NOTE_KEYOFF;
- command = CMD_S3MCMDEX;
- if(param == 0)
- instr = 0;
- param = 0xD0 | (param & 0x0F);
- }
- break;
- case CMD_PANNINGSLIDE:
- // swap L/R, convert to fine slide
- if(param & 0xF0)
- {
- param = 0xF0 | std::min(PARAM(0x0E), static_cast<PARAM>(param >> 4));
- } else
- {
- param = 0x0F | (std::min(PARAM(0x0E), static_cast<PARAM>(param & 0x0F)) << 4);
- }
- break;
- default:
- break;
- }
- } // End if(oldTypeIsMOD_XM && newTypeIsS3M_IT_MPT)
- /////////////////////////////////////////
- // Convert S3M / IT / MPTM to MOD / XM
- else if(oldTypeIsS3M_IT_MPT && newTypeIsMOD_XM)
- {
- if(note == NOTE_NOTECUT)
- {
- // convert note cut to C00 if possible or volume command otherwise (MOD/XM has no real way of cutting notes that cannot be "undone" by volume commands)
- note = NOTE_NONE;
- if(command == CMD_NONE || !newTypeIsXM)
- {
- command = CMD_VOLUME;
- param = 0;
- } else
- {
- volcmd = VOLCMD_VOLUME;
- vol = 0;
- }
- } else if(note == NOTE_FADE)
- {
- // convert note fade to note off
- note = NOTE_KEYOFF;
- }
- switch(command)
- {
- case CMD_S3MCMDEX:
- ExtendedS3MtoMODEffect();
- break;
- case CMD_TONEPORTAVOL: // Can't do fine slides and portamento/vibrato at the same time :(
- case CMD_VIBRATOVOL: // ditto
- if(volcmd == VOLCMD_NONE && (((param & 0xF0) && ((param & 0x0F) == 0x0F)) || ((param & 0x0F) && ((param & 0xF0) == 0xF0))))
- {
- // Try to salvage portamento/vibrato
- if(command == CMD_TONEPORTAVOL)
- volcmd = VOLCMD_TONEPORTAMENTO;
- else if(command == CMD_VIBRATOVOL)
- volcmd = VOLCMD_VIBRATODEPTH;
- vol = 0;
- }
- [[fallthrough]];
- case CMD_VOLUMESLIDE:
- if((param & 0xF0) && ((param & 0x0F) == 0x0F))
- {
- command = CMD_MODCMDEX;
- param = (param >> 4) | 0xA0;
- } else if((param & 0x0F) && ((param & 0xF0) == 0xF0))
- {
- command = CMD_MODCMDEX;
- param = (param & 0x0F) | 0xB0;
- }
- break;
- case CMD_PORTAMENTOUP:
- if(param >= 0xF0)
- {
- command = CMD_MODCMDEX;
- param = (param & 0x0F) | 0x10;
- } else if(param >= 0xE0)
- {
- if(newTypeIsXM)
- {
- command = CMD_XFINEPORTAUPDOWN;
- param = 0x10 | (param & 0x0F);
- } else
- {
- command = CMD_MODCMDEX;
- param = (((param & 0x0F) + 3) >> 2) | 0x10;
- }
- } else
- {
- command = CMD_PORTAMENTOUP;
- }
- break;
- case CMD_PORTAMENTODOWN:
- if(param >= 0xF0)
- {
- command = CMD_MODCMDEX;
- param = (param & 0x0F) | 0x20;
- } else if(param >= 0xE0)
- {
- if(newTypeIsXM)
- {
- command = CMD_XFINEPORTAUPDOWN;
- param = 0x20 | (param & 0x0F);
- } else
- {
- command = CMD_MODCMDEX;
- param = (((param & 0x0F) + 3) >> 2) | 0x20;
- }
- } else
- {
- command = CMD_PORTAMENTODOWN;
- }
- break;
- case CMD_TEMPO:
- if(param < 0x20) command = CMD_NONE; // no tempo slides
- break;
- case CMD_PANNINGSLIDE:
- // swap L/R, convert fine slides to normal slides
- if((param & 0x0F) == 0x0F && (param & 0xF0))
- {
- param = (param >> 4);
- } else if((param & 0xF0) == 0xF0 && (param & 0x0F))
- {
- param = (param & 0x0F) << 4;
- } else if(param & 0x0F)
- {
- param = 0xF0;
- } else if(param & 0xF0)
- {
- param = 0x0F;
- } else
- {
- param = 0;
- }
- break;
- case CMD_RETRIG:
- // Retrig: Q0y doesn't change volume in IT/S3M, but R0y in XM takes the last x parameter
- if(param != 0 && (param & 0xF0) == 0)
- {
- param |= 0x80;
- }
- break;
- default:
- break;
- }
- } // End if(oldTypeIsS3M_IT_MPT && newTypeIsMOD_XM)
- ///////////////////////
- // Convert IT to S3M
- else if(oldTypeIsIT_MPT && newTypeIsS3M)
- {
- if(note == NOTE_KEYOFF || note == NOTE_FADE)
- note = NOTE_NOTECUT;
- switch(command)
- {
- case CMD_S3MCMDEX:
- switch(param & 0xF0)
- {
- case 0x70: command = CMD_NONE; break; // No NNA / envelope control in S3M format
- case 0x90:
- if(param == 0x91)
- {
- // surround remap (this is the "official" command)
- command = CMD_PANNING8;
- param = 0xA4;
- } else if(param == 0x90)
- {
- command = CMD_PANNING8;
- param = 0x40;
- }
- break;
- }
- break;
- case CMD_GLOBALVOLUME:
- param = (std::min(PARAM(0x80), param) + 1) / 2u;
- break;
- default:
- break;
- }
- } // End if(oldTypeIsIT_MPT && newTypeIsS3M)
- //////////////////////
- // Convert IT to XM
- if(oldTypeIsIT_MPT && newTypeIsXM)
- {
- switch(command)
- {
- case CMD_VIBRATO:
- // With linear slides, strength is roughly doubled.
- param = (param & 0xF0) | (((param & 0x0F) + 1) / 2u);
- break;
- case CMD_GLOBALVOLUME:
- param = (std::min(PARAM(0x80), param) + 1) / 2u;
- break;
- }
- } // End if(oldTypeIsIT_MPT && newTypeIsXM)
- //////////////////////
- // Convert XM to IT
- if(oldTypeIsXM && newTypeIsIT_MPT)
- {
- switch(command)
- {
- case CMD_VIBRATO:
- // With linear slides, strength is roughly halved.
- param = (param & 0xF0) | std::min(static_cast<PARAM>((param & 0x0F) * 2u), PARAM(15));
- break;
- case CMD_GLOBALVOLUME:
- param = std::min(PARAM(0x40), param) * 2u;
- break;
- }
- } // End if(oldTypeIsIT_MPT && newTypeIsXM)
- ///////////////////////////////////
- // MOD / XM Speed/Tempo limits
- if(newTypeIsMOD_XM)
- {
- switch(command)
- {
- case CMD_SPEED:
- param = std::min(param, PARAM(0x1F));
- break;
- break;
- case CMD_TEMPO:
- param = std::max(param, PARAM(0x20));
- break;
- }
- }
- ///////////////////////////////////////////////////////////////////////
- // Convert MOD to anything - adjust effect memory, remove Invert Loop
- if(oldTypeIsMOD)
- {
- switch(command)
- {
- case CMD_TONEPORTAVOL: // lacks memory -> 500 is the same as 300
- if(param == 0x00)
- command = CMD_TONEPORTAMENTO;
- break;
- case CMD_VIBRATOVOL: // lacks memory -> 600 is the same as 400
- if(param == 0x00)
- command = CMD_VIBRATO;
- break;
- case CMD_PORTAMENTOUP: // lacks memory -> remove
- case CMD_PORTAMENTODOWN:
- case CMD_VOLUMESLIDE:
- if(param == 0x00)
- command = CMD_NONE;
- break;
- case CMD_MODCMDEX: // This would turn into "Set Active Macro", so let's better remove it
- case CMD_S3MCMDEX:
- if((param & 0xF0) == 0xF0)
- command = CMD_NONE;
- break;
- }
- } // End if(oldTypeIsMOD && newTypeIsXM)
- /////////////////////////////////////////////////////////////////////
- // Convert anything to MOD - remove volume column, remove Set Macro
- if(newTypeIsMOD)
- {
- // convert note off events
- if(IsSpecialNote())
- {
- note = NOTE_NONE;
- // no effect present, so just convert note off to volume 0
- if(command == CMD_NONE)
- {
- command = CMD_VOLUME;
- param = 0;
- // EDx effect present, so convert it to ECx
- } else if((command == CMD_MODCMDEX) && ((param & 0xF0) == 0xD0))
- {
- param = 0xC0 | (param & 0x0F);
- }
- }
- if(command != CMD_NONE) switch(command)
- {
- case CMD_RETRIG: // MOD only has E9x
- command = CMD_MODCMDEX;
- param = 0x90 | (param & 0x0F);
- break;
- case CMD_MODCMDEX: // This would turn into "Invert Loop", so let's better remove it
- if((param & 0xF0) == 0xF0) command = CMD_NONE;
- break;
- }
- if(command == CMD_NONE) switch(volcmd)
- {
- case VOLCMD_VOLUME:
- command = CMD_VOLUME;
- param = vol;
- break;
- case VOLCMD_PANNING:
- command = CMD_PANNING8;
- param = vol < 64 ? vol << 2 : 255;
- break;
- case VOLCMD_VOLSLIDEDOWN:
- command = CMD_VOLUMESLIDE;
- param = vol;
- break;
- case VOLCMD_VOLSLIDEUP:
- command = CMD_VOLUMESLIDE;
- param = vol << 4;
- break;
- case VOLCMD_FINEVOLDOWN:
- command = CMD_MODCMDEX;
- param = 0xB0 | vol;
- break;
- case VOLCMD_FINEVOLUP:
- command = CMD_MODCMDEX;
- param = 0xA0 | vol;
- break;
- case VOLCMD_PORTADOWN:
- command = CMD_PORTAMENTODOWN;
- param = vol << 2;
- break;
- case VOLCMD_PORTAUP:
- command = CMD_PORTAMENTOUP;
- param = vol << 2;
- break;
- case VOLCMD_TONEPORTAMENTO:
- command = CMD_TONEPORTAMENTO;
- param = vol << 2;
- break;
- case VOLCMD_VIBRATODEPTH:
- command = CMD_VIBRATO;
- param = vol;
- break;
- case VOLCMD_VIBRATOSPEED:
- command = CMD_VIBRATO;
- param = vol << 4;
- break;
- }
- volcmd = VOLCMD_NONE;
- } // End if(newTypeIsMOD)
- ///////////////////////////////////////////////////
- // Convert anything to S3M - adjust volume column
- if(newTypeIsS3M)
- {
- if(command == CMD_NONE) switch(volcmd)
- {
- case VOLCMD_VOLSLIDEDOWN:
- command = CMD_VOLUMESLIDE;
- param = vol;
- volcmd = VOLCMD_NONE;
- break;
- case VOLCMD_VOLSLIDEUP:
- command = CMD_VOLUMESLIDE;
- param = vol << 4;
- volcmd = VOLCMD_NONE;
- break;
- case VOLCMD_FINEVOLDOWN:
- command = CMD_VOLUMESLIDE;
- param = 0xF0 | vol;
- volcmd = VOLCMD_NONE;
- break;
- case VOLCMD_FINEVOLUP:
- command = CMD_VOLUMESLIDE;
- param = (vol << 4) | 0x0F;
- volcmd = VOLCMD_NONE;
- break;
- case VOLCMD_PORTADOWN:
- command = CMD_PORTAMENTODOWN;
- param = vol << 2;
- volcmd = VOLCMD_NONE;
- break;
- case VOLCMD_PORTAUP:
- command = CMD_PORTAMENTOUP;
- param = vol << 2;
- volcmd = VOLCMD_NONE;
- break;
- case VOLCMD_TONEPORTAMENTO:
- command = CMD_TONEPORTAMENTO;
- param = vol << 2;
- volcmd = VOLCMD_NONE;
- break;
- case VOLCMD_VIBRATODEPTH:
- command = CMD_VIBRATO;
- param = vol;
- volcmd = VOLCMD_NONE;
- break;
- case VOLCMD_VIBRATOSPEED:
- command = CMD_VIBRATO;
- param = vol << 4;
- volcmd = VOLCMD_NONE;
- break;
- case VOLCMD_PANSLIDELEFT:
- command = CMD_PANNINGSLIDE;
- param = vol << 4;
- volcmd = VOLCMD_NONE;
- break;
- case VOLCMD_PANSLIDERIGHT:
- command = CMD_PANNINGSLIDE;
- param = vol;
- volcmd = VOLCMD_NONE;
- break;
- }
- } // End if(newTypeIsS3M)
- ////////////////////////////////////////////////////////////////////////
- // Convert anything to XM - adjust volume column, breaking EDx command
- if(newTypeIsXM)
- {
- // remove EDx if no note is next to it, or it will retrigger the note in FT2 mode
- if(command == CMD_MODCMDEX && (param & 0xF0) == 0xD0 && note == NOTE_NONE)
- {
- command = CMD_NONE;
- param = 0;
- }
- if(IsSpecialNote())
- {
- // Instrument numbers next to Note Off reset instrument settings
- instr = 0;
- if(command == CMD_MODCMDEX && (param & 0xF0) == 0xD0)
- {
- // Note Off + Note Delay does nothing when using envelopes.
- note = NOTE_NONE;
- command = CMD_KEYOFF;
- param &= 0x0F;
- }
- }
- // Convert some commands which behave differently or don't exist
- if(command == CMD_NONE) switch(volcmd)
- {
- case VOLCMD_PORTADOWN:
- command = CMD_PORTAMENTODOWN;
- param = vol << 2;
- volcmd = VOLCMD_NONE;
- break;
- case VOLCMD_PORTAUP:
- command = CMD_PORTAMENTOUP;
- param = vol << 2;
- volcmd = VOLCMD_NONE;
- break;
- case VOLCMD_TONEPORTAMENTO:
- command = CMD_TONEPORTAMENTO;
- param = ImpulseTrackerPortaVolCmd[vol & 0x0F];
- volcmd = VOLCMD_NONE;
- break;
- }
- } // End if(newTypeIsXM)
- ///////////////////////////////////////////////////
- // Convert anything to IT - adjust volume column
- if(newTypeIsIT_MPT)
- {
- // Convert some commands which behave differently or don't exist
- if(!oldTypeIsIT_MPT && command == CMD_NONE) switch(volcmd)
- {
- case VOLCMD_PANSLIDELEFT:
- command = CMD_PANNINGSLIDE;
- param = vol << 4;
- volcmd = VOLCMD_NONE;
- break;
- case VOLCMD_PANSLIDERIGHT:
- command = CMD_PANNINGSLIDE;
- param = vol;
- volcmd = VOLCMD_NONE;
- break;
- case VOLCMD_VIBRATOSPEED:
- command = CMD_VIBRATO;
- param = vol << 4;
- volcmd = VOLCMD_NONE;
- break;
- case VOLCMD_TONEPORTAMENTO:
- command = CMD_TONEPORTAMENTO;
- param = vol << 4;
- volcmd = VOLCMD_NONE;
- break;
- }
- switch(volcmd)
- {
- case VOLCMD_VOLSLIDEDOWN:
- case VOLCMD_VOLSLIDEUP:
- case VOLCMD_FINEVOLDOWN:
- case VOLCMD_FINEVOLUP:
- case VOLCMD_PORTADOWN:
- case VOLCMD_PORTAUP:
- case VOLCMD_TONEPORTAMENTO:
- case VOLCMD_VIBRATODEPTH:
- // OpenMPT-specific commands
- case VOLCMD_OFFSET:
- vol = std::min(vol, VOL(9));
- break;
- }
- } // End if(newTypeIsIT_MPT)
- // Fix volume column offset for formats that don't have it.
- if(volcmd == VOLCMD_OFFSET && !newSpecs.HasVolCommand(VOLCMD_OFFSET) && (command == CMD_NONE || command == CMD_OFFSET || !newSpecs.HasCommand(command)))
- {
- const ModCommand::PARAM oldOffset = (command == CMD_OFFSET) ? param : 0;
- command = CMD_OFFSET;
- volcmd = VOLCMD_NONE;
- SAMPLEINDEX smp = instr;
- if(smp > 0 && smp <= sndFile.GetNumInstruments() && IsNote() && sndFile.Instruments[smp] != nullptr)
- smp = sndFile.Instruments[smp]->Keyboard[note - NOTE_MIN];
- if(smp > 0 && smp <= sndFile.GetNumSamples() && vol <= std::size(ModSample().cues))
- {
- const ModSample &sample = sndFile.GetSample(smp);
- if(vol == 0)
- param = mpt::saturate_cast<ModCommand::PARAM>(Util::muldivr_unsigned(sample.nLength, oldOffset, 65536u));
- else
- param = mpt::saturate_cast<ModCommand::PARAM>((sample.cues[vol - 1] + (oldOffset * 256u) + 128u) / 256u);
- } else
- {
- param = vol << 3;
- }
- }
- if((command == CMD_REVERSEOFFSET || command == CMD_OFFSETPERCENTAGE) && !newSpecs.HasCommand(command))
- {
- command = CMD_OFFSET;
- }
- if(!newSpecs.HasNote(note))
- note = NOTE_NONE;
- // ensure the commands really exist in this format
- if(!newSpecs.HasCommand(command))
- command = CMD_NONE;
- if(!newSpecs.HasVolCommand(volcmd))
- volcmd = VOLCMD_NONE;
- }
- bool ModCommand::IsContinousCommand(const CSoundFile &sndFile) const
- {
- switch(command)
- {
- case CMD_ARPEGGIO:
- case CMD_TONEPORTAMENTO:
- case CMD_VIBRATO:
- case CMD_TREMOLO:
- case CMD_RETRIG:
- case CMD_TREMOR:
- case CMD_FINEVIBRATO:
- case CMD_PANBRELLO:
- case CMD_SMOOTHMIDI:
- case CMD_NOTESLIDEUP:
- case CMD_NOTESLIDEDOWN:
- case CMD_NOTESLIDEUPRETRIG:
- case CMD_NOTESLIDEDOWNRETRIG:
- return true;
- case CMD_PORTAMENTOUP:
- case CMD_PORTAMENTODOWN:
- if(!param && sndFile.GetType() == MOD_TYPE_MOD)
- return false;
- if(sndFile.GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_AMF0 | MOD_TYPE_DIGI | MOD_TYPE_STP | MOD_TYPE_DTM))
- return true;
- if(param >= 0xF0)
- return false;
- if(param >= 0xE0 && sndFile.GetType() != MOD_TYPE_DBM)
- return false;
- return true;
- case CMD_VOLUMESLIDE:
- case CMD_TONEPORTAVOL:
- case CMD_VIBRATOVOL:
- case CMD_GLOBALVOLSLIDE:
- case CMD_CHANNELVOLSLIDE:
- case CMD_PANNINGSLIDE:
- if(!param && sndFile.GetType() == MOD_TYPE_MOD)
- return false;
- if(sndFile.GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_AMF0 | MOD_TYPE_MED | MOD_TYPE_DIGI))
- return true;
- if((param & 0xF0) == 0xF0 && (param & 0x0F))
- return false;
- if((param & 0x0F) == 0x0F && (param & 0xF0))
- return false;
- return true;
- case CMD_TEMPO:
- return (param < 0x20);
- default:
- return false;
- }
- }
- bool ModCommand::IsContinousVolColCommand() const
- {
- switch(volcmd)
- {
- case VOLCMD_VOLSLIDEUP:
- case VOLCMD_VOLSLIDEDOWN:
- case VOLCMD_VIBRATOSPEED:
- case VOLCMD_VIBRATODEPTH:
- case VOLCMD_PANSLIDELEFT:
- case VOLCMD_PANSLIDERIGHT:
- case VOLCMD_TONEPORTAMENTO:
- case VOLCMD_PORTAUP:
- case VOLCMD_PORTADOWN:
- return true;
- default:
- return false;
- }
- }
- bool ModCommand::IsSlideUpDownCommand() const
- {
- switch(command)
- {
- case CMD_VOLUMESLIDE:
- case CMD_TONEPORTAVOL:
- case CMD_VIBRATOVOL:
- case CMD_GLOBALVOLSLIDE:
- case CMD_CHANNELVOLSLIDE:
- case CMD_PANNINGSLIDE:
- return true;
- default:
- return false;
- }
- }
- bool ModCommand::IsGlobalCommand(COMMAND command, PARAM param)
- {
- switch(command)
- {
- case CMD_POSITIONJUMP:
- case CMD_PATTERNBREAK:
- case CMD_SPEED:
- case CMD_TEMPO:
- case CMD_GLOBALVOLUME:
- case CMD_GLOBALVOLSLIDE:
- case CMD_MIDI:
- case CMD_SMOOTHMIDI:
- case CMD_DBMECHO:
- return true;
- case CMD_MODCMDEX:
- switch(param & 0xF0)
- {
- case 0x00: // LED Filter
- case 0x60: // Pattern Loop
- case 0xE0: // Row Delay
- return true;
- default:
- return false;
- }
- case CMD_XFINEPORTAUPDOWN:
- case CMD_S3MCMDEX:
- switch(param & 0xF0)
- {
- case 0x60: // Tick Delay
- case 0x90: // Sound Control
- case 0xB0: // Pattern Loop
- case 0xE0: // Row Delay
- return true;
- default:
- return false;
- }
- default:
- return false;
- }
- }
- // "Importance" of every FX command. Table is used for importing from formats with multiple effect colums
- // and is approximately the same as in SchismTracker.
- size_t ModCommand::GetEffectWeight(COMMAND cmd)
- {
- // Effect weights, sorted from lowest to highest weight.
- static constexpr COMMAND weights[] =
- {
- CMD_NONE,
- CMD_DUMMY,
- CMD_XPARAM,
- CMD_SETENVPOSITION,
- CMD_KEYOFF,
- CMD_TREMOLO,
- CMD_FINEVIBRATO,
- CMD_VIBRATO,
- CMD_XFINEPORTAUPDOWN,
- CMD_FINETUNE,
- CMD_FINETUNE_SMOOTH,
- CMD_PANBRELLO,
- CMD_S3MCMDEX,
- CMD_MODCMDEX,
- CMD_DELAYCUT,
- CMD_MIDI,
- CMD_SMOOTHMIDI,
- CMD_PANNINGSLIDE,
- CMD_PANNING8,
- CMD_NOTESLIDEUPRETRIG,
- CMD_NOTESLIDEUP,
- CMD_NOTESLIDEDOWNRETRIG,
- CMD_NOTESLIDEDOWN,
- CMD_PORTAMENTOUP,
- CMD_PORTAMENTODOWN,
- CMD_VOLUMESLIDE,
- CMD_VIBRATOVOL,
- CMD_VOLUME,
- CMD_DIGIREVERSESAMPLE,
- CMD_REVERSEOFFSET,
- CMD_OFFSETPERCENTAGE,
- CMD_OFFSET,
- CMD_TREMOR,
- CMD_RETRIG,
- CMD_ARPEGGIO,
- CMD_TONEPORTAMENTO,
- CMD_TONEPORTAVOL,
- CMD_DBMECHO,
- CMD_GLOBALVOLSLIDE,
- CMD_CHANNELVOLUME,
- CMD_GLOBALVOLSLIDE,
- CMD_GLOBALVOLUME,
- CMD_TEMPO,
- CMD_SPEED,
- CMD_POSITIONJUMP,
- CMD_PATTERNBREAK,
- };
- static_assert(std::size(weights) == MAX_EFFECTS);
- for(size_t i = 0; i < std::size(weights); i++)
- {
- if(weights[i] == cmd)
- {
- return i;
- }
- }
- // Invalid / unknown command.
- return 0;
- }
- // Try to convert a fx column command (&effect) into a volume column command.
- // Returns true if successful.
- // Some commands can only be converted by losing some precision.
- // If moving the command into the volume column is more important than accuracy, use force = true.
- // (Code translated from SchismTracker and mainly supposed to be used with loaders ported from this tracker)
- bool ModCommand::ConvertVolEffect(uint8 &effect, uint8 ¶m, bool force)
- {
- switch(effect)
- {
- case CMD_NONE:
- effect = VOLCMD_NONE;
- return true;
- case CMD_VOLUME:
- effect = VOLCMD_VOLUME;
- param = std::min(param, PARAM(64));
- break;
- case CMD_PORTAMENTOUP:
- // if not force, reject when dividing causes loss of data in LSB, or if the final value is too
- // large to fit. (volume column Ex/Fx are four times stronger than effect column)
- if(!force && ((param & 3) || param >= 0xE0))
- return false;
- param /= 4;
- effect = VOLCMD_PORTAUP;
- break;
- case CMD_PORTAMENTODOWN:
- if(!force && ((param & 3) || param >= 0xE0))
- return false;
- param /= 4;
- effect = VOLCMD_PORTADOWN;
- break;
- case CMD_TONEPORTAMENTO:
- if(param >= 0xF0)
- {
- // hack for people who can't type F twice :)
- effect = VOLCMD_TONEPORTAMENTO;
- param = 9;
- return true;
- }
- for(uint8 n = 0; n < 10; n++)
- {
- if(force
- ? (param <= ImpulseTrackerPortaVolCmd[n])
- : (param == ImpulseTrackerPortaVolCmd[n]))
- {
- effect = VOLCMD_TONEPORTAMENTO;
- param = n;
- return true;
- }
- }
- return false;
- case CMD_VIBRATO:
- if(force)
- param = std::min(static_cast<PARAM>(param & 0x0F), PARAM(9));
- else if((param & 0x0F) > 9 || (param & 0xF0) != 0)
- return false;
- param &= 0x0F;
- effect = VOLCMD_VIBRATODEPTH;
- break;
- case CMD_FINEVIBRATO:
- if(force)
- param = 0;
- else if(param)
- return false;
- effect = VOLCMD_VIBRATODEPTH;
- break;
- case CMD_PANNING8:
- if(param == 255)
- param = 64;
- else
- param /= 4;
- effect = VOLCMD_PANNING;
- break;
- case CMD_VOLUMESLIDE:
- if(param == 0)
- return false;
- if((param & 0xF) == 0) // Dx0 / Cx
- {
- param >>= 4;
- effect = VOLCMD_VOLSLIDEUP;
- } else if((param & 0xF0) == 0) // D0x / Dx
- {
- effect = VOLCMD_VOLSLIDEDOWN;
- } else if((param & 0xF) == 0xF) // DxF / Ax
- {
- param >>= 4;
- effect = VOLCMD_FINEVOLUP;
- } else if((param & 0xF0) == 0xF0) // DFx / Bx
- {
- param &= 0xF;
- effect = VOLCMD_FINEVOLDOWN;
- } else // ???
- {
- return false;
- }
- break;
- case CMD_S3MCMDEX:
- switch (param >> 4)
- {
- case 8:
- effect = VOLCMD_PANNING;
- param = ((param & 0xF) << 2) + 2;
- return true;
- case 0: case 1: case 2: case 0xF:
- if(force)
- {
- effect = param = 0;
- return true;
- }
- break;
- default:
- break;
- }
- return false;
- default:
- return false;
- }
- return true;
- }
- // Try to combine two commands into one. Returns true on success and the combined command is placed in eff1 / param1.
- bool ModCommand::CombineEffects(uint8 &eff1, uint8 ¶m1, uint8 &eff2, uint8 ¶m2)
- {
- if(eff1 == CMD_VOLUMESLIDE && (eff2 == CMD_VIBRATO || eff2 == CMD_TONEPORTAVOL) && param2 == 0)
- {
- // Merge commands
- if(eff2 == CMD_VIBRATO)
- {
- eff1 = CMD_VIBRATOVOL;
- } else
- {
- eff1 = CMD_TONEPORTAVOL;
- }
- eff2 = CMD_NONE;
- return true;
- } else if(eff2 == CMD_VOLUMESLIDE && (eff1 == CMD_VIBRATO || eff1 == CMD_TONEPORTAVOL) && param1 == 0)
- {
- // Merge commands
- if(eff1 == CMD_VIBRATO)
- {
- eff1 = CMD_VIBRATOVOL;
- } else
- {
- eff1 = CMD_TONEPORTAVOL;
- }
- param1 = param2;
- eff2 = CMD_NONE;
- return true;
- } else if(eff1 == CMD_OFFSET && eff2 == CMD_S3MCMDEX && param2 == 0x9F)
- {
- // Reverse offset
- eff1 = CMD_REVERSEOFFSET;
- eff2 = CMD_NONE;
- return true;
- } else if(eff1 == CMD_S3MCMDEX && param1 == 0x9F && eff2 == CMD_OFFSET)
- {
- // Reverse offset
- eff1 = CMD_REVERSEOFFSET;
- param1 = param2;
- eff2 = CMD_NONE;
- return true;
- } else
- {
- return false;
- }
- }
- std::pair<EffectCommand, ModCommand::PARAM> ModCommand::TwoRegularCommandsToMPT(uint8 &effect1, uint8 ¶m1, uint8 &effect2, uint8 ¶m2)
- {
- for(uint8 n = 0; n < 4; n++)
- {
- if(ModCommand::ConvertVolEffect(effect1, param1, (n > 1)))
- {
- return {CMD_NONE, ModCommand::PARAM(0)};
- }
- std::swap(effect1, effect2);
- std::swap(param1, param2);
- }
- // Can only keep one command :(
- if(GetEffectWeight(static_cast<COMMAND>(effect1)) > GetEffectWeight(static_cast<COMMAND>(effect2)))
- {
- std::swap(effect1, effect2);
- std::swap(param1, param2);
- }
- std::pair<EffectCommand, PARAM> lostCommand = {static_cast<EffectCommand>(effect1), param1};
- effect1 = VOLCMD_NONE;
- param1 = 0;
- return lostCommand;
- }
- OPENMPT_NAMESPACE_END
|