Initial commit
This commit is contained in:
commit
7264e90ed7
6 files changed
+856
No files matched your search
@@ -0,0 +1,116 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- saved from url=(0048)http://cdgmagic.sourceforge.net/html5_cdgplayer/ -->
|
||||
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
|
||||
|
||||
|
||||
<meta name="description" content="Possibly the first and only HTML5/JavaScript CD+G player, showing example content created with free, open source CD+Graphics Magic.">
|
||||
|
||||
<title>CD+Graphics Magic :: HTML5 CD+G Player</title>
|
||||
|
||||
<style type="text/css">
|
||||
body { background-color:#eeeeee; font-family:arial,sans-serif; width:80%; margin-left:auto; margin-right:auto; }
|
||||
.header_box { background-color:#007777; text-align:center; font-size:24px; font-weight:bold; padding:0.25em;
|
||||
white-space:nowrap; border-color:#111111; border-style:solid; border-width:1px; }
|
||||
.left_navigation { height:100%; float:left; width:150px; background-color:#007777; text-align:center;
|
||||
white-space:nowrap; border-color:#111111; border-style:solid; border-width:1px; }
|
||||
.left_navigation a { font-size:80%; color:#ffffff; font-weight:bold; display:block; padding:0.5em; margin:0.2em;
|
||||
border-color:#005555; border-style:solid; border-width:2px; background-color:#009999; }
|
||||
.left_navigation a:hover { color:#ff8800; background-color:#333333; }
|
||||
.left_navigation a:active { color:#aaaaaa; }
|
||||
.right_content { clear:right; background-color:#dddddd; text-align:left; margin-left:152px; font-size:0.8em;
|
||||
padding:1em; color:#111111; border-color:#111111; border-style:solid; border-width:1px; }
|
||||
.footer { text-align:center; }
|
||||
.footer img { border:0em; }
|
||||
</style>
|
||||
|
||||
<script src="./HTML5-CDGPlayer_files/cdgmagic_cdgplayer.js"></script>
|
||||
<script src="./HTML5-CDGPlayer_files/cdgmagic_cdgdecoder_lowresource.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
function Start_CDGPlayer()
|
||||
{
|
||||
CDG_Player_init( "cdg_audio", "cdg_canvas", "cdg_border", "cdg_status" );
|
||||
};
|
||||
|
||||
var zoom_mode = 0;
|
||||
function toggle_2x_mode()
|
||||
{
|
||||
zoom_mode = (~zoom_mode) & 0x01;
|
||||
var brdr = document.getElementById("cdg_border");
|
||||
var cnvs = document.getElementById("cdg_canvas");
|
||||
var audi = document.getElementById("cdg_audio");
|
||||
brdr.style.width = (zoom_mode) ? 648 + "px" : 324 + "px";
|
||||
cnvs.style.width = (zoom_mode) ? 576 + "px" : 288 + "px";
|
||||
brdr.style.height = (zoom_mode) ? 432 + "px" : 216 + "px";
|
||||
cnvs.style.height = (zoom_mode) ? 384 + "px" : 192 + "px";
|
||||
cnvs.style.padding= (zoom_mode) ? 24 + "px" : 12 + "px";
|
||||
audi.style.width = (zoom_mode) ? 648 + "px" : 324 + "px";
|
||||
};
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="Start_CDGPlayer()">
|
||||
|
||||
<div class="header_box"><span style="color:#ff8800">CD+G</span><span style="color:#ffffff">raphics Magic :: HTML5 CD+G Player</span></div>
|
||||
|
||||
<div class="left_navigation">
|
||||
<a href="http://cdgmagic.sourceforge.net/" class="navigation_link">Home</a>
|
||||
<br>
|
||||
<a href="http://www.sourceforge.net/projects/cdgmagic/" title="CD+Graphics Magic Project Page on SourceForge.">Project Page</a>
|
||||
<a href="http://www.sourceforge.net/projects/cdgmagic/files/" title="Download CD+Graphics Magic, files hosted by SourceForge.">Download Files</a>
|
||||
<br>
|
||||
<a href="http://cdgmagic.sourceforge.net/screenshots/" title="Fullsize screenshots showing various options.">Screenshots</a>
|
||||
<a href="./HTML5-CDGPlayer_files/HTML5-CDGPlayer.htm" title="Example content, playable directly in compatible browsers.">HTML5 CD+G Player<br>(Example Content)</a>
|
||||
<!--<br>
|
||||
<a href="/tutorials/" title="Tutorials for program usage, including general CD+G format information.">Tutorials</a>-->
|
||||
</div><!--end left_navigation-->
|
||||
|
||||
<div class="right_content">
|
||||
Full and Split Page Cut/Fade modes are planned features still in development.
|
||||
<br>All other modes are demonstrated exactly as implemented in Experimental 001b.<br><br><br>
|
||||
<center><!-- <center> is probably evil, but at least it WORKS... TODO: Determine the proper margin-fu to banish said evil.-->
|
||||
<!-- Note: The audio player is above media display because some default interfaces have controls that extend past the top of the player. -->
|
||||
<audio id="cdg_audio" style="width:324px">
|
||||
To use the JavaScript Subcode Graphics Player,<br>you must use an HTML5 audio/canvas compatible browser. [audio]
|
||||
</audio>
|
||||
|
||||
<div id="cdg_border" style="width:324px; height:216px; background-color:#000000;" ondblclick="toggle_2x_mode();">
|
||||
<canvas id="cdg_canvas" width="288" height="192" style="padding:12px"><!--style="image-rendering: optimizeSpeed"-->
|
||||
To use the JavaScript Subcode Graphics Player,<br>you must use an HTML5 audio/canvas compatible browser. [canvas]
|
||||
</canvas>
|
||||
</div>
|
||||
|
||||
<div id="cdg_status" style="width:320px; padding:2px; margin:4px; text-align:left; font-family:arial; font-size:1.0em/*0.8em*/; color:#00dddd; background-color:#444444">
|
||||
Please select a file to play...
|
||||
</div>
|
||||
|
||||
<select onchange="set_file_prefix(this.value)" size="1" style="width:324px; margin:0px; color:#00dddd; background-color:#444444">
|
||||
<option value="">--select one--</option>
|
||||
<!--NOTE: the file is split on the colon for the mp3 and the cdg-->
|
||||
<option value="SC-SBI-REMIX - Billy Idol - Rebel Yell:SC-SBI-REMIX - Billy Idol - Rebel Yell">SC-SBI-REMIX - Billy Idol - Rebel Yell</option>
|
||||
</select>
|
||||
</center>
|
||||
<br><br>This JavaScript based player should work in most HTML5 compatible browsers supporting both the <canvas> and <audio> (note: "Sample Project" 01-04 are silent) tags, including many mobile and embedded devices.
|
||||
<br>Double-click "screen area" to toggle 2x zoom mode (which may consume large amounts of CPU time on certain hardware/software configurations).
|
||||
<br><br>It is, like the main application, licensed under the terms of the <a href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.html">GNU GPL v2</a> (or later).
|
||||
<br>(However, please do not hotlink the script from this site... COPY IT TO YOUR WEB SERVER!)
|
||||
<br><br>Sync and timing accuracy, unfortunately, varies with that of the underlying browser implementation.
|
||||
<br>Perfectly smooth scrolling is not implemented in this version for performance reasons, although block based scrolls work correctly.
|
||||
<br><br>
|
||||
<a href="http://www.jonathancoulton.com/songdetails/Flickr">"Flickr"</a> by <a href="http://www.jonathancoulton.com/">Jonathan Coulton</a> used under <a href="http://creativecommons.org/licenses/by-nc/3.0/">Creative Commons Attribution-NonCommercial 3.0 Unported</a> license.
|
||||
<br><br>
|
||||
<a href="http://ccmixter.org/files/Loveshadow/26157">"Heart on Redial"</a> by <a href="http://ccmixter.org/people/Loveshadow">Loveshadow</a> used under <a href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0 Unported</a> license.
|
||||
<br>
|
||||
<a href="http://cdgmagic.sourceforge.net/html5_cdgplayer/loveshadow_heartonredial_credits.html">Loveshadow - Heart on Redial Complete Music and Images Credits and Licenses Table</a>
|
||||
</div><!--end right_content-->
|
||||
|
||||
<div class="footer">
|
||||
<br><a href="http://sourceforge.net/projects/cdgmagic"><img src="./HTML5-CDGPlayer_files/sflogo.php" width="80" height="15" alt="Get CD+Graphics Magic at SourceForge.net. Fast, secure and Free Open Source software downloads"></a>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</body></html>
|
||||
@@ -0,0 +1,549 @@
|
||||
/*
|
||||
* This file is part of CD+Graphics Magic.
|
||||
*
|
||||
* CD+Graphics Magic is free software: you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation, either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* CD+Graphics Magic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with CD+Graphics Magic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This class instantiates an HTML5/Canvas CD+Graphics decoder object.
|
||||
*
|
||||
* This is the "low resource" version, and should be very close
|
||||
* to as fast as possible with JavaScript.
|
||||
*
|
||||
* The difference between the "low resource" and normal version
|
||||
* is that this one packs each 6 pixel font line in to one
|
||||
* array value, unrolling some loops and minimizing array lookups.
|
||||
*
|
||||
* The only concession made is lack of H/V "offset" support used
|
||||
* for smooth scrolling.
|
||||
* Block based scrolls *are* still supported, however, so the basic
|
||||
* intent of the graphics is presented, but less than ideally.
|
||||
*
|
||||
* It is recommended for CPU constrained (eg. mobile or embedded) devices.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
function CDGMagic_cdgdecoder( canvas_element, border_div )
|
||||
{
|
||||
var CDG_ENUM = // Useful enums for CD+Graphics...
|
||||
{
|
||||
VRAM_SIZE : 300*216, // Total linear size of VRAM, in pixels.
|
||||
VRAM_WIDTH : 300, // Width (or pitch) of VRAM, in pixels.
|
||||
VRAM_HEIGHT : 216, // Height of VRAM, in pixels.
|
||||
VISIBLE_SIZE : 288*192, // Total linear size of visible screen, in pixels.
|
||||
VISIBLE_WIDTH : 288, // Width (or pitch) of visible screen, in pixels.
|
||||
VISIBLE_HEIGHT : 192, // Height of visible screen, in pixels.
|
||||
FONT_WIDTH : 6, // Width of one "font" (or block).
|
||||
FONT_HEIGHT : 12, // Height of one "font" (or block).
|
||||
NUM_X_FONTS : 50, // Number of horizontal fonts contained in VRAM.
|
||||
NUM_Y_FONTS : 18, // Number of vertical fonts contained in VRAM.
|
||||
PALETTE_ENTRIES : 16, // Number of CLUT palette entries.
|
||||
TV_GRAPHICS : 0x09, // 50x18 (48x16) 16 color TV graphics mode.
|
||||
MEMORY_PRESET : 0x01, // Set all VRAM to palette index.
|
||||
BORDER_PRESET : 0x02, // Set border to palette index.
|
||||
LOAD_CLUT_LO : 0x1E, // Load Color Look Up Table index 0 through 7.
|
||||
LOAD_CLUT_HI : 0x1F, // Load Color Look Up Table index 8 through 15.
|
||||
COPY_FONT : 0x06, // Copy 12x6 pixel font to screen.
|
||||
XOR_FONT : 0x26, // XOR 12x6 pixel font with existing VRAM values.
|
||||
SCROLL_PRESET : 0x14, // Update scroll offset, copying if 0x20 or 0x10.
|
||||
SCROLL_COPY : 0x18 // Update scroll offset, setting color if 0x20 or 0x10.
|
||||
};
|
||||
|
||||
// Bind the various public function to member variables.
|
||||
this.get_current_pack = get_current_pack;
|
||||
this.set_dirtyrect = set_dirtyrect;
|
||||
this.reset_cdg_state = reset_cdg_state;
|
||||
this.redraw_canvas = redraw_canvas;
|
||||
this.clear_palette = clear_palette;
|
||||
this.clear_vram = clear_vram;
|
||||
this.decode_packs = decode_packs;
|
||||
|
||||
var internal_border_div = border_div; // DIV element behind graphics canvas.
|
||||
var internal_rgba_canvas = canvas_element; // Canvas element.
|
||||
var internal_rgba_context = internal_rgba_canvas.getContext("2d"); // 2D context of canvas element.
|
||||
var internal_rgba_imagedata = internal_rgba_context.createImageData(CDG_ENUM.VISIBLE_WIDTH, CDG_ENUM.VISIBLE_HEIGHT); // 288x192 image data.
|
||||
var internal_usedirtyrect = (navigator.userAgent.search(/webkit/i) >= 0) ? 1 : 0; // Use the putImage(...) dirty rectangle coordinates?
|
||||
//TODO: Compare array[size] speed to array = new Array(size) speed... Does it make a difference?
|
||||
var internal_palette = new Array(CDG_ENUM.PALETTE_ENTRIES); // Array containing the 16 RGB palette entries.
|
||||
var internal_vram = new Array(CDG_ENUM.NUM_X_FONTS * CDG_ENUM.VRAM_HEIGHT); // Array used for graphics VRAM.
|
||||
|
||||
var internal_border_index = 0x00; // The current border palette index.
|
||||
var internal_current_pack = 0x00; // The current playback position.
|
||||
|
||||
var internal_border_dirty = 0x00; // State variable used to determine if the background DIV needs updated.
|
||||
var internal_screen_dirty = 0x00; // State variable used to determine if a full screen update is needed.
|
||||
var internal_dirty_blocks = new Array(900); // Array used to determine if a given font/block has changed.
|
||||
|
||||
this.reset_cdg_state(); // Set all state variables to known, sane values.
|
||||
//if (internal_usedirtyrect) { alert("Using putImage dirty rect."); };
|
||||
|
||||
//########## PUBLIC FUNCTIONS ##########//
|
||||
|
||||
// Reset all the CDG state variables back to initial values.
|
||||
function reset_cdg_state()
|
||||
{
|
||||
internal_current_pack = 0x00;
|
||||
internal_border_index = 0x00;
|
||||
clear_palette();
|
||||
clear_vram(0x00);
|
||||
clear_dirty_blocks();
|
||||
};
|
||||
|
||||
function get_current_pack() { return internal_current_pack; };
|
||||
|
||||
function set_dirtyrect(requested_value) { internal_usedirtyrect = requested_value; };
|
||||
|
||||
function redraw_canvas()
|
||||
{
|
||||
// If the border color has changed, then update the background div color.
|
||||
if ((internal_border_dirty == 0x01) || (internal_screen_dirty == 0x01))
|
||||
{
|
||||
internal_border_div.style.backgroundColor = palette_index_to_rgb_tuple(internal_border_index);
|
||||
internal_border_dirty = 0x00;
|
||||
};
|
||||
// If the screen is dirty, then it needs a full update.
|
||||
if (internal_screen_dirty == 0x01)
|
||||
{
|
||||
render_screen_to_rgb();
|
||||
internal_screen_dirty = 0;
|
||||
clear_dirty_blocks();
|
||||
internal_rgba_context.putImageData(internal_rgba_imagedata, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// It should be faster to only paint dirty blocks,
|
||||
// BUT it appears the putImageData dirty rectangle isn't always properly supported.
|
||||
// And, of course, when it's NOT, then we're actually updating the ENTIRE canvas for every
|
||||
// small section we intend, which absolutely KILLS performance, instead of helping.
|
||||
// Current, stable Windows/32bit browser release results as of this writing:
|
||||
// Mozilla Firefox 3.6.13 :: Ignored (always updates entire canvas)
|
||||
// Apple Safari 5.0.3 (7533.19.4) :: Works
|
||||
// Google Chrome 8.0.552.237 :: Works
|
||||
// Opera 11.00 (1156) :: Ignored (always updates entire canvas)
|
||||
var local_context = internal_rgba_context;
|
||||
var local_rgba_imagedata = internal_rgba_imagedata;
|
||||
var local_dirty = internal_dirty_blocks;
|
||||
var local_fontwidth = CDG_ENUM.FONT_WIDTH;
|
||||
var local_fontheight = CDG_ENUM.FONT_HEIGHT;
|
||||
var update_needed = 0x00;
|
||||
var blk = 0x00;
|
||||
for (var y_blk = 1; y_blk <= 16; ++y_blk)
|
||||
{
|
||||
blk = y_blk * CDG_ENUM.NUM_X_FONTS + 1;
|
||||
for (var x_blk = 1; x_blk <= 48; ++x_blk)
|
||||
{
|
||||
if ( local_dirty[blk] )
|
||||
{
|
||||
render_block_to_rgb(x_blk, y_blk);
|
||||
if (internal_usedirtyrect == 0x01)
|
||||
{
|
||||
local_context.putImageData(local_rgba_imagedata, 0, 0,
|
||||
(x_blk-1) * local_fontwidth,
|
||||
(y_blk-1) * local_fontheight,
|
||||
local_fontwidth,
|
||||
local_fontheight);
|
||||
}
|
||||
else { update_needed = 0x01; };
|
||||
local_dirty[blk] = 0x00;
|
||||
};
|
||||
++blk;
|
||||
};
|
||||
};
|
||||
// Update the whole screen for browsers where dirty rect isn't supported.
|
||||
// Since this can't be detected(???) in any way, it has to be User Agent selected, or an actual user option.
|
||||
// TODO: See if a dirty rect-based partial update of known pixel values combined with a getImageData
|
||||
// call could be used to determine if it works correctly *without* evil browser sniffing.
|
||||
if (update_needed == 0x01)
|
||||
{
|
||||
local_context.putImageData(local_rgba_imagedata, 0, 0);
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
// Decode to pack playback_position, using cdg_file_data.
|
||||
function decode_packs(cdg_file_data, playback_position)
|
||||
{
|
||||
for ( var curr_pack = internal_current_pack; curr_pack < playback_position; curr_pack++)
|
||||
{
|
||||
var start_offset = curr_pack * 24;
|
||||
var curr_command = cdg_file_data.charCodeAt(start_offset) & 0x3F;
|
||||
if ( curr_command == CDG_ENUM.TV_GRAPHICS )
|
||||
{
|
||||
// Slice the file array down to a single pack array.
|
||||
var this_pack = cdg_file_data.slice(start_offset, start_offset+24);
|
||||
// Pluck out the graphics instruction.
|
||||
var curr_instruction = this_pack.charCodeAt(1) & 0x3F;
|
||||
// Perform the instruction action.
|
||||
switch (curr_instruction)
|
||||
{
|
||||
case CDG_ENUM.MEMORY_PRESET: proc_MEMORY_PRESET(this_pack); break;
|
||||
case CDG_ENUM.BORDER_PRESET: proc_BORDER_PRESET(this_pack); break;
|
||||
|
||||
case CDG_ENUM.LOAD_CLUT_LO:
|
||||
case CDG_ENUM.LOAD_CLUT_HI: proc_LOAD_CLUT(this_pack); break;
|
||||
|
||||
case CDG_ENUM.COPY_FONT:
|
||||
case CDG_ENUM.XOR_FONT: proc_WRITE_FONT(this_pack); break;
|
||||
|
||||
case CDG_ENUM.SCROLL_PRESET:
|
||||
case CDG_ENUM.SCROLL_COPY: proc_DO_SCROLL(this_pack); break;
|
||||
};
|
||||
};
|
||||
};
|
||||
internal_current_pack = playback_position;
|
||||
};
|
||||
|
||||
//########## PRIVATE CONVENIENCE FUNCTIONS ##########//
|
||||
|
||||
// Convenience function to return the string "rgb(r,g,b)" CSS style tuple of a palette index.
|
||||
function palette_index_to_rgb_tuple(requested_index)
|
||||
{
|
||||
return "rgb(" + ((internal_palette[requested_index] >> 020) & 0xFF) + "," +
|
||||
+ ((internal_palette[requested_index] >> 010) & 0xFF) + "," +
|
||||
+ ((internal_palette[requested_index] >> 000) & 0xFF) + ")";
|
||||
};
|
||||
|
||||
// Convenience function to return a line of special packed palette values.
|
||||
function fill_line_with_palette_index(requested_index)
|
||||
{
|
||||
var adjusted_value = requested_index; // Pixel 0
|
||||
adjusted_value |= (requested_index << 004); // Pixel 1
|
||||
adjusted_value |= (requested_index << 010); // Pixel 2
|
||||
adjusted_value |= (requested_index << 014); // Pixel 3
|
||||
adjusted_value |= (requested_index << 020); // Pixel 4
|
||||
adjusted_value |= (requested_index << 024); // Pixel 5
|
||||
return adjusted_value;
|
||||
};
|
||||
|
||||
// Reset the state of all font/blocks to clean.
|
||||
function clear_dirty_blocks()
|
||||
{
|
||||
var local_dirty = internal_dirty_blocks;
|
||||
for (var blk = 0; blk < 900; blk++) { local_dirty[blk] = 0x00; };
|
||||
};
|
||||
|
||||
// Reset all the palette RGB values to black.
|
||||
function clear_palette()
|
||||
{
|
||||
var total_palette_entries = CDG_ENUM.PALETTE_ENTRIES;
|
||||
var local_palette = internal_palette;
|
||||
for (var idx = 0; idx < total_palette_entries; idx++) { local_palette[idx] = 0x00; };
|
||||
};
|
||||
|
||||
// Set all the VRAM index values to requested index.
|
||||
function clear_vram(color_index)
|
||||
{
|
||||
var local_vram = internal_vram;
|
||||
var total_vram_size = local_vram.length;
|
||||
var packed_line_value = fill_line_with_palette_index(color_index);
|
||||
for (var pxl = 0; pxl < total_vram_size; pxl++) { local_vram[pxl] = packed_line_value; };
|
||||
internal_screen_dirty = 0x01;
|
||||
};
|
||||
|
||||
//########## PRIVATE GRAPHICS RENDERING FUNCTIONS ##########//
|
||||
|
||||
function render_screen_to_rgb()
|
||||
{
|
||||
var local_rgba = internal_rgba_imagedata.data;
|
||||
var local_pal = internal_palette;
|
||||
var local_vram = internal_vram;
|
||||
var vis_width = 48;
|
||||
var vis_height = CDG_ENUM.VISIBLE_HEIGHT;
|
||||
|
||||
var vram_loc = 601; // Offset into VRAM array.
|
||||
var rgb_loc = 0x00; // Offset into RGBA array.
|
||||
var curr_rgb = 0x00; // RGBA value of current pixel.
|
||||
var curr_line_indices = 0x00; // Packed font row index values.
|
||||
|
||||
for (var y_pxl = 0; y_pxl < vis_height; ++y_pxl)
|
||||
{
|
||||
for (var x_pxl = 0; x_pxl < vis_width; ++x_pxl)
|
||||
{
|
||||
curr_line_indices = local_vram[vram_loc++]; // Get the current line segment indices.
|
||||
curr_rgb = local_pal[ (curr_line_indices >> 000) & 0x0F ]; // Get the RGB value for pixel 0.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 020) & 0xFF; // Set red value for pixel 0.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 010) & 0xFF; // Set green value for pixel 0.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 000) & 0xFF; // Set blue value for pixel 0.
|
||||
local_rgba[rgb_loc++] = 0xFF; // Set alpha value (fully opaque) for pixel 0.
|
||||
curr_rgb = local_pal[ (curr_line_indices >> 004) & 0x0F ]; // Get the RGB value for pixel 1.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 020) & 0xFF; // Set red value for pixel 1.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 010) & 0xFF; // Set green value for pixel 1.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 000) & 0xFF; // Set blue value for pixel 1.
|
||||
local_rgba[rgb_loc++] = 0xFF; // Set alpha value (fully opaque) for pixel 1.
|
||||
curr_rgb = local_pal[ (curr_line_indices >> 010) & 0x0F ]; // Get the RGB value for pixel 2.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 020) & 0xFF; // Set red value for pixel 2.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 010) & 0xFF; // Set green value for pixel 2.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 000) & 0xFF; // Set blue value for pixel 2.
|
||||
local_rgba[rgb_loc++] = 0xFF; // Set alpha value (fully opaque) for pixel 2.
|
||||
curr_rgb = local_pal[ (curr_line_indices >> 014) & 0x0F ]; // Get the RGB value for pixel 3.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 020) & 0xFF; // Set red value for pixel 3.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 010) & 0xFF; // Set green value for pixel 3.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 000) & 0xFF; // Set blue value for pixel 3.
|
||||
local_rgba[rgb_loc++] = 0xFF; // Set alpha value (fully opaque) for pixel 3.
|
||||
curr_rgb = local_pal[ (curr_line_indices >> 020) & 0x0F ]; // Get the RGB value for pixel 4.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 020) & 0xFF; // Set red value for pixel 4.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 010) & 0xFF; // Set green value for pixel 4.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 000) & 0xFF; // Set blue value for pixel 4.
|
||||
local_rgba[rgb_loc++] = 0xFF; // Set alpha value (fully opaque) for pixel 4.
|
||||
curr_rgb = local_pal[ (curr_line_indices >> 024) & 0x0F ]; // Get the RGB value for pixel 5.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 020) & 0xFF; // Set red value for pixel 5.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 010) & 0xFF; // Set green value for pixel 5.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 000) & 0xFF; // Set blue value for pixel 5.
|
||||
local_rgba[rgb_loc++] = 0xFF; // Set alpha value (fully opaque) for pixel 5.
|
||||
// Or, instead, index 0 could be set transparent to show background image/video.
|
||||
// Alternately, SET_TRANSPARENT instruction could be implemented to set 6bit transparency.
|
||||
// Unfortunately, I don't think many (any?) discs bother to set it :-/...
|
||||
};
|
||||
vram_loc += 2; // Skip the offscreen font blocks.
|
||||
};
|
||||
};
|
||||
|
||||
function render_block_to_rgb(x_start, y_start)
|
||||
{
|
||||
var local_rgba = internal_rgba_imagedata.data;
|
||||
var local_pal = internal_palette;
|
||||
var local_vram = internal_vram;
|
||||
|
||||
var vram_loc = (y_start * CDG_ENUM.NUM_X_FONTS * CDG_ENUM.FONT_HEIGHT) + x_start; // Offset into VRAM array.
|
||||
var vram_inc = CDG_ENUM.NUM_X_FONTS;
|
||||
var vram_end = vram_loc + (CDG_ENUM.NUM_X_FONTS * CDG_ENUM.FONT_HEIGHT); // VRAM location to end.
|
||||
var rgb_loc = (y_start - 1) * CDG_ENUM.FONT_HEIGHT * CDG_ENUM.VISIBLE_WIDTH; // Row start.
|
||||
rgb_loc += (x_start - 1) * CDG_ENUM.FONT_WIDTH; // Column start
|
||||
rgb_loc *= 4; // RGBA, 1 pxl = 4 bytes.
|
||||
var rgb_inc = (CDG_ENUM.VISIBLE_WIDTH - CDG_ENUM.FONT_WIDTH) * 4;
|
||||
var curr_rgb = 0x00; // RGBA value of current pixel.
|
||||
var curr_line_indices = 0x00; // Packed font row index values.
|
||||
|
||||
while (vram_loc < vram_end)
|
||||
{
|
||||
curr_line_indices = local_vram[vram_loc]; // Get the current line segment indices.
|
||||
curr_rgb = local_pal[ (curr_line_indices >> 000) & 0x0F ]; // Get the RGB value for pixel 0.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 020) & 0xFF; // Set red value for pixel 0.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 010) & 0xFF; // Set green value for pixel 0.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 000) & 0xFF; // Set blue value for pixel 0.
|
||||
local_rgba[rgb_loc++] = 0xFF; // Set alpha value (fully opaque) for pixel 0.
|
||||
curr_rgb = local_pal[ (curr_line_indices >> 004) & 0x0F ]; // Get the RGB value for pixel 1.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 020) & 0xFF; // Set red value for pixel 1.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 010) & 0xFF; // Set green value for pixel 1.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 000) & 0xFF; // Set blue value for pixel 1.
|
||||
local_rgba[rgb_loc++] = 0xFF; // Set alpha value (fully opaque) for pixel 1.
|
||||
curr_rgb = local_pal[ (curr_line_indices >> 010) & 0x0F ]; // Get the RGB value for pixel 2.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 020) & 0xFF; // Set red value for pixel 2.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 010) & 0xFF; // Set green value for pixel 2.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 000) & 0xFF; // Set blue value for pixel 2.
|
||||
local_rgba[rgb_loc++] = 0xFF; // Set alpha value (fully opaque) for pixel 2.
|
||||
curr_rgb = local_pal[ (curr_line_indices >> 014) & 0x0F ]; // Get the RGB value for pixel 3.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 020) & 0xFF; // Set red value for pixel 3.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 010) & 0xFF; // Set green value for pixel 3.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 000) & 0xFF; // Set blue value for pixel 3.
|
||||
local_rgba[rgb_loc++] = 0xFF; // Set alpha value (fully opaque) for pixel 3.
|
||||
curr_rgb = local_pal[ (curr_line_indices >> 020) & 0x0F ]; // Get the RGB value for pixel 4.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 020) & 0xFF; // Set red value for pixel 4.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 010) & 0xFF; // Set green value for pixel 4.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 000) & 0xFF; // Set blue value for pixel 4.
|
||||
local_rgba[rgb_loc++] = 0xFF; // Set alpha value (fully opaque) for pixel 4.
|
||||
curr_rgb = local_pal[ (curr_line_indices >> 024) & 0x0F ]; // Get the RGB value for pixel 5.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 020) & 0xFF; // Set red value for pixel 5.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 010) & 0xFF; // Set green value for pixel 5.
|
||||
local_rgba[rgb_loc++] = (curr_rgb >> 000) & 0xFF; // Set blue value for pixel 5.
|
||||
local_rgba[rgb_loc++] = 0xFF; // Set alpha value (fully opaque) for pixel 5.
|
||||
// Or, instead, index 0 could be set transparent to show background image/video.
|
||||
// Alternately, SET_TRANSPARENT instruction could be implemented to set 6bit transparency.
|
||||
// Unfortunately, I don't think many (any?) discs bother to set it :-/...
|
||||
vram_loc += vram_inc; // Move to the first column of the next row of this font block in VRAM.
|
||||
rgb_loc += rgb_inc; // Move to the first column of the next row of this font block in RGB pixels.
|
||||
};
|
||||
};
|
||||
|
||||
//########## PRIVATE GRAPHICS DECODE FUNCTIONS ##########//
|
||||
|
||||
function proc_BORDER_PRESET(cdg_pack)
|
||||
{
|
||||
// NOTE: The "border" is actually a DIV element, which can be very expensive to change in some browsers.
|
||||
// This somewhat bizarre check ensures that the DIV is only touched if the actual RGB color is different,
|
||||
// but the border index variable is always set... A similar check is also performed during palette update.
|
||||
var new_border_index = cdg_pack.charCodeAt(4) & 0x3F; // Get the border index from subcode.
|
||||
// Check if the new border **RGB** color is different from the old one.
|
||||
if (internal_palette[new_border_index] != internal_palette[internal_border_index])
|
||||
{
|
||||
internal_border_dirty = 0x01; // Border needs updating.
|
||||
};
|
||||
internal_border_index = new_border_index; // Set the new index.
|
||||
};
|
||||
|
||||
function proc_MEMORY_PRESET(cdg_pack)
|
||||
{
|
||||
clear_vram(cdg_pack.charCodeAt(4) & 0x3F);
|
||||
};
|
||||
|
||||
|
||||
function proc_LOAD_CLUT(cdg_pack)
|
||||
{
|
||||
var local_palette = internal_palette;
|
||||
// If instruction is 0x1E then 8*0=0, if 0x1F then 8*1=8 for offset.
|
||||
var pal_offset = (cdg_pack.charCodeAt(1) & 0x01) * 8;
|
||||
// Step through the eight color indices, setting the RGB values.
|
||||
for (var pal_inc = 0; pal_inc < 8; pal_inc++)
|
||||
{
|
||||
var temp_idx = pal_inc+pal_offset;
|
||||
var temp_rgb = 0x00000000;
|
||||
var temp_entry = 0x00000000;
|
||||
// Set red.
|
||||
temp_entry = (cdg_pack.charCodeAt(pal_inc*2+4)&0x3C) >> 2;
|
||||
temp_rgb |= (temp_entry * 17) << 020;
|
||||
// Set green.
|
||||
temp_entry = ((cdg_pack.charCodeAt(pal_inc*2+4)&0x03)<<2) | ((cdg_pack.charCodeAt(pal_inc*2+5)&0x30)>>4);
|
||||
temp_rgb |= (temp_entry * 17) << 010;
|
||||
// Set blue.
|
||||
temp_entry = cdg_pack.charCodeAt(pal_inc*2+5)&0x0F;
|
||||
temp_rgb |= (temp_entry * 17) << 000;
|
||||
// Put the full RGB value into the index position, but only if it's different.
|
||||
if (temp_rgb != local_palette[temp_idx])
|
||||
{
|
||||
local_palette[temp_idx] = temp_rgb;
|
||||
internal_screen_dirty = 0x01; // The colors are now different, so we need to update the whole screen.
|
||||
if (temp_idx == internal_border_index) { internal_border_dirty = 0x01; }; // The border color has changed.
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
function proc_WRITE_FONT(cdg_pack)
|
||||
{
|
||||
var local_vram = internal_vram;
|
||||
var local_dirty = internal_dirty_blocks;
|
||||
// Hacky hack to play channels 0 and 1 only... Ideally, there should be a function and user option to get/set.
|
||||
var active_channels = 0x03;
|
||||
// First, get the channel...
|
||||
var subcode_channel = ((cdg_pack.charCodeAt(4) & 0x30) >> 2) | ((cdg_pack.charCodeAt(5) & 0x30) >> 4);
|
||||
var xor_var = cdg_pack.charCodeAt(1) & 0x20;
|
||||
// Then see if we should display it.
|
||||
if ( (active_channels>>subcode_channel)&0x01 )
|
||||
{
|
||||
var x_location = cdg_pack.charCodeAt(7) & 0x3F; // Get horizontal font location.
|
||||
var y_location = cdg_pack.charCodeAt(6) & 0x1F; // Get vertical font location.
|
||||
|
||||
// Verify we're not going to overrun the boundaries (i.e. bad data from a scratched disc).
|
||||
if ( (x_location<=49) && (y_location<=17) )
|
||||
{
|
||||
var start_pixel = y_location * 600 + x_location; // Location of first pixel of this font in linear VRAM.
|
||||
// NOTE: Profiling indicates charCodeAt() uses ~80% of the CPU consumed for this function.
|
||||
// Caching these values reduces that to a negligible amount.
|
||||
var current_indexes = [(cdg_pack.charCodeAt(4) & 0x0F), (cdg_pack.charCodeAt(5) & 0x0F)]; // Current colors.
|
||||
var current_row = 0x00; // Subcode byte for current pixel row.
|
||||
var temp_pxl = 0x00; // Decoded and packed 4bit pixel index values of current row.
|
||||
for (var y_inc = 0; y_inc < 12; y_inc++)
|
||||
{
|
||||
var pix_pos = y_inc * 50 + start_pixel; // Location of the first pixel of this row in linear VRAM.
|
||||
current_row = cdg_pack.charCodeAt(y_inc+8); // Get the subcode byte for the current row.
|
||||
temp_pxl = (current_indexes[(current_row >> 5) & 0x01] << 000);
|
||||
temp_pxl |= (current_indexes[(current_row >> 4) & 0x01] << 004);
|
||||
temp_pxl |= (current_indexes[(current_row >> 3) & 0x01] << 010);
|
||||
temp_pxl |= (current_indexes[(current_row >> 2) & 0x01] << 014);
|
||||
temp_pxl |= (current_indexes[(current_row >> 1) & 0x01] << 020);
|
||||
temp_pxl |= (current_indexes[(current_row >> 0) & 0x01] << 024);
|
||||
if ( xor_var ) { local_vram[pix_pos] ^= temp_pxl; } else { local_vram[pix_pos] = temp_pxl; };
|
||||
}; // End of Y loop.
|
||||
// Mark this block as needing an update.
|
||||
local_dirty[y_location * 50 + x_location] = 0x01;
|
||||
}; // End of location check.
|
||||
}; // End of channel check.
|
||||
};
|
||||
|
||||
function proc_DO_SCROLL(cdg_pack)
|
||||
{
|
||||
var direction = 0; // H/V direction flag.
|
||||
var copy_flag = (cdg_pack.charCodeAt(1)&0x08) >> 3; // Type of copy (memory preset or copy).
|
||||
var color = cdg_pack.charCodeAt(4)&0x0F; // Color index to use for preset type.
|
||||
|
||||
// Process horizontal commands.
|
||||
if ( (direction = ((cdg_pack.charCodeAt(5)&0x30) >> 4)) ) { proc_VRAM_HSCROLL( direction, copy_flag, color ); };
|
||||
// Process vertical commands.
|
||||
if ( (direction = ((cdg_pack.charCodeAt(6)&0x30) >> 4)) ) { proc_VRAM_VSCROLL( direction, copy_flag, color ); };
|
||||
|
||||
internal_screen_dirty = 1; // Entire screen needs to be redrawn.
|
||||
};
|
||||
|
||||
function proc_VRAM_HSCROLL(direction, copy_flag, color)
|
||||
{
|
||||
var buf = 0;
|
||||
var line_color = fill_line_with_palette_index(color);
|
||||
var local_vram = internal_vram;
|
||||
if ( direction == 0x02 )
|
||||
{
|
||||
// Step through the lines one at a time...
|
||||
for (var y_src = 0; y_src < (50*216); y_src += 50)
|
||||
{
|
||||
var y_start = y_src;
|
||||
buf = local_vram[y_start];
|
||||
for (var x_src = y_start+1; x_src < y_start+50; x_src++) { local_vram[x_src-1] = local_vram[x_src]; };
|
||||
if ( copy_flag ) { local_vram[y_start+49] = buf; } else { local_vram[y_start+49] = line_color; };
|
||||
};
|
||||
}
|
||||
else if ( direction == 0x01 )
|
||||
{
|
||||
// Step through the lines on at a time.
|
||||
for (var y_src = 0; y_src < (50*216); y_src += 50)
|
||||
{
|
||||
// Copy the last six lines to the buffer.
|
||||
var y_start = y_src;
|
||||
buf = local_vram[y_start+49];
|
||||
for (var x_src = y_start+48; x_src >= y_start; x_src--) { local_vram[x_src+1] = local_vram[x_src]; };
|
||||
if ( copy_flag ) { local_vram[y_start] = buf; } else { local_vram[y_start] = line_color; };
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
function proc_VRAM_VSCROLL(direction, copy_flag, color)
|
||||
{
|
||||
var offscreen_size = CDG_ENUM.NUM_X_FONTS * CDG_ENUM.FONT_HEIGHT;
|
||||
var buf = new Array(offscreen_size);
|
||||
var line_color = fill_line_with_palette_index(color);
|
||||
var local_vram = internal_vram;
|
||||
if ( direction == 0x02 )
|
||||
{
|
||||
var dst_idx = 0; // Buffer destination starts at 0.
|
||||
// Copy the top 300x12 pixels into the buffer.
|
||||
for (var src_idx = 0; src_idx < offscreen_size; src_idx++) { buf[dst_idx++] = local_vram[src_idx]; };
|
||||
dst_idx = 0; // Destination starts at the first line.
|
||||
for (var src_idx = offscreen_size; src_idx < (50*216); src_idx++) { local_vram[dst_idx++] = local_vram[src_idx]; };
|
||||
dst_idx = (CDG_ENUM.NUM_X_FONTS * 204); // Destination begins at line 204.
|
||||
if ( copy_flag )
|
||||
{
|
||||
for (var src_idx = 0; src_idx < offscreen_size; src_idx++) { local_vram[dst_idx++] = buf[src_idx]; };
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var src_idx = 0; src_idx < offscreen_size; src_idx++) { local_vram[dst_idx++] = line_color; };
|
||||
};
|
||||
}
|
||||
else if ( direction == 0x01 )
|
||||
{
|
||||
var dst_idx = 0; // Buffer destination starts at 0.
|
||||
// Copy the bottom 300x12 pixels into the buffer.
|
||||
for (var src_idx = (50*204); src_idx < (50*216); src_idx++) { buf[dst_idx++] = local_vram[src_idx]; };
|
||||
for (var src_idx = (50*204)-1; src_idx > 0; src_idx--) { local_vram[src_idx+offscreen_size] = local_vram[src_idx]; };
|
||||
if ( copy_flag )
|
||||
{
|
||||
for (var src_idx = 0; src_idx < offscreen_size; src_idx++) { local_vram[src_idx] = buf[src_idx]; };
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var src_idx = 0; src_idx < offscreen_size; src_idx++) { local_vram[src_idx] = line_color; };
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* This file is part of CD+Graphics Magic.
|
||||
*
|
||||
* CD+Graphics Magic is free software: you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation, either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* CD+Graphics Magic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with CD+Graphics Magic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// HTML5 audio player element to play the Ogg/Vorbis or MP3 audio file.
|
||||
var audio_player = null;
|
||||
var vorbis_source_element = null;
|
||||
var layer3_source_element = null;
|
||||
var cdg_timer_id = null;
|
||||
|
||||
// XML object and variable to get/contain the CDG file data.
|
||||
// NOTE: XMLHttpRequest is absolutely NOT designed to be used this way.
|
||||
// It does, however, seem to work for every browser tested, PROVIDED the PQ subcode bits are zero...
|
||||
// That is to say all "character codes" are <=127.
|
||||
var xml_file_request = null;
|
||||
var cdg_file_data = null;
|
||||
var status_div = null;
|
||||
|
||||
var my_cdgdecoder = null;
|
||||
|
||||
// Text used for the XML object status display.
|
||||
var XML_STATUS_TEXT = new Array( "No operations pending.",
|
||||
"Setting up file request...",
|
||||
"Headers received...",
|
||||
"Downloading graphics data...",
|
||||
"Graphics download complete." );
|
||||
|
||||
function CDG_Player_init( audio_id, canvas_id, border_id, status_id )
|
||||
{
|
||||
// Status text display... This should probably be done differently, but works for now.
|
||||
status_div = document.getElementById(status_id);
|
||||
|
||||
// HTML5 audio player element to play the Ogg/Vorbis or MP3 audio file.
|
||||
audio_player = document.getElementById(audio_id);
|
||||
audio_player.addEventListener("error", audio_error_dialog, true);
|
||||
audio_player.addEventListener("play", start_cdg_timer, true);
|
||||
audio_player.addEventListener("pause", stop_cdg_timer, true);
|
||||
audio_player.addEventListener("ended", stop_cdg_timer, true);
|
||||
audio_player.addEventListener("abort", stop_cdg_timer, true);
|
||||
|
||||
// HTML5 canvas elements for accessing RGBA pixels.
|
||||
var border_div = document.getElementById(border_id);
|
||||
var rgba_canvas = document.getElementById(canvas_id);
|
||||
|
||||
my_cdgdecoder = new CDGMagic_cdgdecoder(rgba_canvas, border_div);
|
||||
};
|
||||
|
||||
function set_file_prefix(file_url_prefixes)
|
||||
{
|
||||
// Ignore this call if we weren't given a file name.
|
||||
if (file_url_prefixes == "") { return; };
|
||||
// Construct the file paths.
|
||||
var audio_file_prefix = file_url_prefixes.split(":", 2)[0];
|
||||
var cdg_file_prefix = file_url_prefixes.split(":", 2)[1];
|
||||
// Prepend the media files source directoy.
|
||||
// file_url_prefix = "media/" + file_url_prefix;
|
||||
// Stop, reset, and blank all the CD+G related stuff.
|
||||
stop_cdg_timer();
|
||||
my_cdgdecoder.reset_cdg_state();
|
||||
my_cdgdecoder.redraw_canvas();
|
||||
cdg_file_data = null;
|
||||
// Destroy any old XML object... Seems to fix some memory issues... Find a better way to do this, perhaps?
|
||||
// Moved this to before the audio, so there's a good chance we'll get the CDG before the audio starts.
|
||||
// Probably should make smarter autoplay function that checks periodically and
|
||||
// invokes audio_player.play() only when there's sufficient data for BOTH audio and graphics.
|
||||
if ( xml_file_request ) { xml_file_request = null; };
|
||||
xml_file_request = new XMLHttpRequest();
|
||||
xml_file_request.onreadystatechange = xml_status_handler;
|
||||
xml_file_request.open( "GET", cdg_file_prefix + ".cdg", true ); // Remove _gz for normal files.
|
||||
xml_file_request.setRequestHeader( "Content-Type", "text/html" );
|
||||
xml_file_request.send();
|
||||
// Create new source elements if needed;
|
||||
if (vorbis_source_element == null)
|
||||
{
|
||||
vorbis_source_element = document.createElement("source"); vorbis_source_element.type = "audio/ogg; codecs=\"vorbis\"";
|
||||
layer3_source_element = document.createElement("source"); layer3_source_element.type = "audio/mpeg; codecs=\"mp3\"";
|
||||
};
|
||||
// Set the new source elements
|
||||
vorbis_source_element.src = audio_file_prefix + ".ogg";
|
||||
layer3_source_element.src = audio_file_prefix + ".mp3";
|
||||
// Readd them (this should just update the new values, not duplicate) to the audio player element.
|
||||
audio_player.appendChild(vorbis_source_element);
|
||||
audio_player.appendChild(layer3_source_element);
|
||||
// Reload the sources, and make sure controls are available.
|
||||
audio_player.load();
|
||||
audio_player.controls = 1;
|
||||
audio_player.autoplay = 1;
|
||||
};
|
||||
|
||||
// This handles the various events fired by the XMLHTTPObject used to download the CDG data.
|
||||
function xml_status_handler()
|
||||
{
|
||||
// Just return if there's not a valid XML object... (I don't understand why/how this happens, but it does?!...)
|
||||
if (xml_file_request == null) { return; };
|
||||
// Initial status text string.
|
||||
var status_text = "Status: " + XML_STATUS_TEXT[ xml_file_request.readyState ];
|
||||
// Try out a progressive download here...
|
||||
if (xml_file_request.readyState == 3)
|
||||
{
|
||||
cdg_file_data = xml_file_request.responseText;
|
||||
status_text += " (" + cdg_file_data.length + " bytes)";
|
||||
};
|
||||
// The data is ready, so copy it one last time and destroy the XML object.
|
||||
if (xml_file_request.readyState == 4)
|
||||
{
|
||||
// Replace the status with an error message if we didn't get HTTP 200.
|
||||
if (xml_file_request.status != 200)
|
||||
{
|
||||
status_text = "Error: HTTP " + xml_file_request.status + "/" + xml_file_request.statusText + " (No graphics!)";
|
||||
};
|
||||
cdg_file_data = xml_file_request.responseText;
|
||||
xml_file_request = null;
|
||||
};
|
||||
// Actually set the status text...
|
||||
status_div.innerHTML = status_text;
|
||||
};
|
||||
|
||||
function audio_error_dialog()
|
||||
{
|
||||
if (audio_player.error) // I don't know how/why this handler is called when the error is null, but it
|
||||
{ // happens far more often than never... Maybe related to multiple source fallback?
|
||||
// I'm not exactly how sure how this is supposed to work, but certain browsers put
|
||||
// the error in object.error, and some one level deeper in object.error.code...
|
||||
// (And still others might actually have it in the <source> object... ???)
|
||||
var error_result = audio_player.error;
|
||||
if (audio_player.error.code) { error_result = audio_player.error.code; };
|
||||
alert("The audio control fired an error event.\nCould be: " + error_result);
|
||||
};
|
||||
};
|
||||
|
||||
// A little note about the timer silliness...
|
||||
// Most browsers only seem to update the stream position
|
||||
// at around 500ms intervals... Far too slowly to be useful...
|
||||
// The timer smooths out the framerate, and occasionally readjusts
|
||||
// if the deviation from the reported stream time is wide enough.
|
||||
function start_cdg_timer()
|
||||
{
|
||||
cdg_timer_id = setInterval(update_play_position, 20);
|
||||
/*cdg_timer_id = setTimeout(update_play_position, 20);*/
|
||||
};
|
||||
|
||||
function stop_cdg_timer()
|
||||
{
|
||||
clearInterval(cdg_timer_id);
|
||||
/*clearTimeout(cdg_timer_id);*/
|
||||
//my_cdgdecoder.reset_cdg_state();
|
||||
//my_cdgdecoder.redraw_canvas();
|
||||
};
|
||||
|
||||
function update_play_position()
|
||||
{
|
||||
// Ignore if we don't have anything to play.
|
||||
if (cdg_file_data != null)
|
||||
{
|
||||
// Get the current media playback position, and convert it to an integer packet position.
|
||||
// Different browsers seem to have widely differing offsets compared to what's actually playing.
|
||||
// Maybe add a user control for sync?... And/or find some common default values to start with?...
|
||||
var play_position = Math.floor(audio_player.currentTime * 300);
|
||||
var current_pack = my_cdgdecoder.get_current_pack();
|
||||
// Constrain the play position to positive values only.
|
||||
play_position = (play_position < 0) ? 0 : play_position;
|
||||
// Render from the beginning of the stream if a reverse seek of more than one second occurred.
|
||||
if (play_position < (current_pack-300)) { my_cdgdecoder.reset_cdg_state(); current_pack = 0; };
|
||||
// Calculate the next probable play position, in packs... See "timer" note above.
|
||||
var position_to_play = current_pack+6;
|
||||
// Jump to the actual play position if it's ahead of our calculated smoothed position.
|
||||
position_to_play = (play_position > position_to_play) ? play_position : position_to_play;
|
||||
// Check if we should render any packs, and do so if needed.
|
||||
if (position_to_play > current_pack)
|
||||
{
|
||||
my_cdgdecoder.decode_packs(cdg_file_data, position_to_play);
|
||||
my_cdgdecoder.redraw_canvas();
|
||||
};
|
||||
};
|
||||
// Time to go around again, if not using setInterval...
|
||||
/*cdg_timer_id = setTimeout(update_play_position, 20);*/
|
||||
};
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.6 KiB |
Binary file not shown.
Binary file not shown.
Reference in new issue
Block a user