401 lines
12 KiB
C
Raw Permalink Blame History

#ifdef CONFIG_MUSIC_ENABLE
#include <includes.h>
#include "ff.h"
#include "app_serv.h"
#include "music_tsk.h"
#include "player.h"
#include "data.h"
#include "datadesc.h"
#include "sdcard.h"
static FATFS FatFs; /* FatFs work area needed for each volume */
static FIL Fil; /* File object needed for each open file */
CPU_INT32U fat_status;
CPU_INT08U music_status;
CPU_INT08U music_cansel;
#define FILE_BUFFER_SIZE 512
/* How many transferred bytes between collecting data.
A value between 1-8 KiB is typically a good value.
If REPORT_ON_SCREEN is defined, a report is given on screen each time
data is collected. */
#define REPORT_INTERVAL 4096
#define REPORT_INTERVAL_MIDI 512
static u_int8 playBuf[FILE_BUFFER_SIZE];
OS_STK MusicTaskStk[MUSIC_TASK_STK_SIZE];
OS_EVENT *MusicQuery = NULL;
void *MusicQueryTbl[MUSIC_QUERY_LEN];
///
int IsMusicPlaying(void)
{
if (music_status == MUSIC_STATUS_PLAYING) return 1;
return 0;
}
///
void MusicCanselPlay(void)
{
music_cansel = 1;
}
///
int GetMusicEvent(int* event)
{
CPU_INT08U err = 0;
int evt = (int)OSQPend(MusicQuery, 1, &err);
if (err != 0) return 0;
*event = evt;
return 1;
}
///
void PostMusicEvent(int event)
{
OSQPost(MusicQuery, (void *)event);
}
enum AudioFormat {
afUnknown,
afRiff,
afOggVorbis,
afMp1,
afMp2,
afMp3,
afAacMp4,
afAacAdts,
afAacAdif,
afFlac,
afWma,
afMidi,
} audioFormat = afUnknown;
const char *afName[] = {
"unknown",
"RIFF",
"Ogg",
"MP1",
"MP2",
"MP3",
"AAC MP4",
"AAC ADTS",
"AAC ADIF",
"FLAC",
"WMA",
"MIDI",
};
enum PlayerStates {
psPlayback = 0,
psUserRequestedCancel,
psCancelSentToVS10xx,
psStopped
} playerState;
int playFile(FIL *readFp, uint16_t volume)
{
UINT bytesInBuffer; // How many bytes in buffer left
u_int32 pos=0; // File position
int endFillByte = 0; // What byte value to send after file
int endFillBytes = 2050; // How many of those to send
long nextReportPos=0; // File pointer where to next collect/report
int i;
playerState = psPlayback; // Set state to normal playback
WriteSci(SCI_DECODE_TIME, 0); // Reset DECODE_TIME
// volume = 0..255
WriteSci(SCI_VOL, volume*0x101);
// Main playback loop
while ((f_read(readFp, playBuf, FILE_BUFFER_SIZE, &bytesInBuffer) == FR_OK) && (bytesInBuffer > 0) &&
(playerState != psStopped))
{
u_int8 *bufP = playBuf;
while (bytesInBuffer && (playerState != psStopped))
{
int t = min(SDI_MAX_TRANSFER_SIZE, bytesInBuffer);
// This is the heart of the algorithm: on the following line
// actual audio data gets sent to VS10xx.
if (WriteSdi(bufP, t) != 0) return -1;
bufP += t;
bytesInBuffer -= t;
pos += t;
// If the user has requested cancel, set VS10xx SM_CANCEL bit
if (playerState == psUserRequestedCancel)
{
unsigned short oldMode;
playerState = psCancelSentToVS10xx;
oldMode = ReadSci(SCI_MODE);
WriteSci(SCI_MODE, oldMode | SM_CANCEL);
}
// If VS10xx SM_CANCEL bit has been set, see if it has gone
// through. If it is, it is time to stop playback.
if (playerState == psCancelSentToVS10xx)
{
unsigned short mode = ReadSci(SCI_MODE);
if (!(mode & SM_CANCEL))
{
playerState = psStopped;
}
}
// If playback is going on as normal, see if we need to collect and
// possibly report
if (playerState == psPlayback && pos >= nextReportPos)
{
nextReportPos += (audioFormat == afMidi || audioFormat == afUnknown) ? REPORT_INTERVAL_MIDI : REPORT_INTERVAL;
/* It is important to collect endFillByte while still in normal
playback. If we need to later cancel playback or run into any
trouble with e.g. a broken file, we need to be able to repeatedly
send this byte until the decoder has been able to exit. */
endFillByte = ReadVS10xxMem(PAR_END_FILL_BYTE);
}
} // if (playerState == psPlayback && pos >= nextReportPos)
if ((music_cansel) &&(playerState == psPlayback))
{
playerState = psUserRequestedCancel;
}
} // while ((bytesInBuffer = fread(...)) > 0 && playerState != psStopped)
// Earlier we collected endFillByte. Now, just in case the file was
// broken, or if a cancel playback command has been given, write
// lots of endFillBytes.
memset(playBuf, endFillByte, sizeof(playBuf));
for (i=0; i<endFillBytes; i+=SDI_MAX_TRANSFER_SIZE)
{
if (WriteSdi(playBuf, SDI_MAX_TRANSFER_SIZE) != 0) return -2;
}
// If the file actually ended, and playback cancellation was not
// done earlier, do it now.
if (playerState == psPlayback)
{
unsigned short oldMode = ReadSci(SCI_MODE);
WriteSci(SCI_MODE, oldMode | SM_CANCEL);
while (ReadSci(SCI_MODE) & SM_CANCEL) WriteSdi(playBuf, 2);
}
// That's it. Now we've played the file as we should, and left VS10xx
// in a stable state. It is now safe to call this function again for
// the next song, and again, and again...
return 0;
}
/// <20><><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
FRESULT find_file(char* dirpath, UINT index, char* filename)
{
FRESULT res;
DIR dir;
static FILINFO fno;
res = f_opendir(&dir, dirpath); // Open the directory
if (res == FR_OK)
{
char index_str[4];
sprintf(index_str, "%03d", index);
for (;;)
{
res = f_readdir(&dir, &fno); // Read a directory item
if (res != FR_OK || fno.fname[0] == 0) break; // Break on error or end of dir
if (!(fno.fattrib & AM_DIR))
{
if (strncmp(fno.fname, index_str, 3) == 0)
{
strcpy(filename, fno.fname);
f_closedir(&dir);
return FR_OK;
}
}
}
f_closedir(&dir);
res = FR_NO_FILE;
}
return res;
}
///
void MusicTask(void *p_arg)
{
CPU_INT32U time_stamp;
int event;
if (f_mount(&FatFs, "", 1) == FR_OK)
{
fat_status = FAT_STATUS_OK;
}
else
{
fat_status = FAT_STATUS_EMPTY;
}
time_stamp = OSTimeGet();
while (1)
{
if (fat_status == FAT_STATUS_OK)
{
CPU_INT32U sound_type;
GetData(&SoundTypeDesc, &sound_type, 0, DATA_FLAG_SYSTEM_INDEX);
if (sound_type != 1)
{
while (GetMusicEvent(&event)); // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
file_menu_cmd = 0;
OSTimeDly(10000);
continue;
}
if (GetMusicEvent(&event))
{
if (event >= MUSIC_EVENT_PLAY_FILE)
{
music_cansel = 0;
if (!player_connected()) player_init();
if (player_connected())
{
char filepath[12 + 1];
if (find_file("/audio", event - MUSIC_EVENT_PLAY_FILE, filepath) == FR_OK)
{
if (f_chdir("/audio") == FR_OK)
{
music_status = MUSIC_STATUS_PLAYING;
if (f_open(&Fil, filepath, FA_READ | FA_OPEN_EXISTING) == FR_OK)
{
CPU_INT32U volume100;
uint16_t volume;
GetData(&SoundVolumeDesc, &volume100, 0, DATA_FLAG_SYSTEM_INDEX);
volume = (255 * (100 - volume100)) / 100;
playFile(&Fil, volume);
f_close(&Fil);
}
f_chdir("/");
music_status = MUSIC_STATUS_STOPPED;
}
}
}
file_menu_cmd = 0;
}
}
else
{
if (OSTimeGet() - time_stamp > 10000)
{
time_stamp = OSTimeGet();
if (f_open(&Fil, "fstab.tmp", FA_READ | FA_OPEN_EXISTING) == FR_OK)
{
UINT rbytes;
if ((f_read(&Fil, playBuf, 10, &rbytes) == FR_OK) && (rbytes == 10))
{
f_close(&Fil);
}
else
{
f_close(&Fil);
fat_status = FAT_STATUS_EMPTY;
f_unmount("");
}
}
else
{
fat_status = FAT_STATUS_EMPTY;
f_unmount("");
}
}
OSTimeDly(10);
}
}
else
{
while (GetMusicEvent(&event)); // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
if (OSTimeGet() - time_stamp > 10000)
{
time_stamp = OSTimeGet();
sd_init();
if (f_mount(&FatFs, "", 1) == FR_OK)
{
UINT rbytes;
if (f_open(&Fil, "fstab.tmp", FA_READ | FA_OPEN_EXISTING) == FR_OK)
{
if ((f_read(&Fil, playBuf, 10, &rbytes) == FR_OK) && (rbytes == 10))
{
if (f_close(&Fil) == FR_OK)
{
fat_status = FAT_STATUS_OK;
}
}
}
if (fat_status != FAT_STATUS_OK)
{
if (f_open(&Fil, "fstab.tmp", FA_WRITE | FA_CREATE_NEW) == FR_OK)
{
if ((f_read(&Fil, playBuf, 10, &rbytes) == FR_OK) && (rbytes == 10))
{
if (f_close(&Fil) == FR_OK)
{
fat_status = FAT_STATUS_OK;
}
}
else
{
UINT wbytes;
if ((f_write(&Fil, playBuf, 10, &wbytes) == FR_OK) && (wbytes == 10))
{
if (f_close(&Fil) == FR_OK)
{
fat_status = FAT_STATUS_OK;
}
}
else
{
f_close(&Fil);
}
}
}
else
{
f_unmount("");
}
}
}
else
{
fat_status = FAT_STATUS_EMPTY;
}
}
else
{
OSTimeDly(10);
}
}
}
}
///
void InitMusicTask(void)
{
if (MusicQuery == NULL)
{
MusicQuery = OSQCreate(&MusicQueryTbl[0], MUSIC_QUERY_LEN);
}
fat_status = FAT_STATUS_NOCONN;
music_status = MUSIC_STATUS_STOPPED;
OSTaskCreate(MusicTask, (void *)0, (OS_STK *)&MusicTaskStk[MUSIC_TASK_STK_SIZE-1], MUSIC_TASK_PRIO);
}
#endif