📄 music.c
字号:
// -*- tab-width: 4 -*-
// TRAXMOD 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 <stdlib.h>
#include "file.h"
#include "main.h"
#include "mmc.h"
#include "modplay/modplay.h"
#include "modplay/mixer.h"
#include "modplay/music.h"
#include "modplay/effects.h"
#include "interfaces/lpc2000_dbg_printf.h"
#define rprintf lpc2000_debug_printf
euint8 iTempoCount = 0;
euint8 iTempoPeriod = 0x06;
euint8 iPatternDelay = 0;
euint8 iRow = 0;
euint8 iPattern = 0xFF;
euint8 iOrder = 0;
EventChannelType EventChannel[CHANNELS];
//eint16 PeriodTable[] =
// { 1712, 1616, 1524, 1440, 1356, 1280, 1208, 1140, 1076, 1016, 960, 907 };
euint16 FinetuneTable[] =
{ 8363,8424,8485,8547,8608,8671,8734,8797,7894,7951,8009,8067,8125,8184,8244,8303 };
void initMusic(void)
{
EventChannel[0].iPan = 0;
EventChannel[1].iPan = 0x80;
EventChannel[2].iPan = 0x80;
EventChannel[3].iPan = 0;
for(int i = 0; i < iNumChannels; ++i)
{
EventChannelType* p = &EventChannel[i];
p->iEffect = 0;
p->iWaveform = 0;
p->iVibratoPosition = 0;
p->iVibratoSpeed = 0;
p->iTremoloPosition = 0;
p->iTremoloSpeed = 0;
}
iPattern = 0xFF;
SetOrder(0);
LoadPattern(iPattern);
iRow = 0;
iTempoCount = 0xFF;
iTempoPeriod = 6;
SetBPM(125);
frameCountdown = 0;
framePointer = &audioBuffer[2];
FrameInterrupt();
for(int i = 0; i < iNumChannels; ++i)
{
EventChannelType* eventChannel = &EventChannel[i];
MixChannelType* channel = &MixChannel[i];
copyEventToChannel(channel, eventChannel);
channel->mixingFrame = 0;
}
FrameInterrupt();
playingFrame = 0;
waitForFrame = 1;
}
/**
* This is not really invoked by any hardware interrupt, but it is called
* by the mixer everytime a frame has been finished mixing. All processing
* between frames must then be initiated by this routine.
*
* @author edwards
* @date 7/30/2005
*/
void FrameInterrupt(void)
{
euint8 originalPattern = iPattern;
for(int i = 0; i < iNumChannels; i++)
{
// reset all mixer action flags
EventChannel[i].flags = 0;
}
if(iTempoCount >= iTempoPeriod)
{
if(iPatternDelay)
{
iPatternDelay--;
DoEffects(); // not 100% sure this should be here.
}
else
{
DoMusic();
FIOCLR = BIT(GREENLED);
}
iTempoCount = 0;
}
else
{
DoEffects();
FIOSET = BIT(GREENLED);
}
waitForFrame = playingFrame + 1;
iTempoCount++;
if(originalPattern != iPattern)
{
LoadPattern(iPattern);
}
frameCountdown += iFramePeriod;
if(frameCountdown < 0)
{
// this isn't supposed to happen. if it does, it means we've been too slow and so
// the audio playback ISR has gotten too far ahead of us.
frameCountdown = 10;
}
}
void LoadPattern(unsigned int iPatNum)
{
// rprintf("Loading Pattern: %u\n", iPatNum);
unsigned int iPatPtr = iPatternPtr + (1024 * iPatNum);
// euint32 iRead =
while(readingData != NULL)
{
// We need to use the SD Card to read a pattern into RAM, but the mixer is
// busy using the SD Card. Wait here until that transfer completes.
finishReading();
}
SD_BeginRead(fileSDPhysicalAddress + iPatPtr, (unsigned char*)Pattern, 1024);
// file_fread( &fp, iPatPtr, 1024, Pattern );
// rprintf("iPatPtr: %X Pattern: %X iRead: %X\n", iPatPtr, (unsigned int)Pattern, iRead);
iPattern = iPatNum;
iLoopRow = 0;
iLoopCount = 0xFF;
// TODO: Fix this so we don't have to block here. We could be doing mixing during this time.
while(BytesLeftToRead != 0);
SD_StopTransmission();
GRAPHDBG('p');
}
void DoMusic(void)
{
if(iOrder >= iNumOrders)
{
bExit = 1;
return;
}
// Clear effects (yes, this is necessary)
for(int i = 0; i < iNumChannels; ++i)
{
EventChannelType *p = &EventChannel[i];
p->iEffect = 0;
}
euint8* pPat = &Pattern[4 * iNumChannels * iRow];
// Increment Row number. We do this here in case
// during playback of the row we get a 0xB position jump effect
// which needs to be able to override current row number.
iRow++;
// rprintf("%X ", iRow);
iEffectFlags = 0;
for(int i = 0; i < iNumChannels; ++i)
{
unsigned int iSample = (pPat[0] & 0xF0) | ((pPat[2] & 0xF0) >> 4);
unsigned int iPeriod = ((pPat[0] & 0x0F) << 8) | pPat[1];
unsigned int iEffect = pPat[2] & 0x0F;
unsigned int iEffectData = pPat[3];
EventChannelType *eventChannel = &EventChannel[i];
if(iSample != 0)
{
SetSample(eventChannel, iSample);
}
if(iPeriod != 0)
{
if(iEffect == 0x3)
{
// Porta to Note
eventChannel->iPortaDest = iPeriod;
eventChannel->iPortaSpeed = iEffectData;
}
else if(iEffect == 0x5)
{
// Porta to Note + Volume Slide
eventChannel->iPortaDest = iPeriod;
}
else if(iEffect == 0xE && ((iEffectData >> 4) == 0xD))
{
// Note Delay
eventChannel->iPeriod = iPeriod;
SetPeriod(eventChannel, iPeriod);
eventChannel->flags |= SET_ACTIVE;
eventChannel->flags &= ~IS_ACTIVE;
}
else
{
eventChannel->iPeriod = iPeriod;
SetPeriod(eventChannel, iPeriod);
eventChannel->flags |= SET_ACTIVE | IS_ACTIVE | SET_iLoc;
eventChannel->iLoc = 0;
}
}
if(iEffect != 0)
{
StartEffect(eventChannel, iEffect, iEffectData);
}
// rprintf("[P:%X S:%X E:%X D:%X Vol: %X] ", iPeriod, iSample, iEffect, iEffectData, p->iVolume);
pPat += 4;
}
// rprintf("\n");
if(iRow >= 64)
{
SetOrder(iOrder + 1);
}
}
void SetOrder(euint8 iNewOrder)
{
iOrder = iNewOrder;
if(iOrder < iNumOrders)
{
iPattern = iOrderTable[iNewOrder];
iRow = 0;
}
}
void SetBPM(unsigned int iNewBPM)
{
// HZ = 2 * BPM / 5
// Period = PlayFreq/ HZ
iFramePeriod = ((iPlayFrequency * 5) / iNewBPM ) >> 1;
}
// AMIGASPD / Period = Sampling Frequency
// Step = Sampling Frequency / DAC Playback Frequency
// scale everything up by 32 bits to get a fractional increment value
#define AMIGASPD (3579545.25 * 0x100000000)
void SetPeriod(EventChannelType* eventChannel, int iPeriod)
{
iPeriod = (8363 * iPeriod) / FinetuneTable[eventChannel->iFinetune];
unsigned int period = (unsigned int) iPeriod;
unsigned long long step = AMIGASPD / iPlayFrequency; //(AMIGASPD * (2^32)) / 44100;
step = step / period;
eventChannel->flags |= SET_iLocFract | SET_STEP;
eventChannel->iLocFract = 0;
eventChannel->iStep = step >> 32;
eventChannel->iStepFract = step & 0xFFFFFFFF;
}
void SetPeriodCheckLimits(EventChannelType* eventChannel, int iPeriod)
{
if(iPeriod)
{
if(iPeriod < 113)
{
iPeriod = 113;
}
else if(iPeriod > 856)
{
iPeriod = 856;
}
eventChannel->iPeriod = iPeriod;
SetPeriod(eventChannel, iPeriod);
}
}
void SetSample(EventChannelType* eventChannel, unsigned int iSample)
{
SampleType* p = &sample[iSample - 1];
eventChannel->iFinetune = p->iFinetune;
eventChannel->flags |= SET_SAMPLE;
eventChannel->sample = &sample[iSample - 1];
// rprintf("Sample: %d Length: %d LoopEnd: %d\n", iSample, p->iLength, p->iLoopEnd);
SetVolume(eventChannel, p->iVolume);
}
void SetVolume(EventChannelType* eventChannel, int iNewVolume)
{
if(iNewVolume < 0)
{
iNewVolume = 0;
}
else if(iNewVolume > MAXVOLUME)
{
iNewVolume = MAXVOLUME;
}
eventChannel->iVolume = iNewVolume;
eventChannel->flags |= SET_VOLUME;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -