⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 music.c

📁 基于lpc2148(arm7)的wav音乐格式播放器的设计
💻 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 + -