📄 mixer.c
字号:
// -*- tab-width: 4 -*-
// TRAXMOD Audio Mixer
//
// 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 "main.h"
#include "mixer.h"
#include "modplay.h"
#include "music.h"
#include "mmc.h"
#include "uart.h"
#include "interfaces/lpc2000_dbg_printf.h"
#define rprintf lpc2000_debug_printf
int mix(MixChannelType *mixChannel, unsigned int iLength);
unsigned int iFramePeriod;
int frameCountdown = 0;
eint16* framePointer;
unsigned int waitForFrame;
//int skipMisaligned = 0x100;
MixChannelType MixChannel[CHANNELS];
StreamDataType StreamData[2];
StreamDataType* readingData = NULL; // data that is being read into RAM and will be ready for mixing eventually
StreamDataType* mixingData = NULL; // data that has been fully read into RAM and ready for mixing
StreamDataType* waitingData = NULL; // data that is has been read into RAM, but is not being mixed until the mixingData buffer is finished mixing.
//euint8 readingChannel = -1;
void startNextReader(void)
{
// Choose the data buffer to read into next: use buffer that is not being mixed.
readingData = &StreamData[0];
if(mixingData == readingData)
{
// mixer is using buffer 0, so lets read into buffer 1 instead.
readingData = &StreamData[1];
}
euint8 bCurrentChannelIsActive = 0;
int nextChannel = -1;
MixChannelType* channel;
/*
for(int i = 0; i < iNumChannels; i++)
{
readingChannel++;
if(readingChannel >= iNumChannels)
{
readingChannel = 0;
}
channel = &MixChannel[readingChannel];
if(channel->bActive)
{
nextChannel = readingChannel;
break;
}
}
*/
int j;
int biggestLength = 0;
int smallestMisalignment = iSD_BlockLength;
for(int i = 0; i < iNumChannels; i++)
{
channel = &MixChannel[i];
if(channel->bActive == 0)
{
// ABORT: this channel is not active, no need to load data for it
continue;
}
if(mixingData == NULL)
{
// mixer starving, give priority to most aligned data channel first.
j = (channel->iSDLocation + channel->iLoc) & 0x1FF;
if(j <= smallestMisalignment)
{
// this channel's data has the smallest block misalignment of all channels.
if(bufferLength(channel->outputPointer, playPointer) < 70)
{
// but this channel doesn't have much room to mix into right now, so it's not really going
// to help us to mix it first. skip it.
if(nextChannel == -1)
{
// well, we don't have any other candidates, so let's put this one up
// on the roster, but don't list it's block misalignment number, in case some
// other channel comes along.
nextChannel = i;
}
}
else
{
nextChannel = i;
smallestMisalignment = j;
}
}
}
else
{
if(i == mixingData->Channel)
{
// ABORT: don't want to re-read the channel that is already read and being mixed.
bCurrentChannelIsActive = 1; // but if it's the only active channel, we might want to use it afterall
continue;
}
// mixer is working, use aggressive priority algorithm instead.
j = bufferLength(channel->outputPointer, playPointer);
if(j > biggestLength)
{
// This channel has more mix buffer to fill than any other channel.
// Make it the next channel to mix.
nextChannel = i;
biggestLength = j;
}
}
}
if(nextChannel == -1)
{
// couldn't find a new channel to mix next.
if(bCurrentChannelIsActive == 0 || waitingData != NULL)
{
// No channels are actively playing, so we don't need to load any more data at the moment.
// Or: the current mix channel is the only one actively playing right now,
// and it already has more data waiting in RAM, so we don't need to read more right now.
readingData = NULL;
GRAPHDBG('R');
return;
}
readingData->Channel = mixingData->Channel;
channel = &MixChannel[readingData->Channel];
// load data one SD Card block ahead of the currently mixing RAM data buffer.
readingData->iSDLoc = mixingData->iSDLoc + iSD_BlockLength;
}
else
{
// we found a new channel to mix next, start loading its data from the SD Card
waitingData = NULL; // dump waiting data (if any), we've got a higher priority channel data to read.
readingData->Channel = nextChannel;
channel = &MixChannel[nextChannel];
// load data beginning at the current mix location of the sample data.
readingData->iSDLoc = channel->iLoc;
if(readingData->iSDLoc > channel->iEnd)
{
// wait a minute, this is past the end of the sample's data, why bother loading this??
rprintf("channel: %d SDLoc: %X Loc: %X End: %X\n", readingData->Channel, readingData->iSDLoc, channel->iLoc, channel->iEnd);
}
}
if(readingData->iSDLoc > channel->iEnd)
{
// wait a minute, this is past the end of the sample's data, why bother loading this??
readingData = NULL;
GRAPHDBG('H');
return;
}
SD_BeginRead(channel->iSDLocation + readingData->iSDLoc, (unsigned char*)readingData->Data, iSD_BlockLength);
/*
// unfortunately, this optimization causes some MODs to crash the player, dunno why.
if(mixingData == NULL)
{
mixingData = readingData;
GRAPHDBG('g');
}
else
{
GRAPHDBG('r');
}
n*/
}
void stopSD(void)
{
if(readingData != NULL)
{
while(BytesLeftToRead != 0); // wait until done reading block
SD_StopTransmission();
readingData = NULL;
}
waitingData = NULL;
mixingData = NULL;
}
void printStatus(int i)
{
MixChannelType* channel;
rprintf("%X - Speed: %X Tempo: %d Row: %X Pat: %X playPtr: %X frmCnt: %d frmPtr: %X playFrame: %X waitForFrm: %X active[",
SSPIMSC, iFramePeriod, iTempoPeriod, iRow, iPattern, playPointer, frameCountdown,
framePointer, playingFrame, waitForFrame);
for(int x = 0; x < iNumChannels; x++)
{
channel = &MixChannel[x];
if(channel->bActive)
{
uart0Putch('1');
}
else
{
uart0Putch('0');
}
}
if(mixingData != NULL)
{
channel = &MixChannel[mixingData->Channel];
rprintf("] mixing: %d[%X %X/%X SDPHY: %X] %X ", mixingData->Channel,
mixingData->iSDLoc, channel->iLoc, mixingData->Length,
channel->iSDLocation + mixingData->iSDLoc, channel->outputPointer);
}
else
{
rprintf("] not mixing ");
}
if(waitingData != NULL)
{
rprintf("waiting: %d[%X] ", waitingData->Channel, waitingData->iSDLoc);
if(waitingData == mixingData)
{
rprintf("waiting==mixing ");
}
}
if(readingData != NULL)
{
rprintf("reading: %d[%X] len: %X(%X)", readingData->Channel, readingData->iSDLoc,
readingData->Length, BytesLeftToRead);
}
else
{
rprintf("not reading");
}
rprintf("\n");
for(int x = 0; x < iNumChannels; x++)
{
channel = &MixChannel[x];
rprintf("%d(frmNo: %X outPtr: %X a? %X)", x, channel->mixingFrame, channel->outputPointer, channel->bActive);
}
rprintf("\n");
}
void finishReading(void)
{
/*
if(readingData->Channel == -1)
{
// abort read early -- mixer has already stopped using this channel data
}
*/
if(BytesLeftToRead == 0)
{
// Done reading next channel's data from SD Card, tell the SD Card to stop
// sending us data.
SD_StopTransmission();
// We have new data read into RAM, mark it as ready to be mixed.
if(readingData->Channel == -1 || mixingData == readingData)
{
// if readingData->Channel equals -1, the mixer has decided to flush this data from
// being mixed (sample looped back before we could use this data).
readingData = NULL;
GRAPHDBG('f');
}
else if(mixingData == NULL || mixingData->Channel != readingData->Channel)
{
// force mixer to switch channels so that we can keep the SD card busy loading
// another channel's data into RAM.
mixingData = readingData;
readingData = NULL; // flag that we're ready to start reading more data
GRAPHDBG('s');
}
else
{
// the next buffer is for the same channel as the currently mixing channel.
// we can not switch RAM buffers until the mixer completely uses up the current RAM buffer.
waitingData = readingData;
readingData = NULL; // flag that we can potentially read more data, if it is higher priority than the waitingData.
GRAPHDBG('w');
}
}
}
int doMix(void)
{
MixChannelType* channel;
// printStatus(0);
if(readingData != NULL)
{
finishReading();
}
if(framePointer != NULL)
{
euint8 stillMixingOldFrame = 0;
EventChannelType* eventChannel;
for(int i = 0; i < iNumChannels; i++)
{
channel = &MixChannel[i];
if(channel->mixingFrame < playingFrame)
{
if((channel->bActive == 0) || (channel->outputPointer == framePointer))
{
eventChannel = &EventChannel[i];
copyEventToChannel(channel, eventChannel);
channel->mixingFrame = playingFrame;
channel->outputPointer = framePointer;
}
else
{
stillMixingOldFrame++;
}
}
}
if(stillMixingOldFrame == 0)
{
FrameInterrupt();
framePointer = NULL;
//printStatus(0);
}
}
if(readingData == NULL)
{
if(filterFlags & FILTER_DIRTY)
{
// give the system a chance to write DAC control registers
return 0;
}
startNextReader();
}
// printStatus(1);
if(mixingData == NULL)
{
// ABORT: no channel data to mix and nothing being read, can't do jack right now.
FIOSET = BIT(REDLED);
for(int i = 0; i < iNumChannels; i++)
{
if(MixChannel[i].bActive)
{
// no data, but this channel is active, show warning red light.
FIOCLR = BIT(REDLED);
return 0;
}
}
return 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -