1116 lines
27 KiB
C
1116 lines
27 KiB
C
/***********************************************************************
|
|
*
|
|
* Copyright (c) 2002 Palm Computing, Inc. or its subsidiaries.
|
|
* All rights reserved.
|
|
*
|
|
***********************************************************************/
|
|
|
|
/***********************************************************************
|
|
*
|
|
* File:
|
|
* PlayForm.c
|
|
*
|
|
* Description:
|
|
* This file manages teh Player form.
|
|
*
|
|
* Version 1.0 - Initial Revision (03/04/03)
|
|
*
|
|
***********************************************************************/
|
|
|
|
#include <PalmOS.h>
|
|
#include <VFSMgr.h>
|
|
#include <HsExt.h>
|
|
#include "Common.h"
|
|
#include "Wave.h"
|
|
#include "PalmSoundMgrExt.h"
|
|
#include "PalmSoundCustomCtrl.h"
|
|
#include "PlayForm.h"
|
|
#include "MyPlayer.h"
|
|
#include "MyPlayerRsc.h"
|
|
|
|
// The only globals for the special sample rate
|
|
static UInt32 gUISampleRate = 8000;
|
|
static Boolean gUseSampleRate = false;
|
|
|
|
/***********************************************************************
|
|
*
|
|
* FUNCTION: SndStreamCallback
|
|
*
|
|
* DESCRIPTION: Sound callback for playback.
|
|
*
|
|
* PARAMETERS: userData - Pointer to user data.
|
|
* streamRef - Reference to the sound stream
|
|
* bufferP - Data buffer
|
|
* bufferSizeP - Pointer to the data buffer size
|
|
*
|
|
* RETURNED: errNone is the function succeeded.
|
|
*
|
|
***********************************************************************/
|
|
|
|
static Err SndStreamCallback(void *userDataP, SndStreamRef streamRef,
|
|
void *bufferP, UInt32 *bufferSizeP)
|
|
{
|
|
Err err = errNone;
|
|
UInt32 dataRead = 0;
|
|
PlayerInfo *myPlayerInfoP = (PlayerInfo *)userDataP;
|
|
|
|
// We expect the user data to be valid
|
|
if( myPlayerInfoP == NULL ) {
|
|
err = -1; //
|
|
goto Done;
|
|
}
|
|
|
|
// Save the actual buffer size
|
|
myPlayerInfoP->actualSize = *bufferSizeP;
|
|
|
|
// This is the output section. We are sending buffers to the sound thread until
|
|
// we read the complete audio file,
|
|
// A FileRead generates an error when the end of file is reached
|
|
// which we want to avoid since there might be some data not played yet.
|
|
if( myPlayerInfoP->fileType == VFS_File ) {
|
|
err = VFSFileRead(myPlayerInfoP->file.fileRef, *bufferSizeP, bufferP, &dataRead);
|
|
} else {
|
|
dataRead = FileRead(myPlayerInfoP->file.fileHandle, bufferP, 1, *bufferSizeP, &err);
|
|
}
|
|
|
|
*bufferSizeP = dataRead;
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// Q: What to do when done reading the file?
|
|
// A: Multiple solutions. Some of them:
|
|
// 1. When dataRead < bufferSize, fill buffer with silence,
|
|
// then a) Send an Alarm or b) Send a Notification
|
|
// => Up to the UI to stop the stream, might leave enough time
|
|
// for the stream to flush.
|
|
// 2. When dataRead = 0, stop the stream,
|
|
// then a) Send an Alarm or b) Send a Notification
|
|
// => Simple.
|
|
// 3. When dataRead < bufferSize, fill 2 or 3 buffers with silence,
|
|
// then stop the stream and a) Send an Alarm or b) Send a Notification
|
|
// => As 1, but the stream handles it's own stops
|
|
// 4. When done, use function pointers to inform the player the file
|
|
// has been played completely.
|
|
// => Faster feedback.
|
|
// 5. Your technique....
|
|
// => Better ? :-)
|
|
//////////////////////////////////////////////////////////
|
|
|
|
// Also, stop the stream if we're done
|
|
if( dataRead == 0 ) {
|
|
|
|
SysNotifyParamType notifyParam;
|
|
PlayerNotification notifyDetail;
|
|
|
|
// Stop the stream
|
|
err = SndStreamStop(streamRef);
|
|
|
|
// Set the notification detials
|
|
notifyDetail.notificationType = DONE_PLAYING;
|
|
notifyDetail.playerInfoP = myPlayerInfoP;
|
|
|
|
// Set the notification info
|
|
notifyParam.notifyType = appCreatorID;
|
|
notifyParam.broadcaster = appCreatorID;
|
|
notifyParam.handled = false;
|
|
notifyParam.notifyDetailsP = ¬ifyDetail;
|
|
|
|
// Send a notification to turn the screen off
|
|
err = SysNotifyBroadcastDeferred(¬ifyParam, sizeof(PlayerNotification));
|
|
}
|
|
|
|
// Check for the end of file
|
|
if( err == vfsErrFileEOF || err == fileErrEOF ) {
|
|
if( dataRead > 0 )
|
|
err = errNone;
|
|
}
|
|
|
|
Done:
|
|
// In case of an error, we should inform the player about it.
|
|
// We could have the same notification as above but with a different notification type.
|
|
// This is sample code... :)
|
|
return err;
|
|
}
|
|
|
|
#if 0
|
|
#pragma mark -
|
|
#endif
|
|
|
|
/***********************************************************************
|
|
*
|
|
* FUNCTION: PrvOpenFile
|
|
*
|
|
* DESCRIPTION: Open a file
|
|
*
|
|
* PARAMETERS: recording - Trus if recording, false if playing
|
|
*
|
|
* RETURNED: Ture if succeeded.
|
|
*
|
|
***********************************************************************/
|
|
|
|
static Boolean PrvOpenFile()
|
|
{
|
|
Err err = errNone;
|
|
Char fullFilePath[256] = { NULL };
|
|
Boolean result = true;
|
|
|
|
// For VFS we need to recreate the full path of the file location
|
|
if(gPlayerInfo.fileType == VFS_File)
|
|
{
|
|
// Create the full path name for the file
|
|
StrPrintF(fullFilePath, "%s/%s", gPlayerInfo.directory, gPlayerInfo.fileName);
|
|
|
|
// Open file for reading
|
|
err = VFSFileOpen(gPlayerInfo.volumeRef, fullFilePath, vfsModeRead, (FileRef *)&gPlayerInfo.file.fileRef);
|
|
}
|
|
// Memory file
|
|
else
|
|
{
|
|
// Open file for reading
|
|
gPlayerInfo.file.fileHandle = FileOpen(0, gPlayerInfo.fileName, sysFileTFileStream, 0, fileModeReadOnly, &err);
|
|
}
|
|
|
|
// Check for errors
|
|
if(err)
|
|
{
|
|
result = false;
|
|
// Since the handle might not be null when
|
|
// an error occurs always fix it.
|
|
gPlayerInfo.file.fileHandle = NULL;
|
|
|
|
StrPrintF(gErrorCode, "Error code: %x", err);
|
|
FrmCustomAlert(PlayErrorAlert, "Error opening the file.", (Char*)gErrorCode, NULL);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/***********************************************************************
|
|
*
|
|
* FUNCTION: PrvCloseFile
|
|
*
|
|
* DESCRIPTION: Close a file
|
|
*
|
|
* PARAMETERS: -
|
|
*
|
|
* RETURNED: Ture if succeeded.
|
|
*
|
|
***********************************************************************/
|
|
|
|
static Boolean PrvCloseFile()
|
|
{
|
|
Err err = errNone;
|
|
Boolean result = true;
|
|
|
|
if(gPlayerInfo.fileType == VFS_File) {
|
|
if(gPlayerInfo.file.fileRef)
|
|
err = VFSFileClose(gPlayerInfo.file.fileRef);
|
|
} else {
|
|
if(gPlayerInfo.file.fileHandle)
|
|
err = FileClose(gPlayerInfo.file.fileHandle);
|
|
}
|
|
|
|
if(err) {
|
|
StrPrintF(gErrorCode, "Error code: %x", err);
|
|
FrmCustomAlert(PlayErrorAlert, "Error closing the file.", (Char*)gErrorCode, NULL);
|
|
result = false;
|
|
}
|
|
return result;
|
|
|
|
}
|
|
|
|
/***********************************************************************
|
|
*
|
|
* FUNCTION: PrvDeleteFile
|
|
*
|
|
* DESCRIPTION: Delete a file
|
|
*
|
|
* PARAMETERS: -
|
|
*
|
|
* RETURNED: Ture if succeeded.
|
|
*
|
|
***********************************************************************/
|
|
|
|
static Boolean PrvDeleteFile()
|
|
{
|
|
Err err = errNone;
|
|
Char fullFilePath[256] = { NULL };
|
|
Boolean result = true;
|
|
|
|
if(gPlayerInfo.fileType == VFS_File)
|
|
{
|
|
// Create the full path name for the file
|
|
StrPrintF(fullFilePath, "%s/%s", gPlayerInfo.directory, gPlayerInfo.fileName);
|
|
|
|
err = VFSFileDelete(gPlayerInfo.volumeRef, fullFilePath);
|
|
}
|
|
else
|
|
{
|
|
err = FileDelete(0, gPlayerInfo.fileName);
|
|
}
|
|
|
|
if(err) {
|
|
StrPrintF(gErrorCode, "Error code: %x", err);
|
|
FrmCustomAlert(PlayErrorAlert, "Error deleting the file.", (Char*)gErrorCode, NULL);
|
|
result = false;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/***********************************************************************
|
|
*
|
|
* FUNCTION: PrvStartStream
|
|
*
|
|
* DESCRIPTION: Function called when user press play.
|
|
* 1) Open a file
|
|
* 2) Parse the wave header
|
|
* 3) Create the sound stream
|
|
* 4) Set the custom control(s)
|
|
* 5) Start the stream
|
|
*
|
|
* PARAMETERS: frmP - Form pointer
|
|
*
|
|
* RETURNED: True if succeeded.
|
|
*
|
|
***********************************************************************/
|
|
|
|
static Boolean PrvStartStream(FormType *frmP)
|
|
{
|
|
Err err = errNone;
|
|
Boolean result = true;
|
|
Char fileInfoText[128] = {NULL};
|
|
UInt32 sampleRate = 0;
|
|
UInt16 bps = 0;
|
|
FieldType *fieldP = FrmGetPtr(frmP, PlayFileField);
|
|
Char *fileName = FldGetTextPtr(fieldP);
|
|
|
|
if( *fileName == NULL ) {
|
|
err = -1;
|
|
StrPrintF(gErrorCode, "Error code: NULL Pointer", err);
|
|
FrmCustomAlert(PlayErrorAlert, "File name cannot be null.", (Char*)gErrorCode, NULL);
|
|
goto Done;
|
|
}
|
|
|
|
// Copy the current field to the file name
|
|
StrCopy(gPlayerInfo.fileName, fileName);
|
|
|
|
// Read the buffer size
|
|
fieldP = FrmGetPtr(frmP, PlayBufferField);
|
|
gPlayerInfo.bufferSize = StrAToI(FldGetTextPtr(fieldP));
|
|
|
|
// Tell the callback the stream direction
|
|
gPlayerInfo.streamMode = sndOutput;
|
|
|
|
// 1. Open a file for recording
|
|
result = PrvOpenFile();
|
|
if(!result)
|
|
goto Done;
|
|
|
|
// 2. Parse the wave file
|
|
err = ReadWAVEHeader(&gPlayerInfo);
|
|
if(err) {
|
|
StrPrintF(gErrorCode, "Error code: %x", err);
|
|
FrmCustomAlert(PlayErrorAlert, "Error parsing the WAVE file.", (Char*)gErrorCode, NULL);
|
|
goto Done;
|
|
}
|
|
|
|
// Select the sample Rate to use
|
|
sampleRate = (gUseSampleRate)?gUISampleRate:gPlayerInfo.sampleRate;
|
|
bps = (gPlayerInfo.streamFormat==sndFormatIMA_ADPCM)?4:(gPlayerInfo.sampleType==sndUInt8)?8:16;
|
|
|
|
// Display the wav information
|
|
StrPrintF(fileInfoText, "%d bps - %ld Hz - %s", bps, gPlayerInfo.sampleRate, (gPlayerInfo.streamWidth==sndMono)?"Mono":"Stereo");
|
|
fieldP = FrmGetPtr(frmP, PlayFileInfoField);
|
|
SetFieldTextFromStr(fieldP, fileInfoText, true);
|
|
|
|
// 3. Create the stream
|
|
err = SndStreamCreateExtended( &gPlayerInfo.streamRef,
|
|
gPlayerInfo.streamMode,
|
|
gPlayerInfo.streamFormat,
|
|
sampleRate,
|
|
(gPlayerInfo.streamFormat==sndFormatIMA_ADPCM)?0:gPlayerInfo.sampleType,
|
|
gPlayerInfo.streamWidth,
|
|
&SndStreamCallback,
|
|
&gPlayerInfo,
|
|
gPlayerInfo.bufferSize,
|
|
false);
|
|
|
|
// Check for errors
|
|
if(err != errNone) {
|
|
StrPrintF(gErrorCode, "Error code: %x", err);
|
|
FrmCustomAlert(PlayErrorAlert, "Error creating the stream.", (Char*)gErrorCode, NULL);
|
|
goto Done;
|
|
}
|
|
|
|
// The volume
|
|
err = SndStreamSetVolume(gPlayerInfo.streamRef, gPlayerInfo.streamVolume);
|
|
if(err != errNone) {
|
|
StrPrintF(gErrorCode, "Error code: %x", err);
|
|
FrmCustomAlert(PlayErrorAlert, "Error setting the volume of the stream.", (Char*)gErrorCode, NULL);
|
|
goto Done;
|
|
}
|
|
|
|
// 4. Custom Control
|
|
if(gPlayerInfo.streamFormat==sndFormatIMA_ADPCM)
|
|
{
|
|
SndCodecCustomControlType ct;
|
|
UInt16 blockAlign = 0;
|
|
|
|
blockAlign = Swap16(gPlayerInfo.blockAlign);
|
|
|
|
ct.apiCreator = Swap32(codecDriverIMAADPCM);
|
|
ct.apiSelector = Swap32(codecIMAADPCMSetBlockSize);
|
|
ct.valueP = (void*)Swap32(&blockAlign);
|
|
ct.valueLenP = NULL;
|
|
|
|
err = SndStreamDeviceControl(gPlayerInfo.streamRef, sndControlCodecCustomControl, &ct, sizeof(ct));
|
|
if(err) {
|
|
StrPrintF(gErrorCode, "Error code: %x", err);
|
|
FrmCustomAlert(PlayErrorAlert, "Error setting the stream custom control.", (Char*)gErrorCode, NULL);
|
|
goto Done;
|
|
}
|
|
}
|
|
|
|
// 5. Start the stream
|
|
err = SndStreamStart(gPlayerInfo.streamRef);
|
|
if(err)
|
|
{
|
|
StrPrintF(gErrorCode, "Error code: %x", err);
|
|
FrmCustomAlert(PlayErrorAlert, "Error while staring the stream.", (Char*)gErrorCode, NULL);
|
|
goto Done;
|
|
}
|
|
|
|
Done:
|
|
if(err) {
|
|
// Close the file
|
|
PrvCloseFile();
|
|
// Delete the stream is needed
|
|
if(gPlayerInfo.streamRef)
|
|
SndStreamDelete(gPlayerInfo.streamRef);
|
|
result = false;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#if 0
|
|
#pragma mark -
|
|
#endif
|
|
|
|
/***********************************************************************
|
|
*
|
|
* FUNCTION: PrvChangeBufferSizeField
|
|
*
|
|
* DESCRIPTION: Set the buffer size text field.
|
|
*
|
|
* PARAMETERS: frmP - Pointer to a form
|
|
* size - The buffer size
|
|
*
|
|
* RETURNED: -
|
|
*
|
|
***********************************************************************/
|
|
/*
|
|
static void PrvChangeBufferSizeField(FormType *frmP, UInt32 size)
|
|
{
|
|
static Char bufferSizeText[10] = { NULL };
|
|
|
|
// Simply convert the size parameter to a string and display it
|
|
FieldType *fieldP = FrmGetPtr(frmP, PlayBufferField);
|
|
|
|
if( fieldP ) {
|
|
StrIToA(bufferSizeText, size);
|
|
SetFieldTextFromStr(fieldP, bufferSizeText, true);
|
|
}
|
|
}
|
|
*/
|
|
|
|
/***********************************************************************
|
|
*
|
|
* FUNCTION: PrvBlinkPause
|
|
*
|
|
* DESCRIPTION: Flash the current track time when in pause mode.
|
|
*
|
|
* PARAMETERS: frmP - Pointer to the current active form
|
|
*
|
|
* RETURNED: -
|
|
*
|
|
***********************************************************************/
|
|
|
|
static void PrvBlinkPause(FormType *frmP)
|
|
{
|
|
UInt16 seconds, minutes;
|
|
Char time[10] = { NULL };
|
|
FieldType* fieldP = FrmGetPtr(frmP, PlayTimeField);
|
|
|
|
// Blink only if enough time ellapsed
|
|
if( (TimGetTicks() - gPreviousBlinkTime) > kBlinkTime)
|
|
{
|
|
gPreviousBlinkTime = TimGetTicks();
|
|
|
|
// Two states: time on/timeoff
|
|
if(gBlinkState)
|
|
{
|
|
seconds = gCurrentTime / sysTicksPerSecond;
|
|
minutes = seconds / 60;
|
|
seconds = seconds - (minutes * 60);
|
|
StrPrintF(time, "%02d:%02d", minutes, seconds);
|
|
SetFieldTextFromStr(fieldP, time, true);
|
|
}
|
|
else
|
|
{
|
|
SetFieldTextFromStr(fieldP, "", true);
|
|
}
|
|
|
|
gBlinkState = 1 - gBlinkState; // Switch flag
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
*
|
|
* FUNCTION: PrvRefreshUI
|
|
*
|
|
* DESCRIPTION: Refresh the current time and actual buffer.
|
|
*
|
|
* PARAMETERS: frmP - Pointer to the current active form
|
|
*
|
|
* RETURNED: -
|
|
*
|
|
***********************************************************************/
|
|
|
|
static void PrvRefreshUI(FormType *frmP)
|
|
{
|
|
UInt16 seconds, minutes;
|
|
Char time[10] = { NULL };
|
|
|
|
// Refresh the time
|
|
FieldType* fieldP = FrmGetPtr(frmP, PlayTimeField);
|
|
if(fieldP) {
|
|
seconds = gCurrentTime / sysTicksPerSecond;
|
|
minutes = seconds / 60;
|
|
seconds = seconds - (minutes * 60);
|
|
StrPrintF(time, "%02d:%02d", minutes, seconds);
|
|
SetFieldTextFromStr(fieldP, time, true);
|
|
}
|
|
|
|
// Refresh the actual buffer size
|
|
fieldP = FrmGetPtr(frmP, PlayActualField);
|
|
if(fieldP) {
|
|
if( gPlayerInfo.actualSize ) {
|
|
StrIToA(time, gPlayerInfo.actualSize);
|
|
SetFieldTextFromStr(fieldP, time, true);
|
|
} else {
|
|
SetFieldTextFromStr(fieldP, "-", true);
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
*
|
|
* FUNCTION: PrvSetButtonBitmaps
|
|
*
|
|
* DESCRIPTION: Changes the button bitmaps depending on the current state.
|
|
*
|
|
* PARAMETERS: frmP - Form pointer
|
|
*
|
|
* RETURNED: -
|
|
*
|
|
***********************************************************************/
|
|
|
|
static void PrvSetButtonBitmaps(FormType *frmP)
|
|
{
|
|
// Change the button icon
|
|
ControlType* controlP = FrmGetPtr(frmP, PlayPlayGraphicButton);
|
|
if( controlP )
|
|
{
|
|
switch(gPlayerInfo.playerState)
|
|
{
|
|
case IDLE_PLAY:
|
|
CtlSetGraphics(controlP, PlayBitmapFamily, PlaySelectedBitmapFamily);
|
|
break;
|
|
case PAUSED_PLAY:
|
|
CtlSetGraphics(controlP, PlayBitmapFamily, PlaySelectedBitmapFamily);
|
|
break;
|
|
case PLAYING:
|
|
CtlSetGraphics(controlP, PauseBitmapFamily, PauseSelectedBitmapFamily);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
*
|
|
* FUNCTION: PrvPlayButtonPressed
|
|
*
|
|
* DESCRIPTION: Handles the player states when Play is pressed.
|
|
*
|
|
* PARAMETERS: frmP - Form pointer
|
|
*
|
|
* RETURNED: -
|
|
*
|
|
***********************************************************************/
|
|
|
|
static void PrvPlayButtonPressed(FormType *frmP)
|
|
{
|
|
Err err = errNone;
|
|
|
|
// State Machine for Play button
|
|
switch(gPlayerInfo.playerState)
|
|
{
|
|
case IDLE_PLAY:
|
|
if( PrvStartStream(frmP) )
|
|
{
|
|
gPlayerInfo.playerState = PLAYING;
|
|
gStartTime = TimGetTicks();
|
|
gCurrentTime = 0;
|
|
gPreviousRun = 0;
|
|
}
|
|
break;
|
|
|
|
case PAUSED_PLAY:
|
|
// Resume the stream
|
|
err = SndStreamPause(gPlayerInfo.streamRef, false);
|
|
if(err) {
|
|
gPlayerInfo.playerState = IDLE_PLAY;
|
|
StrPrintF(gErrorCode, "Error code: %x", err);
|
|
FrmCustomAlert(RecordErrorAlert, "Error resuming the stream.", (Char*)gErrorCode, NULL);
|
|
} else {
|
|
gPlayerInfo.playerState = PLAYING;
|
|
gStartTime = TimGetTicks();
|
|
gPreviousRun = gCurrentTime;
|
|
}
|
|
break;
|
|
|
|
case PLAYING:
|
|
// Pause the stream
|
|
err = SndStreamPause(gPlayerInfo.streamRef, true);
|
|
if(err) {
|
|
gPlayerInfo.playerState = IDLE_PLAY;
|
|
StrPrintF(gErrorCode, "Error code: %x", err);
|
|
FrmCustomAlert(RecordErrorAlert, "Error pausing the stream.", (Char*)gErrorCode, NULL);
|
|
} else {
|
|
gPlayerInfo.playerState = PAUSED_PLAY;
|
|
gBlinkState = 0;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
gPlayerInfo.playerState = IDLE_PLAY;
|
|
break;
|
|
}
|
|
|
|
// Quick refresh
|
|
PrvRefreshUI(frmP);
|
|
PrvSetButtonBitmaps(frmP);
|
|
}
|
|
|
|
/***********************************************************************
|
|
*
|
|
* FUNCTION: PrvStopButtonPressed
|
|
*
|
|
* DESCRIPTION: Handles the player states when Stop is pressed.
|
|
*
|
|
* PARAMETERS: frmP - Form pointer
|
|
*
|
|
* RETURNED: -
|
|
*
|
|
***********************************************************************/
|
|
|
|
static void PrvStopButtonPressed(FormType *frmP)
|
|
{
|
|
Err err = errNone;
|
|
|
|
// Reset some values
|
|
gCurrentTime = 0;
|
|
gPreviousRun = 0;
|
|
gPlayerInfo.actualSize = 0;
|
|
|
|
// State machine for Stop button
|
|
switch(gPlayerInfo.playerState)
|
|
{
|
|
case IDLE_PLAY:
|
|
gPlayerInfo.playerState = IDLE_PLAY;
|
|
break;
|
|
|
|
case PAUSED_PLAY:
|
|
case PLAYING:
|
|
gPlayerInfo.playerState = IDLE_PLAY;
|
|
|
|
// Stop the stream
|
|
err = SndStreamStop(gPlayerInfo.streamRef);
|
|
if(err) {
|
|
gPlayerInfo.playerState = IDLE_PLAY;
|
|
StrPrintF(gErrorCode, "Error code: %x", err);
|
|
FrmCustomAlert(RecordErrorAlert, "Error stopping the stream.", (Char*)gErrorCode, NULL);
|
|
}
|
|
// Delete the stream
|
|
err = SndStreamDelete(gPlayerInfo.streamRef);
|
|
if(err) {
|
|
gPlayerInfo.playerState = IDLE_PLAY;
|
|
StrPrintF(gErrorCode, "Error code: %x", err);
|
|
FrmCustomAlert(RecordErrorAlert, "Error deleting the stream.", (Char*)gErrorCode, NULL);
|
|
}
|
|
// Close the file
|
|
PrvCloseFile();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Quick refresh
|
|
PrvRefreshUI(frmP);
|
|
PrvSetButtonBitmaps(frmP);
|
|
}
|
|
|
|
/***********************************************************************
|
|
*
|
|
* FUNCTION: PrvNewButtonPressed
|
|
*
|
|
* DESCRIPTION: Called when the user presses the new button.
|
|
* Switch to the recoring form.
|
|
*
|
|
* PARAMETERS: frmP - Form pointer
|
|
*
|
|
* RETURNED: -
|
|
*
|
|
***********************************************************************/
|
|
|
|
static void PrvNewButtonPressed(FormType *frmP)
|
|
{
|
|
FrmGotoForm(RecordForm);
|
|
}
|
|
|
|
/***********************************************************************
|
|
*
|
|
* FUNCTION: PrvDeleteButtonPressed
|
|
*
|
|
* DESCRIPTION: Called when the user presses the delete button.
|
|
*
|
|
* PARAMETERS: frmP - Form pointer
|
|
*
|
|
* RETURNED: -
|
|
*
|
|
***********************************************************************/
|
|
|
|
static void PrvDeleteButtonPressed(FormType *frmP)
|
|
{
|
|
UInt16 buttonID = FrmAlert(DeleteFileAlert);
|
|
|
|
if(buttonID == 0) {
|
|
PrvDeleteFile();
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
*
|
|
* FUNCTION: PrvHandleNilEvent
|
|
*
|
|
* DESCRIPTION: Handles nil events. We will refresh the record/play time.
|
|
*
|
|
* PARAMETERS: frmP - Form pointer
|
|
*
|
|
* RETURNED: -
|
|
*
|
|
***********************************************************************/
|
|
|
|
static void PrvHandleNilEvent(FormType *frmP)
|
|
{
|
|
switch(gPlayerInfo.playerState)
|
|
{
|
|
case IDLE_PLAY:
|
|
break;
|
|
case PAUSED_PLAY:
|
|
// Blink the current time
|
|
PrvBlinkPause(frmP);
|
|
break;
|
|
case PLAYING:
|
|
// Refresh the time
|
|
gCurrentTime = TimGetTicks() - gStartTime + gPreviousRun;
|
|
PrvRefreshUI(frmP);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
*
|
|
* FUNCTION: PrvPlayerFormShowMenu
|
|
*
|
|
* DESCRIPTION: Display the player menu.
|
|
*
|
|
* PARAMETERS: command - Menu to handle
|
|
*
|
|
* RETURNED: True if handled
|
|
*
|
|
***********************************************************************/
|
|
|
|
static Boolean PrvPlayerFormShowMenu(UInt16 command)
|
|
{
|
|
Boolean handled = false;
|
|
FormType *frmP = NULL;
|
|
UInt16 cardNo;
|
|
LocalID dbID;
|
|
DmSearchStateType searchState;
|
|
|
|
DmGetNextDatabaseByTypeCreator(true, &searchState, sysFileTApplication,
|
|
appCreatorID, true, &cardNo, &dbID);
|
|
|
|
switch (command)
|
|
{
|
|
case PlayOptionsAbout:
|
|
MenuEraseStatus(0);
|
|
HsAboutHandspringApp(cardNo, dbID, "2006", "Palm DTS Team");
|
|
handled = true;
|
|
break;
|
|
|
|
case PlayOptionsDelete:
|
|
frmP = FrmGetActiveForm();
|
|
PrvDeleteButtonPressed(frmP);
|
|
handled = true;
|
|
break;
|
|
|
|
case PlayOptionsRecord:
|
|
FrmGotoForm(RecordForm);
|
|
handled = true;
|
|
break;
|
|
}
|
|
|
|
return handled;
|
|
}
|
|
|
|
#if 0
|
|
#pragma mark -
|
|
#endif
|
|
|
|
/***********************************************************************
|
|
*
|
|
* FUNCTION: PlayFormInit
|
|
*
|
|
* DESCRIPTION: Form initialiser.
|
|
*
|
|
* PARAMETERS: frmP - Pointer to a form
|
|
*
|
|
* RETURNED: -
|
|
*
|
|
***********************************************************************/
|
|
|
|
static void PlayFormInit(FormType *frmP)
|
|
{
|
|
FieldType *fieldP = NULL;
|
|
ControlType *controlP = FrmGetPtr(frmP, PlayLocationPopTrigger);
|
|
ListType *listP = FrmGetPtr(frmP, PlayLocationList);
|
|
Char text[10];
|
|
|
|
// Initialize the default file string
|
|
if( !*gPlayerInfo.fileName )
|
|
StrCopy(gPlayerInfo.fileName, "default.wav");
|
|
|
|
fieldP = FrmGetPtr(frmP, PlayFileField);
|
|
if( fieldP )
|
|
SetFieldTextFromStr(fieldP, gPlayerInfo.fileName, false);
|
|
|
|
// Initialize the default buffer size
|
|
StrIToA(text, gPlayerInfo.bufferSize);
|
|
fieldP = FrmGetPtr(frmP, PlayBufferField);
|
|
if( fieldP )
|
|
SetFieldTextFromStr(fieldP, "0", false);
|
|
|
|
// Initialize the actual buffer size
|
|
fieldP = FrmGetPtr(frmP, PlayActualField);
|
|
if( fieldP )
|
|
SetFieldTextFromStr(fieldP, "-", false);
|
|
|
|
// Initialize the default time
|
|
fieldP = FrmGetPtr(frmP, PlayTimeField);
|
|
if( fieldP )
|
|
SetFieldTextFromStr(fieldP, "00:00", false);
|
|
|
|
// Initialize the file location field (since we might come from the record form)
|
|
if( controlP && listP ) {
|
|
CtlSetLabel(controlP, LstGetSelectionText(listP, gPlayerInfo.fileType));
|
|
LstSetSelection(listP, gPlayerInfo.fileType);
|
|
}
|
|
|
|
// Initialize the default time
|
|
controlP = FrmGetPtr(frmP, PlayUseSampleRateCheckbox);
|
|
if( controlP )
|
|
CtlSetValue(controlP, gUseSampleRate);
|
|
|
|
// Initialize the sample rate
|
|
controlP = FrmGetPtr(frmP, PlaySampleRatePopTrigger);
|
|
listP = FrmGetPtr(frmP, PlaySampleRateList);
|
|
if( controlP && listP ) {
|
|
UInt16 listPosition = 0;
|
|
switch(gUISampleRate) {
|
|
case 8000:
|
|
listPosition = 0;
|
|
break;
|
|
case 11025:
|
|
listPosition = 1;
|
|
break;
|
|
case 16000:
|
|
listPosition = 2;
|
|
break;
|
|
case 22050:
|
|
listPosition = 3;
|
|
break;
|
|
case 32000:
|
|
listPosition = 4;
|
|
break;
|
|
case 44100:
|
|
listPosition = 5;
|
|
break;
|
|
}
|
|
CtlSetLabel(controlP, LstGetSelectionText(listP, listPosition));
|
|
LstSetSelection(listP, listPosition);
|
|
}
|
|
|
|
// Initialize the volume
|
|
controlP = FrmGetPtr(frmP, PlayVolumeFeedbackSliderControl);
|
|
if( controlP ) {
|
|
UInt16 value = 0;
|
|
UInt16 minValue = 0, maxValue = 0;
|
|
CtlGetSliderValues(controlP, &minValue, &maxValue, NULL, NULL);
|
|
value = (gPlayerInfo.streamVolume * (maxValue - minValue)) >> 11;
|
|
CtlSetValue(controlP, value);
|
|
}
|
|
|
|
// Set the initial state
|
|
gPlayerInfo.playerState = IDLE_PLAY;
|
|
}
|
|
|
|
/***********************************************************************
|
|
*
|
|
* FUNCTION: PlayFormDeInit
|
|
*
|
|
* DESCRIPTION: Form de-initialiser.
|
|
*
|
|
* PARAMETERS: frmP - Pointer to a form
|
|
*
|
|
* RETURNED: -
|
|
*
|
|
***********************************************************************/
|
|
|
|
static void PlayFormDeInit(FormType *frmP)
|
|
{
|
|
Err err = errNone;
|
|
|
|
// Check the current player state
|
|
switch(gPlayerInfo.playerState)
|
|
{
|
|
case PAUSED_PLAY:
|
|
case PLAYING:
|
|
// Stop the stream
|
|
err = SndStreamStop(gPlayerInfo.streamRef);
|
|
if(err) {
|
|
StrPrintF(gErrorCode, "Error code: %x", err);
|
|
FrmCustomAlert(RecordErrorAlert, "Error stopping the stream.", (Char*)gErrorCode, NULL);
|
|
}
|
|
// Delete the stream
|
|
err = SndStreamDelete(gPlayerInfo.streamRef);
|
|
if(err) {
|
|
StrPrintF(gErrorCode, "Error code: %x", err);
|
|
FrmCustomAlert(RecordErrorAlert, "Error deleting the stream.", (Char*)gErrorCode, NULL);
|
|
}
|
|
// Close the file
|
|
PrvCloseFile();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
*
|
|
* FUNCTION: PlayFormHandleEvent
|
|
*
|
|
* DESCRIPTION: Form event handler.
|
|
*
|
|
* PARAMETERS: eventP - Pointer to an event
|
|
*
|
|
* RETURNED: True if event was handled.
|
|
*
|
|
***********************************************************************/
|
|
|
|
Boolean PlayFormHandleEvent(EventType *eventP)
|
|
{
|
|
Boolean handled = false;
|
|
FormType *frmP = FrmGetActiveForm();
|
|
|
|
switch(eventP->eType)
|
|
{
|
|
//
|
|
// Display the menu
|
|
//
|
|
case menuEvent:
|
|
handled = PrvPlayerFormShowMenu(eventP->data.menu.itemID);
|
|
PrvRefreshUI(frmP);
|
|
PrvSetButtonBitmaps(frmP);
|
|
break;
|
|
|
|
//
|
|
// Handle the form open event
|
|
//
|
|
case frmOpenEvent:
|
|
PlayFormInit(frmP);
|
|
FrmDrawForm(frmP);
|
|
handled = true;
|
|
break;
|
|
|
|
//
|
|
// Handle the form close event
|
|
//
|
|
case frmCloseEvent:
|
|
PlayFormDeInit(frmP);
|
|
break;
|
|
|
|
//
|
|
// Handle the control select events
|
|
//
|
|
case ctlSelectEvent:
|
|
switch(eventP->data.ctlSelect.controlID)
|
|
{
|
|
case PlayPlayGraphicButton:
|
|
PrvPlayButtonPressed(frmP);
|
|
handled = true;
|
|
break;
|
|
|
|
case PlayStopGraphicButton:
|
|
PrvStopButtonPressed(frmP);
|
|
handled = true;
|
|
break;
|
|
case PlayNewGraphicButton:
|
|
PrvNewButtonPressed(frmP);
|
|
handled = true;
|
|
break;
|
|
|
|
case PlayDeleteGraphicButton:
|
|
PrvDeleteButtonPressed(frmP);
|
|
handled = true;
|
|
break;
|
|
|
|
case PlayUseSampleRateCheckbox:
|
|
gUseSampleRate = eventP->data.ctlSelect.on;
|
|
handled = true;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
//
|
|
// Handle the pop-up trigger event
|
|
//
|
|
case popSelectEvent:
|
|
switch(eventP->data.popSelect.controlID)
|
|
{
|
|
case PlayLocationPopTrigger:
|
|
gPlayerInfo.fileType = gUIFileTypeTable[eventP->data.popSelect.selection];
|
|
break;
|
|
|
|
case PlaySampleRatePopTrigger:
|
|
gUISampleRate = gUISampleRateTable[eventP->data.popSelect.selection];
|
|
break;
|
|
|
|
case PlayFormPopTrigger:
|
|
if( eventP->data.popSelect.selection )
|
|
FrmGotoForm(RecordForm);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
//
|
|
// Handle control repeat event
|
|
//
|
|
case ctlRepeatEvent:
|
|
|
|
switch(eventP->data.ctlRepeat.controlID)
|
|
{
|
|
case PlayVolumeFeedbackSliderControl:
|
|
{
|
|
UInt16 minValue = 0, maxValue = 0;
|
|
CtlGetSliderValues(eventP->data.ctlRepeat.pControl, &minValue, &maxValue, NULL, NULL);
|
|
gPlayerInfo.streamVolume = ((UInt32)eventP->data.ctlRepeat.value << 11) / (maxValue - minValue);
|
|
if( gPlayerInfo.streamRef ) {
|
|
SndStreamSetVolume(gPlayerInfo.streamRef, gPlayerInfo.streamVolume);
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Handle NilEvent
|
|
//
|
|
case nilEvent:
|
|
PrvHandleNilEvent(frmP);
|
|
handled = true;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return handled;
|
|
}
|
|
|
|
/***********************************************************************
|
|
*
|
|
* FUNCTION: PlayStreamEventNotify
|
|
*
|
|
* DESCRIPTION: Our player own notification system. Used from the callback
|
|
* to stop the timer and change the player state.
|
|
*
|
|
* PARAMETERS: notifyParamP - Notification parameters
|
|
*
|
|
* RETURNED: Error code.
|
|
*
|
|
***********************************************************************/
|
|
|
|
Err PlayStreamEventNotify(SysNotifyParamType *notifyParamP)
|
|
{
|
|
Err err = errNone;
|
|
FormType *frmP = FrmGetActiveForm();
|
|
PlayerNotification* detailsP = (PlayerNotification*)(notifyParamP->notifyDetailsP);
|
|
|
|
// Check the notification type
|
|
switch( detailsP->notificationType ) {
|
|
case DONE_PLAYING:
|
|
// Reset some values
|
|
gCurrentTime = 0;
|
|
gPreviousRun = 0;
|
|
gPlayerInfo.actualSize = 0;
|
|
// Delete the stream
|
|
err = SndStreamDelete(gPlayerInfo.streamRef);
|
|
if(err) {
|
|
gPlayerInfo.playerState = IDLE_PLAY;
|
|
StrPrintF(gErrorCode, "Error code: %x", err);
|
|
FrmCustomAlert(RecordErrorAlert, "Error deleting the stream.", (Char*)gErrorCode, NULL);
|
|
}
|
|
// Close the file
|
|
PrvCloseFile();
|
|
// Change state
|
|
gPlayerInfo.playerState = IDLE_PLAY;
|
|
// Quick refresh
|
|
if( FrmGetActiveFormID() == PlayForm ) {
|
|
PrvRefreshUI(frmP);
|
|
PrvSetButtonBitmaps(frmP);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// We handle the notification
|
|
notifyParamP->handled = true;
|
|
|
|
return errNone;
|
|
}
|
|
|