Audio cleanup.

This commit is contained in:
Will Scullin 2015-07-10 22:55:36 -07:00
parent 8a761be9a9
commit 1dfbc0b61b
4 changed files with 179 additions and 259 deletions

View File

@ -1,7 +1,7 @@
<!DOCTYPE html><!-- -*- mode: HTML; indent-tabs-mode: nil -*- -->
<!--
<!--
Copyright 2010-2013 Will Scullin <scullin@scullinsteel.com>
Permission to use, copy, modify, distribute, and sell this software and its
documentation for any purpose is hereby granted without fee, provided that
the above copyright notice appear in all copies and that both that
@ -27,7 +27,7 @@
<link rel="apple-touch-icon" size="72x72" href="img/webapp-ipad.png" />
<link rel="shortcut icon" href="logoicon.png" />
<link rel="stylesheet" type="text/css" href="css/apple2.css" />
<link rel="stylesheet" type="text/css"
<link rel="stylesheet" type="text/css"
href="http://code.jquery.com/ui/1.10.3/themes/mint-choc/jquery-ui.css" />
<meta property="og:title" content="Apple ][js" />
@ -88,8 +88,8 @@ function DriveLights()
{
return {
driveLight: function(drive, on) {
$("#disk" + drive).css("background-image",
on ? "url(css/red-on-16.png)" :
$("#disk" + drive).css("background-image",
on ? "url(css/red-on-16.png)" :
"url(css/red-off-16.png)");
},
dirty: function(drive, dirty) {
@ -277,7 +277,7 @@ function loadHTTP(url) {
var req = new XMLHttpRequest();
req.open("GET", url, true);
req.responseType = "arraybuffer";
req.onload = function(event) {
var parts = url.split(/[\/\.]/);
var name = decodeURIComponent(parts[parts.length - 2]);
@ -360,7 +360,7 @@ function updateKHz() {
}
/* Audio Handling */
initAudio();
initAudio(io);
function updateSound()
{
@ -403,9 +403,9 @@ function updateSpeed()
}
var _requestAnimationFrame =
window.requestAnimationFrame ||
window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;
function run(pc) {
@ -418,8 +418,8 @@ function run(pc) {
}
var ival = 30;
var now, last = Date.now(), lastSample = last;
var runFn = function() {
var now, last = Date.now();
var runFn = function() {
now = Date.now();
frames++;
@ -439,18 +439,13 @@ function run(pc) {
loadAjax("json/disks/" + filename + ".json");
}
}
}
}
if (!loading) {
running = true;
cpu.stepCycles(step);
running = false;
vm.blit();
if (now - lastSample >= 200) {
if (!audioAPI) {
playSample();
}
lastSample = now;
}
io.sampleTick();
}
processGamepad(io);
@ -510,7 +505,7 @@ function selectDisk(event) {
}
function clickDisk(event) {
doLoad();
doLoad();
}
function loadDisk(data) {
@ -540,10 +535,10 @@ function loadJSON(data) {
loading = false;
}
/*
* LocalStorage Disk Storage
/*
* LocalStorage Disk Storage
*/
function updateLocalStorage() {
var diskIndex = JSON.parse(window.localStorage.diskIndex || "{}");
var names = [], name, cat;
@ -555,38 +550,38 @@ function updateLocalStorage() {
}
cat = disk_categories['Local Saves'] = [];
$("#manage").empty();
$("#manage").empty();
names.forEach(function(name) {
cat.push({'category': 'Local Saves',
'name': name,
'filename': 'local:' + name});
$("#manage").append("<span class='local_save'>" +
name +
" <a href='#' onclick='doDelete(\"" +
name +
'filename': 'local:' + name});
$("#manage").append("<span class='local_save'>" +
name +
" <a href='#' onclick='doDelete(\"" +
name +
"\")'>Delete</a><br /></span>");
});
cat.push({'category': 'Local Saves',
cat.push({'category': 'Local Saves',
'name': 'Manage Saves...',
'filename': 'local:__manage'});
}
function saveLocalStorage(drive, name) {
var diskIndex = JSON.parse(window.localStorage.diskIndex || "{}");
var json = disk2.getJSON(drive);
diskIndex[name] = json;
window.localStorage.diskIndex = JSON.stringify(diskIndex);
window.alert("Saved");
drivelights.label(drive, name);
drivelights.dirty(drive, false);
updateLocalStorage();
}
function deleteLocalStorage(name) {
var diskIndex = JSON.parse(window.localStorage.diskIndex || "{}");
if (diskIndex[name]) {
@ -640,7 +635,7 @@ function _keydown(evt) {
keyboard.controlKey(true);
} else if (!focused && (!evt.metaKey || evt.ctrlKey)) {
evt.preventDefault();
var key = keyboard.mapKeyEvent(evt);
if (key != 0xff) {
io.keyDown(key);
@ -754,12 +749,12 @@ $(function() {
$("input,textarea").blur(function() { focused = false; });
keyboard.create($("#keyboard"));
if (prefs.havePrefs()) {
$("input[type=checkbox]").each(function() {
var val = prefs.readPref(this.id);
if (val != null)
this.checked = JSON.parse(val);
this.checked = JSON.parse(val);
});
$("input[type=checkbox]").change(function() {
prefs.writePref(this.id, JSON.stringify(this.checked));
@ -794,7 +789,7 @@ $(function() {
buttons: {"Close": cancel }});
if (window.localStorage !== undefined) {
$("button.disksave").show();
$("button.disksave").show();
}
var oldcat = "";
@ -806,11 +801,11 @@ $(function() {
$("<option />").val(cat).text(cat).appendTo("#category_select");
disk_categories[cat] = [];
oldcat = cat;
}
}
disk_categories[cat].push(file);
if (disk) {
if (!disk_sets[name]) {
disk_sets[name] = []
disk_sets[name] = []
}
disk_sets[name].push(file);
}
@ -887,7 +882,7 @@ $(function() {
<div style="float: right">
<input type="button" onclick="window.open('about.html','_html')"
name="About" value="About">
<input type="button" onclick="$('#options').dialog('open')"
<input type="button" onclick="$('#options').dialog('open')"
name="Options" value="Options">
</div>
</div>
@ -911,21 +906,21 @@ $(function() {
<h3>Joystick</h3>
<ul>
<li>
<input type="checkbox" id="flip_x"
<input type="checkbox" id="flip_x"
onclick="updateJoystick()" />
<label for="flip_x">
Flip X-Axis
</label>
</li>
<li>
<input type="checkbox" id="flip_y"
<input type="checkbox" id="flip_y"
onclick="updateJoystick()" />
<label for="flip_y">
Flip Y-Axis
</label>
</li>
<li>
<input type="checkbox" id="swap_x_y"
<input type="checkbox" id="swap_x_y"
onclick="updateJoystick()" />
<label for="swap_x_y">
Swap X-Y Axis
@ -935,14 +930,14 @@ $(function() {
<h3>Monitor</h3>
<ul>
<li>
<input type="checkbox" id="green_screen"
<input type="checkbox" id="green_screen"
onclick="updateScreen()" />
<label for="green_screen">
Green Screen
</label>
</li>
<li>
<input type="checkbox" id="show_scanlines"
<input type="checkbox" id="show_scanlines"
onclick="updateScreen()" />
<label for="show_scanlines">
Show Scanlines
@ -952,7 +947,7 @@ $(function() {
<h3>Sound</h3>
<ul>
<li>
<input type="checkbox" id="enable_sound"
<input type="checkbox" id="enable_sound"
onclick="updateSound()" checked="checked" />
<label for="enable_sound">
Enable (Experimental)
@ -962,7 +957,7 @@ $(function() {
</div>
<div id="save" title="Save Disk">
<form action="#" onsubmit="return false;">
Save Name: <input type="text" name="name" id="save_name"
Save Name: <input type="text" name="name" id="save_name"
style="width: 200px" />
</form>
</div>
@ -972,7 +967,7 @@ $(function() {
<table>
<tr>
<td>
<select id="category_select" multiple="multiple"
<select id="category_select" multiple="multiple"
class="ui-widget ui-state-default"
onchange="selectCategory(event)" >
</select>
@ -981,7 +976,7 @@ $(function() {
<select id="disk_select" multiple="multiple"
class="ui-widget ui-state-default"
onchange="selectDisk(event)"
ondblclick="clickDisk(event)">
ondblclick="clickDisk(event)">
</select>
</td>
</tr>

View File

@ -1,7 +1,7 @@
<!DOCTYPE html><!-- -*- mode: HTML; indent-tabs-mode: nil -*- -->
<!--
<!--
Copyright 2010-2013 Will Scullin <scullin@scullinsteel.com>
Permission to use, copy, modify, distribute, and sell this software and its
documentation for any purpose is hereby granted without fee, provided that
the above copyright notice appear in all copies and that both that
@ -27,7 +27,7 @@
<link rel="apple-touch-icon" size="72x72" href="img/webapp-ipad.png" />
<link rel="shortcut icon" href="logoicon.png" />
<link rel="stylesheet" type="text/css" href="css/apple2.css" />
<link rel="stylesheet" type="text/css"
<link rel="stylesheet" type="text/css"
href="http://code.jquery.com/ui/1.10.3/themes/mint-choc/jquery-ui.css" />
<meta property="og:title" content="Apple //js" />
@ -59,7 +59,7 @@
<script type="text/javascript" src="js/ramfactor.js"></script>
<script type="text/javascript" src="js/thunderclock.js"></script>
<script type="text/javascript" src="js/cpu6502.js"></script>
<script type="text/javascript" src="js/base64.js"></script><
<script type="text/javascript" src="js/base64.js"></script>
<script type="text/javascript" src="js/ui/audio.js"></script>
<script type="text/javascript" src="js/ui/keyboard2e.js"></script>
<script type="text/javascript" src="js/ui/gamepad.js"></script>
@ -89,8 +89,8 @@ function DriveLights()
{
return {
driveLight: function(drive, on) {
$("#disk" + drive).css("background-image",
on ? "url(css/red-on-16.png)" :
$("#disk" + drive).css("background-image",
on ? "url(css/red-on-16.png)" :
"url(css/red-off-16.png)");
},
dirty: function(drive, dirty) {
@ -278,7 +278,7 @@ function loadHTTP(url) {
var req = new XMLHttpRequest();
req.open("GET", url, true);
req.responseType = "arraybuffer";
req.onload = function(event) {
var parts = url.split(/[\/\.]/);
var name = decodeURIComponent(parts[parts.length - 2]);
@ -353,7 +353,7 @@ function updateKHz() {
}
/* Audio Handling */
initAudio();
initAudio(io);
function updateSound()
{
@ -396,9 +396,9 @@ function updateSpeed()
}
var _requestAnimationFrame =
window.requestAnimationFrame ||
window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;
function run(pc) {
@ -411,8 +411,8 @@ function run(pc) {
}
var ival = 30;
var now, last = Date.now(), lastSample = last;
var runFn = function() {
var now, last = Date.now();
var runFn = function() {
now = Date.now();
frames++;
@ -432,19 +432,14 @@ function run(pc) {
loadAjax("json/disks/" + filename + ".json");
}
}
}
}
if (!loading) {
mmu.resetVB();
running = true;
cpu.stepCycles(step);
running = false;
vm.blit();
if (now - lastSample >= 200) {
if (!audioAPI) {
playSample();
}
lastSample = now;
}
io.sampleTick();
}
processGamepad(io);
@ -504,7 +499,7 @@ function selectDisk(event) {
}
function clickDisk(event) {
doLoad();
doLoad();
}
function loadDisk(data) {
@ -534,10 +529,10 @@ function loadJSON(data) {
loading = false;
}
/*
* LocalStorage Disk Storage
/*
* LocalStorage Disk Storage
*/
function updateLocalStorage() {
var diskIndex = JSON.parse(window.localStorage.diskIndex || "{}");
var names = [], name, cat;
@ -549,38 +544,38 @@ function updateLocalStorage() {
}
cat = disk_categories['Local Saves'] = [];
$("#manage").empty();
$("#manage").empty();
names.forEach(function(name) {
cat.push({'category': 'Local Saves',
'name': name,
'filename': 'local:' + name});
$("#manage").append("<span class='local_save'>" +
name +
" <a href='#' onclick='doDelete(\"" +
name +
'filename': 'local:' + name});
$("#manage").append("<span class='local_save'>" +
name +
" <a href='#' onclick='doDelete(\"" +
name +
"\")'>Delete</a><br /></span>");
});
cat.push({'category': 'Local Saves',
cat.push({'category': 'Local Saves',
'name': 'Manage Saves...',
'filename': 'local:__manage'});
}
function saveLocalStorage(drive, name) {
var diskIndex = JSON.parse(window.localStorage.diskIndex || "{}");
var json = disk2.getJSON(drive);
diskIndex[name] = json;
window.localStorage.diskIndex = JSON.stringify(diskIndex);
window.alert("Saved");
drivelights.label(drive, name);
drivelights.dirty(drive, false);
updateLocalStorage();
}
function deleteLocalStorage(name) {
var diskIndex = JSON.parse(window.localStorage.diskIndex || "{}");
if (diskIndex[name]) {
@ -638,7 +633,7 @@ function _keydown(evt) {
keyboard.optionKey(true);
} else if (!focused) {
evt.preventDefault();
var key = keyboard.mapKeyEvent(evt);
if (key != 0xff) {
io.keyDown(key);
@ -754,17 +749,17 @@ $(function() {
$("input,textarea").focus(function() { focused = true; })
.blur(function() { focused = false; });
$("body > div").hover(function() { focused = false; },
$("body > div").hover(function() { focused = false; },
function() { focused = true; });
keyboard.create($("#keyboard"));
if (prefs.havePrefs()) {
$("input[type=checkbox]").each(function() {
var val = prefs.readPref(this.id);
if (val != null)
this.checked = JSON.parse(val);
this.checked = JSON.parse(val);
});
$("input[type=checkbox]").change(function() {
prefs.writePref(this.id, JSON.stringify(this.checked));
@ -799,7 +794,7 @@ $(function() {
buttons: {"Close": cancel }});
if (window.localStorage !== undefined) {
$("button.disksave").show();
$("button.disksave").show();
}
var oldcat = "";
@ -811,11 +806,11 @@ $(function() {
$("<option />").val(cat).text(cat).appendTo("#category_select");
disk_categories[cat] = [];
oldcat = cat;
}
}
disk_categories[cat].push(file);
if (disk) {
if (!disk_sets[name]) {
disk_sets[name] = []
disk_sets[name] = []
}
disk_sets[name].push(file);
}
@ -852,7 +847,7 @@ $(function() {
<table id="display">
<tr>
<td style="vertical-align: top">
<div role="textbox" class="overscan"
<div role="textbox" class="overscan"
onKeyDown="_keydown(event);"
onKeyUp="_keyup(event);">
<canvas id="screen" width="560" height="384"></canvas>
@ -894,7 +889,7 @@ $(function() {
<div style="float: right">
<input type="button" onclick="window.open('about.html','_html')"
name="About" value="About">
<input type="button" onclick="$('#options').dialog('open')"
<input type="button" onclick="$('#options').dialog('open')"
name="Options" value="Options">
</div>
</div>
@ -918,21 +913,21 @@ $(function() {
<h3>Joystick</h3>
<ul>
<li>
<input type="checkbox" id="flip_x"
<input type="checkbox" id="flip_x"
onclick="updateJoystick()" />
<label for="flip_x">
Flip X-Axis
</label>
</li>
<li>
<input type="checkbox" id="flip_y"
<input type="checkbox" id="flip_y"
onclick="updateJoystick()" />
<label for="flip_y">
Flip Y-Axis
</label>
</li>
<li>
<input type="checkbox" id="swap_x_y"
<input type="checkbox" id="swap_x_y"
onclick="updateJoystick()" />
<label for="swap_x_y">
Swap X-Y Axis
@ -942,14 +937,14 @@ $(function() {
<h3>Monitor</h3>
<ul>
<li>
<input type="checkbox" id="green_screen"
<input type="checkbox" id="green_screen"
onclick="updateScreen()" />
<label for="green_screen">
Green Screen
</label>
</li>
<li>
<input type="checkbox" id="show_scanlines"
<input type="checkbox" id="show_scanlines"
onclick="updateScreen()" />
<label for="show_scanlines">
Show Scanlines
@ -959,7 +954,7 @@ $(function() {
<h3>Sound</h3>
<ul>
<li>
<input type="checkbox" id="enable_sound"
<input type="checkbox" id="enable_sound"
onclick="updateSound()" checked="checked" />
<label for="enable_sound">
Enable (Experimental)
@ -969,7 +964,7 @@ $(function() {
</div>
<div id="save" title="Save Disk">
<form action="#" onsubmit="return false;">
Save Name: <input type="text" name="name" id="save_name"
Save Name: <input type="text" name="name" id="save_name"
style="width: 200px" />
</form>
</div>
@ -979,7 +974,7 @@ $(function() {
<table>
<tr>
<td>
<select id="category_select" multiple="multiple"
<select id="category_select" multiple="multiple"
class="ui-widget ui-state-default"
onchange="selectCategory(event)" >
</select>
@ -988,7 +983,7 @@ $(function() {
<select id="disk_select" multiple="multiple"
class="ui-widget ui-state-default"
onchange="selectDisk(event)"
ondblclick="clickDisk(event)">
ondblclick="clickDisk(event)">
</select>
</td>
</tr>

View File

@ -18,7 +18,8 @@ function Apple2IO(cpu, callbacks)
"use strict";
var _hz = 1023000;
var _rate = 16000;
var _rate = 44000;
var _sample_size = 4096;
var _cycles_per_sample = _hz / _rate;
@ -31,10 +32,11 @@ function Apple2IO(cpu, callbacks)
var _sample = [];
var _sampleTime = 0;
var _high = "%A0";
var _mid = "%80";
var _low = "%60";
var _high = 0.5;
var _low = -0.5;
var _audioListener = null;
var _trigger = 0;
var _tape = [];
@ -94,6 +96,20 @@ function Apple2IO(cpu, callbacks)
var _locs = [];
function _tick() {
var now = cpu.cycles();
var phase = _phase > 0 ? _high : _low;
for (; _sampleTime < now; _sampleTime += _cycles_per_sample) {
_sample.push(phase);
if (_sample.length >= _sample_size) {
if (_audioListener) {
_audioListener(_sample);
}
_sample = [];
}
}
}
function _access(off) {
var result = 0;
var now = cpu.cycles();
@ -204,13 +220,8 @@ function Apple2IO(cpu, callbacks)
if ('doublehires' in callbacks) callbacks.doublehires(true);
break;
case LOC.SPEAKER:
if (_sampleTime) {
var phase = _phase > 0 ? _high : _low;
for (; _sampleTime < now; _sampleTime += _cycles_per_sample) {
_sample.push(phase);
}
_phase = -_phase;
}
_phase = -_phase;
_tick();
break;
case LOC.STROBE:
_key &= 0x7f;
@ -342,45 +353,14 @@ function Apple2IO(cpu, callbacks)
paddle: function apple2io_paddle(p, v) {
_paddle[p] = v;
},
getSample: function apple2io_getSample() {
var result = _sample;
var now = cpu.cycles();
var phase = _mid;
if (_sample.length) {
phase = _phase > 0 ? _high : _low;
}
for (; _sampleTime < now; _sampleTime += _cycles_per_sample) {
_sample.push(phase);
}
_sample = [];
return result;
},
floatAudio: function apple2io_floatAudio(rate) {
_rate = rate;
_low = -0.5;
_mid = 0.0;
_high = 0.5;
_cycles_per_sample = _hz / _rate;
},
byteAudio: function apple2io_byteAudio(rate) {
_rate = rate;
_low = 0xa0;
_mid = 0x80;
_high = 0x60;
_cycles_per_sample = _hz / _rate;
},
updateHz: function apple2io_updateHz(hz) {
_hz = hz;
_cycles_per_sample = _hz / _rate;
},
setKeyBuffer: function apple2io_setKeyBuffer(buffer) {
_buffer = buffer.split("");
_buffer = buffer.split('');
if (_buffer.length > 0) {
_keyDown = true;
_key = _buffer.shift().charCodeAt(0) | 0x80;
@ -391,6 +371,19 @@ function Apple2IO(cpu, callbacks)
debug('Tape length: ' + tape.length);
_tape = tape;
_tapeOffset = -1;
},
sampleRate: function sampleRate(rate) {
_rate = rate;
_cycles_per_sample = _hz / _rate;
},
sampleTick: function sampleTick() {
_tick();
},
addSampleListener: function addSampleListener(cb) {
_audioListener = cb;
}
};
}

View File

@ -11,132 +11,69 @@
*/
/*jshint jquery: true, browser: true */
/*globals io: false, toHex:false, debug: false */
/*exported playSample, enableSound, initAudio */
/*globals debug: false */
/*exported enableSound, initAudio */
/*
* Audio Handling
*/
var sound = true;
var sound = true;
var _samples = [];
// 8000 = 0x1f40 = 64, 31
// 16000 = 0x3e80 = 128, 62
var audioContext;
var audioNode;
var AC = window.webkitAudioContext || window.AudioContext;
var wavHeader =
['R','I','F','F',68 ,3 ,0 ,0 ,'W','A','V','E','f','m','t',' ',
16 ,0 ,0 ,0 ,1 ,0 ,1 ,0 ,128,62 ,0 ,0 ,128,62 ,0 ,0 ,
1 ,0 ,8 ,0 ,'d','a','t','a',32 ,3 ,0 ,0 ];
if (typeof AC !== 'undefined') {
audioContext = new AC();
audioNode = audioContext.createScriptProcessor(4096, 1, 1);
function percentEncode(ary) {
var buf = "";
for (var idx = 0; idx < ary.length; idx++) {
buf += "%" + toHex(ary[idx]);
}
return buf;
}
audioNode.onaudioprocess = function(event) {
var data = event.outputBuffer.getChannelData(0);
var sample = _samples.shift();
var idx = 0;
wavHeader = $.map(wavHeader, function(n) {
return typeof(n) == "string" ? n.charCodeAt(0) : n;
});
var len = data.length;
if (sample) {
len = Math.min(sample.length, len);
for (; idx < len; idx++) {
data[idx] = sample[idx];
}
}
for (; idx < data.length; idx++) {
data[idx] = 0.0;
}
};
var wavHeaderStr = percentEncode(wavHeader);
/*
// Create and specify parameters for the low-pass filter.
var filter = audioContext.createBiquadFilter();
filter.type = 'lowpass';
filter.frequency.value = 11000;
filter.connect(audioContext.destination);
audioNode.connect(filter);
*/
var audioAPI = false;
var audioMoz = false;
var audioContext;
var audioNode;
var audio, audio2;
audioNode.connect(audioContext.destination);
}
function initAudio() {
var AC = window.webkitAudioContext || window.AudioContext;
if (typeof AC != "undefined") {
debug("Using Web Audio API");
audioAPI = true;
audioContext = new AC();
audioNode = audioContext.createScriptProcessor(4096, 1, 1);
io.floatAudio(audioContext.sampleRate);
audioNode.onaudioprocess = function(event) {
var data = event.outputBuffer.getChannelData(0);
var sample = io.getSample();
var delta = 1; // sample.length / data.length;
var idx, kdx;
for (idx = 0, kdx = 0;
idx < data.length && parseInt(kdx, 10) < sample.length;
kdx += delta, idx++) {
data[idx] = sample[parseInt(kdx, 10)];
function initAudio(io) {
if (audioContext) {
debug('Using Webkit Audio');
io.sampleRate(audioContext.sampleRate);
io.addSampleListener(function(sample) {
if (sound) {
_samples.push(sample);
while (_samples.length > 5) {
_samples.shift();
}
}
for (; idx < data.length; idx++) {
data[idx] = sample[sample.length - 1];
}
};
} else {
audio = document.createElement("audio");
if (audio.mozSetup) {
debug("Using Mozilla Audio API");
audio.mozSetup(1, 16000);
io.floatAudio(16000);
audioMoz = true;
} else {
debug("Using audio elements");
audio2 = document.createElement("audio");
}
});
}
}
function playSample() {
// [audio,audio2] = [audio2,audio];
var sample = io.getSample();
if (!sound) {
return;
}
if (audioMoz) {
audio.mozWriteAudio(sample);
return;
}
var tmp = audio;
audio = audio2;
audio2 = tmp;
if (sample && sample.length) {
var len = sample.length,
buf = sample.join(""),
o1 = percentEncode([(len + 36) & 0xff, (len + 36) >> 8]),
o2 = percentEncode([len & 0xff, len >> 8]),
header = wavHeaderStr.replace("%44%03",o1).replace("%20%03", o2);
audio.src = "data:audio/x-wav," + header + buf;
// debug(audio.src);
audio.play();
}
function enableSound(enable) {
sound = enable;
}
function enableSound(on)
{
sound = on;
if (audioAPI) {
if (sound) {
audioNode.connect(audioContext.destination);
} else {
audioNode.disconnect();
}
} else {
if (sound) {
if (audio) audio.volume = 0.5;
if (audio2) audio2.volume = 0.5;
} else {
io.getSample(true);
}
}
}