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

@ -360,7 +360,7 @@ function updateKHz() {
}
/* Audio Handling */
initAudio();
initAudio(io);
function updateSound()
{
@ -418,7 +418,7 @@ function run(pc) {
}
var ival = 30;
var now, last = Date.now(), lastSample = last;
var now, last = Date.now();
var runFn = function() {
now = Date.now();
frames++;
@ -445,12 +445,7 @@ function run(pc) {
cpu.stepCycles(step);
running = false;
vm.blit();
if (now - lastSample >= 200) {
if (!audioAPI) {
playSample();
}
lastSample = now;
}
io.sampleTick();
}
processGamepad(io);

View File

@ -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>
@ -353,7 +353,7 @@ function updateKHz() {
}
/* Audio Handling */
initAudio();
initAudio(io);
function updateSound()
{
@ -411,7 +411,7 @@ function run(pc) {
}
var ival = 30;
var now, last = Date.now(), lastSample = last;
var now, last = Date.now();
var runFn = function() {
now = Date.now();
frames++;
@ -439,12 +439,7 @@ function run(pc) {
cpu.stepCycles(step);
running = false;
vm.blit();
if (now - lastSample >= 200) {
if (!audioAPI) {
playSample();
}
lastSample = now;
}
io.sampleTick();
}
processGamepad(io);

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,9 +32,10 @@ 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;
@ -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];
}
}
var wavHeaderStr = percentEncode(wavHeader);
for (; idx < data.length; idx++) {
data[idx] = 0.0;
}
};
var audioAPI = false;
var audioMoz = false;
var audioContext;
var audioNode;
var audio, audio2;
/*
// 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);
*/
function initAudio() {
var AC = window.webkitAudioContext || window.AudioContext;
if (typeof AC != "undefined") {
debug("Using Web Audio API");
audioNode.connect(audioContext.destination);
}
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);
}
}
}