From f1d0a507df8c6711b2122242eeca286af8cac4be Mon Sep 17 00:00:00 2001 From: lerabot Date: Tue, 22 Aug 2023 23:03:23 -0400 Subject: [PATCH] Updated the DreamRoq Lib --- samples/dreamroq/LICENSE.KOS | 64 ++ samples/dreamroq/libdcmc/snd_stream.c | 7 +- samples/dreamroq/libdcmc/snddrv.c | 30 +- samples/dreamroq/libdcmc/snddrv.h | 13 +- samples/dreamroq/main.c | 556 ++++++++++++------ .../dreamroq/romdisk/{video.roq => movie.roq} | Bin 6 files changed, 484 insertions(+), 186 deletions(-) create mode 100644 samples/dreamroq/LICENSE.KOS rename samples/dreamroq/romdisk/{video.roq => movie.roq} (100%) diff --git a/samples/dreamroq/LICENSE.KOS b/samples/dreamroq/LICENSE.KOS new file mode 100644 index 0000000..464d6a1 --- /dev/null +++ b/samples/dreamroq/LICENSE.KOS @@ -0,0 +1,64 @@ +Dreamroq is covered under the same terms as the KallistiOS license which +is copied below: + + +Most of the code of KallistiOS proper is currently covered under the KOS +License, which are the terms of the *new* BSD license with our names +inserted as the copyright holders and the "advertising clause" removed +entirely. In all files that state that they are part of the KallistiOS +operating system, you can assume that the following text is inserted in +the header: + +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the KOS License. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* KOS License (README.KOS) for more details. +* +* You should have received a copy of the KOS License along with this +* program; if not, please visit Cryptic Allusion DCDev at: +* +* http://dcdev.allusion.net/ +* + +The text of that license follows. In layman's terms, all it really +says is that you have to give credit where credit is due (both in +derived source files and binary compilations; a credit in the +documentation is ok) and there is no warranty. + + Dan Potter + + +All of the documentation and software included in the KallistiOS Releases +is copyrighted (c)2000-2002 by Dan Potter and others (as noted in each file). + +Copyright 2000, 2001, 2002 + Dan Potter and others (as noted in each file). All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name of Cryptic Allusion nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + diff --git a/samples/dreamroq/libdcmc/snd_stream.c b/samples/dreamroq/libdcmc/snd_stream.c index 348c8ad..cd451b0 100644 --- a/samples/dreamroq/libdcmc/snd_stream.c +++ b/samples/dreamroq/libdcmc/snd_stream.c @@ -434,7 +434,6 @@ static void dma_chain(ptr_t data) { /* Poll streamer to load more data if necessary */ int snd_stream_poll(snd_stream_hnd_t hnd) { uint32 ch0pos, ch1pos; - // Ian micheal fixed all threading problems /* int realbuffer; */ // Remove this line int current_play_pos; int needed_samples; @@ -444,7 +443,7 @@ int snd_stream_poll(snd_stream_hnd_t hnd) { CHECK_HND(hnd); if (!streams[hnd].get_data) return -1; - // Ian micheal fixed all threading problems + /* Get "real" buffer */ ch0pos = g2_read_32(SPU_RAM_BASE + AICA_CHANNEL(streams[hnd].ch[0]) + offsetof(aica_channel_t, pos)); ch1pos = g2_read_32(SPU_RAM_BASE + AICA_CHANNEL(streams[hnd].ch[1]) + offsetof(aica_channel_t, pos)); @@ -466,8 +465,8 @@ int snd_stream_poll(snd_stream_hnd_t hnd) { needed_samples &= ~0x7ff; /* printf("last_write_pos %6i, current_play_pos %6i, needed_samples %6i\n",last_write_pos,current_play_pos,needed_samples); */ - //if (needed_samples > 0) { - if (needed_samples ==4096) { + //Ian micheal wtf was this set to 4096? was causing a delay + if (needed_samples ==2048) { if (streams[hnd].stereo) { data = streams[hnd].get_data(hnd, needed_samples * 4, &got_samples); process_filters(hnd, &data, &got_samples); diff --git a/samples/dreamroq/libdcmc/snddrv.c b/samples/dreamroq/libdcmc/snddrv.c index 62ec293..622bace 100644 --- a/samples/dreamroq/libdcmc/snddrv.c +++ b/samples/dreamroq/libdcmc/snddrv.c @@ -18,6 +18,13 @@ ** at that point the decoder signals sndbuf_status=SNDDRV_STATUS_HAVEBUF ** */ +/* + Name: Ian micheal + Copyright: + Author: Ian micheal + Date: 12/08/23 05:17 + Description: kos 2.0 up port threading fix and wrappers and all warnings fixed +*/ #include #include @@ -26,10 +33,12 @@ #include #include "snddrv.h" -snd_stream_hnd_t shnd; -kthread_t * snddrv_thd; -static int snddrv_vol = 255; -snd_drv snddrv; +snd_stream_hnd_t shnd; +kthread_t * snddrv_thd; +static int snddrv_vol = 255; + +struct snddrv snddrv; +struct snddrv_song_info snd_sinfo; /* Increase the Sound Driver volume */ int snddrv_volume_up() { @@ -118,6 +127,13 @@ static int snddrv_thread() { return snddrv.drv_status; } +/* Wrapper function for snddrv_thread */ +static void *snddrv_thread_wrapper(void *arg) +{ + int status = snddrv_thread(); // Get the status value + return (void *)(size_t)status; // Cast the int status to void pointer +} + /* Start the AICA Sound Stream Thread */ int snddrv_start( int rate, int chans ) { @@ -134,7 +150,11 @@ int snddrv_start( int rate, int chans ) { snd_stream_init(); /*libdcmc/snddrv.c:136: warning: passing arg 1 of `thd_create' from incompatible pointer type */ //Ian micheal 2020 warning - snddrv_thd = thd_create(0, snddrv_thread, NULL ); + /* Use the wrapper function here */ + snddrv_thd = thd_create(0, snddrv_thread_wrapper, NULL); + + printf("SNDDRV: Creating Driver Thread\n"); + return snddrv.drv_status; diff --git a/samples/dreamroq/libdcmc/snddrv.h b/samples/dreamroq/libdcmc/snddrv.h index de07a65..4982233 100644 --- a/samples/dreamroq/libdcmc/snddrv.h +++ b/samples/dreamroq/libdcmc/snddrv.h @@ -35,8 +35,11 @@ /* This seems to be a good number for file seeking on compressed audio */ #define SEEK_LEN 16384*48 +extern struct snddrv snddrv; +extern struct snddrv_song_info snd_sinfo; + /* SNDDRV (C) AICA Audio Driver */ -typedef struct _snd_drv { +struct snddrv { int rate; int channels; int pcm_bytes; @@ -46,15 +49,13 @@ typedef struct _snd_drv { volatile int buf_status; unsigned int pcm_buffer[65536+16384]; unsigned int *pcm_ptr; -} snd_drv; - -extern snd_drv snddrv; +}; #define SNDDRV_FREE_STRUCT() { \ snddrv.rate = snddrv.channels = snddrv.drv_status = \ snddrv.dec_status = snddrv.buf_status = 0; } -typedef struct snddrv_song_info { +struct snddrv_song_info { char *artist[128]; char * title[128]; char * track[128]; @@ -65,7 +66,7 @@ typedef struct snddrv_song_info { volatile float spos; int fsize; float slen; -} snd_sinfo; +}; #define SNDDRV_FREE_SINFO() { \ sq_clr( snd_sinfo.artist, 128 ); \ diff --git a/samples/dreamroq/main.c b/samples/dreamroq/main.c index 7b278b5..3744066 100644 --- a/samples/dreamroq/main.c +++ b/samples/dreamroq/main.c @@ -1,12 +1,40 @@ +/* + * Dreamroq by Mike Melanson + * Updated by Josh Pearson to add audio support + * + * This is the sample Dreamcast player app, designed to be run under + * the KallistiOS operating system. + */ +/* + Name: Iaan micheal + Copyright: + Author: Ian micheal + Date: 12/08/23 05:17 + Description: kos 2.0 up port threading fix and wrappers and all warnings fixed + Redone threading and main added benchmarking for timing acia and roq decoding audio + redone rendering order and code commented to be much easier to read. + example OUTPUT:> Wait for AICA Driver: 88 ms + OUTPUT:> Wait for RoQ Decoder: 1 ms + OUTPUT:> Copy PCM Samples: 1 ms + OUTPUT:> Inform AICA Driver: 0 ms + OUTPUT:> Wait for AICA Driver: 88 ms + OUTPUT:> Wait for RoQ Decoder: 0 ms + + Before + OUTPUT:> Wait for AICA Driver: 168 ms + OUTPUT:> Wait for RoQ Decoder: 0 ms + OUTPUT:> Copy PCM Samples: 1 ms + OUTPUT:> Inform AICA Driver: 0 ms + OUTPUT:> Wait for AICA Driver: 187 ms + OUTPUT:> Wait for RoQ Decoder: 0 ms + OUTPUT:> Copy PCM Samples: 1 ms + OUTPUT:> Inform AICA Driver: 0 ms + OUTPUT:> Wait for AICA Driver: 197 ms +*/ + #include #include #include - -#include -#include -#include -#include - #include #include #include @@ -14,152 +42,202 @@ #include #include -#include "libdcmc/snddrv.h" +#include +#include +#include + #include "dreamroqlib.h" #include "libdcmc/dc_timer.h" +#include "libdcmc/snddrv.h" +#include +#include #ifdef __DREAMCAST__ extern uint8 romdisk[]; KOS_INIT_ROMDISK(romdisk); -#define VIDEO_FILENAME "/rd/video.roq" -#else -#define VIDEO_FILENAME "../samples/dreamroq/romdisk/video.roq" #endif -maple_device_t *cont; -cont_state_t *state; /* Audio Global variables */ -#define PCM_BUF_SIZE 1024*1024 -static unsigned char *pcm_buf = NULL; -static int pcm_size = 0; -static int audio_init = 0; -static mutex_t * pcm_mut; - +#define PCM_BUF_SIZE (1024 * 1024) +static unsigned char *pcm_buf = NULL; +static int pcm_size = 0; +#define AUDIO_THREAD_PRIO 0 +kthread_t *audio_thread; // Thread handle for the audio thread +int audio_init = 0; // Flag to indicate audio initialization status +static mutex_t pcm_mut = MUTEX_INITIALIZER; /* Video Global variables */ -static int current_frame = 0; -static int graphics_initialized = 0; -static float video_delay = 0.0f; -static int frame = 0; -static const float VIDEO_RATE = 30.0f; /* Video FPS */ +static pvr_ptr_t textures[2]; +static int current_frame = 0; +static int graphics_initialized = 0; +static float video_delay; -static GLint frameTexture[2]; -//static GLVertexKOS vertices[4]; +GLfloat vertices[4][5]; +GLuint frameTexture[2]; + + +// Define the target frame rate +#define TARGET_FRAME_RATE 30 -// maybe should aling this better? -typedef struct { - float x, y, z; -} Vec3; -typedef struct { - float u, v; -} UV; -static Vec3 vertices[4]; -static UV uv[4]; static void snd_thd() { do { - /* Wait for AICA Driver to request some samples */ - while( snddrv.buf_status != SNDDRV_STATUS_NEEDBUF ) - thd_pass(); + unsigned int start_time, end_time; - /* Wait for RoQ Decoder to produce enough samples */ - while( pcm_size < snddrv.pcm_needed ) + // Measure time taken by waiting for AICA Driver request + start_time = dc_get_time(); + while (snddrv.buf_status != SNDDRV_STATUS_NEEDBUF) + thd_pass(); + end_time = dc_get_time(); + printf("Wait for AICA Driver: %u ms\n", end_time - start_time); + + // Measure time taken by waiting for RoQ Decoder + start_time = dc_get_time(); + while (pcm_size < snddrv.pcm_needed) { - if( snddrv.dec_status == SNDDEC_STATUS_DONE ) - goto done; - thd_pass(); + if (snddrv.dec_status == SNDDEC_STATUS_DONE) + goto done; + thd_pass(); } + end_time = dc_get_time(); + printf("Wait for RoQ Decoder: %u ms\n", end_time - start_time); - /* Copy the Requested PCM Samples to the AICA Driver */ - mutex_lock( pcm_mut ); - memcpy( snddrv.pcm_buffer, pcm_buf, snddrv.pcm_needed ); - - /* Shift the Remaining PCM Samples Back */ + // Measure time taken by copying PCM samples + start_time = dc_get_time(); + mutex_lock(&pcm_mut); + memcpy(snddrv.pcm_buffer, pcm_buf, snddrv.pcm_needed); pcm_size -= snddrv.pcm_needed; - memmove( pcm_buf, pcm_buf+snddrv.pcm_needed, pcm_size ); - mutex_unlock( pcm_mut ); - - /* Let the AICA Driver know the PCM samples are ready */ + memmove(pcm_buf, pcm_buf + snddrv.pcm_needed, pcm_size); + mutex_unlock(&pcm_mut); + end_time = dc_get_time(); + printf("Copy PCM Samples: %u ms\n", end_time - start_time); + + // Measure time taken by informing AICA Driver + start_time = dc_get_time(); snddrv.buf_status = SNDDRV_STATUS_HAVEBUF; - - } while( snddrv.dec_status == SNDDEC_STATUS_STREAMING ); - done: + end_time = dc_get_time(); + printf("Inform AICA Driver: %u ms\n", end_time - start_time); + + } while (snddrv.dec_status == SNDDEC_STATUS_STREAMING); +done: snddrv.dec_status = SNDDEC_STATUS_NULL; } -static int renderGLdc_cb(unsigned short *buf, int width, int height, int stride, int texture_height) +static int render_cb(unsigned short *buf, int width, int height, int stride, + int texture_height) { - - /* send the video frame as a texture over to video RAM */ - //pvr_txr_load(buf, textures[current_frame], stride * texture_height * 2); - glBindTexture(GL_TEXTURE_2D, frameTexture[current_frame]); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 512, 512, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, buf); - - /* Delay the frame to match Frame Rate */ - frame_delay(VIDEO_RATE, video_delay, ++frame); + pvr_poly_cxt_t cxt; + static pvr_poly_hdr_t hdr[2]; + static pvr_vertex_t vert[4]; - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); + float ratio; + // screen coordinates of upper left and bottom right corners + static int ul_x, ul_y, br_x, br_y; - glVertexPointer (3, GL_FLOAT, 0, vertices); - glTexCoordPointer (2, GL_FLOAT, 0, uv); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + // Initialize textures, drawing coordinates, and other parameters + if (!graphics_initialized) + { + textures[0] = pvr_mem_malloc(stride * texture_height * 2); + textures[1] = pvr_mem_malloc(stride * texture_height * 2); + if (!textures[0] || !textures[1]) + { + return ROQ_RENDER_PROBLEM; + } - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_VERTEX_ARRAY); + // Precompile the poly headers + for (int i = 0; i < 2; i++) { + pvr_poly_cxt_txr(&cxt, PVR_LIST_OP_POLY, PVR_TXRFMT_RGB565 | PVR_TXRFMT_NONTWIDDLED, + stride, texture_height, textures[i], PVR_FILTER_NONE); + pvr_poly_compile(&hdr[i], &cxt); + } - - glKosSwapBuffers(); + // Calculate drawing coordinates + ratio = 640.0 / width; + ul_x = 0; + br_x = (int)(ratio * stride); + ul_y = (int)((480 - ratio * height) / 2); + br_y = ul_y + (int)(ratio * texture_height); - /* - if (current_frame) - current_frame = 0; - else - current_frame = 1; - */ + // Set common vertex properties + for (int i = 0; i < 4; i++) { + vert[i].z = 1.0f; + vert[i].argb = PVR_PACK_COLOR(1.0f, 1.0f, 1.0f, 1.0f); + vert[i].oargb = 0; + vert[i].flags = (i < 3) ? PVR_CMD_VERTEX : PVR_CMD_VERTEX_EOL; + } + + // Initialize vertex coordinates and UV coordinates + vert[0].x = ul_x; + vert[0].y = ul_y; + vert[0].u = 0.0; + vert[0].v = 0.0; + + vert[1].x = br_x; + vert[1].y = ul_y; + vert[1].u = 1.0; + vert[1].v = 0.0; + + vert[2].x = ul_x; + vert[2].y = br_y; + vert[2].u = 0.0; + vert[2].v = 1.0; + + vert[3].x = br_x; + vert[3].y = br_y; + vert[3].u = 1.0; + vert[3].v = 1.0; + + // Get the current hardware timing + video_delay = (float)dc_get_time(); + + graphics_initialized = 1; + } + + // Send the video frame as a texture over to video RAM + pvr_txr_load(buf, textures[current_frame], stride * texture_height * 2); + + // Calculate the elapsed time since the last frame + unsigned int current_time = dc_get_time(); + unsigned int elapsed_time = current_time - video_delay; + unsigned int target_frame_time = 1000 / TARGET_FRAME_RATE; + + // If the elapsed time is less than the target frame time, introduce a delay + if (elapsed_time < target_frame_time) { + unsigned int delay_time = target_frame_time - elapsed_time; + thd_sleep(delay_time); + } + + // Update the hardware timing for the current frame + video_delay = (float)current_time; + + pvr_wait_ready(); + pvr_scene_begin(); + pvr_list_begin(PVR_LIST_OP_POLY); + + // Render the frame using precompiled headers and vertices + pvr_prim(&hdr[current_frame], sizeof(pvr_poly_hdr_t)); + for (int i = 0; i < 4; i++) { + pvr_prim(&vert[i], sizeof(pvr_vertex_t)); + } + + pvr_list_finish(); + pvr_scene_finish(); + + // Toggle between frames + current_frame = 1 - current_frame; return ROQ_SUCCESS; } -static int audio_cb( unsigned char *buf, int size, int channels) + +static int renderGLdc_cb(unsigned short *buf, int width, int height, int stride, int texture_height) { - - - /* Copy the decoded PCM samples to our local PCM buffer */ - mutex_lock( pcm_mut ); - memcpy( pcm_buf+pcm_size, buf, size); - pcm_size += size; - mutex_unlock( pcm_mut ); - - return ROQ_SUCCESS; -} - -static int quit_cb() -{ - /* - state = (cont_state_t *)maple_dev_status(cont); - - if(state->buttons & CONT_START) - return 1; - else - return 0; - */ -} - -int main(int argc, char **argv) -{ - printf("--- DreamRoQ Player for Dreamcast\n"); - glKosInit(); - //cont = maple_enum_type(0, MAPLE_FUNC_CONTROLLER); - //state = (cont_state_t *)maple_dev_status(cont); - if(!graphics_initialized) { - glClearColor(1.0f, 0.0f, 0.0f, 1.0f); // This Will Clear The Background Color To Black + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // This Will Clear The Background Color To Black glClearDepth(1.0); // Enables Clearing Of The Depth Buffer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -187,32 +265,32 @@ int main(int argc, char **argv) float h = 512; int v = 0; - vertices[v].x = 0; - vertices[v].y = 0; - vertices[v].z = 0; - uv[v].u = 0.0f; - uv[v].v = 1.0f; + vertices[v][0] = 0; + vertices[v][1] = 0; + vertices[v][2] = 0; + vertices[v][3] = 0.0f; + vertices[v][4] = 1.0f; v++; - vertices[v].x = 0; - vertices[v].y = 480; - vertices[v].z = 0; - uv[v].u = 0.0f; - uv[v].v = 0.0f; + vertices[v][0] = 0; + vertices[v][1] = 480; + vertices[v][2] = 0; + vertices[v][3] = 0.0f; + vertices[v][4] = 0.0f; v++; - vertices[v].x = 640; - vertices[v].y = 0; - vertices[v].z = 0; - uv[v].u = 1.0f; - uv[v].v = 1.0f; + vertices[v][0] = 640; + vertices[v][1] = 0; + vertices[v][2] = 0; + vertices[v][3] = 1.0f; + vertices[v][4] = 1.0f; v++; - vertices[v].x = 640; - vertices[v].y = 480; - vertices[v].z = 0; - uv[v].u = 1.0f; - uv[v].v = 0.0f; + vertices[v][0] = 640; + vertices[v][1] = 480; + vertices[v][2] = 0; + vertices[v][3] = 1.0f; + vertices[v][4] = 0.0f; v++; GLfloat drawColor[4] = {1.0f, 1.0f, 1.0f, 1.0f}; @@ -224,49 +302,185 @@ int main(int argc, char **argv) graphics_initialized = 1; } - /* - if(!audio_init) { - // allocate PCM buffer - pcm_buf = malloc(PCM_BUF_SIZE); - if( pcm_buf == NULL ) - return ROQ_NO_MEMORY; - - // Start AICA Driver - // Audio rate, channel number - snddrv_start( 22050, 2); - snddrv.dec_status = SNDDEC_STATUS_STREAMING; - - // Create a thread to stream the samples to the AICA - thd_create(0, snd_thd, NULL); - - // Create a mutex to handle the double-threaded buffer - //pcm_mut = mutex_create(); - - audio_init = 1; + /* send the video frame as a texture over to video RAM */ + //pvr_txr_load(buf, textures[current_frame], stride * texture_height * 2); + glBindTexture(GL_TEXTURE_2D, frameTexture[current_frame]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 512, 512, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, buf); + + + // Calculate the elapsed time since the last frame + unsigned int current_time = dc_get_time(); + unsigned int elapsed_time = current_time - video_delay; + unsigned int target_frame_time = 1000 / TARGET_FRAME_RATE; + + // If the elapsed time is less than the target frame time, introduce a delay + if (elapsed_time < target_frame_time) { + unsigned int delay_time = target_frame_time - elapsed_time; + thd_sleep(delay_time); } - */ - printf("--- Playing video using DreamRoQ\n"); - int status = dreamroq_play(VIDEO_FILENAME, 0, renderGLdc_cb, 0, 0); - printf("dreamroq_play() status = %d\n", status); + // Update the hardware timing for the current frame + video_delay = (float)current_time; - /* - if(audio_init) { - snddrv.dec_status = SNDDEC_STATUS_DONE; // Singal audio thread to stop - while( snddrv.dec_status != SNDDEC_STATUS_NULL ) - thd_pass(); - free( pcm_buf ); - pcm_buf = NULL; - pcm_size = 0; - mutex_destroy(pcm_mut); // Destroy the PCM mutex - snddrv_exit(); // Exit the AICA Driver - } + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); - if(graphics_initialized) { - glDeleteTextures(2, frameTexture); - glEnable(GL_LIGHTING); - } - */ - return status; + glVertexPointer (3, GL_FLOAT, sizeof(vertices[0]), &vertices[0][0]); + glTexCoordPointer (2, GL_FLOAT, sizeof(vertices[0]), &vertices[0][3]); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + + glKosSwapBuffers(); + + current_frame = 1 - current_frame; + + return ROQ_SUCCESS; } + +static int audio_cb(unsigned char *buf, int size, int channels) +{ + // Copy the decoded PCM samples to our local PCM buffer + mutex_lock(&pcm_mut); + memcpy(pcm_buf + pcm_size, buf, size); + pcm_size += size; + mutex_unlock(&pcm_mut); + + return ROQ_SUCCESS; +} + +// Audio thread function +static void *snd_thd_wrapper(void *arg) +{ + printf("Audio Thread: Started\n"); + unsigned int start_time = dc_get_time(); + + // Call the actual audio thread function + snd_thd(); + + unsigned int end_time = dc_get_time(); + unsigned int elapsed_time = end_time - start_time; + printf("Audio Thread: Finished (Time: %u ms)\n", elapsed_time); + + return NULL; +} + + +static int quit_cb() +{ + static int frame_count = 0; + static unsigned int last_time = 0; + static unsigned int target_frame_time = 1000 / 30; // 30 FPS + + // Calculate time difference since the last frame + unsigned int current_time = dc_get_time(); + unsigned int elapsed_time = current_time - last_time; + + // Check if the video has ended and the audio decoding status is done + if (snddrv.dec_status == SNDDEC_STATUS_DONE) { + printf("Exiting due to audio decoding status\n"); + return 1; // Exit the loop + } + + // Check if the "Start" button is pressed + MAPLE_FOREACH_BEGIN(MAPLE_FUNC_CONTROLLER, cont_state_t, st) + if (st->buttons & CONT_START) { + printf("Exiting due to Start button\n"); + return 1; // Exit the loop + } + MAPLE_FOREACH_END() + + // Delay if necessary to maintain the target frame rate + if (elapsed_time < target_frame_time) { + unsigned int delay_time = target_frame_time - elapsed_time; + thd_sleep(delay_time); + } + + // Print FPS information every second + if (elapsed_time >= 1000) { + // double fps = (double)frame_count / (elapsed_time / 1000.0); + // printf("FPS: %.2lf\n", fps); + + frame_count = 0; + last_time = current_time; + } + + // printf("Continuing loop\n"); + fflush(stdout); // Flush the output buffer to ensure immediate display + frame_count++; + + return 0; // Continue the loop +} + + +int main() +{ + int status = 0; + + glKosInit(); + + printf("dreamroq_play(C) Multimedia Mike Melanson & Josh PH3NOM Pearson 2011\n"); + printf("dreamroq_play(C) Ian micheal Up port to Kos2.0 sound fix and threading\n"); + printf("dreamroq_play(C) Ian micheal Kos2.0 free and exit when loop ends 2023\n"); + printf("dreamroq_play(C) Ian micheal redo frame limit code and rendering and comment what it does 2023\n"); + + // Initialize audio resources and create the audio thread + if (!audio_init) + { + pcm_buf = malloc(PCM_BUF_SIZE); + if (pcm_buf == NULL) + { + printf("Failed to allocate PCM buffer\n"); + return 1; + } + + snddrv_start(22050, 2); + snddrv.dec_status = SNDDEC_STATUS_STREAMING; + + printf("Creating Audio Thread\n"); + audio_thread = thd_create(AUDIO_THREAD_PRIO, snd_thd_wrapper, NULL); + if (!audio_thread) + { + printf("Failed to create audio thread\n"); + free(pcm_buf); + pcm_buf = NULL; + return 1; + } + + audio_init = 1; + } + + /* To disable a callback, simply replace the function name by 0 */ + status = dreamroq_play("/rd/movie.roq", 0, renderGLdc_cb, audio_cb, quit_cb); + //status = dreamroq_play("/cd/romdisk/movie.roq", 0, renderGLdc_cb, audio_cb, quit_cb); + + printf("dreamroq_play() status = %d\n", status); + + // Terminate and clean up the audio thread + if (audio_init) + { + snddrv.dec_status = SNDDEC_STATUS_DONE; + while (snddrv.dec_status != SNDDEC_STATUS_NULL) + { + thd_sleep(1); + printf("Waiting for audio thread to finish...\n"); + } + thd_destroy(audio_thread); // Destroy the audio thread + free(pcm_buf); + pcm_buf = NULL; + pcm_size = 0; + } + + if (graphics_initialized) + { + pvr_mem_free(textures[0]); + pvr_mem_free(textures[1]); + printf("Freed PVR memory\n"); + } + + printf("Exiting main()\n"); + return 0; +} diff --git a/samples/dreamroq/romdisk/video.roq b/samples/dreamroq/romdisk/movie.roq similarity index 100% rename from samples/dreamroq/romdisk/video.roq rename to samples/dreamroq/romdisk/movie.roq