📄 mp3pc.c
字号:
/******************************************************************************
Title: Frank's MP3 Player PlayControl state machine
Author: Frank Van Hooft
Date: 11 February 2004
This file contains the PlayControl state machine. PlayControl is the
controlling brain of the player - it controls the player from one
song to selecting the next song. It is a state machine, and for
simplicity of understanding, each state is actually it's own subroutine
(to avoid having a massive and unreadable single "switch" statement).
The state variable and other variables of interest are initialized
in the PC_init routine.
Currently the states cycle in a fixed and simple order:
Idle -> Play -> Play -> ... -> Idle
In the future, when things like playlists and multiple directories are
supported, additional states may be added.
Take note of the operation of the "stop" command. If a song is playing and
stop is pressed, playing will cease and the state machine will revert to the
idle state. However FindFile is not altered, so if "play" is now pressed
playing will resume with the current song. If however stop is pressed a second
time, ie stop is pressed while we're in the idle state, then we reset the file
number. This provides a means of (a) keeping your place within a large number
of files, and (b) resetting to the start of the directory if desired.
******************************************************************************/
void PlayControl (void)
/* State dispatcher code. */
{
switch (PlayControlState) {
case PC_St_Play:
PlayControl_Play();
return;
case PC_St_Idle:
PlayControl_Idle();
return;
default:
StopOnError(PC_error_unknownstate);
return;
}
}
void PlayControl_Idle (void)
/* Idle state */
/* Responds to the user command "play" by displaying "Now playing:" on the lcd, starting-off
// the playing of the track, and changing to the FindNextFile state. */
/* Responds to the "stop" command by resetting FindFile to the beginning of the directory. */
/* In other words: press "stop" once to stop playing but retain place in the list. Press */
/* "stop" a second time (which is what's detected here) and you reset to beginning of the */
/* directory. Note that this doesn't support playlists at the moment. */
/* Ignores anything else (next, prev, whatever) - converts them to idle commands. */
{
switch (PC_Command) {
case PC_Cmd_Play:
// Check the serial eeprom to ensure there is a non-zero number of MP3 files
// in the current directory - display an error if there are zero MP3s.
if (!(EEPROM_ReadC(StoreNumFilesH)+EEPROM_ReadC(StoreNumFilesL))) {
ClearLCDlines(2, 4); // blank some LCD display lines
PositionLCDcursor(3, 1); // place cursor at start of 3rd display line
uart1_putwaitPROGstr(PSTR("ERROR - Cannot Play. No MP3 files in current directory."));
PC_Command = PC_Cmd_Idle;
return;
}
/* write "Now playing:" on the LCD display */
UART1_TxCharWaitC(0x5c);
UART1_TxCharWaitC(0x42);
UART1_TxCharWaitC(0x20);
UART1_TxCharWaitC(0x21); /* put cursor on start of 2nd row (row #1) */
uart1_putwaitPROGstr(PSTR("Now playing: ")); /* write message on that line; clear next line too */
PlayFileAndDisplay(); // set file playing
PlayControlState = PC_St_Play; /* change to the next state */
return;
case PC_Cmd_Stop:
FileNumber = 1; // 1st "stop" got us to idle state, this 2nd "stop" resets filenumber
PC_Command = PC_Cmd_Idle;
return;
default:
FeedSTcodec_Command = FSC_Cmd_Stop; /* ensure no MP3 q data going to the ST codec */
Q_clear_C((u08*)&MP3DATAQ[0]); /* ensure MP3 data queue empty, ready for next song */
PC_Command = PC_Cmd_Idle;
return;
}
}
void PlayControl_Play (void)
/* Play state */
/* Goal is to "return" as fast as possible if everything playing normally */
/* "Playing normally" means we have a play command AND StreamFile is busily streaming a file. */
/* If StreamFile has finished streaming then we wait until the MP3data queue is empty before */
/* moving on, to ensure the song has finished playing. */
/* And of course, if the command isn't "play" then we deal with that. */
{
u08 c;
switch (PC_Command) {
case PC_Cmd_Play:
if (STREAMFILE_ST != StreamFile_St_Idle)
return; /* return if we're "playing normally", streaming a song */
else if (Q_CheckEmptyC((u08*)&MP3DATAQ[0]) != Q_empty)
return; /* return if StreamFile finished, but data still in MP3data q */
else {
/* we have a play command && streamfile idle && MP3 data q empty, ie we've finished streaming the song */
/* off the disk into the mp3 data q, and we've finished streaming the song out of the q to the codec, */
/* so it's time now for the next song. */
PC_Command = PC_Cmd_Next; // order a "next" command
return;
}
case PC_Cmd_Stop: case PC_Cmd_Idle:
PlayControlStop(); /* deal with user pressing stop or an unexpected idle */
return;
case PC_Cmd_Next:
c = NextFileNumber(); // determine what the next file number should be
if (c!=0) {
PlayControlStop(); // stop playing if we're not allowed to play
return;
}
PlayFileAndDisplay(); // play the file
PC_Command = PC_Cmd_Play; /* clear out the "next" command; we're still playing though */
return;
case PC_Cmd_Prev:
c = PrevFileNumber(); // determine what the next file number should be
if (c!=0) {
PlayControlStop(); // stop playing if we're not allowed to play
return;
}
PlayFileAndDisplay(); // play the file
PC_Command = PC_Cmd_Play; /* clear out the "prev" command; we're still playing though */
return;
case PC_Cmd_Add10:
PCChangeFileNumber(10,1); // Add 10 to FileNumber (jump forwards 10 songs)
PlayFileAndDisplay(); // play the file
PC_Command = PC_Cmd_Play; /* clear out the "Add10" command; we're still playing though */
return;
case PC_Cmd_Add100:
PCChangeFileNumber(100,1); // Add 100 to FileNumber (jump forwards 100 songs)
PlayFileAndDisplay(); // play the file
PC_Command = PC_Cmd_Play; /* clear out the "Add100" command; we're still playing though */
return;
case PC_Cmd_Bak10:
PCChangeFileNumber(10,0); // Subtract 10 from FileNumber (jump backwards 10 songs)
PlayFileAndDisplay(); // play the file
PC_Command = PC_Cmd_Play; /* clear out the "Bak10" command; we're still playing though */
return;
case PC_Cmd_Bak100:
PCChangeFileNumber(100,0); // Subtract 100 from FileNumber (jump backwards 100 songs)
PlayFileAndDisplay(); // play the file
PC_Command = PC_Cmd_Play; /* clear out the "Bak100" command; we're still playing though */
return;
case PC_Cmd_Pause: // if told to pause, do exactly nothing,
return; // just return
default:
/* PC_Command value is unknown */
StopOnError(PC_error_PlayCommand);
return;
}
}
/******************************************************************************
That's it for the states. Various routines used by those states
are below.
******************************************************************************/
void PlayControlStop (void)
// Execute this if the user presses the stop button while we're playing,
// or we run out of files to play.
{
u08 i;
MuteSTcodec(); // quiet down the audio
STREAMFILE_CMD = StreamFile_Cmd_Stop; /* stop reading data off the drive */
FeedSTcodec_Command = FSC_Cmd_Stop; /* stop feeding data to the ST codec chip */
PC_Command = PC_Cmd_Idle; /* clear out the stop command */
PlayControlState = PC_St_Idle; // play control state reverts to idle
/* the following blanks out "Now playing:" & the filename on the LCD display */
UART1_TxCharWaitC(0x5c);
UART1_TxCharWaitC(0x42);
UART1_TxCharWaitC(0x20);
UART1_TxCharWaitC(0x21); /* put cursor on start of 2nd row (row #1) */
for (i=0; i<24*5; i++)
UART1_TxCharWaitC(' '); /* overwrite rows #1 through 5 with spaces */
/* (did say: Now playing: & filename) */
Q_clear_C((u08*)&MP3DATAQ[0]); /* empty the MP3 data queue */
IDE_Standby(); /* tell drive to stop spinning */
DisplayReady(); /* tell user we're ready to do something */
return;
}
u08 PlayFileAndDisplay (void)
// This routine gets the details of the file FileNumber, sets up all the
// various flags, controls, etc, and kicks off the playing of the file. All it
// really looks at is the FileNumber variable. It assumes that a file is currently
// playing - this is the worst-case scenario. It returns zero if all went well.
{
u08 c;
MuteSTcodec(); /* mute the ST codec output */
STREAMFILE_CMD = StreamFile_Cmd_Stop; /* stop reading current song off the drive */
while (STREAMFILE_ST!=StreamFile_St_Idle) StreamFileC(); /* ensure that StreamFileC is idle */
FeedSTcodec_Command = FSC_Cmd_Stop; /* stop giving MP3 q data to the ST codec */
Q_clear_C((u08*)&MP3DATAQ[0]); /* empty the MP3 data queue - get rid of current song data */
c = FindNthMP3(FileNumber); // go and get new file's details
if (c==FindFile_err) {
uart0_putwaitPROGstr(PSTR("FindFile error in PlayFileAndDisplay. File number: $"));
UART_PutHexWaitC((u08)(FileNumber>>8));
UART_PutHexWaitC((u08) FileNumber);
uart0_putwaitPROGstr(PSTR("\r\n"));
FileNumber = 1; // reset to files start if there was a file problem
return 1;
}
GetLongFileName(); // get the file's long filename
FileNameOnLCD(); /* print filename on LCD display */
STREAMFILE_CMD = StreamFile_Cmd_Stop;
while (STREAMFILE_ST!=StreamFile_St_Idle) StreamFileC(); /* ensure that StreamFileC is idle */
STREAMFILE_FILESZ = FINDFILE_RET_SIZE; /* file size */
STREAMFILE_CLUS = FINDFILE_RET_CLUS; /* file starting cluster number */
STREAMFILE_QPOINTER = &MP3DATAQ[0]; /* point StreamFile to the MP3 Data queue */
Q_clear_C((u08*)&MP3DATAQ[0]); /* ensure that Q is empty (yes I'm paranoid) */
STREAMFILE_CMD = StreamFile_Cmd_Stream; /* tell StreamFile to stream the file to the Q */
while (STREAMFILE_ST==StreamFile_St_Idle) StreamFileC(); /* wait until StreamFileC is no longer idle */
// Wait until MP3 data queue is mostly full before allowing MP3 codec chip routine to read out of it
// Note this will hang up with extremely small files (files < 3kB).
while ( Q_HowMuchDataC((u08*)&MP3DATAQ[0]) < (u16)(0.8*MP3DATAQ_size) )
StreamFileC();
FeedSTcodec_Command = FSC_Cmd_Play; /* allow the MP3 data to reach the codec chip */
unMuteSTcodec(); /* unmute the ST codec output */
return 0;
}
u08 NextFileNumber (void)
// Determines NEXT file number. Just adds StepSize to FileNumber, and
// deals with any wraparound (ie FileNumber becoming greater than the number of
// files in the current directory). Returns 0 if filenumber OK to play,
// non-zero otherwise. Non-zero occurs if we're not in random mode AND repeat mode
// is off, and we hit the end of the files. Put another way, if we're playing songs
// normally (not randomly) and repeat mode is off, then when we reach the end of the
// songs we want to stop. We force the stop by returning non-zero.
// Valid values of FileNumber are 1..NumFiles
{
u16 NumFiles;
/* determine how many files in the player */
NumFiles = (((u16)EEPROM_ReadC(StoreNumFilesH))<<8) + EEPROM_ReadC(StoreNumFilesL);
// Now step forward to the next file. If we step beyond the end of the files, wrap
// around to the beginning again. Check to see if we're supposed to wrap around,
// or just stop playing.
FileNumber += StepSize;
if (FileNumber <= NumFiles)
return 0; // if no wraparound occured then we're done
else {
// execute here if a wraparound occured
FileNumber -= NumFiles; // correct FileNumber to deal with the wraparound
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -