📄 player.c
字号:
// or backwards as required.
//--
PUBLIC void PlayOneMP3 (PXDIRENT pxDirEnt)
{
PIBCB pBCB; BOOL fFinished; BOOL fPaused;
#ifdef DEBUG
BYTE bFreeBufferCount;
#endif
// This is handy for debugging memory leaks in the player, not that anything
// like that has ever happened to _me_ !
#ifdef DEBUG
bFreeBufferCount = ShowBufferPool();
//DBGOUT(("PlayOneMP3: Starting with %bd buffers\n", bFreeBufferCount));
#endif
// Open the MP3 file, initialize the STA013 data pump, start the STA013,
// and then start the elapsed time counter. Since the STA013 doesn't have
// any feature to actually report the current running time of the song, we
// simply time it independently of the STA013...
DBGOUT(("PlayOneMP3: playing file %8.8s.%3.3s ...\n", pxDirEnt->szFileName, pxDirEnt->szFileType));
OpenFile(pxDirEnt);
InitializeMP3DRQ(); fPaused = FALSE;
StartMP3Decoder();
ResetElapsedTime(); StartElapsedTimer();
// Keep playing until (what else??) we're finished. The main purpose of
// this loop is to keep the STA013 supplied with data from the MP3 file.
// The secondary purpose, which we do as time permits, is to "pump" the
// UI - update the LCD/VFD display and check the knob/button for activity.
for (fFinished = FALSE; !fFinished; ) {
// Finally, here it is, all the magic happens here. The basic plan is
// that the background code (that's us!) continuously trys to remove empty
// disk buffers from the free list, read the next file sector into them,
// and then put the filled buffers on the STA013 play queue. The foreground
// task (that's the MP3DRQ interrupt routine) removes filled disk buffers
// from the play list and sends their data to the STA013. So long as we
// stay ahead of them, everything will be fine...
INT_OFF; ALLOCATE_BUFFER(pBCB); INT_ON;
if (pBCB != NULL) {
pBCB->cbBuffer = ReadFile(pBCB->pbBuffer);
fFinished = (pBCB->cbBuffer) < IDE_SECTOR_SIZE;
//DBGOUT(("PlayOneMP3: read buffer pBCB=0x%02bX, pbBuffer=0x%04X, cbBuffer=%u\n",
// (BYTE) pBCB, (WORD) pBCB->pbBuffer, pBCB->cbBuffer));
EX0 = 0; ADD_TO_QUEUE(pBCB,g_pPlayListHead,g_pPlayListEnd); EX0 = 1;
}
// If the elapsed time timer has ticked (it happens once a second), then
// update the display with the current runtime, bit rate, and other status.
// Note that in the case of VBR files, the bit rate isn't constant and will
// change with every update!
if (IsTimerChanged()) ShowPlaybackStatus(fPaused);
// In the current UI, the button is a simple pause/resume function (push
// it once and the playback pauses; push it again and playback resumes).
// This operation is implemented entirely here - when the button is pushed
// we pause the STA013 but continue looping. Eventually all the free buffers
// will be filled and placed on the play list and nothing more will happen
// until the button is pressed again. When that happens the STA013 is
// restarted and playback continues...
if (g_bButtonTime > 0) {
// Let's think about this a minute - the button is down right this moment.
// If we simply process that fact and do nothing more, then we'll come right
// back here and think that the button was pressed a second time. To prevent
// this we have to wait for the button to be released, but waiting prevents
// any disk buffers from being filled! Turns out that this doesn't matter
// much - if we're playing now, then the STA013 will go silent when it runs
// out of data, but we're going to pause anyway so nobody will notice. If
// we paused now, then the STA013 isn't playing anyway and it doesn't need
// any data...
while (g_bButtonTime > 0) ;
// Now that that's over, and the rest is easy...
fPaused = !fPaused;
if (fPaused) {
PauseMP3(); StopElapsedTimer();
DBGOUT(("PlayOneMP3: playback paused ...\n"));
} else {
ResumeMP3(); StartElapsedTimer();
DBGOUT(("PlayOneMP3: playback resumed ...\n"));
}
// Update the display to show PAUSED or the current status...
ShowPlaybackStatus(fPaused);
}
// If the encoder has been turned, then the user wants to skip to either
// the next or previous song. Doesn't matter to us - we simply abort the
// current song and let the caller worry about that...
if (g_nEncoderCount != 0) {
ResetMP3Decoder(); FlushPlayList(); fFinished = TRUE;
DBGOUT(("PlayOneMP3: playback aborted ...\n"));
}
}
// WAIT for the play queue to drain!!
while (!IsPlayListEmpty()) ;
StopMP3Decoder();
ASSERT( (ShowBufferPool() == bFreeBufferCount), ("PlayOneMP3: BUFFERS HAVE DISAPPEARED!!\n") );
CloseFile();
DBGOUT(("PlayOneMP3: finished playing %8.8s.%3.3s\n", pxDirEnt->szFileName, pxDirEnt->szFileType));
}
PUBLIC void PlayAllMP3s (void)
{
PXDIRENT pxDirEnt;
pxDirEnt = FirstMP3File();
ShowPlaybackNames(pxDirEnt);
while (pxDirEnt != NULL) {
PlayOneMP3(pxDirEnt);
if (g_nEncoderCount > 0) {
while (g_nEncoderCount > 0) {
pxDirEnt = NextMP3File();
if (pxDirEnt == NULL) return;
ShowPlaybackNames(pxDirEnt);
if (--g_nEncoderCount == 0) DelayMS(500);
}
} else if (g_nEncoderCount < 0) {
ShowPlaybackNames(pxDirEnt);
if (++g_nEncoderCount == 0) DelayMS(750);
while (g_nEncoderCount < 0) {
pxDirEnt = PrevMP3File();
if (pxDirEnt == NULL) return;
ShowPlaybackNames(pxDirEnt);
if (++g_nEncoderCount == 0) DelayMS(500);
}
} else {
pxDirEnt = NextMP3File();
ShowPlaybackNames(pxDirEnt);
}
}
}
//++
// This routine initializes the system and all modules, both hardware and
// software. It's only ever called once, as the first thing in main(). Note
// that the POST is implict in many of these routines - for examine, Initialize-
// STA013() also tests the STA013 and POSTs an error code if it's not working.
//--
PRIVATE void InitializeSystem (void)
{
P0 = P1 = P2 = P3 = 0xFF;
// The LED is carefully arranged so that it is ON after a CPU reset (that's
// because RESET floats all the I/O port pins, and a HIGH output turns the
// LED on). By turning it OFF as the first thing, it lets us know that the
// CPU is at least alive and running...
LED_OFF;
// If this is the debug version then turn on the 89C420's secondary serial
// port and say hello...
#ifdef DEBUG
InitializeDebugSerial();
printf("\n\n%s V%03u ROM %bdK Checksum %04X\n%s\n\n",
g_szFirmware, (WORD) VERSION, (BYTE) (ROMSIZE >> 10), g_wROMChecksum, g_szCopyright);
#endif
// Test the CPU's internal flash and verify the code checksum...
if (ROMChecksum(ROMSIZE) != g_wROMChecksum) POST(PC_CHECKSUM, FALSE);
DBGOUT(("Flash ROM checksum OK ...\n"));
// Turn off ALE and enable the 89C66x internal XRAM. Test the size of the
// SRAM and if there aren't at least 1792 bytes of internal RAM (a P89C664)
// then just POST and give up. There's not enough room for disk buffers in
// anything less...
AUXR = AO;
DBGOUT(("XRAM size %u bytes ...\n", RAMSize()));
if (RAMSize() < 1792) POST(PC_SRAM, FALSE);
// Initialize all interrupts to low priority, disable all the individual
// interrupt sources, and then set the global interrupt enable. This allows
// individual devices to turn on their own interrupt enable bits as soon as
// they are ready...
IP = IPH = IE = IE1 = 0; EA = 1;
// Initialize the other software and hardware modules....
InitializeTimer(); // the interval timer and elapsed time meter
InitializeDisplay(); // the LCD or VF display
InitializeSwitches(); // the rotary encoder and push button polling
InitializeSTA013(); // the (in)famous STA013
// This call will initialize the free buffer pool in XDATA RAM and the parameter
// specifies how many bytes of XDATA to reserve for static variables (i.e. anything
// you may see in one of these modules declared with XDATA!). This value is really
// just an approximation - it doesn't matter so long as it's at least as big as
// what we actually use. It'd be nice if there was a way to have the linker set
// this for us, but I don't know how.
//
// By the way, there's no point in trying to fine tune this value - since
// buffers are always 512 bytes long, unless you can reduce it to 256 bytes
// or less (pretty much impossible with the current code), there won't be
// enough room for another buffer anyway. In fact, any value between 256 and
// 767 will give the same results!
InitializeBuffers(512+256-1);
// That's it - we're as ready as we'll ever be...
DBGOUT(("System initialization complete ...\n\n"));
}
//++
// System initialization and startup...
//--
void main (void)
{
InitializeSystem();
ShowCopyright();
// Make sure a readable CF card is inserted. Since there's currently no way
// to hot swap cards, if there's anything wrong with the one we've got all we
// can do is loop forever until the power is turned off.
if (!IsCardReady()) HALT;
DBGOUT(("Playing (%u MP3 files found) ...\n\n", g_wTotalMP3Files));
while (TRUE) PlayAllMP3s();
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -