*it still lags*

This commit is contained in:
CrashOveride95
2020-12-18 22:36:09 -05:00
parent 84c58a66d2
commit dba8608561
15 changed files with 617 additions and 243 deletions

View File

@@ -230,7 +230,7 @@ ACTOR_DIR := actors
LEVEL_DIRS := $(patsubst levels/%,%,$(dir $(wildcard levels/*/header.h)))
# Directories containing source files
SRC_DIRS := src src/usb src/engine src/game src/audio src/menu src/buffers actors levels bin data assets asm lib sound
SRC_DIRS := src src/usb src/engine src/game src/hvqm src/audio src/menu src/buffers actors levels bin data assets asm lib sound
BIN_DIRS := bin bin/$(VERSION)
# File dependencies and variables for specific files

View File

@@ -85,6 +85,9 @@ SECTIONS
BEGIN_NOLOAD(buffers)
{
BUILD_DIR/src/buffers/buffers.o(.bss*);
BUILD_DIR/src/buffers/hvqmwork.o(.bss*);
BUILD_DIR/src/buffers/adpcmbuf.o(.bss*);
BUILD_DIR/src/buffers/hvqbuf.o(.bss*);
BUILD_DIR/src/audio/globals_start.o(.bss*);
BUILD_DIR/src/audio/synthesis.o(.bss*);
BUILD_DIR/src/audio/heap.o(.bss*);
@@ -116,6 +119,7 @@ SECTIONS
BUILD_DIR/asm/entry.o(.text);
BUILD_DIR/src/game*.o(.text);
BUILD_DIR/src/hvqm*.o(.text);
#ifdef UNF
BUILD_DIR/src/usb*.o(.text);
#endif
@@ -164,6 +168,7 @@ SECTIONS
{
BUILD_DIR/src/game*.o(.bss*);
BUILD_DIR/src/game*.o(.sbss*);
BUILD_DIR/src/hvqm*.o(.bss*);
BUILD_DIR/src/usb*.o(.bss*);
BUILD_DIR/src/usb*.o(.sbss*);
BUILD_DIR/src/audio*.o(.bss*);

View File

@@ -927,7 +927,7 @@ f32 D_EU_802298D0;
s32 gRefreshRate;
#endif
s16 *gAiBuffers[NUMAIBUFFERS];
ALIGNED8 s16 *gAiBuffers[NUMAIBUFFERS];
s16 gAiBufferLengths[NUMAIBUFFERS];
#if defined(VERSION_JP) || defined(VERSION_US)

23
src/buffers/adpcmbuf.c Normal file
View File

@@ -0,0 +1,23 @@
/*
* N64-HVQM2 library Sample program
*
* FILE : adpcmbuf.c
*
* Copyright (C) 1998,1999 NINTENDO Co.,Ltd.
*
*/
/* 1999-02-22 */
#include <ultra64.h>
#include <HVQM2File.h>
#include <macros.h>
#include <hvqm/hvqm.h>
/*
* Buffer for audio records (ADPCM data) read from the HVQM2 data.
* (Note) Please locate at a 16byte aligned address with the spec file.
*/
ALIGNED16 u8 adpcmbuf[AUDIO_RECORD_SIZE_MAX];
/* end */

View File

@@ -2,7 +2,7 @@
#include <hvqm2dec.h>
#include "buffers.h"
#include <game/hvqm.h>
#include <hvqm/hvqm.h>
#include "config.h"
ALIGNED8 u8 gDecompressionHeap[0xD000];
@@ -42,14 +42,3 @@ ALIGNED8 u8 gAudioSPTaskYieldBuffer[OS_YIELD_AUDIO_SIZE];
#if !defined(F3DEX_GBI_SHARED) && !defined(VERSION_EU)
ALIGNED8 u8 gUnusedThread2Stack[0x1400];
#endif
ALIGNED16 u8 hvqbuf[HVQ_DATASIZE_MAX];
ALIGNED16 u64 hvq_yieldbuf[HVQM2_YIELD_DATA_SIZE/8];
ALIGNED16 u8 adpcmbuf[AUDIO_RECORD_SIZE_MAX];
ALIGNED16 u16 hvqwork[(MAXWIDTH/8)*(MAXHEIGHT/4)*4];
// Data area for the HVQ microcode
ALIGNED16 HVQM2Info hvq_spfifo[HVQ_SPFIFO_SIZE];

View File

@@ -3,4 +3,4 @@
#include "config.h"
// 0x70800 bytes
u16 gFrameBuffers[3][SCREEN_WIDTH * SCREEN_HEIGHT];
__attribute__((aligned (64))) u16 gFrameBuffers[3][SCREEN_WIDTH * SCREEN_HEIGHT];

24
src/buffers/hvqbuf.c Normal file
View File

@@ -0,0 +1,24 @@
/*
* N64-HVQM2 library Sample program
*
* FILE : hvqbuf.c
*
* Copyright (C) 1998,1999 NINTENDO Co.,Ltd.
*
*/
/* 1999-02-22 */
#include <ultra64.h>
#include <macros.h>
#include <HVQM2File.h>
#include <hvqm/hvqm.h>
/*
* Buffer for video records (HVQM2 compressed data) read from
* the HVQM2 data.
* (Note) Please locate at a 16byte aligned address with the spec file.
*/
ALIGNED16 u8 hvqbuf[HVQ_DATASIZE_MAX];
/* end */

43
src/buffers/hvqmwork.c Normal file
View File

@@ -0,0 +1,43 @@
/*
* N64-HVQM2 library Sample program
*
* FILE : hvqmwork.c
*
* Copyright (C) 1998,1999 NINTENDO Co.,Ltd.
*
*/
/* 1999-02-12 */
#include <ultra64.h>
#include <hvqm2dec.h>
#include <hvqm/hvqm.h>
/*
* Work area needed by the HVQM2 library
*
* (Note) MAXWIDTH and MAXHEIGHT are set with Makefile
*/
#if 0
/*
* If the compressed data for decoding is only in 4:1:1 format:
*/
u16 hvqwork[(MAXWIDTH/8)*(MAXHEIGHT/8)*6];
#else
/*
* If 4:2:2 data will (also) be decoded:
*/
u16 hvqwork[(MAXWIDTH/8)*(MAXHEIGHT/4)*4];
#endif
/*
* Data area for the HVQ microcode
*/
HVQM2Info hvq_spfifo[HVQ_SPFIFO_SIZE];
/*
* Buffer for RSP task yield
*/
u64 hvq_yieldbuf[HVQM2_YIELD_DATA_SIZE/8];
/* end */

View File

@@ -19,7 +19,7 @@
#include "segment2.h"
#include "segment_symbols.h"
#include "rumble_init.h"
#include "hvqm.h"
#include <hvqm/hvqm.h>
#include "usb/usb.h"
#include "usb/debug.h"
#include <prevent_bss_reordering.h>
@@ -354,7 +354,7 @@ void display_and_vsync(void) {
// this function records distinct inputs over a 255-frame interval to RAM locations and was likely
// used to record the demo sequences seen in the final game. This function is unused.
static void record_demo(void) {
UNUSED static void record_demo(void) {
// record the player's button mask and current rawStickX and rawStickY.
u8 buttonMask =
((gPlayer1Controller->buttonDown & (A_BUTTON | B_BUTTON | Z_TRIG | START_BUTTON)) >> 8)

110
src/hvqm/cfbkeep.c Normal file
View File

@@ -0,0 +1,110 @@
/*
* N64-HVQM2 library Sample program
*
* FILE : cfbkeep.c (frame buffer management)
*
* Copyright (C) 1998,1999 NINTENDO Co.,Ltd.
*
*/
/* 1998-12-16 */
#include <ultra64.h>
#include <HVQM2File.h>
#include "hvqm.h"
#include "buffers/framebuffers.h"
/***********************************************************************
* Array maintaining the state of the frame buffer
* (cfb_status[N] corresponds to the frame buffer itself cfb[N]
***********************************************************************/
u32 cfb_status[NUM_CFBs];
/***********************************************************************
*
* void init_cfb(void)
*
* Explanation
* Clears all frame buffers and initializes state of frame buffer
*
***********************************************************************/
void init_cfb(void) {
int i, j;
for ( i = 0; i < NUM_CFBs; i++ ) {
for ( j = 0; j < SCREEN_WD*SCREEN_HT; j++ ) gFrameBuffers[i][j] = 0;
cfb_status[i] = CFB_FREE;
}
}
/***********************************************************************
*
* void keep_cfb(int cfbno)
*
* Explanation
* Frame buffer indicated by the index cfbno is protected by
* being made unavailable.
*
***********************************************************************/
void
keep_cfb( int cfbno )
{
cfb_status[cfbno] |= CFB_PRECIOUS;
}
/***********************************************************************
*
* void release_cfb(int cfbno)
*
* Explanation
* Frame buffer indicated by the index cfbno is released
* from protection.
*
***********************************************************************/
void
release_cfb( int bufno )
{
if ( bufno >= 0 ) cfb_status[bufno] &= ~CFB_PRECIOUS;
}
/***********************************************************************
*
* void release_all_cfb(void)
*
* Explanation
* All frame buffers are released from protection.
*
***********************************************************************/
void
release_all_cfb(void)
{
int i;
for ( i = 0; i < NUM_CFBs; i++ ) cfb_status[i] &= ~CFB_PRECIOUS;
}
/***********************************************************************
*
* int get_cfb()
*
* Explanation
* Search for available frame buffer and return that index.
* Thread yields until an available frame buffer is found.
*
* Returned value
* The index of the available frame buffer.
*
***********************************************************************/
int
get_cfb()
{
int cfbno;
for ( ; ; ) {
for ( cfbno = 0; cfbno < NUM_CFBs; cfbno++ )
if ( cfb_status[cfbno] == 0 )
return cfbno;
osYieldThread();
}
}
/* end */

77
src/hvqm/getrecord.c Normal file
View File

@@ -0,0 +1,77 @@
/*
* N64-HVQM2 library Sample program
*
* FILE : getrecord.c (module for reading HVQM2 records)
*
* Copyright (C) 1998,1999 NINTENDO Co.,Ltd.
*
*/
/* 1999-02-22 */
#include <ultra64.h>
#include <HVQM2File.h>
#include "hvqm.h"
/*
* u8 *get_record(HVQM2Record *headerbuf, void *bodybuf,
* u16 type, u8 *stream, OSIoMesg *mb, OSMesgQueue *mq)
*
* Arguments
* headerbuf Buffer storing the record header (HVQM2Record structure)
* bodybuf Buffer storing the record's data (record body)
* type The requested record type
* stream The current HVQM2 record address
* mb I/O message block request sent to PI manager
* mq Message queue receiving notice of DMA end from PI manager
*
* Explanation:
* Finds the next record of the specified "type" from the HVQM2 data
* address "stream" in ROM, and reads the record header into "headerbuf"
* and the data body into "bodybuf." Specify HVQM2_AUDIO for "type" to
* read an audio record, and specify HVQM2_VIDEO to read a video record.
*
* This function assumes that the reading of audio records and video
* records is performed in parallel by separate threads. Accordingly,
* you must prepare in advance a separate mb (I/O message block request
* sent to PI manager) and separate mq (message queue receiving notice
* of DMA end from PI manager) for audio records and video records.
*
* The reading of audio records has priority over the reading of
* video records.
*
* "stream" must have 2byte alignment. Please give headerbuf and
* bodybuf 16byte alignment to conform with the R4300 data cache line
* size.
*
* Please be sure to reserve sufficient space for bodybuf.
*
*
* Returned value:
* The address of the next record
*/
u8 *
get_record( HVQM2Record *headerbuf, void *bodybuf, u16 type, u8 *stream, OSIoMesg *mb, OSMesgQueue *mq )
{
u16 record_type;
u32 record_size;
s32 pri;
pri = (type == HVQM2_AUDIO) ? OS_MESG_PRI_HIGH : OS_MESG_PRI_NORMAL;
for ( ; ; ) {
romcpy( headerbuf, stream, sizeof(HVQM2Record), pri, mb, mq );
stream += sizeof(HVQM2Record);
record_type = load16( headerbuf->type );
record_size = load32( headerbuf->size );
if ( record_type == type ) break;
stream += record_size;
}
if ( record_size > 0 ) {
romcpy( bodybuf, stream, record_size, pri, mb, mq );
stream += record_size;
}
return stream;
}
/* end */

View File

@@ -6,6 +6,7 @@
#include <adpcmdec.h>
#include "hvqm.h"
#define AUDIO_DMA_MSG_SIZE 1
static OSIoMesg audioDmaMesgBlock;
static OSMesgQueue audioDmaMessageQ;
static OSMesg audioDmaMessages[AUDIO_DMA_MSG_SIZE];
@@ -57,77 +58,6 @@ static ADPCMstate adpcm_state; /* Buffer for state information passed to the ADP
extern u8 _capcomSegmentRomStart[];
extern u8 _seinSegmentRomStart[];
u32 cfb_status[NUM_CFBs];
// Clears all frame buffers and initializes state of frame buffer
void init_cfb(void) {
int i, j;
for ( i = 0; i < NUM_CFBs; i++ ) {
for ( j = 0; j < SCREEN_WD*SCREEN_HT; j++ ) gFrameBuffers[i][j] = 0;
cfb_status[i] = CFB_FREE;
}
}
// Frame buffer indicated by the index cfbno is protected by being made unavailable.
void keep_cfb( int cfbno ) {
cfb_status[cfbno] |= CFB_PRECIOUS;
}
//Frame buffer indicated by the index cfbno is released from protection.
void release_cfb( int bufno ) {
if ( bufno >= 0 ) cfb_status[bufno] &= ~CFB_PRECIOUS;
}
// All frame buffers are released from protection.
void release_all_cfb(void) {
int i;
for ( i = 0; i < NUM_CFBs; i++ ) cfb_status[i] &= ~CFB_PRECIOUS;
}
// Search for available frame buffer and return that index.
// Thread yields until an available frame buffer is found.
int get_cfb() {
int cfbno;
for ( ; ; ) {
for ( cfbno = 0; cfbno < NUM_CFBs; cfbno++ )
if ( cfb_status[cfbno] == CFB_FREE )
return cfbno;
osYieldThread();
}
}
void romcpy(void *dest, void *src, u32 len, s32 pri, OSIoMesg *mb, OSMesgQueue *mq) {
osInvalDCache(dest, (s32)len);
while (osPiStartDma(mb, pri, OS_READ, (u32)src, dest, len, mq) == -1) {}
osRecvMesg(mq, (OSMesg *)NULL, OS_MESG_BLOCK);
}
u8 *get_record(HVQM2Record *headerbuf, void *bodybuf, u16 type, u8 *stream, OSIoMesg *mb, OSMesgQueue *mq) {
u16 record_type;
u32 record_size;
s32 pri, i;
pri = (type == HVQM2_AUDIO) ? OS_MESG_PRI_HIGH : OS_MESG_PRI_NORMAL;
while (1) {
romcpy(headerbuf, stream, sizeof(HVQM2Record), pri, mb, mq);
stream += sizeof(HVQM2Record);
record_type = load16(headerbuf->type);
record_size = load32(headerbuf->size);
if (record_type == type) break;
stream += record_size;
}
if (record_size > 0) {
romcpy(bodybuf, stream, record_size, pri, mb, mq);
stream += record_size;
}
return stream;
}
static u32 next_audio_record( void *pcmbuf ) {
ALIGNED16 u8 header_buffer[sizeof(HVQM2Record)+16];
HVQM2Record *record_header;

View File

@@ -30,26 +30,11 @@
#define APP_GFX_UCODE_HVQ 6 /* HVQ2 microcode */
#define AUDIO_DMA_MSG_SIZE 1
#define CFB_FREE 0 /* Available */
#define CFB_PRECIOUS (1<<0) /* Constrained for decoding of next frame */
#define CFB_SHOWING (1<<1) /* Waiting to display or displaying */
typedef u32 (*tkAudioProc)(void *pcmbuf);
typedef tkAudioProc (*tkRewindProc)(void);
void createTimekeeper();
void tkStart(tkRewindProc rewind, u32 samples_per_sec);
void tkPushVideoframe(void *vaddr, u32 *statP, u64 disptime);
u64 tkGetTime(void);
void tkStop(void);
void createHvqmThread(void);
#define NUM_CFBs 3
#define STACKSIZE 0x2000
#define PCM_CHANNELS 2 /* Number of channels */
@@ -72,6 +57,12 @@ void createHvqmThread(void);
#define PCMBUF_ALIGNED ((PCMBUF_SPREAD+(PCM_ALIGN-1))&(~(PCM_ALIGN-1))) /* pcmbuf[i] address is aligned */
#define PCMBUF_SIZE 0xA00
/*
* Macro for loading multi-byte data from buffer holding data from stream
*/
#define load32(from) (*(u32*)&(from))
#define load16(from) (*(u16*)&(from))
/*
* Thread ID and priority
*/
@@ -92,5 +83,36 @@ void createHvqmThread(void);
typedef u32 (*tkAudioProc)(void *pcmbuf);
typedef tkAudioProc (*tkRewindProc)(void);
void createTimekeeper();
void tkStart(tkRewindProc rewind, u32 samples_per_sec);
void tkPushVideoframe(void *vaddr, u32 *statP, u64 disptime);
u64 tkGetTime(void);
void tkStop(void);
void createHvqmThread(void);
/*
* in system.c
*/
void romcpy(void *dest, void *src, u32 len, s32 pri, OSIoMesg *mb, OSMesgQueue *mq);
/*
* in getrecord.c
*/
u8 *get_record(HVQM2Record *headerbuf, void *bodybuf, u16 type, u8 *stream, OSIoMesg *mb, OSMesgQueue *mq);
/*
* in cfbkeep.c
*/
extern u32 cfb_status[NUM_CFBs];
void init_cfb(void);
void keep_cfb(int cfbno);
void release_cfb(int cfbno);
void release_all_cfb(void);
int get_cfb();
typedef u32 (*tkAudioProc)(void *pcmbuf);
typedef tkAudioProc (*tkRewindProc)(void);
#endif // HVQM_H

46
src/hvqm/system.c Normal file
View File

@@ -0,0 +1,46 @@
/*
* N64-HVQM2 library Sample program
*
* FILE : system.c (boot program/system utility)
*
* Copyright (C) 1998,1999 NINTENDO Co.,Ltd.
*
*/
/* 1998-12-15 */
#include <ultra64.h>
#include <HVQM2File.h>
#include "hvqm.h"
/***********************************************************************
*
* void romcpy(void *dest, void *src, u32 len, s32 pri, OSIoMesg *mb,
* OSMesgQueue *mq)
*
* Arguments
* dest DRAM address
* src PI device (ROM) address
* len Transfer length (bytes)
* pri Priority of the transfer request
* mb I/O message block request
* mq Message queue receiving notification of end of DMA
*
* Explanation
* DMA transfers "len" bytes from ROM address "SRC" to DRAM
* address "dest" and returns after waiting for end of DMA. The
* data cache of the transfer destination in DRAM is invalidated
* ahead of time.
*
* The parameters have the same meaning as for osPiStartDma()
*
***********************************************************************/
void
romcpy(void *dest, void *src, u32 len, s32 pri, OSIoMesg *mb, OSMesgQueue *mq)
{
osInvalDCache( dest, (s32)len );
while ( osPiStartDma( mb, pri, OS_READ, (u32)src, dest, len, mq ) == -1 ) {}
osRecvMesg( mq, (OSMesg *)NULL, OS_MESG_BLOCK );
}
/* end */

View File

@@ -1,42 +1,9 @@
#include <ultra64.h>
#include <HVQM2File.h>
#include "hvqm.h"
#include "audio/data.h"
#include "buffers/framebuffers.h"
#define NUM_CFBs 3
#define STACKSIZE 0x2000
#define PCM_CHANNELS 2 /* Number of channels */
#define PCM_CHANNELS_SHIFT 1 /* log2(PCM_CHANNELS) */
#define PCM_ALIGN 2 /* Alignment of number of samples to send */
#define PCM_BYTES_PER_SAMPLE (2 * PCM_CHANNELS) /* Number of bytes in one sample */
#define PCM_BYTES_PER_SAMPLE_SHIFT 2 /* log2(PCM_BYTES_PER_SAMPLE) */
/*
* Audio record definitions
*/
#define AUDIO_SAMPLE_BITS 4
#define AUDIO_SAMPLES_MAX (((AUDIO_RECORD_SIZE_MAX-sizeof(HVQM2Audio))*8/AUDIO_SAMPLE_BITS)+1) /* Maximum number of records per sample */
/*
* Thread ID and priority
*/
#define IDLE_THREAD_ID 1
#define MAIN_THREAD_ID 3
#define HVQM_THREAD_ID 7
#define TIMEKEEPER_THREAD_ID 8
#define DA_COUNTER_THREAD_ID 9
#define IDLE_PRIORITY 10
#define MAIN_PRIORITY 10
#define HVQM_PRIORITY 11
#define TIMEKEEPER_PRIORITY 12
#define DA_COUNTER_PRIORITY 13
#define PI_COMMAND_QUEUE_SIZE 4
typedef u32 (*tkAudioProc)(void *pcmbuf);
typedef tkAudioProc (*tkRewindProc)(void);
/***********************************************************************
* Timekeeper thread
***********************************************************************/
@@ -158,14 +125,22 @@ static u64 samples_played;
***********************************************************************/
static OSTime last_time;
#define OS_CLOCK_RATE 62500000LL
#define OS_CPU_COUNTER (OS_CLOCK_RATE*3/4)
#define OS_NSEC_TO_CYCLES(n) (((u64)(n)*(OS_CPU_COUNTER/15625000LL))/(1000000000LL/15625000LL))
#define OS_USEC_TO_CYCLES(n) (((u64)(n)*(OS_CPU_COUNTER/15625LL))/(1000000LL/15625LL))
#define OS_CYCLES_TO_NSEC(c) (((u64)(c)*(1000000000LL/15625000LL))/(OS_CPU_COUNTER/15625000LL))
#define OS_CYCLES_TO_USEC(c) (((u64)(c)*(1000000LL/15625LL))/(OS_CPU_COUNTER/15625LL))
u64 tkGetTime(void) {
/***********************************************************************
*
* u64 tkGetTime(void)
*
* Explanation
* The time that has passed since the start of HVQM2 movie playback
* is calculated back from the number of samples that have finished
* playing (samples_played) and the sampling rate (samples_per_sec).
* The time after samples_played was first updated is supplemented
* with osGetTime().
*
* Returned value
* The time that has passed since playback of the HVQM2 movie started
*
***********************************************************************/
u64 tkGetTime(void){
u64 retval;
if (!clock_alive) return 0;
@@ -175,28 +150,62 @@ u64 tkGetTime(void) {
return retval;
}
static void tkClockDisable(void) {
clock_alive = 0;
/***********************************************************************
*
* void tkClockDisable(void)
*
* Explanation
* Disable measurement of time from start of HVQM2 movie playback
*
***********************************************************************/
static void tkClockDisable(void)
{
clock_alive = 0;
}
static void tkClockStart(void) {
samples_played = 0;
/***********************************************************************
*
* void tkClockStart(void)
*
* Explanation
* Start measurement of time from start of HVQM2 movie playback
*
***********************************************************************/
static void tkClockStart(void)
{
samples_played = 0;
last_time = osGetTime();
clock_alive = 1;
}
/***********************************************************************
*
* daCounterProc - Audio DA counter thread procedure
*
* Counts the number of audio samples that have finished playing.
*
***********************************************************************/
static void daCounterProc(void UNUSED*argument)
{
for ( ; ; ) {
osRecvMesg( &aiMessageQ, NULL, OS_MESG_BLOCK );
last_time = osGetTime();
clock_alive = 1;
if ( aiDAsamples > 0 ) --pcmBufferCount;
samples_played += aiDAsamples;
aiDAsamples = aiFIFOsamples;
aiFIFOsamples = 0;
}
}
static void daCounterProc(void *argument) {
while (1) {
osRecvMesg( &aiMessageQ, NULL, OS_MESG_BLOCK );
last_time = osGetTime();
if (aiDAsamples > 0) --pcmBufferCount;
samples_played += aiDAsamples;
aiDAsamples = aiFIFOsamples;
aiFIFOsamples = 0;
}
}
static void timekeeperProc(void *argument) {
/***********************************************************************
*
* timekeeperProc - Timekeeper thread procedure
*
* Plays the HVQM2 movie's audio records and takes charge of
* completed frame buffers, displaying them at the scheduled times.
*
***********************************************************************/
static void timekeeperProc(void UNUSED*argument) {
TKCMD *cmd;
void *showing_cfb = NULL; /* Frame buffer being displayed */
u32 *showing_cfb_statP = NULL; /* Pointer to state flag of the frame buffer being displayed */
@@ -211,6 +220,7 @@ static void timekeeperProc(void *argument) {
tkAudioProc audioproc;
OSTime present_vtime;
/* Acquire retrace event */
osCreateMesgQueue( &viMessageQ, viMessages, VI_MSG_SIZE );
osViSetEvent( &viMessageQ, 0, 1 );
@@ -228,9 +238,12 @@ static void timekeeperProc(void *argument) {
osRecvMesg(&tkCmdMesgQ, (OSMesg *)&cmd, OS_MESG_BLOCK);
while (cmd == NULL) {
osSendMesg(&tkResMesgQ, NULL, OS_MESG_BLOCK);
osRecvMesg(&tkCmdMesgQ, (OSMesg *)&cmd, OS_MESG_BLOCK);
/*
* Wait for movie playback command
*/
while ( cmd == NULL ) {
osSendMesg( &tkResMesgQ, NULL, OS_MESG_BLOCK );
osRecvMesg( &tkCmdMesgQ, (OSMesg *)&cmd, OS_MESG_BLOCK );
}
tkClockDisable();
@@ -259,7 +272,7 @@ static void timekeeperProc(void *argument) {
next_pcmbufno = 0;
pcm_mod_samples = 0;
osSendMesg(&tkResMesgQ, NULL, OS_MESG_BLOCK);
osSendMesg( &tkResMesgQ, NULL, OS_MESG_BLOCK ); /* Notification that playback preparations are finished */
present_vtime = osGetTime();
@@ -267,7 +280,9 @@ static void timekeeperProc(void *argument) {
!audio_done || audioRingCount > 0 || aiFIFOsamples > 0) {
u64 present_time;
OSTime last_vtime;
/*
* Block until retrace comes
*/
osRecvMesg(&viMessageQ, NULL, OS_MESG_BLOCK);
last_vtime = present_vtime;
@@ -297,89 +312,118 @@ static void timekeeperProc(void *argument) {
pushed_cfb_statP = NULL;
}
if ( video_started || audioRingCount > 0 || audio_done ) {
while ( videoRingCount ) {
void *next_cfb;
if ( videoRing[videoRingRead].disptime > present_time ) break;
if ( pushed_cfb_statP != NULL ) *pushed_cfb_statP &= ~CFB_SHOWING;
pushed_cfb = videoRing[videoRingRead].vaddr;
pushed_cfb_statP = videoRing[videoRingRead].statP;
*pushed_cfb_statP |= CFB_SHOWING;
osViSwapBuffer( pushed_cfb );
if ( ++videoRingRead == VIDEO_RING_SIZE ) videoRingRead = 0;
--videoRingCount;
}
/*
* Push the next frame buffer to the VI
*
* Before the start of video display (video_started == 0),
* wait until audio data is prepared for playback
* (audioRingCount > 0).
*/
if ( video_started || audioRingCount > 0 || audio_done ) {
while ( videoRingCount ) {
if ( videoRing[videoRingRead].disptime > present_time ) break;
if ( pushed_cfb_statP != NULL ) *pushed_cfb_statP &= ~CFB_SHOWING;
pushed_cfb = videoRing[videoRingRead].vaddr;
pushed_cfb_statP = videoRing[videoRingRead].statP;
*pushed_cfb_statP |= CFB_SHOWING;
osViSwapBuffer( pushed_cfb );
if ( ++videoRingRead == VIDEO_RING_SIZE ) videoRingRead = 0;
--videoRingCount;
}
}
/*
* Push the next PCM data to the AI
*
* Before the start of video display (video_started == 0)
* wait until the display of the first image frame
* has begun (video_started == 1).
*/
if ( video_started ) {
osSetThreadPri( NULL, (OSPri)(DA_COUNTER_PRIORITY + 1) );
while ( audioRingCount > 0 && aiFIFOsamples == 0 ) {
void *buffer = audioRing[audioRingRead].buf;
u32 length = audioRing[audioRingRead].len;
if ( osAiGetStatus() & AI_STATUS_FIFO_FULL ) break;
if ( osAiSetNextBuffer( buffer, length ) == -1 ) break;
aiFIFOsamples = length >> PCM_BYTES_PER_SAMPLE_SHIFT;
if ( ! audio_started ) {
/*
* Audio playback has begun.
* Here the time count starts to synchronize audio and video
*/
audio_started = 1;
tkClockStart();
}
if ( video_started ) {
osSetThreadPri( NULL, (OSPri)(DA_COUNTER_PRIORITY + 1) );
while ( audioRingCount > 0 && aiFIFOsamples == 0 ) {
void *buffer = audioRing[audioRingRead].buf;
u32 length = audioRing[audioRingRead].len;
//!if ( osAiGetStatus() & AI_STATUS_AI_FULL ) break;
if ( osAiSetNextBuffer( buffer, length ) == -1 ) break;
aiFIFOsamples = length >> PCM_BYTES_PER_SAMPLE_SHIFT;
if ( ! audio_started ) {
/*
* Audio playback has begun.
* Here the time count starts to synchronize audio and video
*/
audio_started = 1;
tkClockStart();
}
if ( ++audioRingRead == AUDIO_RING_BUFFER_SIZE ) audioRingRead = 0;
--audioRingCount;
}
osSetThreadPri( NULL, (OSPri)TIMEKEEPER_PRIORITY );
}
if ( ! audio_done && audioRingCount < AUDIO_RING_BUFFER_SIZE && pcmBufferCount < NUM_PCMBUFs ) {
u32 samples;
u32 length;
void *buffer;
s16 *sp, *dp;
int i;
if ( ++audioRingRead == AUDIO_RING_BUFFER_SIZE ) audioRingRead = 0;
--audioRingCount;
}
osSetThreadPri( NULL, (OSPri)TIMEKEEPER_PRIORITY );
}
/*
* Prepare the next PCM data
*/
if ( ! audio_done && audioRingCount < AUDIO_RING_BUFFER_SIZE && pcmBufferCount < NUM_PCMBUFs ) {
u32 samples;
u32 length;
void *buffer;
s16 *sp, *dp;
int i;
samples = audioproc( &gAiBuffers[next_pcmbufno][pcm_mod_samples<<PCM_CHANNELS_SHIFT] );
samples = audioproc( &gAiBuffers[next_pcmbufno][pcm_mod_samples<<PCM_CHANNELS_SHIFT] );
if ( samples > 0 ) {
++pcmBufferCount;
if ( samples > 0 ) {
++pcmBufferCount;
sp = pcmModBuf;
dp = (s16 *)((u8 *)gAiBuffers[next_pcmbufno]);
i = pcm_mod_samples << PCM_CHANNELS_SHIFT;
while ( i-- ) *dp++ = *sp++;
samples += pcm_mod_samples;
samples -= (pcm_mod_samples = samples & (PCM_ALIGN-1));
length = samples << PCM_BYTES_PER_SAMPLE_SHIFT;
buffer = gAiBuffers[next_pcmbufno];
osWritebackDCache( buffer, length );
audioRing[audioRingWrite].buf = buffer;
audioRing[audioRingWrite].len = length;
if ( ++audioRingWrite == AUDIO_RING_BUFFER_SIZE ) audioRingWrite = 0;
++audioRingCount;
sp = pcmModBuf;
dp = (s16 *)((u8 *)gAiBuffers[next_pcmbufno]);
i = pcm_mod_samples << PCM_CHANNELS_SHIFT;
while ( i-- ) *dp++ = *sp++;
samples += pcm_mod_samples;
samples -= (pcm_mod_samples = samples & (PCM_ALIGN-1));
length = samples << PCM_BYTES_PER_SAMPLE_SHIFT;
buffer = gAiBuffers[next_pcmbufno];
osWritebackDCache( buffer, length );
audioRing[audioRingWrite].buf = buffer;
audioRing[audioRingWrite].len = length;
if ( ++audioRingWrite == AUDIO_RING_BUFFER_SIZE ) audioRingWrite = 0;
++audioRingCount;
sp = (s16 *)((u8 *)(gAiBuffers[next_pcmbufno]) + length);
dp = pcmModBuf;
i = pcm_mod_samples << PCM_CHANNELS_SHIFT;
while ( i-- ) *dp++ = *sp++;
sp = (s16 *)((u8 *)(gAiBuffers[next_pcmbufno]) + length);
dp = pcmModBuf;
i = pcm_mod_samples << PCM_CHANNELS_SHIFT;
while ( i-- ) *dp++ = *sp++;
if ( ++next_pcmbufno >= NUM_PCMBUFs ) next_pcmbufno = 0;
}
else audio_done = 1;
}
if ( ++next_pcmbufno >= NUM_PCMBUFs ) next_pcmbufno = 0;
}
else audio_done = 1;
}
//! Push audio
//! Prepare audio
if (osRecvMesg(&tkCmdMesgQ, (OSMesg *)&cmd, OS_MESG_NOBLOCK) == 0) {
video_done = 1;
}
if ( osRecvMesg( &tkCmdMesgQ, (OSMesg *)&cmd, OS_MESG_NOBLOCK ) == 0 )
{
/*
* tkStop() or tkStart() has been executed
*
* Set 1 in video_done and end this playback loop
* after playback of the current movie's video and
* and audio has completely finished.
*/
video_done = 1;
}
}
}
void createTimekeeper(void) {
/***********************************************************************
*
* void createTimekeeper(void)
*
* Explanation
* Creates and starts the timekeeper thread
*
***********************************************************************/
void
createTimekeeper(void)
{
osCreateMesgQueue( &tkCmdMesgQ, &tkCmdMesgBuf, 1 );
osCreateMesgQueue( &tkResMesgQ, &tkResMesgBuf, 1 );
osCreateThread( &tkThread, TIMEKEEPER_THREAD_ID, timekeeperProc,
@@ -388,7 +432,27 @@ void createTimekeeper(void) {
osStartThread( &tkThread );
}
void tkStart( tkRewindProc rewind, u32 samples_per_sec ) {
/***********************************************************************
*
* void tkStart(tkRewindProc rewind, u32 samples_per_sec)
*
* Argument
* rewind Pointer to movie rewind callback function
*
* Explanation
* Directs the timekeeper to play a new movie. Playback of the
* movie starts after callback of "rewind" by the timekeeper. After
* this, the HVQM2 video records are read and decoded and the
* completed frame buffers are handed over to the timekeeper by
* tkPushVideoframe() in succession to play the video part of the movie.
* As for the audio, the timekeeper itself calls the callback
* function (the value returned by rewind) that gets the PCM data as
* needed to playback the audio.
*
***********************************************************************/
void
tkStart( tkRewindProc rewind, u32 samples_per_sec )
{
TKCMD tkcmd;
tkcmd.samples_per_sec = samples_per_sec;
tkcmd.rewind = rewind;
@@ -396,12 +460,53 @@ void tkStart( tkRewindProc rewind, u32 samples_per_sec ) {
osRecvMesg( &tkResMesgQ, (OSMesg *)NULL, OS_MESG_BLOCK );
}
void tkStop( void ) {
/***********************************************************************
*
* void tkStop(void)
*
* Explanation
* Stops the timekeeper. However, the timekeeper continues to
* run until playback of the audio of the playing movie is complete.
* To stop the stream in mid-stream, make 0 the value returned by
* the callback function that reads audio and is called from the
* timekeeper, and then execute tkStop().
*
* To immediately start the playback of a new movie, it is OK to
* to execute tkStart() without executing tkStop().
*
***********************************************************************/
void
tkStop( void )
{
osSendMesg( &tkCmdMesgQ, (OSMesg *)NULL, OS_MESG_BLOCK );
osRecvMesg( &tkResMesgQ, (OSMesg *)NULL, OS_MESG_BLOCK );
}
void tkPushVideoframe( void *vaddr, u32 *statP, u64 disptime ) {
/***********************************************************************
*
* void tkPushVideoframe(void *vaddr, u32 *statP, u64 disptime)
*
* Argument
* vaddr Frame buffer address
* statP Pointer to state flag data regarding vaddr
* disptime Display time
*
* Explanation
* Entrusts the completed frame buffer vaddr to the timekeeper.
* The timekeeper displays the frame buffer after waiting for the
* "disptime" amount of time to pass from the start of the movie.
*
* To wait for the display of the vaddr frame buffer or to
* indicate that something is being displayed, the CFB_SHOWING
* flag is set in the u32 type data pointed to by the statP pointer.
* At the time when display of vaddr is finished, (once its display
* is over and display has switched to another frame buffer) this
* flag is cleared by the timekeeper.
*
***********************************************************************/
void
tkPushVideoframe( void *vaddr, u32 *statP, u64 disptime )
{
*statP |= CFB_SHOWING;
while ( videoRingCount >= VIDEO_RING_SIZE ) osYieldThread();
videoRing[videoRingWrite].disptime = disptime;