Skip to content

Commit 7169e72

Browse files
committed
Add monkeypatch audio
1 parent 73f3760 commit 7169e72

File tree

5 files changed

+186
-9
lines changed

5 files changed

+186
-9
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,7 @@ I don't have much experience working with audio before but the Web Audio API loo
293293

294294
- See the [official documentation][webaudio]
295295
- See how I load the mp3 file and store it in [sound-manager.service.ts][sound-manager]
296+
- [Writing Web Audio API code that works in every browser][Web_Audio_API_cross_browser]
296297

297298
### Keyboard handling
298299

@@ -409,3 +410,4 @@ Feel free to use my code on your project. It would be great if you put a referen
409410
[jira-clone]: https://github.com/trungk18/jira-clone-angular
410411
[marathon]: https://www.strava.com/activities/2902245728
411412
[todolist]: https://www.notion.so/trungk18/Phase-1-be1ae0fbbf2c4c2fb92887e2218413db
413+
[Web_Audio_API_cross_browser]: https://developer.mozilla.org/en-US/docs/Web/Guide/Audio_and_video_delivery/Web_Audio_API_cross_browser

src/app/services/sound-manager.service.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,6 @@ export class SoundManagerService {
1313

1414
}
1515

16-
private get _hasWebAudioAPI(): boolean {
17-
return !!AudioContext && location.protocol.indexOf('http') !== -1;
18-
}
19-
2016
start() {
2117
this._playMusic(0, 3.7202, 3.6224);
2218
}
@@ -52,10 +48,6 @@ export class SoundManagerService {
5248

5349
private _loadSound(): Promise<AudioBufferSourceNode> {
5450
return new Promise((resolve, reject) => {
55-
if (!this._hasWebAudioAPI) {
56-
resolve(null);
57-
return;
58-
}
5951
if (this._context && this._buffer) {
6052
resolve(this._getSource(this._context, this._buffer));
6153
return;
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
/* Copyright 2013 Chris Wilson
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
*/
15+
16+
/*
17+
18+
This monkeypatch library is intended to be included in projects that are
19+
written to the proper AudioContext spec (instead of webkitAudioContext),
20+
and that use the new naming and proper bits of the Web Audio API (e.g.
21+
using BufferSourceNode.start() instead of BufferSourceNode.noteOn()), but may
22+
have to run on systems that only support the deprecated bits.
23+
24+
This library should be harmless to include if the browser supports
25+
unprefixed "AudioContext", and/or if it supports the new names.
26+
27+
The patches this library handles:
28+
if window.AudioContext is unsupported, it will be aliased to webkitAudioContext().
29+
if AudioBufferSourceNode.start() is unimplemented, it will be routed to noteOn() or
30+
noteGrainOn(), depending on parameters.
31+
32+
The following aliases only take effect if the new names are not already in place:
33+
34+
AudioBufferSourceNode.stop() is aliased to noteOff()
35+
AudioContext.createGain() is aliased to createGainNode()
36+
AudioContext.createDelay() is aliased to createDelayNode()
37+
AudioContext.createScriptProcessor() is aliased to createJavaScriptNode()
38+
AudioContext.createPeriodicWave() is aliased to createWaveTable()
39+
OscillatorNode.start() is aliased to noteOn()
40+
OscillatorNode.stop() is aliased to noteOff()
41+
OscillatorNode.setPeriodicWave() is aliased to setWaveTable()
42+
AudioParam.setTargetAtTime() is aliased to setTargetValueAtTime()
43+
44+
This library does NOT patch the enumerated type changes, as it is
45+
recommended in the specification that implementations support both integer
46+
and string types for AudioPannerNode.panningModel, AudioPannerNode.distanceModel
47+
BiquadFilterNode.type and OscillatorNode.type.
48+
49+
*/
50+
(function (global, exports, perf) {
51+
'use strict';
52+
53+
function fixSetTarget(param) {
54+
if (!param) // if NYI, just return
55+
return;
56+
if (!param.setTargetAtTime)
57+
param.setTargetAtTime = param.setTargetValueAtTime;
58+
}
59+
60+
if (window.hasOwnProperty('webkitAudioContext') &&
61+
!window.hasOwnProperty('AudioContext')) {
62+
window.AudioContext = webkitAudioContext;
63+
64+
if (!AudioContext.prototype.hasOwnProperty('createGain'))
65+
AudioContext.prototype.createGain = AudioContext.prototype.createGainNode;
66+
if (!AudioContext.prototype.hasOwnProperty('createDelay'))
67+
AudioContext.prototype.createDelay = AudioContext.prototype.createDelayNode;
68+
if (!AudioContext.prototype.hasOwnProperty('createScriptProcessor'))
69+
AudioContext.prototype.createScriptProcessor = AudioContext.prototype.createJavaScriptNode;
70+
if (!AudioContext.prototype.hasOwnProperty('createPeriodicWave'))
71+
AudioContext.prototype.createPeriodicWave = AudioContext.prototype.createWaveTable;
72+
73+
74+
AudioContext.prototype.internal_createGain = AudioContext.prototype.createGain;
75+
AudioContext.prototype.createGain = function() {
76+
var node = this.internal_createGain();
77+
fixSetTarget(node.gain);
78+
return node;
79+
};
80+
81+
AudioContext.prototype.internal_createDelay = AudioContext.prototype.createDelay;
82+
AudioContext.prototype.createDelay = function(maxDelayTime) {
83+
var node = maxDelayTime ? this.internal_createDelay(maxDelayTime) : this.internal_createDelay();
84+
fixSetTarget(node.delayTime);
85+
return node;
86+
};
87+
88+
AudioContext.prototype.internal_createBufferSource = AudioContext.prototype.createBufferSource;
89+
AudioContext.prototype.createBufferSource = function() {
90+
var node = this.internal_createBufferSource();
91+
if (!node.start) {
92+
node.start = function ( when, offset, duration ) {
93+
if ( offset || duration )
94+
this.noteGrainOn( when || 0, offset, duration );
95+
else
96+
this.noteOn( when || 0 );
97+
};
98+
} else {
99+
node.internal_start = node.start;
100+
node.start = function( when, offset, duration ) {
101+
if( typeof duration !== 'undefined' )
102+
node.internal_start( when || 0, offset, duration );
103+
else
104+
node.internal_start( when || 0, offset || 0 );
105+
};
106+
}
107+
if (!node.stop) {
108+
node.stop = function ( when ) {
109+
this.noteOff( when || 0 );
110+
};
111+
} else {
112+
node.internal_stop = node.stop;
113+
node.stop = function( when ) {
114+
node.internal_stop( when || 0 );
115+
};
116+
}
117+
fixSetTarget(node.playbackRate);
118+
return node;
119+
};
120+
121+
AudioContext.prototype.internal_createDynamicsCompressor = AudioContext.prototype.createDynamicsCompressor;
122+
AudioContext.prototype.createDynamicsCompressor = function() {
123+
var node = this.internal_createDynamicsCompressor();
124+
fixSetTarget(node.threshold);
125+
fixSetTarget(node.knee);
126+
fixSetTarget(node.ratio);
127+
fixSetTarget(node.reduction);
128+
fixSetTarget(node.attack);
129+
fixSetTarget(node.release);
130+
return node;
131+
};
132+
133+
AudioContext.prototype.internal_createBiquadFilter = AudioContext.prototype.createBiquadFilter;
134+
AudioContext.prototype.createBiquadFilter = function() {
135+
var node = this.internal_createBiquadFilter();
136+
fixSetTarget(node.frequency);
137+
fixSetTarget(node.detune);
138+
fixSetTarget(node.Q);
139+
fixSetTarget(node.gain);
140+
return node;
141+
};
142+
143+
if (AudioContext.prototype.hasOwnProperty( 'createOscillator' )) {
144+
AudioContext.prototype.internal_createOscillator = AudioContext.prototype.createOscillator;
145+
AudioContext.prototype.createOscillator = function() {
146+
var node = this.internal_createOscillator();
147+
if (!node.start) {
148+
node.start = function ( when ) {
149+
this.noteOn( when || 0 );
150+
};
151+
} else {
152+
node.internal_start = node.start;
153+
node.start = function ( when ) {
154+
node.internal_start( when || 0);
155+
};
156+
}
157+
if (!node.stop) {
158+
node.stop = function ( when ) {
159+
this.noteOff( when || 0 );
160+
};
161+
} else {
162+
node.internal_stop = node.stop;
163+
node.stop = function( when ) {
164+
node.internal_stop( when || 0 );
165+
};
166+
}
167+
if (!node.setPeriodicWave)
168+
node.setPeriodicWave = node.setWaveTable;
169+
fixSetTarget(node.frequency);
170+
fixSetTarget(node.detune);
171+
return node;
172+
};
173+
}
174+
}
175+
176+
if (window.hasOwnProperty('webkitOfflineAudioContext') &&
177+
!window.hasOwnProperty('OfflineAudioContext')) {
178+
window.OfflineAudioContext = webkitOfflineAudioContext;
179+
}
180+
181+
}(window));
182+

src/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141

4242
<body>
4343
<app-root></app-root>
44+
<script src="/assets/js/AudioContextMonkeyPatch.js"></script>
4445
<script>
4546
var gtag;
4647
</script>

src/index.prod.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141

4242
<body>
4343
<app-root></app-root>
44-
44+
<script src="/assets/js/AudioContextMonkeyPatch.js"></script>
4545
<!-- Global site tag (gtag.js) - Google Analytics -->
4646
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-80363801-6"></script>
4747
<script>

0 commit comments

Comments
 (0)