Using some things I learned from @stevekinney today, I've tweaked the PC speaker code to no longer stop() and destroy oscillators every time the speaker is toggled off; instead, the ChipSet component now uses one oscillator for the life of the machine (created the first time the PC makes a sound) and simply zeros/sets the gain as needed. We still have the "easing" issue to deal with (whenever the PC the makes sudden changes to the frequency) and I still can't use dot names for any of the AudioContext properties (until I either update the Closure Compiler or manually add an externs file for the AudioContext interfaces).

This commit is contained in:
Jeff Parsons 2017-08-11 20:05:48 -07:00 • committed by Jeff Parsons
commit d8e329b12c
8 changed files with 632 additions and 682 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -146,19 +146,11 @@ class ChipSet extends Component {
this.sDateRTC = parmsChipSet['dateRTC'];
/*
* Here, I'm finally getting around to trying the Web Audio API. Fortunately, based on what little I know about
* sound generation, using the API to make the same noises as the IBM PC speaker seems straightforward.
* Here, I'm finally getting around to trying the Web Audio API. Fortunately, based on what little
* I know about sound generation, using the API to make the same noises as the IBM PC speaker seems
* straightforward.
*
* To start, we create an audio context, unless the 'sound' parameter has been explicitly set to false.
*
* From:
*
* http://developer.apple.com/library/safari/#documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/PlayingandSynthesizingSounds/PlayingandSynthesizingSounds.html
*
* "Similar to how HTML5 canvas requires a context on which lines and curves are drawn, Web Audio requires an audio context
* on which sounds are played and manipulated. This context will be the parent object of further audio objects to come....
* Your audio context is typically created when your page initializes and should be long-lived. You can play multiple sounds
* coming from multiple sources within the same context, so it is unnecessary to create more than one audio context per page."
*/
this.fSpeaker = false;
if (parmsChipSet['sound']) {
@ -4607,39 +4599,22 @@ class ChipSet extends Component {
*/
if (freq < 20 || freq > 20000) fOn = false;
if (fOn) {
if (this.sourceAudio) {
this.sourceAudio['frequency']['value'] = freq;
if (this.messageEnabled(Messages.SPEAKER)) this.printMessage("speaker set to " + freq + "hz", true);
} else {
this.sourceAudio = this.contextAudio['createOscillator']();
if (this.sourceAudio) {
if (typeof this.sourceAudio['type'] == "number") {
this.sourceAudio['type'] = 1; // deprecated: 0: "sine", 1: "square", 2: "sawtooth", 3: "triangle"
} else {
this.sourceAudio['type'] = "square";
}
this.sourceAudio['connect'](this.contextAudio['destination']);
this.sourceAudio['frequency']['value'] = freq;
if ('start' in this.sourceAudio) {
this.sourceAudio['start'](0);
} else {
this.sourceAudio['noteOn'](0); // deprecated: this.sourceAudio['noteOn'](0)
if (!this.oscillatorAudio) {
this.oscillatorAudio = this.contextAudio['createOscillator']();
this.gainAudio = this.contextAudio['createGain']();
this.oscillatorAudio['connect'](this.gainAudio);
this.gainAudio['connect'](this.contextAudio['destination']);
this.oscillatorAudio['frequency']['value'] = freq;
this.oscillatorAudio['type'] = "square";
this.oscillatorAudio['start'](0);
}
this.oscillatorAudio['frequency']['value'] = freq;
this.gainAudio['gain']['value'] = 1;
if (this.messageEnabled(Messages.SPEAKER)) this.printMessage("speaker on at " + freq + "hz", true);
}
}
} else {
if (this.sourceAudio) {
if ('stop' in this.sourceAudio) {
this.sourceAudio['stop'](0);
} else {
this.sourceAudio['noteOff'](0); // deprecated: this.sourceAudio['noteOff'](0)
}
this.sourceAudio['disconnect'](); // QUESTION: is this automatic following a stop(), since this particular source cannot be started again?
delete this.sourceAudio; // QUESTION: ditto?
} else if (this.oscillatorAudio) {
this.gainAudio['gain']['value'] = 0;
if (this.messageEnabled(Messages.SPEAKER)) this.printMessage("speaker off at " + freq + "hz", true);
}
}
} catch(e) {
this.notice("AudioContext exception: " + e.message);
this.contextAudio = null;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -37606,19 +37606,11 @@ class ChipSet extends Component {
this.sDateRTC = parmsChipSet['dateRTC'];
/*
* Here, I'm finally getting around to trying the Web Audio API. Fortunately, based on what little I know about
* sound generation, using the API to make the same noises as the IBM PC speaker seems straightforward.
* Here, I'm finally getting around to trying the Web Audio API. Fortunately, based on what little
* I know about sound generation, using the API to make the same noises as the IBM PC speaker seems
* straightforward.
*
* To start, we create an audio context, unless the 'sound' parameter has been explicitly set to false.
*
* From:
*
* http://developer.apple.com/library/safari/#documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/PlayingandSynthesizingSounds/PlayingandSynthesizingSounds.html
*
* "Similar to how HTML5 canvas requires a context on which lines and curves are drawn, Web Audio requires an audio context
* on which sounds are played and manipulated. This context will be the parent object of further audio objects to come....
* Your audio context is typically created when your page initializes and should be long-lived. You can play multiple sounds
* coming from multiple sources within the same context, so it is unnecessary to create more than one audio context per page."
*/
this.fSpeaker = false;
if (parmsChipSet['sound']) {
@ -42067,39 +42059,22 @@ class ChipSet extends Component {
*/
if (freq < 20 || freq > 20000) fOn = false;
if (fOn) {
if (this.sourceAudio) {
this.sourceAudio['frequency']['value'] = freq;
if (this.messageEnabled(Messages.SPEAKER)) this.printMessage("speaker set to " + freq + "hz", true);
} else {
this.sourceAudio = this.contextAudio['createOscillator']();
if (this.sourceAudio) {
if (typeof this.sourceAudio['type'] == "number") {
this.sourceAudio['type'] = 1; // deprecated: 0: "sine", 1: "square", 2: "sawtooth", 3: "triangle"
} else {
this.sourceAudio['type'] = "square";
}
this.sourceAudio['connect'](this.contextAudio['destination']);
this.sourceAudio['frequency']['value'] = freq;
if ('start' in this.sourceAudio) {
this.sourceAudio['start'](0);
} else {
this.sourceAudio['noteOn'](0); // deprecated: this.sourceAudio['noteOn'](0)
if (!this.oscillatorAudio) {
this.oscillatorAudio = this.contextAudio['createOscillator']();
this.gainAudio = this.contextAudio['createGain']();
this.oscillatorAudio['connect'](this.gainAudio);
this.gainAudio['connect'](this.contextAudio['destination']);
this.oscillatorAudio['frequency']['value'] = freq;
this.oscillatorAudio['type'] = "square";
this.oscillatorAudio['start'](0);
}
this.oscillatorAudio['frequency']['value'] = freq;
this.gainAudio['gain']['value'] = 1;
if (this.messageEnabled(Messages.SPEAKER)) this.printMessage("speaker on at " + freq + "hz", true);
}
}
} else {
if (this.sourceAudio) {
if ('stop' in this.sourceAudio) {
this.sourceAudio['stop'](0);
} else {
this.sourceAudio['noteOff'](0); // deprecated: this.sourceAudio['noteOff'](0)
}
this.sourceAudio['disconnect'](); // QUESTION: is this automatic following a stop(), since this particular source cannot be started again?
delete this.sourceAudio; // QUESTION: ditto?
} else if (this.oscillatorAudio) {
this.gainAudio['gain']['value'] = 0;
if (this.messageEnabled(Messages.SPEAKER)) this.printMessage("speaker off at " + freq + "hz", true);
}
}
} catch(e) {
this.notice("AudioContext exception: " + e.message);
this.contextAudio = null;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long