When PlayCurrentFile() is called, the Ogg Vorbis decoder software routine becomes the "master" of the system. It runs, asking for blocks of data from the logical disk mapper as needed, and outputs samples to the audio sample buffer by calling AudioOutputSamples().
When there is free CPU time, the Vorbis decoder calls the Idle Hook, which is a RAM vector, which by default points to a ROM handler UserInterfaceIdleHook:
Code: Select all
void UserInterfaceIdleHook(void) {
if (uiTrigger) {
uiTrigger = 0;
KeyScan();
// Update LEDs
{
register u_int16 leds = LED1;
if (player.pauseOn > 0 && (uiTime & 0x06) != 0) {
leds = 0; /* power led flashes when in pause mode */
}
if (player.randomOn) {
leds |= LED2;
}
PERIP(GPIO1_ODATA) = (PERIP(GPIO1_ODATA) & ~(LED1 | LED2)) | leds;
}
}
}
Code: Select all
/* KeyScan handles
1) usb attach detection -- cancels playback and pause if attached
2) Actual reading of keys.
3) Key event handling -- hook-up the KeyEventHandler() if you don't like it.
*/
void KeyScan(void) {
register u_int16 check = 0;
register u_int16 keyNew;
if (USBIsAttached()) {
cs.cancel = 1;
player.pauseOn = 0;
}
keyNew = ReadGPIO() & 31;
if (PERIP(SCI_STATUS) & SCISTF_REGU_POWERBUT) {
keyNew |= KEY_POWER;
}
if (keyNew == keyOld) {
if (++keyOldTime >= SHORT_LIMIT) {
/* long press */
check = keyOld | KEY_LONG_PRESS;
if (keyOld & KEY_POWER) {
/* Any combination containing power key requires longer press*/
if (keyOldTime < OFF_LIMIT) /* 2 seconds */
check = 0;
}
}
} else {
if ((s_int16)keyOldTime >= 0 && keyOldTime < SHORT_LIMIT) {
/* Short press: was pressed < 0.5 seconds */
check = keyOld;
} else {
check = KEY_RELEASED | KEY_LONG_PRESS; /*any long press released!*/
}
keyOld = keyNew;
keyOldTime = 0;
}
if (check) {
register const struct KeyMapping *p = currentKeyMap;
keyCheck = check; /* for polled key handling */
while (p->key) {
if (check == p->key &&
(p->event > 0 || keyOldTime <= SHORT_LIMIT)) {
KeyEventHandler(p->event & 0x7fff);
}
p++;
}
}
}
auto u_int16 ReadGPIO(void) {
register u_int16 i;
register u_int16 mode = PERIP(GPIO0_MODE);
PERIP(GPIO0_MODE) = mode & 0xff00U;
/* Wait a bit... about 200us */
for (i=0;i<10000;i++)
PERIP(GPIO0_IDATA);
i = PERIP(GPIO0_IDATA) & 255;
PERIP(GPIO0_MODE) = mode;
return i;
}
Code: Select all
void RealKeyEventHandler(enum keyEvent event) {
switch (event) {
case ke_previous:
if (cs.playTimeSeconds < 5) {
player.nextFile = player.currentFile - 1;
} else {
player.nextFile = player.currentFile;
}
player.nextStep = -1; /* skip unsupported files backward */
cs.cancel = 1; /* jump to the new file */
player.pauseOn = 0;
break;
case ke_next:
player.nextStep = 1; /* skip unsupported files forward */
cs.cancel = 1;
player.pauseOn = 0;
break;
case ke_rewind:
VorbisSkip(-5);
break;
case ke_forward:
VorbisSkip(5);
break;
case ke_volumeUp2:
player.volume -= 1; /* volume up 1.0dB */
/* fall through! */
case ke_volumeUp:
player.volume -= 1; /* volume up 0.5dB */
goto volCheck;
case ke_volumeDown2:
player.volume += 1; /* volume down 1.0dB */
/* fall through! */
case ke_volumeDown:
player.volume += 1; /* volume down 0.5dB */
volCheck:
if (player.volume < -24) {
player.volume = -24;
} else if (player.volume > 180) {
player.volume = 180;
}
PlayerVolume();
break;
case ke_earSpeaker: /* 0, 18000U, 36000U, 54000U */
if (earSpeakerReg >= 47535U) {
earSpeakerReg = 0U;
} else {
earSpeakerReg += 18000U;
}
break;
case ke_earSpeakerToggle: /* 0, 36000U */
if (earSpeakerReg) {
earSpeakerReg = 0U;
} else {
earSpeakerReg = 36000U;
}
break;
case ke_randomToggleNewSong:
if (!player.randomOn) {
cs.cancel = 1;
player.pauseOn = 0;
}
/* fall through! */
case ke_randomToggle:
player.randomOn ^= 1;
srand(PERIP(TIMER_T0CNTL));
break;
case ke_pauseToggle:
player.pauseOn = player.pauseOn ? 0 : 1;
break;
case ke_powerOff:
PowerOff(); /* Can now be powered off even when attached to USB */
break;
case ke_ff_faster:
player.ffCount++;
ff_action:
{
register s_int16 speed = 2 + (QsortLog2(player.ffCount/8));
if (speed > 5) {
cs.fastForward = 1;
VorbisSkip(speed);
} else {
cs.fastForward = speed;
}
}
break;
case ke_ff_slower:
if (player.ffCount) {
--player.ffCount;
goto ff_action;
}
/* fall through! */
case ke_ff_off:
cs.fastForward = 1;
player.ffCount = 0;
break;
default:
break;
}
}
Code: Select all
enum keyEvent {
ke_null = 0,
ke_previous,
ke_next,
ke_rewind,
ke_forward,
ke_volumeUp, /* +0.5dB */
ke_volumeDown, /* -0.5dB */
ke_earSpeaker,
ke_earSpeakerToggle,
ke_randomToggle,
ke_randomToggleNewSong,
ke_pauseToggle,
ke_powerOff,
ke_ff_faster, /* increase play speed */
ke_ff_slower, /* decrease play speed */
ke_ff_off, /* back to normal play speed */
ke_volumeUp2, /* +1.0dB */
ke_volumeDown2,/* -1.0dB */
};
extern struct Player {
s_int16 totalFiles;
s_int16 currentFile;
s_int16 nextFile;
s_int16 nextStep; /* 1 = next file, -1 = previous file */
s_int16 pauseOn;
s_int16 randomOn;
s_int16 volume;
s_int16 volumeOffset;
u_int16 offDelay; /* pause timeout in 5 sec increments -> power off */
u_int16 ffCount;
u_int16 maxClock; /* max clock used by player: 7=3.5x, 6=3.0x etc.. */
} player;
struct CodecServices {
/** Version number. 8 MSBs contain version number, 8 LSBs size of
the structure in words. */
u_int16 version;
/** Read data from a file. If \a firstOdd is set, only the LSB of the
first word is filled. If the last word is not completely filled
(either \a firstOdd set or \a bytes is odd, but not both),
only the MSB is changed. */
u_int16 (*Read)(struct CodecServices *cs, u_int16 *ptr,
u_int16 firstOdd, u_int16 bytes);
/** Skip data in a file. This should also be supported for streams. */
u_int32 (*Skip)(struct CodecServices *cs, u_int32 bytes);
/** Seek in a file. \a offset and \a whence are equivalent with their
fseek() counterparts.
If \a Seek is NULL, the input is a stream and cannot be seeked. */
s_int16 (*Seek)(struct CodecServices *cs, s_int32 offset, s_int16 whence);
/** Tell location in a file (in bytes). */
s_int32 (*Tell)(struct CodecServices *cs);
/** Output to audio file.
\a data is a pointer to the data. There are \a n \a chan channel samples
(Example: \a n = 32, \a cs->chan = 2, there are a total of 64 samples in
\a data). */
s_int16 (*Output)(struct CodecServices *cs, s_int16 *data, s_int16 n);
/** Offers comments fields one character at the time. Special code
0x4000U is reserved for end-of-line. 0xC000U means end of comments. */
void (*Comment)(struct CodecServices *cs, u_int16 c);
/** Spectrum analyzer hook. */
void (*Spectrum)(struct CodecServices *cs, s_int16 __y *data, s_int16 n,
s_int16 ch);
/** File size in bytes. If set to 0xFFFFFFFFU, then the file is a
stream and the file size is not known. */
u_int32 fileSize;
/** Bytes left in a file. If set to 0xFFFFFFFFU, then the file is a
stream and never ends. */
u_int32 fileLeft;
/** Point to move to in an audio file in seconds. When set to
anything other than 0xFFFFU, the codec has the responsibility
to jump to that point.
The codec has the freedom of deciding for itself the actual
landing point in the file. When the codec has reached its
destination it will clear this variable, and the actual position
in the file can be read from \a playTimeSeconds. If the codec
cannot jump to a given point (e.g. the file is a stream and the
jump point would require jumping backwards), goTo is silently
cleared to 0xFFFFU.
*/
u_int16 goTo;
/** Request codec to cancel playing current file.
To request cancellation, set this to a non-zero positive value. When the
codec has finished cancelling, it will clear this value and
return ceCancelled.
*/
s_int16 cancel;
/** Playback time from beginning of file in seconds. Updated by the codec.
If set to -1, the codec doesn't know where it is in the file. */
s_int32 playTimeSeconds;
/** Samples played since last full second. Updated by the codec. If set to
-1, the codec doesn't know where it is in the file. */
s_int32 playTimeSamples;
/** Total playback time in seconds. Updated by the codec. May be a
changing estimate if an exact figure isn't available. 0xFFFFFFFFU
if there is no information available. */
u_int32 playTimeTotal;
/** Sample rate. Updated by the codec. 0 if unknown. */
u_int32 sampleRate;
/** Number of channels. Updated by the codec. */
u_int16 channels;
/** Channel matrix. Updated by the codec. */
enum ChannelMatrix matrix[MAX_SOURCE_CHANNELS];
/** Average bitrate. Updated by the codec. May change during playback. */
u_int32 avgBitRate;
/** Current bitrate. Updated by the codec. May change during playback. */
u_int32 currBitRate;
/** Peak bitrate of the file. Updated by the codec.
May change during playback. */
u_int32 peakBitRate;
/** Volume gain recommended by codec in 1/2 dB steps. Updated by the codec.*/
s_int16 gain;
/** Request codec to fast forward current file. If set, playback is
requested at fastForward times normal playback speed.
To stop fast forwarding, set value to 1 or 0. */
u_int16 fastForward;
};
To adjust the user interface, you can
- declare a new keymap, as explained in the Programmer's Guide,
- hook your own UserInterfaceIdleHook, and/or
- hook you own KeyHandler.
You can study our example codes to see alternative versions for these.
Have fun experimenting!
-Panu