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

📄 player.c

📁 这项工程将让您把自己的MP3播放器平台
💻 C
📖 第 1 页 / 共 2 页
字号:
// 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 + -