📄 effects.c
字号:
// -*- tab-width: 4 -*-
// TRAXMOD/ARM Digital Audio Player
//
// Copyright (c) 2006-2008, K9spud LLC.
// http://www.k9spud.com/traxmod/
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "modplay/effects.h"
#include "modplay/music.h"
#include "modplay/mixer.h"
#include "modplay/modplay.h"
euint8 iEffectFlags;
euint8 iLoopRow;
euint8 iLoopCount;
eint16 VibrateTable[] =
{ 0, 24, 49, 74, 97,120,141,161,
180,197,212,224,235,244,250,253,
255,253,250,244,235,224,212,197,
180,161,141,120, 97, 74, 49, 24 };
inline euint8 Hex2Dec(euint8 iData)
{
return ((iData & 0xF0) >> 4) * 10 + (iData & 0x0F);
}
void StartEffect(EventChannelType* eventChannel, euint8 iEffect, euint8 iData)
{
switch(iEffect)
{
case 0x1: // Porta Up
case 0x2: // Porta Down
case 0x3: // Porta to Note
if(iData != 0)
{
eventChannel->iPortaSpeed = iData;
}
eventChannel->iEffect = iEffect;
eventChannel->iEffectData = iData;
break;
case 0x4: // Vibrato
if(iData != 0)
{
eventChannel->iVibratoSpeed = iData;
}
if((eventChannel->iWaveform & 0x0F) < 4)
{
eventChannel->iVibratoPosition = 0;
}
// fall through...
case 0x0: // Arpeggio
case 0x5: // Porta to Note + Volume Slide
case 0x6: // Vibrato + Volume Slide
case 0xA: // Volume Slide
eventChannel->iEffect = iEffect;
eventChannel->iEffectData = iData;
break;
case 0x7: // Tremolo
if(iData != 0)
{
eventChannel->iTremoloSpeed = iData;
}
if(((eventChannel->iWaveform & 0xF0) >> 4) < 4)
{
eventChannel->iTremoloPosition = 0;
}
eventChannel->iEffect = iEffect;
eventChannel->iEffectData = iData;
break;
case 0x8: // Set Pan
eventChannel->iPan = iData;
SetVolume(eventChannel, eventChannel->iVolume);
break;
case 0x9: // Set Sample Offset
eventChannel->flags |= SET_iLoc;
eventChannel->iLoc = (iData << 8);
if(eventChannel->iLoc > eventChannel->sample->iLength)
{
eventChannel->iLoc = eventChannel->sample->iLength;
eventChannel->flags |= SET_ACTIVE;
eventChannel->flags &= ~IS_ACTIVE;
}
break;
case 0xB: // Order Jump
if(iData > iOrder)
{
SetOrder(iData);
}
iEffectFlags |= ORDER_JUMP;
break;
case 0xC: // Set Volume
SetVolume(eventChannel, iData);
break;
case 0xD: // Pattern Break
if((iEffectFlags & ORDER_JUMP) == 0)
{
SetOrder(iOrder + 1);
iEffectFlags |= ORDER_JUMP;
}
iRow = Hex2Dec(iData);
break;
case 0xE:
switch((iData & 0xF0) >> 4)
{
case 0: // Set filter
break;
case 1: // Fine Porta Up
SetPeriodCheckLimits(eventChannel, eventChannel->iPeriod - (iData & 0x0F));
break;
case 2: // Fine Porta Down
SetPeriodCheckLimits(eventChannel, eventChannel->iPeriod + (iData & 0x0F));
break;
case 3: // Set Glissando
break;
case 4: // Set Vibrato Waveform
eventChannel->iWaveform = (eventChannel->iWaveform & 0xF0) | (iData & 0x0F);
break;
case 5: // Set Finetune
eventChannel->iFinetune = (iData & 0x0F);
break;
case 6: // Loop Pattern
if((iData & 0x0F) == 0)
{
// If we are already inside this loop, don't reset Loopcount
if(iLoopCount == 0xFF)
{
// Set Loop Beginning
iLoopRow = iRow - 1;
iLoopCount = 0xFF;
}
}
else
{
// Are we looping for the first time?
if(iLoopCount == 0xFF)
{
iLoopCount = iData & 0x0F;
}
if(iLoopCount--)
{
iRow = iLoopRow;
}
else
{
iLoopCount = 0xFF;
}
}
break;
case 7: // Set Tremolo Waveform
eventChannel->iWaveform = (eventChannel->iWaveform & 0x0F) | ((iData & 0x0F) << 4);
break;
case 8: // Set Pan
eventChannel->iPan = (iData & 0x0F) << 3;
SetVolume(eventChannel, eventChannel->iVolume);
break;
case 9: // Retrigger
case 0xC: // Cut Sample
case 0xD: // Delay sample
eventChannel->iEffect = iEffect;
eventChannel->iEffectData = iData;
break;
case 0xA: // Fine Volume Up
SetVolume(eventChannel, eventChannel->iVolume + (iData & 0x0F));
break;
case 0xB: // Fine Volume Down
SetVolume(eventChannel, eventChannel->iVolume - (iData & 0x0F));
break;
case 0xE: // Delay Pattern
iPatternDelay = iData & 0x0F;
break;
case 0xF: // Invert Loop
break;
}
break;
case 0xF:
if(iData > 32)
{
SetBPM(iData);
}
else if(iData > 0)
{
iTempoPeriod = iData;
}
break;
}
}
/**
* This procedure is called on each frame between rows to update
* effects that may be in process.
*/
void DoEffects(void)
{
for(int i = 0; i < CHANNELS; ++i)
{
EventChannelType *eventChannel = &EventChannel[i];
euint8 iEffect = eventChannel->iEffect;
euint8 iData = eventChannel->iEffectData;
switch(iEffect)
{
case 0x0: // Arpeggio
if(iData)
{
switch(iTempoCount % 3)
{
case 0:
// SetPeriodCheckLimitsDontSetActive(p, p->iPeriod);
break;
case 1:
// SetPeriodCheckLimitsDontSetActive(p, p->
break;
case 2:
break;
}
}
break;
case 0x1: // Porta Up
SetPeriodCheckLimits(eventChannel, eventChannel->iPeriod - eventChannel->iPortaSpeed);
break;
case 0x2: // Porta Down
SetPeriodCheckLimits(eventChannel, eventChannel->iPeriod + eventChannel->iPortaSpeed);
break;
case 0x5: // Porta to Note + Volume Slide
DoVolumeSlide(eventChannel, iData);
// fall through...
case 0x3: // Porta to Note
DoPortaNote(eventChannel);
break;
case 0x6: // Vibrato + Volume Slide
DoVolumeSlide(eventChannel, iData);
// fall through...
case 0x4: // Vibrato
{
int iDelta = 0;
int iPos = eventChannel->iVibratoPosition & 0x1F;
switch(eventChannel->iWaveform & 0x0F)
{
default:
case 3:
case 7:
case 0:
case 4:
iDelta = VibrateTable[iPos];
break;
case 1:
case 5:
iPos <<= 3;
if(eventChannel->iVibratoPosition > 31)
{
iPos = 255 - iPos;
}
iDelta = iPos;
break;
case 2:
case 6:
iDelta = 255;
break;
}
iDelta = (iDelta * (eventChannel->iVibratoSpeed & 0x0F)) >> 7;
if(eventChannel->iVibratoPosition > 31)
{
iDelta += eventChannel->iPeriod;
}
else
{
iDelta = eventChannel->iPeriod - iDelta;
}
SetPeriod(eventChannel, iDelta);
eventChannel->iVibratoPosition += (eventChannel->iVibratoSpeed & 0xF0) >> 4;
if(eventChannel->iVibratoPosition > 64)
{
eventChannel->iVibratoPosition -= 64;
}
}
break;
case 0x7: // Tremolo
{
int iDelta = 0;
int iPos = eventChannel->iTremoloPosition & 0x1F;
switch(((eventChannel->iWaveform) & 0xF0) >> 4)
{
default:
case 3:
case 7:
case 0:
case 4:
iDelta = VibrateTable[iPos];
break;
case 1:
case 5:
iPos <<= 3;
if(eventChannel->iTremoloPosition > 31)
{
iPos = 255 - iPos;
}
iDelta = iPos;
break;
case 2:
case 6:
iDelta = 255;
break;
}
iDelta = (iDelta * (eventChannel->iTremoloSpeed & 0x0F)) >> 6;
if(eventChannel->iTremoloPosition > 31)
{
iDelta += eventChannel->iVolume;
}
else
{
iDelta = eventChannel->iVolume - iDelta;
}
SetVolume(eventChannel, iDelta);
eventChannel->iTremoloPosition += (eventChannel->iTremoloSpeed & 0xF0) >> 4;
if(eventChannel->iTremoloPosition > 64)
{
eventChannel->iTremoloPosition -= 64;
}
}
break;
case 0xA: // Volume Slide
DoVolumeSlide(eventChannel, iData);
break;
case 0xE: // Extended Effects
switch((iData & 0xF0) >> 4)
{
case 0x9: // Retrigger
if((iTempoCount % (iData & 0x0F)) == 0)
{
//SetSample(p, p->iSample);
eventChannel->flags |=
SET_ACTIVE|IS_ACTIVE|SET_iLoc|SET_iLocFract;
eventChannel->iLoc = 0;
eventChannel->iLocFract = 0;
}
break;
case 0xC: // Note Cut
if((iData & 0x0F) == iTempoCount)
{
eventChannel->flags |= SET_ACTIVE;
eventChannel->flags &= ~IS_ACTIVE;
eventChannel->iVolume = 0;
}
break;
case 0xD: // Note Delay
if((iData & 0x0F) == iTempoCount)
{
eventChannel->flags |= SET_ACTIVE|IS_ACTIVE|SET_iLoc;
eventChannel->iLoc = 0;
}
break;
}
break;
}
}
}
void DoPortaNote(EventChannelType* eventChannel)
{
if(eventChannel->iPeriod > eventChannel->iPortaDest)
{
int iPeriod = eventChannel->iPeriod - eventChannel->iPortaSpeed;
if(iPeriod < eventChannel->iPortaDest)
{
iPeriod = eventChannel->iPortaDest;
}
SetPeriodCheckLimits(eventChannel, iPeriod);
}
else if(eventChannel->iPeriod < eventChannel->iPortaDest)
{
int iPeriod = eventChannel->iPeriod + eventChannel->iPortaSpeed;
if(iPeriod > eventChannel->iPortaDest)
{
iPeriod = eventChannel->iPortaDest;
}
SetPeriodCheckLimits(eventChannel, iPeriod);
}
}
void DoVolumeSlide(EventChannelType* eventChannel, euint8 iSpeed)
{
SetVolume(eventChannel, (eventChannel->iVolume - (iSpeed & 0x0F)) + ((iSpeed & 0xF0) >> 4));
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -