|
1 | 1 | const SCREEN_WIDTH = 256; |
2 | 2 | const SCREEN_HEIGHT = 240; |
3 | | -const FRAMEBUFFER_SIZE = SCREEN_WIDTH*SCREEN_HEIGHT; |
| 3 | +const FRAMEBUFFER_SIZE = SCREEN_WIDTH * SCREEN_HEIGHT; |
4 | 4 |
|
5 | 5 | let canvas_ctx, image; |
6 | 6 | let framebuffer_u8, framebuffer_u32; |
7 | 7 |
|
8 | 8 | const AUDIO_BUFFERING = 512; |
9 | | -const SAMPLE_COUNT = 4*1024; |
| 9 | +const SAMPLE_COUNT = 4 * 1024; |
10 | 10 | const SAMPLE_MASK = SAMPLE_COUNT - 1; |
11 | 11 | let audio_samples_L = new Float32Array(SAMPLE_COUNT); |
12 | 12 | let audio_samples_R = new Float32Array(SAMPLE_COUNT); |
13 | | -let audio_write_cursor = 0, audio_read_cursor = 0; |
| 13 | +let audio_write_cursor = 0, |
| 14 | + audio_read_cursor = 0; |
14 | 15 | let audio_started = false; |
15 | 16 |
|
16 | 17 | let nes = new jsnes.NES({ |
17 | | - onFrame: function(framebuffer_24){ |
18 | | - for(let i = 0; i < FRAMEBUFFER_SIZE; i++) framebuffer_u32[i] = 0xFF000000 | framebuffer_24[i]; |
19 | | - }, |
20 | | - onAudioSample: function(l, r){ |
21 | | - audio_samples_L[audio_write_cursor] = l; |
22 | | - audio_samples_R[audio_write_cursor] = r; |
23 | | - audio_write_cursor = (audio_write_cursor + 1) & SAMPLE_MASK; |
24 | | - }, |
| 18 | + onFrame: function (framebuffer_24) { |
| 19 | + for (let i = 0; i < FRAMEBUFFER_SIZE; i++) |
| 20 | + framebuffer_u32[i] = 0xff000000 | framebuffer_24[i]; |
| 21 | + }, |
| 22 | + onAudioSample: function (l, r) { |
| 23 | + audio_samples_L[audio_write_cursor] = l; |
| 24 | + audio_samples_R[audio_write_cursor] = r; |
| 25 | + audio_write_cursor = (audio_write_cursor + 1) & SAMPLE_MASK; |
| 26 | + }, |
25 | 27 | }); |
26 | 28 |
|
27 | | -function onAnimationFrame(){ |
28 | | - window.requestAnimationFrame(onAnimationFrame); |
29 | | - if (!audio_started) { |
30 | | - nes.frame(); |
31 | | - } |
| 29 | +function onAnimationFrame() { |
| 30 | + window.requestAnimationFrame(onAnimationFrame); |
| 31 | + if (!audio_started) { |
| 32 | + nes.frame(); |
| 33 | + } |
32 | 34 |
|
33 | | - image.data.set(framebuffer_u8); |
34 | | - canvas_ctx.putImageData(image, 0, 0); |
| 35 | + image.data.set(framebuffer_u8); |
| 36 | + canvas_ctx.putImageData(image, 0, 0); |
35 | 37 | } |
36 | 38 |
|
37 | | -function audio_remain(){ |
38 | | - return (audio_write_cursor - audio_read_cursor) & SAMPLE_MASK; |
| 39 | +function audio_remain() { |
| 40 | + return (audio_write_cursor - audio_read_cursor) & SAMPLE_MASK; |
39 | 41 | } |
40 | 42 |
|
41 | | -function audio_callback(event){ |
42 | | - let dst = event.outputBuffer; |
43 | | - let len = dst.length; |
44 | | - |
45 | | - // Attempt to avoid buffer underruns. |
46 | | - if(audio_remain() < AUDIO_BUFFERING) nes.frame(); |
47 | | - |
48 | | - let dst_l = dst.getChannelData(0); |
49 | | - let dst_r = dst.getChannelData(1); |
50 | | - for(let i = 0; i < len; i++){ |
51 | | - let src_idx = (audio_read_cursor + i) & SAMPLE_MASK; |
52 | | - dst_l[i] = audio_samples_L[src_idx]; |
53 | | - dst_r[i] = audio_samples_R[src_idx]; |
54 | | - } |
55 | | - |
56 | | - audio_read_cursor = (audio_read_cursor + len) & SAMPLE_MASK; |
| 43 | +function audio_callback(event) { |
| 44 | + let dst = event.outputBuffer; |
| 45 | + let len = dst.length; |
| 46 | + |
| 47 | + // Attempt to avoid buffer underruns. |
| 48 | + if (audio_remain() < AUDIO_BUFFERING) nes.frame(); |
| 49 | + |
| 50 | + let dst_l = dst.getChannelData(0); |
| 51 | + let dst_r = dst.getChannelData(1); |
| 52 | + for (let i = 0; i < len; i++) { |
| 53 | + let src_idx = (audio_read_cursor + i) & SAMPLE_MASK; |
| 54 | + dst_l[i] = audio_samples_L[src_idx]; |
| 55 | + dst_r[i] = audio_samples_R[src_idx]; |
| 56 | + } |
| 57 | + |
| 58 | + audio_read_cursor = (audio_read_cursor + len) & SAMPLE_MASK; |
57 | 59 | } |
58 | 60 |
|
59 | | -function keyboard(callback, event){ |
60 | | - event.preventDefault(); |
61 | | - let player = 1; |
62 | | - switch(event.keyCode){ |
63 | | - case 38: // UP |
64 | | - callback(player, jsnes.Controller.BUTTON_UP); break; |
65 | | - case 40: // Down |
66 | | - callback(player, jsnes.Controller.BUTTON_DOWN); break; |
67 | | - case 37: // Left |
68 | | - callback(player, jsnes.Controller.BUTTON_LEFT); break; |
69 | | - case 39: // Right |
70 | | - callback(player, jsnes.Controller.BUTTON_RIGHT); break; |
71 | | - case 65: // 'a' - qwerty, dvorak |
72 | | - case 81: // 'q' - azerty |
73 | | - callback(player, jsnes.Controller.BUTTON_A); break; |
74 | | - case 83: // 's' - qwerty, azerty |
75 | | - case 79: // 'o' - dvorak |
76 | | - callback(player, jsnes.Controller.BUTTON_B); break; |
77 | | - case 9: // Tab |
78 | | - callback(player, jsnes.Controller.BUTTON_SELECT); break; |
79 | | - case 13: // Return |
80 | | - callback(player, jsnes.Controller.BUTTON_START); break; |
81 | | - default: break; |
82 | | - } |
| 61 | +function keyboard(callback, event) { |
| 62 | + event.preventDefault(); |
| 63 | + let player = 1; |
| 64 | + switch (event.keyCode) { |
| 65 | + case 38: // UP |
| 66 | + callback(player, jsnes.Controller.BUTTON_UP); |
| 67 | + break; |
| 68 | + case 40: // Down |
| 69 | + callback(player, jsnes.Controller.BUTTON_DOWN); |
| 70 | + break; |
| 71 | + case 37: // Left |
| 72 | + callback(player, jsnes.Controller.BUTTON_LEFT); |
| 73 | + break; |
| 74 | + case 39: // Right |
| 75 | + callback(player, jsnes.Controller.BUTTON_RIGHT); |
| 76 | + break; |
| 77 | + case 65: // 'a' - qwerty, dvorak |
| 78 | + case 81: // 'q' - azerty |
| 79 | + callback(player, jsnes.Controller.BUTTON_A); |
| 80 | + break; |
| 81 | + case 83: // 's' - qwerty, azerty |
| 82 | + case 79: // 'o' - dvorak |
| 83 | + callback(player, jsnes.Controller.BUTTON_B); |
| 84 | + break; |
| 85 | + case 9: // Tab |
| 86 | + callback(player, jsnes.Controller.BUTTON_SELECT); |
| 87 | + break; |
| 88 | + case 13: // Return |
| 89 | + callback(player, jsnes.Controller.BUTTON_START); |
| 90 | + break; |
| 91 | + default: |
| 92 | + break; |
| 93 | + } |
83 | 94 | } |
84 | 95 |
|
85 | 96 | let audio_ctx; |
86 | | -function nes_init(canvas_id){ |
87 | | - let canvas = document.getElementById(canvas_id); |
88 | | - canvas_ctx = canvas.getContext("2d"); |
89 | | - image = canvas_ctx.getImageData(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); |
90 | | - |
91 | | - canvas_ctx.fillStyle = "black"; |
92 | | - canvas_ctx.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); |
93 | | - |
94 | | - // Allocate framebuffer array. |
95 | | - let buffer = new ArrayBuffer(image.data.length); |
96 | | - framebuffer_u8 = new Uint8ClampedArray(buffer); |
97 | | - framebuffer_u32 = new Uint32Array(buffer); |
98 | | - |
99 | | - // Setup audio. |
100 | | - audio_ctx = new window.AudioContext(); |
101 | | - let script_processor = audio_ctx.createScriptProcessor(AUDIO_BUFFERING, 0, 2); |
102 | | - script_processor.onaudioprocess = audio_callback; |
103 | | - script_processor.connect(audio_ctx.destination); |
104 | | - if (audio_ctx.state === 'suspended') { |
105 | | - let audioButton = document.getElementById('audio'); |
106 | | - audioButton.style.display = 'block'; |
107 | | - } else { |
108 | | - audio_started = true; |
109 | | - } |
| 97 | +function nes_init(canvas_id) { |
| 98 | + let canvas = document.getElementById(canvas_id); |
| 99 | + canvas_ctx = canvas.getContext("2d"); |
| 100 | + image = canvas_ctx.getImageData(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); |
| 101 | + |
| 102 | + canvas_ctx.fillStyle = "black"; |
| 103 | + canvas_ctx.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); |
| 104 | + |
| 105 | + // Allocate framebuffer array. |
| 106 | + let buffer = new ArrayBuffer(image.data.length); |
| 107 | + framebuffer_u8 = new Uint8ClampedArray(buffer); |
| 108 | + framebuffer_u32 = new Uint32Array(buffer); |
| 109 | + |
| 110 | + // Setup audio. |
| 111 | + audio_ctx = new window.AudioContext(); |
| 112 | + let script_processor = audio_ctx.createScriptProcessor(AUDIO_BUFFERING, 0, 2); |
| 113 | + script_processor.onaudioprocess = audio_callback; |
| 114 | + script_processor.connect(audio_ctx.destination); |
| 115 | + if (audio_ctx.state === "suspended") { |
| 116 | + let audioButton = document.getElementById("audio"); |
| 117 | + audioButton.style.display = "block"; |
| 118 | + } else { |
| 119 | + audio_started = true; |
| 120 | + } |
110 | 121 | } |
111 | 122 |
|
112 | | -function nes_boot(rom_data){ |
113 | | - nes.loadROM(rom_data); |
114 | | - window.requestAnimationFrame(onAnimationFrame); |
| 123 | +function nes_boot(rom_data) { |
| 124 | + nes.loadROM(rom_data); |
| 125 | + window.requestAnimationFrame(onAnimationFrame); |
115 | 126 | } |
116 | 127 |
|
117 | | -function nes_load_data(canvas_id, rom_data){ |
118 | | - nes_init(canvas_id); |
119 | | - nes_boot(rom_data); |
| 128 | +function nes_load_data(canvas_id, rom_data) { |
| 129 | + nes_init(canvas_id); |
| 130 | + nes_boot(rom_data); |
120 | 131 | } |
121 | 132 |
|
122 | | -function nes_load_url(canvas_id, path){ |
123 | | - nes_init(canvas_id); |
124 | | - |
125 | | - let req = new XMLHttpRequest(); |
126 | | - req.open("GET", path); |
127 | | - req.overrideMimeType("text/plain; charset=x-user-defined"); |
128 | | - req.onerror = () => console.log(`Error loading ${path}: ${req.statusText}`); |
129 | | - |
130 | | - req.onload = function() { |
131 | | - if (this.status === 200) { |
132 | | - nes_boot(this.responseText); |
133 | | - } else if (this.status === 0) { |
134 | | - // Aborted, so ignore error |
135 | | - } else { |
136 | | - req.onerror(); |
137 | | - } |
138 | | - }; |
139 | | - |
140 | | - req.send(); |
| 133 | +function nes_load_url(canvas_id, path) { |
| 134 | + nes_init(canvas_id); |
| 135 | + |
| 136 | + let req = new XMLHttpRequest(); |
| 137 | + req.open("GET", path); |
| 138 | + req.overrideMimeType("text/plain; charset=x-user-defined"); |
| 139 | + req.onerror = () => console.log(`Error loading ${path}: ${req.statusText}`); |
| 140 | + |
| 141 | + req.onload = function () { |
| 142 | + if (this.status === 200) { |
| 143 | + nes_boot(this.responseText); |
| 144 | + } else if (this.status === 0) { |
| 145 | + // Aborted, so ignore error |
| 146 | + } else { |
| 147 | + req.onerror(); |
| 148 | + } |
| 149 | + }; |
| 150 | + |
| 151 | + req.send(); |
141 | 152 | } |
142 | 153 |
|
143 | | -document.addEventListener('keydown', (event) => {keyboard(nes.buttonDown, event)}); |
144 | | -document.addEventListener('keyup', (event) => {keyboard(nes.buttonUp, event)}); |
| 154 | +document.addEventListener("keydown", (event) => { |
| 155 | + keyboard(nes.buttonDown, event); |
| 156 | +}); |
| 157 | +document.addEventListener("keyup", (event) => { |
| 158 | + keyboard(nes.buttonUp, event); |
| 159 | +}); |
145 | 160 |
|
146 | | -document.addEventListener('DOMContentLoaded', (event) => { |
147 | | - let audioButton = document.getElementById('audio'); |
148 | | - audioButton.addEventListener('click', () => { |
149 | | - audio_ctx.resume().then(() => { |
150 | | - audio_started = true; |
151 | | - }); |
152 | | - audioButton.style.display = 'none'; |
| 161 | +document.addEventListener("DOMContentLoaded", (event) => { |
| 162 | + let audioButton = document.getElementById("audio"); |
| 163 | + audioButton.addEventListener("click", () => { |
| 164 | + audio_ctx.resume().then(() => { |
| 165 | + audio_started = true; |
153 | 166 | }); |
| 167 | + audioButton.style.display = "none"; |
| 168 | + }); |
154 | 169 | }); |
0 commit comments