Robustify AudioContext code (thanks for the surprise, Chrome!)
This commit is contained in:
parent
e8b7298919
commit
568c4e031e
6 changed files with 1628 additions and 1597 deletions
|
|
@ -3,20 +3,22 @@ PCjs Sources
|
|||
|
||||
Structure
|
||||
---
|
||||
All the code for PCjs is contained in the following JavaScript files, which roughly divide the
|
||||
functionality into major PC components, aka "devices". However, not every file implements a device,
|
||||
and "component" is an overloaded term, since *[Component](/docs/pcjs/component/)* is also the name of
|
||||
the shared base class used for most PCjs devices (see [component.js](../../shared/lib/component.js)).
|
||||
So it's best to refer to these files generically as "modules", and more specifically as "device modules"
|
||||
whenever they implement a specific device (or set of devices, in the case of [*ChipSet*](/docs/pcjs/chipset/)).
|
||||
These JavaScript files divide PCjs functionality into major PC components. Most of the files are device
|
||||
components, implementing a specific device (or set of devices, in the case of [chipset.js](chipset.js)).
|
||||
|
||||
Examples of non-device modules include UI modules like [panel.js](panel.js) and [debugger.js](debugger.js),
|
||||
and sub-modules like [x86opxx.js](x86opxx.js) and [x86func.js](x86func.js) that separate the CPU functionality
|
||||
of [x86.js](x86.js) into more manageable pieces.
|
||||
Be aware that *component* is an overloaded term, since **Component** is also the name of the
|
||||
shared base class in [component.js](../../shared/lib/component.js) used by most machine components.
|
||||
A few low-level components (eg, the **Memory** and **State** components, the Card class of the **Video**
|
||||
component, the Color and Rectangle classes of the **Panel** component, etc) do not extend **Component**,
|
||||
so don't assume that every PCjs object has access to [component.js](../../shared/lib/component.js) methods.
|
||||
|
||||
These modules should always be loaded or compiled in the order listed by the *pcJSFiles* property in
|
||||
[package.json](../../../package.json), which includes all the necessary *shared* modules as well.
|
||||
At the time of this writing, the order is:
|
||||
Examples of non-device components include UI components like [panel.js](panel.js) and [debugger.js](debugger.js),
|
||||
and sub-components like [x86opxx.js](x86opxx.js) and [x86func.js](x86func.js) that separate the CPU
|
||||
functionality of [x86.js](x86.js) into more manageable pieces.
|
||||
|
||||
These components should always be loaded or compiled in the order listed by the *pcJSFiles* property in
|
||||
[package.json](../../../package.json), which includes all the necessary *shared* components as well.
|
||||
At the time of this writing, the recommended order is:
|
||||
|
||||
* [shared/defines.js](../../shared/lib/defines.js)
|
||||
* [shared/diskapi.js](../../shared/lib/diskapi.js)
|
||||
|
|
@ -38,6 +40,8 @@ At the time of this writing, the order is:
|
|||
* [pcjs/x86seg.js](x86seg.js)
|
||||
* [pcjs/x86cpu.js](x86cpu.js)
|
||||
* [pcjs/x86func.js](x86func.js)
|
||||
* [pcjs/x86opxx.js](x86opxx.js)
|
||||
* [pcjs/x86op0f.js](x86op0f.js)
|
||||
* [pcjs/x86modb.js](x86modb.js)
|
||||
* [pcjs/x86modw.js](x86modw.js)
|
||||
* [pcjs/x86modb16.js](x86modb16.js)
|
||||
|
|
@ -45,8 +49,6 @@ At the time of this writing, the order is:
|
|||
* [pcjs/x86modb32.js](x86modb32.js)
|
||||
* [pcjs/x86modw32.js](x86modw32.js)
|
||||
* [pcjs/x86modsib.js](x86modsib.js)
|
||||
* [pcjs/x86opxx.js](x86opxx.js)
|
||||
* [pcjs/x86op0f.js](x86op0f.js)
|
||||
* [pcjs/chipset.js](chipset.js)
|
||||
* [pcjs/rom.js](rom.js)
|
||||
* [pcjs/ram.js](ram.js)
|
||||
|
|
@ -62,16 +64,16 @@ At the time of this writing, the order is:
|
|||
* [pcjs/computer.js](computer.js)
|
||||
* [shared/embed.js](../../shared/lib/embed.js)
|
||||
|
||||
Some of the modules *can* be reordered or even omitted (eg, [debugger.js](debugger.js) or
|
||||
Some of the components *can* be reordered or even omitted (eg, [debugger.js](debugger.js) or
|
||||
[embed.js](../../shared/lib/embed.js)), but you should observe the following:
|
||||
|
||||
* [component.js](../../shared/lib/component.js) must be listed before any module that extends [*Component*](/docs/pcjs/component/)
|
||||
* [component.js](../../shared/lib/component.js) must be listed before any component that extends **Component**
|
||||
* [panel.js](panel.js) should be loaded early to initialize the Control Panel (if any) as soon as possible
|
||||
* [computer.js](computer.js) should be the last device module, as it supervises and notifies all the other device modules
|
||||
* [computer.js](computer.js) should be the last device component, as it supervises and notifies all the other device components
|
||||
|
||||
To minimize ordering requirements, the init() handlers and constructors of all modules should avoid
|
||||
referencing other modules. Device modules should define an initBus() notification handler, which the
|
||||
[*Computer*](/docs/pcjs/computer/) will call after it has created/initialized the *Bus* object.
|
||||
To minimize ordering requirements, the init() handlers and constructors of all components should avoid
|
||||
referencing other components. Device components should define an initBus() notification handler, which the
|
||||
*Computer* component will call after it has created/initialized the *Bus* component.
|
||||
|
||||
Features
|
||||
---
|
||||
|
|
|
|||
|
|
@ -233,10 +233,14 @@ function ChipSet(parmsChipSet)
|
|||
*/
|
||||
this.fSpeaker = false;
|
||||
if (parmsChipSet['sound']) {
|
||||
if (window && 'webkitAudioContext' in window) {
|
||||
this.contextAudio = new window['webkitAudioContext']();
|
||||
this.classAudio = this.contextAudio = null;
|
||||
if (window) {
|
||||
this.classAudio = window['AudioContext'] || window['webkitAudioContext'];
|
||||
}
|
||||
if (this.classAudio) {
|
||||
this.contextAudio = new this.classAudio();
|
||||
} else {
|
||||
if (DEBUG) this.log("webkitAudioContext not available");
|
||||
if (DEBUG) this.log("AudioContext not available");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4771,37 +4775,56 @@ ChipSet.prototype.parseSwitches = function(s, def)
|
|||
ChipSet.prototype.setSpeaker = function(fOn)
|
||||
{
|
||||
if (this.contextAudio) {
|
||||
if (fOn !== undefined) {
|
||||
this.fSpeaker = fOn;
|
||||
} else {
|
||||
fOn = this.fSpeaker && this.cpu && this.cpu.isRunning();
|
||||
}
|
||||
var freq = Math.round(ChipSet.TIMER_TICKS_PER_SEC / this.getTimerInit(ChipSet.TIMER2.INDEX));
|
||||
/*
|
||||
* Treat frequencies outside the normal hearing range (below 20hz or above 20Khz) as a clever attempt
|
||||
* to turn sound off; we have to explicitly turn the sound off in those cases, to prevent the Audio API
|
||||
* from "easing" the audio to the target frequency and creating odd sound effects.
|
||||
*/
|
||||
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);
|
||||
try {
|
||||
if (fOn !== undefined) {
|
||||
this.fSpeaker = fOn;
|
||||
} else {
|
||||
this.sourceAudio = this.contextAudio['createOscillator']();
|
||||
this.sourceAudio['type'] = "square"; // any of: "sine", "square", "sawtooth", "triangle", "custom"
|
||||
this.sourceAudio['connect'](this.contextAudio['destination']);
|
||||
this.sourceAudio['frequency']['value'] = freq;
|
||||
if (this.messageEnabled(Messages.SPEAKER)) this.printMessage("speaker on at " + freq + "hz", true);
|
||||
this.sourceAudio['start'](0);
|
||||
fOn = this.fSpeaker && this.cpu && this.cpu.isRunning();
|
||||
}
|
||||
} else {
|
||||
if (this.sourceAudio) {
|
||||
this.sourceAudio['stop'](0);
|
||||
this.sourceAudio['disconnect'](); // QUESTION: is this automatic following a stop(), since this particular source cannot be started again?
|
||||
delete this.sourceAudio; // QUESTION: ditto?
|
||||
if (this.messageEnabled(Messages.SPEAKER)) this.printMessage("speaker off at " + freq + "hz", true);
|
||||
var freq = Math.round(ChipSet.TIMER_TICKS_PER_SEC / this.getTimerInit(ChipSet.TIMER2.INDEX));
|
||||
/*
|
||||
* Treat frequencies outside the normal hearing range (below 20hz or above 20Khz) as a clever attempt
|
||||
* to turn sound off; we have to explicitly turn the sound off in those cases, to prevent the Audio API
|
||||
* from "easing" the audio to the target frequency and creating odd sound effects.
|
||||
*/
|
||||
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.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?
|
||||
if (this.messageEnabled(Messages.SPEAKER)) this.printMessage("speaker off at " + freq + "hz", true);
|
||||
}
|
||||
}
|
||||
} catch(e) {
|
||||
this.notice("AudioContext exception: " + e.message);
|
||||
this.contextAudio = null;
|
||||
}
|
||||
} else if (fOn) {
|
||||
this.printMessage("BEEP", Messages.SPEAKER);
|
||||
|
|
|
|||
Loading…
Reference in a new issue