📄 main.c
字号:
/* * MP3 Player, Main Program, http://www.pjrc.com/tech/mp3 * Copyright (c) 2000, PJRC.COM, LLC * * 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. * * As a specific exception to the GPL, the executable object code built * from this source code may be combined with hardware configuration data * files. A hardware configuration data file is a set of data that is * transmitted to an intergrated circuit that is not a general purpose * microprocessor or microcontroller, in order to establish its normal * operation. The process of combining the executable ojbect code built * from the GPL licensed source code with the hardware configuration data * shall be considered an aggregation of another work not based on the * Program. While the GPL does not restrict use of the program, any * use restriction associated with the hardware configuration data (for * example, that it only be used with particular hardware) shall apply * to the combined file which includes a copy of the hardware configuration * data. * * Contact: paul@pjrc.com */#include "paulmon2.h"#include "as31glue.h"#include "startup.h"#include "printf.h"#include "display.h"#include "parse.h"#include "malloc.h"#include "dirlist.h"#include "playlist.h"#include "sta013.h"#include "stricmp.h"#include "rand.h"#include "id3.h"#include "params.h"#include "treedir.h"#include <8051.h>#include "main.h"char is_state_normal(unsigned char state);unsigned int get_move_count(char direction);void pl_l_next();void pl_l_prev();void pl_next();void pl_prev();char play_blocks(char mp3_fd);void process_resume();void save_resume_info();void dram_test(void);typedef enum { S_READ_DIR, S_CACHING, S_PLAYING_AND_CACHING, S_PLAYING, S_FINISH_PLAYING, S_PAUSED} state_t;// When debugging, waiting for the drive to spin up can be a pain. Use// the parameter editor to set the spin value to Neverbit dummy_bit;volatile xdata unsigned char playing;unsigned int dir_position;idata unsigned char ibuf[4];xdata simm_id current_file;xdata unsigned long current_file_size=0xFFFFFFFF;char resume;play_modes_t play_mode;unsigned int playlist_index,track_index;// We reset this from the sta013 before we start each song. It's used in display.c// to display elapsed time. -ZSB 12-Aug-2001 3:15 PMunsigned long songStartFrameCount=0;bit update_rand_needed; // Whether timer 3 updates the random seedbit update_mode_needed;bit update_sta013_needed;bit do_id3; // Whether timer 3 scans the ID3 tagvoid main(){ event_t event; char mp3_fd=-1; char r; state_t state; state_t prior_state; char caching_step; xdata struct playlist_list_struct * pl_l_struct; xdata struct playlist_struct * pl_struct; xdata struct filelist_struct * fl_struct; update_rand_needed = 0; do_id3 = 0; /* initialize everything */ init_uart(); // from here forward, use print/printf print("\r\n\r\n\\\[\\A0\\@!0PJRC MP3 Player 0.6.10\\]\r\n\MP3 Player, Version 0.6.10, April 30, 2002\r\n\Copyright (C) 2001, PJRC.COM, LLC\r\n\r\n\Type 'QUIT' to return to the monitor\r\n\r\n"); // indicate EXPERIMENTAL RELEASE on relevant versions //print("\\[\\B &EXPERIMENTAL DEV RELEASE\\]\r\n"); printf("\\[\\C \\B 'Built %s\\]\r\n", __DATE__); playback.mode_known = 0; timer_setup(); param_init(); play_dma_irq_enable(); print("\\[\\B !Waiting for Hard Drive\r\n\r\n\\]\r\n"); while (!ide_init()) ; // keep waiting until init is complete lcd_setup(); parse_init(); if (detect_filesystem() == 0) pm2_exit(); restore_sta013_settings(); update_sta013_needed = 0; debug = 0; print_memory_available(); playlist_init(); print("\\[\\B !Scanning directories \\]\r\n"); print("Building Playlist(s): Scan All Directories:\r\n"); dirlist_init(); treedir_init(); // must be done after root defined print_memory_available(); // these print lots of info, but they take a very long // time to print if the drive has many gigs of files. //dirlist_show(); print("\\[\\B \"Building M3U playlists \\]\r\n"); m3u_list_show(); //process_m3u(); // playlist_show_all(); // loop until we find a non-empty playlist // or reach the end of the playlists list current_playlist = first_playlist; while(current_playlist != 0) { pl_l_struct = addr6(current_playlist); if(pl_l_struct->playlist_start==0) { current_playlist=pl_l_struct->next; } else { break; } } //current_playlist = first_playlist; //pl_l_struct = addr6(current_playlist); if(pl_l_struct->playlist_start == 0) { print("\\[\\B \" - No files found - \\]\r\n"); print("No files found...\r\n"); pm2_exit(); } else { current_file = pl_l_struct->playlist_start; } dir_position = 0; restore_rand_settings(); /* This is the code for a simple resume function. Since there is no way to set resume to 1 via the display, this feature will be off unless explicitly turned on in the code. This is just as well, because at the moment it breaks if the playlist- or prev buttons are used until they cause a rollover. Im thinking about the best way to resolve this issue. */ resume = param_value[PARAM_RESUME]; //resume=1; playlist_index=0; track_index=0; if(resume) { process_resume(); } play_mode = param_value[PARAM_PLAY_MODE]; if (play_mode == MODE_ALLRAND || play_mode == MODE_DIRRAND) { // Since we always start at the first file of the first playlist, // we'll always start with the same song regardless of whether // we've retrieved a random mode from NV storage or not. The next // line forces us to jump forward one track on startup if the saved // mode was random. // // NOTE: If the conditions that determine a random state ever // change (IE in pl_next()), please be // be sure to update those condtions here. // // -ZSB 12-Aug-2001 12:17 AM // play_abort(); // ide_flush(); pl_next(); } else if (play_mode == MODE_PLSTRAND) { // next playlist in playlist random to force a new random playlist pl_l_next(); } update_mode_needed = 0; playing = 1; print("\r\nMP3 Player, Begin Playback\r\n"); lcd_change_menu(0); init_silent_mp3_clip(); lcd_display_id3_init(); /* this infinite loop runs all the actions of the MP3 player */ state = prior_state = S_READ_DIR; while (1) { /* first, process any events from the user interface */ parse_serial_input(); event = get_next_event(); switch (event) { case E_NOTHING: { break; } case E_PLAY_PAUSE: { if (state == S_PAUSED) { print("Play\r\n"); state = prior_state; play_resume(); playing = 1; lcd_change(PARAM_PLAY_MODE); // lcd_play_pause_change(); } else { if (is_state_normal(state)) { print("Pause\r\n"); prior_state = state; state = S_PAUSED; play_suspend(); playing = 0; lcd_change(PARAM_PLAY_MODE); // lcd_play_pause_change(); } else { put_back_event(event); } } break; } case E_NEXT: if (is_state_normal(state)) { print("Next File\r\n"); mute_sta013_output(); play_abort(); ide_flush(); pl_next(); play_silent_mp3_clip(); state = S_FINISH_PLAYING; } else { put_back_event(event); } break; case E_PREV: if (is_state_normal(state)) { print("Previous File\r\n"); mute_sta013_output(); play_abort(); ide_flush(); pl_prev(); play_silent_mp3_clip(); state = S_FINISH_PLAYING; } else { put_back_event(event); } break; case E_VOL_UP: action_vol_up(); break; case E_VOL_DOWN: action_vol_down(); break; case E_NEXT_LIST: if (is_state_normal(state)) { print("Next Play List\r\n"); mute_sta013_output(); play_abort(); ide_flush(); pl_l_next(); play_silent_mp3_clip(); state = S_FINISH_PLAYING; } else { put_back_event(event); } break; case E_PREV_LIST: if (is_state_normal(state)) { print("Previous Play List\r\n"); mute_sta013_output(); play_abort(); ide_flush(); pl_l_prev(); play_silent_mp3_clip(); state = S_FINISH_PLAYING; } else { put_back_event(event); } break; case E_RANDOM: random_mode_next(); lcd_change(PARAM_PLAY_MODE); break; case E_LCD_SCROLL_TIMER: set_timer(TIMER_LCD_SCROLL, SCROLL_SPEED); lcd_timer_callback(); if (state == S_PLAYING_AND_CACHING || state == S_PLAYING) { lcd_screen_dynamic(0); } break; case E_PARAM_WRITE_TIMER: // Any time the various NV parameters are changed, set timer 3 // to a reasonable timeout (like 100 = 10 seconds). That way, // if the user is cycling through an option a bunch of times, // hitting the volume buttons repeatedly, etc., we should // reduce the number of writes to the flash by only saving // them every 10 seconds, not for every single keypress. If // we lose power within the 10 second timeout, the new // settings won't be saved, but that's hardly the end of the // world... -ZSB 11-Aug-2001 11:39 PM eparam_write_flash(); if (update_sta013_needed) { backup_sta013_settings(); update_sta013_needed = 0; } if (update_mode_needed) { ibuf[0] = play_mode; write_flash_param(PARAM_PLAY_MODE, ibuf); update_mode_needed = 0; } if (update_rand_needed) { // First 8 backup_rand_settings(); update_rand_needed = 0; } break; case E_ID3_UPDATE_TIMER: if (do_id3) { process_id3(mp3_fd, current_file_size); do_id3 = 0; } break; default: lcd_user_action(event); break; } do_ide_loop(6); /* and then do the next step to actually play MP3 files */#ifndef NEW_STATE_MACHINE switch(state) { case S_READ_DIR: pl_l_struct = addr6(current_playlist); lcd_set_playlistname(pl_l_struct->name); pl_struct = addr6(current_file); fl_struct = addr5(pl_struct->fl_rec); current_file_size = fl_struct->size; lcd_set_filename(fl_struct->long_name); // uses addr7 mp3_fd = file_open_by_1st_cluster(fl_struct->cluster); file_cache(mp3_fd, 0, current_file_size); state = S_CACHING; caching_step = 0; r = SP; printfd("S_READ_DIR, mp3_fd=%d %x\r\n", mp3_fd, r); songStartFrameCount = GetFrameCount(); play_blocks(-1); set_timer(TIMER_LCD_SCROLL, SCROLL_SPEED); break; case S_CACHING: r = SP; printfd("S_CACHING, mp3_fd=%d %x\r\n", mp3_fd, r); r = file_cache_work(mp3_fd); /* TODO: check return value from file_cache_work */ if (++caching_step > 6) { file_seek(mp3_fd, 0); state = S_PLAYING_AND_CACHING; } break; case S_PLAYING_AND_CACHING: printfd("S_PLAYING_CACHING, mp3_fd=%d\r\n", mp3_fd); r = file_cache_work(mp3_fd); if (r) { state = S_PLAYING; if (param_value[PARAM_DISK_SPIN_DELAY] != 0) { ide_sleep(); } do_id3 = 1; clear_timer(TIMER_ID3_UPDATE); set_timer(TIMER_ID3_UPDATE, 50); // process_id3(mp3_fd, current_file_size); free_fat_memory(); } /* fall through into S_PLAYING's code */ case S_PLAYING: printfd("S_PLAYING\r\n"); if (play_blocks(mp3_fd) != 0) { pl_next(); play_silent_mp3_clip(); state = S_FINISH_PLAYING; } break; case S_FINISH_PLAYING: // Resettings the songStartFrameCount here should // get us a new count at the start of each song printfd("S_FINISH_PLAYING, q=\r\n", play_queue_avail()); if (is_play_queue_empty() && is_ide_idle()) { lcd_display_id3_init(); file_close(mp3_fd); current_file_size = 0xFFFFFFFF; free_fat_memory(); print_memory_available(); if (is_ide_sleeping()) { ide_hard_reset(); while (!ide_init()) ; // wait until init complete } state = S_READ_DIR; } songStartFrameCount = GetFrameCount(); break; case S_PAUSED: if (prior_state == S_PLAYING_AND_CACHING) { r = file_cache_work(mp3_fd); if (r) { prior_state = S_PLAYING; if (param_value[PARAM_DISK_SPIN_DELAY] != 0) { ide_sleep(); } do_id3 = 1; clear_timer(TIMER_ID3_UPDATE); set_timer(TIMER_ID3_UPDATE, 50); // process_id3(mp3_fd, current_file_size); free_fat_memory(); } } break; default: print("ILLEGAL STATE:"); print_hex16(state); print_crlf(); }#else //NEW_STATE_MACHINE // This state machine will control playing data from memory
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -