Skip to content
/ cdgraphics Public

A fast, flexible CD+Graphics (CD+G) renderer

License

Notifications You must be signed in to change notification settings

bhj/cdgraphics

Repository files navigation

cdgraphics

A fast, flexible CD+Graphics (CD+G) renderer.

Installation

$ npm i cdgraphics

API

new CDGraphics()

Instantiates a new parser/renderer.

import CDGraphics from 'cdgraphics'
const cdg = new CDGraphics()

.load(buffer)

Loads a CD+G file from an ArrayBuffer, which can be had via the Response of a fetch(). You must load() a CD+G file before calling render().

fetch(cdgFileUrl)
  .then(response => response.arrayBuffer())
  .then(buffer => cdg.load(buffer))

.render(time, [options])

  • time: Number (in seconds) of the frame to render. Should usually be the currentTime of an <audio> element.
  • options: Object with one or more of the following:
    • forceKey: Boolean forcing the background to be transparent, even if the CD+G title did not explicitly specify it. Defaults to false.

Returns an object with the following properties:

  • imageData: ImageData object containing the rendered frame's pixel data.
  • isChanged: Boolean indicating whether the frame changed since the last render. Useful for skipping unnecessary re-paints to a canvas.
  • backgroundRGBA: Array containing the frame's background color in the form [r, g, b, a] with alpha being 0 or 1. The reported alpha includes the effect of the forceKey option, if enabled.
  • contentBounds: Array containing the coordinates of a bounding box that fits the frame's non-transparent pixels in the form [x1, y1, x2, y2]. Typically only useful when the forceKey option is enabled.

Usage

The following excerpt demonstrates an audio-synced render loop that draws to a canvas. See the demo code for a more complete example.

const audio = document.getElementById('audio')
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
let frameId

const doRender = time => {
 const frame = cdg.render(time)
 if (!frame.isChanged) return

 createImageBitmap(frame.imageData)
   .then(bitmap => {
     ctx.clearRect(0, 0, canvas.clientWidth, canvas.clientHeight)
     ctx.drawImage(bitmap, 0, 0, canvas.clientWidth, canvas.clientHeight)
   })
}

// render loop
const pause = () => cancelAnimationFrame(frameId)
const play = () => {
 frameId = requestAnimationFrame(play)
 doRender(audio.currentTime)
}

// follow <audio> events
audio.addEventListener('play', play)
audio.addEventListener('pause', pause)
audio.addEventListener('ended', pause)
audio.addEventListener('seeked', () => doRender(audio.currentTime))

Demo

To run the demo and see how it all comes together:

  1. Clone the repo
  2. Place your audio and .cdg file in the demo folder
  3. Update lines 1 and 2 of demo/demo.js with those filenames
  4. $ npm i
  5. $ npm run demo

Acknowledgements

License

ISC