misairu 4.1.0 → 5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +3 -0
- package/.eslintrc.js +18 -0
- package/.prettierignore +2 -0
- package/.prettierrc.json +8 -0
- package/LICENSE +20 -20
- package/README.md +144 -115
- package/dist/index.d.ts +2 -0
- package/dist/misairu.d.ts +161 -0
- package/dist/misairu.iife.js +2 -0
- package/dist/misairu.iife.js.map +1 -0
- package/dist/misairu.js +2 -0
- package/dist/misairu.js.map +1 -0
- package/dist/misairu.modern.js +2 -0
- package/dist/misairu.modern.js.map +1 -0
- package/dist/misairu.module.js +2 -0
- package/dist/misairu.module.js.map +1 -0
- package/dist/misairu.umd.js +2 -0
- package/dist/misairu.umd.js.map +1 -0
- package/dist/processors/repeat.d.ts +6 -0
- package/dist/types.d.ts +46 -0
- package/dist/utilities/audio.d.ts +11 -0
- package/dist/utilities/source.d.ts +15 -0
- package/package.json +52 -26
- package/src/index.ts +2 -0
- package/src/misairu.ts +308 -0
- package/src/processors/repeat.ts +35 -0
- package/src/types.ts +58 -0
- package/src/utilities/audio.ts +22 -0
- package/src/utilities/source.ts +28 -0
- package/tsconfig.json +11 -0
- package/misairu.js +0 -255
package/.eslintignore
ADDED
package/.eslintrc.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
root: true,
|
|
3
|
+
parser: '@typescript-eslint/parser',
|
|
4
|
+
plugins: [
|
|
5
|
+
'@typescript-eslint',
|
|
6
|
+
'prettier'
|
|
7
|
+
],
|
|
8
|
+
extends: [
|
|
9
|
+
'eslint:recommended',
|
|
10
|
+
'plugin:@typescript-eslint/recommended',
|
|
11
|
+
'prettier'
|
|
12
|
+
],
|
|
13
|
+
rules: {
|
|
14
|
+
'prettier/prettier': 'warn',
|
|
15
|
+
'@typescript-eslint/no-var-requires': 'off',
|
|
16
|
+
'@typescript-eslint/no-non-null-assertion': 'off'
|
|
17
|
+
}
|
|
18
|
+
}
|
package/.prettierignore
ADDED
package/.prettierrc.json
ADDED
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2017 pixeldesu
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2017 pixeldesu
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
21
|
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,116 +1,145 @@
|
|
|
1
|
-
# misairu
|
|
2
|
-
_ミサイル // MISSILE_
|
|
3
|
-
|
|
4
|
-
:rocket: Fire events for specific timeframes easily
|
|
5
|
-
|
|
6
|
-
## Getting it
|
|
7
|
-
|
|
8
|
-
**NPM:**
|
|
9
|
-
|
|
10
|
-
```
|
|
11
|
-
$ npm install misairu
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
**unpkg:**
|
|
15
|
-
|
|
16
|
-
```html
|
|
17
|
-
<script src="https://unpkg.com/misairu/misairu.js"></script>
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
Or you can go the traditional way, grab `misairu.js` from the repository and put it somewhere in your project with a `<script>` tag!
|
|
21
|
-
|
|
22
|
-
## Usage
|
|
23
|
-
|
|
24
|
-
**Text:**
|
|
25
|
-
|
|
26
|
-
```js
|
|
27
|
-
const media = document.getElementById('audioplayer')
|
|
28
|
-
const text = document.getElementById('text')
|
|
29
|
-
|
|
30
|
-
// define timings, tracks and their functions
|
|
31
|
-
const timings = {
|
|
32
|
-
"default": {
|
|
33
|
-
"0": function() {
|
|
34
|
-
text.innerHTML = 'New text at start'
|
|
35
|
-
}
|
|
36
|
-
},
|
|
37
|
-
"default:2": {
|
|
38
|
-
"10": function() {
|
|
39
|
-
document.body.style.backgroundColor = 'lightblue'
|
|
40
|
-
text.innerHTML = 'New text and background color after 10 seconds'
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// create misairu instance
|
|
46
|
-
const ev = new
|
|
47
|
-
|
|
48
|
-
// you only need this if your audiosource is external e.g. a link to an audio file
|
|
49
|
-
document.addEventListener('misairu.ready', function(event) {
|
|
50
|
-
// start playback once misairu is ready
|
|
51
|
-
ev.start()
|
|
52
|
-
})
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
## Reference
|
|
56
|
-
|
|
57
|
-
### `new
|
|
58
|
-
|
|
59
|
-
* **`audioSource`:** (required) Link to an audio file or `<audio>`-Tag DOM Element
|
|
60
|
-
* **`timings`:** (required) Object where the keys represent the time and the values are functions or function references
|
|
61
|
-
|
|
62
|
-
**Note on `audioSource`:**
|
|
63
|
-
|
|
64
|
-
If you use a `HTMLMediaElement`, misairu won't fire the `misairu.ready` event, as the content is already given. To be really
|
|
65
|
-
sure that everything starts when you want it to, give your media element the attribute `preload="auto"` to have it pre-buffer
|
|
66
|
-
ahead of usage.
|
|
67
|
-
|
|
68
|
-
### `misairu.start()`
|
|
69
|
-
|
|
70
|
-
This will start playback of the specified audio and execute the events at the given time. If your audio source is external, the audio buffer is
|
|
71
|
-
loaded asynchronously and you should listen to the `misairu.ready` event on `document` and execute this function once
|
|
72
|
-
the event was emitted.
|
|
73
|
-
|
|
74
|
-
### `misairu.[un]pause()`
|
|
75
|
-
|
|
76
|
-
Pauses/Resumes playback and event execution.
|
|
77
|
-
|
|
78
|
-
### `misairu.[un]mute()`
|
|
79
|
-
|
|
80
|
-
(Un)mutes audio of the misairu instance.
|
|
81
|
-
|
|
82
|
-
### `misairu.volume = x`
|
|
83
|
-
|
|
84
|
-
Sets the volume to `x`, can be a value between -80 and 5.
|
|
85
|
-
|
|
86
|
-
### Anatomy of the `timings` object
|
|
87
|
-
|
|
88
|
-
```js
|
|
89
|
-
// timings object, you pass this to the misairu constructor
|
|
90
|
-
const timings = {
|
|
91
|
-
// track object
|
|
92
|
-
"default": {
|
|
93
|
-
// timing key - function
|
|
94
|
-
"0": function (instance, timingKey, track, time) {
|
|
95
|
-
// instance - misairu instance
|
|
96
|
-
// timingKey - current timing key ("0")
|
|
97
|
-
// track - current track ("default")
|
|
98
|
-
// time - current time (accurate time calculated from the start time and audio context time)
|
|
99
|
-
|
|
100
|
-
// >> put some code here <<
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
All of the parameters passed to a timing function are optional and don't need to be used as they are only passed for convenience, so you can omit them.
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
1
|
+
# misairu
|
|
2
|
+
_ミサイル // MISSILE_
|
|
3
|
+
|
|
4
|
+
:rocket: Fire events for specific timeframes easily
|
|
5
|
+
|
|
6
|
+
## Getting it
|
|
7
|
+
|
|
8
|
+
**NPM:**
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
$ npm install misairu
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
**unpkg:**
|
|
15
|
+
|
|
16
|
+
```html
|
|
17
|
+
<script src="https://unpkg.com/misairu/dist/misairu.iife.js"></script>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Or you can go the traditional way, grab `misairu.js` from the repository and put it somewhere in your project with a `<script>` tag!
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
**Text:**
|
|
25
|
+
|
|
26
|
+
```js
|
|
27
|
+
const media = document.getElementById('audioplayer')
|
|
28
|
+
const text = document.getElementById('text')
|
|
29
|
+
|
|
30
|
+
// define timings, tracks and their functions
|
|
31
|
+
const timings = {
|
|
32
|
+
"default": {
|
|
33
|
+
"0": function() {
|
|
34
|
+
text.innerHTML = 'New text at start'
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"default:2": {
|
|
38
|
+
"10": function() {
|
|
39
|
+
document.body.style.backgroundColor = 'lightblue'
|
|
40
|
+
text.innerHTML = 'New text and background color after 10 seconds'
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// create misairu instance
|
|
46
|
+
const ev = new Misairu(media, timings)
|
|
47
|
+
|
|
48
|
+
// you only need this if your audiosource is external e.g. a link to an audio file
|
|
49
|
+
document.addEventListener('misairu.ready', function(event) {
|
|
50
|
+
// start playback once misairu is ready
|
|
51
|
+
ev.start()
|
|
52
|
+
})
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Reference
|
|
56
|
+
|
|
57
|
+
### `new Misairu(audioSource, timings)`
|
|
58
|
+
|
|
59
|
+
* **`audioSource`:** (required) Link to an audio file or `<audio>`-Tag DOM Element
|
|
60
|
+
* **`timings`:** (required) Object where the keys represent the time and the values are functions or function references
|
|
61
|
+
|
|
62
|
+
**Note on `audioSource`:**
|
|
63
|
+
|
|
64
|
+
If you use a `HTMLMediaElement`, misairu won't fire the `misairu.ready` event, as the content is already given. To be really
|
|
65
|
+
sure that everything starts when you want it to, give your media element the attribute `preload="auto"` to have it pre-buffer
|
|
66
|
+
ahead of usage.
|
|
67
|
+
|
|
68
|
+
### `misairu.start()`
|
|
69
|
+
|
|
70
|
+
This will start playback of the specified audio and execute the events at the given time. If your audio source is external, the audio buffer is
|
|
71
|
+
loaded asynchronously and you should listen to the `misairu.ready` event on `document` and execute this function once
|
|
72
|
+
the event was emitted.
|
|
73
|
+
|
|
74
|
+
### `misairu.[un]pause()`
|
|
75
|
+
|
|
76
|
+
Pauses/Resumes playback and event execution.
|
|
77
|
+
|
|
78
|
+
### `misairu.[un]mute()`
|
|
79
|
+
|
|
80
|
+
(Un)mutes audio of the misairu instance.
|
|
81
|
+
|
|
82
|
+
### `misairu.volume = x`
|
|
83
|
+
|
|
84
|
+
Sets the volume to `x`, can be a value between -80 and 5.
|
|
85
|
+
|
|
86
|
+
### Anatomy of the `timings` object
|
|
87
|
+
|
|
88
|
+
```js
|
|
89
|
+
// timings object, you pass this to the misairu constructor
|
|
90
|
+
const timings = {
|
|
91
|
+
// track object
|
|
92
|
+
"default": {
|
|
93
|
+
// timing key - function
|
|
94
|
+
"0": function (instance, timingKey, track, time) {
|
|
95
|
+
// instance - misairu instance
|
|
96
|
+
// timingKey - current timing key ("0")
|
|
97
|
+
// track - current track ("default")
|
|
98
|
+
// time - current time (accurate time calculated from the start time and audio context time)
|
|
99
|
+
|
|
100
|
+
// >> put some code here <<
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
All of the parameters passed to a timing function are optional and don't need to be used as they are only passed for convenience, so you can omit them.
|
|
107
|
+
|
|
108
|
+
### `repeat` tracks
|
|
109
|
+
|
|
110
|
+
It's possible to define repeating actions for a specific timeframe with special track type, following the naming scheme of `repeat:start-time:interval:end-time`.
|
|
111
|
+
|
|
112
|
+
As an example:
|
|
113
|
+
|
|
114
|
+
```js
|
|
115
|
+
const timings = {
|
|
116
|
+
"repeat:1:2:10": myCoolFunction
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
On creation of the `misairu` instance, the timing object gets compiled, so the repeat statement will be unfolded into:
|
|
121
|
+
|
|
122
|
+
```js
|
|
123
|
+
// misairu_instance.timings
|
|
124
|
+
{
|
|
125
|
+
// the original track "repeat:1:2:10" was deleted
|
|
126
|
+
// and replaced with a repeat-randomhash track containing all timed events
|
|
127
|
+
"repeat-xjas34f": {
|
|
128
|
+
"1": myCoolFunction,
|
|
129
|
+
"3": myCoolFunction,
|
|
130
|
+
"5": myCoolFunction,
|
|
131
|
+
"7": myCoolFunction,
|
|
132
|
+
"9": myCoolFunction
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Shoutouts
|
|
138
|
+
|
|
139
|
+
* [Rocket](https://github.com/rocket/rocket) for basically being the "big brother" of this small project
|
|
140
|
+
* [coderobe](https://github.com/coderobe) for [microhues](https://github.com/coderobe/microhues), which helped me understanding the Web Audio APIs
|
|
141
|
+
* [ed](https://github.com/9001), as I'm basically building this to have an easy framework to build as creative things as he does
|
|
142
|
+
|
|
143
|
+
## License
|
|
144
|
+
|
|
116
145
|
misairu is licensed under the MIT license
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { TimingObject } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Main misairu class
|
|
4
|
+
*/
|
|
5
|
+
export declare class Misairu {
|
|
6
|
+
/**
|
|
7
|
+
* The HTML5 AudioContext used for accurately timing our events
|
|
8
|
+
*/
|
|
9
|
+
private readonly _audioContext;
|
|
10
|
+
/**
|
|
11
|
+
* A source node to play our audio from, either a buffered source from a downloaded media file or a media element source
|
|
12
|
+
*
|
|
13
|
+
* @default null
|
|
14
|
+
*/
|
|
15
|
+
private _audioSource;
|
|
16
|
+
/**
|
|
17
|
+
* A object containing the last executed time key per track to not execute an event on every tick
|
|
18
|
+
*
|
|
19
|
+
* @default {}
|
|
20
|
+
*/
|
|
21
|
+
private _cache;
|
|
22
|
+
/**
|
|
23
|
+
* Reference to the `requestAnimationFrame` handler
|
|
24
|
+
*
|
|
25
|
+
* @default null
|
|
26
|
+
*/
|
|
27
|
+
private _eventHandler;
|
|
28
|
+
/**
|
|
29
|
+
* Gain node from our audio context to control audio volume
|
|
30
|
+
*/
|
|
31
|
+
private readonly _gainNode;
|
|
32
|
+
/**
|
|
33
|
+
* Boolean value describing if this instance is currently muted
|
|
34
|
+
*/
|
|
35
|
+
private _muted;
|
|
36
|
+
/**
|
|
37
|
+
* Boolean value describing if this instance is currently paused
|
|
38
|
+
*/
|
|
39
|
+
private _paused;
|
|
40
|
+
/**
|
|
41
|
+
* The time when event handling was started, based on the audio contexts `currentTime` when `start()` was called
|
|
42
|
+
*/
|
|
43
|
+
private _startTime;
|
|
44
|
+
/**
|
|
45
|
+
* Object containing all timing tracks and events to be executed
|
|
46
|
+
*/
|
|
47
|
+
private _timings;
|
|
48
|
+
/**
|
|
49
|
+
* Volume of the current instance
|
|
50
|
+
*/
|
|
51
|
+
private _volume;
|
|
52
|
+
/**
|
|
53
|
+
* List of (predefined) track processors
|
|
54
|
+
*/
|
|
55
|
+
private _processors;
|
|
56
|
+
get volume(): number;
|
|
57
|
+
set volume(db: number);
|
|
58
|
+
/**
|
|
59
|
+
* misairu constructor
|
|
60
|
+
*
|
|
61
|
+
* @param audioSource a string or HTML element to be used as audio source
|
|
62
|
+
* @param timings a object containing event timing information
|
|
63
|
+
*/
|
|
64
|
+
constructor(audioSource: string | HTMLMediaElement, timings: TimingObject);
|
|
65
|
+
/**
|
|
66
|
+
* Method to figure out the best course of action to take with the passed audio source
|
|
67
|
+
*
|
|
68
|
+
* @param audioSource the audio source `Misairu` has been constructed with
|
|
69
|
+
* @internal
|
|
70
|
+
*/
|
|
71
|
+
private getOptimalAudioSource;
|
|
72
|
+
/**
|
|
73
|
+
* Mutes the instance audio
|
|
74
|
+
*
|
|
75
|
+
* @public
|
|
76
|
+
*/
|
|
77
|
+
mute(): void;
|
|
78
|
+
/**
|
|
79
|
+
* Unmutes the instance audio
|
|
80
|
+
*
|
|
81
|
+
* @public
|
|
82
|
+
*/
|
|
83
|
+
unmute(): void;
|
|
84
|
+
/**
|
|
85
|
+
* Pauses instance playback
|
|
86
|
+
*
|
|
87
|
+
* @public
|
|
88
|
+
*/
|
|
89
|
+
pause(): void;
|
|
90
|
+
/**
|
|
91
|
+
* Resumes instance playback
|
|
92
|
+
*
|
|
93
|
+
* @public
|
|
94
|
+
*/
|
|
95
|
+
unpause(): void;
|
|
96
|
+
/**
|
|
97
|
+
* Set a cache entry for the given track
|
|
98
|
+
*
|
|
99
|
+
* @param track track to set a cache entry for
|
|
100
|
+
* @param entry value of the cache entry
|
|
101
|
+
* @internal
|
|
102
|
+
*/
|
|
103
|
+
private setCacheEntry;
|
|
104
|
+
/**
|
|
105
|
+
* Get cache entry for the given track
|
|
106
|
+
*
|
|
107
|
+
* @param track track to get a cache entry for
|
|
108
|
+
* @returns a cache entry
|
|
109
|
+
* @internal
|
|
110
|
+
*/
|
|
111
|
+
private getCacheEntry;
|
|
112
|
+
/**
|
|
113
|
+
* Get all tracks from the timing configuration
|
|
114
|
+
*
|
|
115
|
+
* @returns a list of all track names
|
|
116
|
+
* @internal
|
|
117
|
+
*/
|
|
118
|
+
private getAllTracks;
|
|
119
|
+
/**
|
|
120
|
+
* Returns the current active timing key for a given track
|
|
121
|
+
*
|
|
122
|
+
* @param track track to get the timing key from
|
|
123
|
+
* @param currentTime current playback time
|
|
124
|
+
* @returns the current active timing key
|
|
125
|
+
* @internal
|
|
126
|
+
*/
|
|
127
|
+
private getActiveTimingKey;
|
|
128
|
+
/**
|
|
129
|
+
* Main method to process special sections in the timing configuration
|
|
130
|
+
*
|
|
131
|
+
* @internal
|
|
132
|
+
*/
|
|
133
|
+
private processTracks;
|
|
134
|
+
/**
|
|
135
|
+
* Start audio playback and event handling
|
|
136
|
+
*
|
|
137
|
+
* @public
|
|
138
|
+
*/
|
|
139
|
+
start(): void;
|
|
140
|
+
/**
|
|
141
|
+
* Method to start event handler loop
|
|
142
|
+
*
|
|
143
|
+
* @internal
|
|
144
|
+
*/
|
|
145
|
+
private startEventHandling;
|
|
146
|
+
/**
|
|
147
|
+
* Event handler method, is running in a loop using `requestAnimationFrame`
|
|
148
|
+
*
|
|
149
|
+
* @internal
|
|
150
|
+
*/
|
|
151
|
+
private handleEvents;
|
|
152
|
+
/**
|
|
153
|
+
* Method to execute the event for a given timing key on a given track
|
|
154
|
+
*
|
|
155
|
+
* @param track the track to execute the event on
|
|
156
|
+
* @param timingKey the timing key to execute
|
|
157
|
+
* @param time current playback time
|
|
158
|
+
* @internal
|
|
159
|
+
*/
|
|
160
|
+
private executeEvent;
|
|
161
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var Misairu=function(){var t=new RegExp(/repeat:\d:\d:\d/),e=function(){function e(){this.deleteOriginTrack=!0}var n=e.prototype;return n.matches=function(e){return null!==e.match(t)},n.process=function(t,e){if("function"!=typeof e)throw Error('The value of repeat track "'+e+'" is not a function');var n=t.split(":");if(4!=n.length)throw Error('The repeat track "'+t+'" does not supply the valid amount of arguments');var i=parseFloat(n[1]),o=parseFloat(n[2]),r=parseFloat(n[3]),s=i,a={};do{a[s.toString()]=e,s+=o}while(s<r);return["repeat-"+Math.random().toString(36).substring(7),a]},e}();function n(t){return Math.pow(10,t/20)}return function(){function t(t,n){this._audioContext=void 0,this._audioSource=null,this._cache={},this._eventHandler=null,this._gainNode=void 0,this._muted=!1,this._paused=!1,this._startTime=0,this._timings=void 0,this._volume=0,this._processors=[new e],null===n&&console.error("You need to specify a timings object"),this._timings=n,this._audioContext=new AudioContext,this._gainNode=this._audioContext.createGain(),this._gainNode.connect(this._audioContext.destination),this.getOptimalAudioSource(t),this.processTracks()}var i,o=t.prototype;return o.getOptimalAudioSource=function(t){var e=this;"string"==typeof t?function(t,e){try{var n=e.createBufferSource();return Promise.resolve(fetch(t)).then(function(t){return Promise.resolve(t.arrayBuffer()).then(function(t){return Promise.resolve(e.decodeAudioData(t)).then(function(t){return n.buffer=t,n})})})}catch(t){return Promise.reject(t)}}(t,this._audioContext).then(function(t){e._audioSource=t,e._audioSource.connect(e._gainNode),document.dispatchEvent(new Event("misairu.ready"))}):"object"!=typeof t||"AUDIO"!=t.tagName&&"VIDEO"!=t.tagName||(this._audioSource=function(t,e){return e.createMediaElementSource(t)}(t,this._audioContext),this._audioSource.connect(this._gainNode))},o.mute=function(){this._muted||(this._muted=!0,this._gainNode.gain.value=0)},o.unmute=function(){this._muted&&(this._muted=!1,this._gainNode.gain.value=n(this._volume))},o.pause=function(){this._paused||(this._audioContext.suspend(),this._paused=!0)},o.unpause=function(){this._paused&&(this._audioContext.resume(),this._paused=!1)},o.setCacheEntry=function(t,e){this._cache[t]=e},o.getCacheEntry=function(t){return this._cache[t]},o.getAllTracks=function(){return Object.keys(this._timings)},o.getActiveTimingKey=function(t,e){var n=Object.keys(this._timings[t]).filter(function(t){if(e>=parseFloat(t))return!0});return n[n.length-1]},o.processTracks=function(){var t=this;this.getAllTracks().forEach(function(e){t._processors.forEach(function(n){if(n.matches(e)){var i=n.process(e,t._timings[e]);t._timings[i[0]]=i[1],n.deleteOriginTrack&&delete t._timings[e]}})})},o.start=function(){this._startTime=this._audioContext.currentTime,this._audioSource.start(),this.startEventHandling()},o.startEventHandling=function(){var t=this;null==this._eventHandler&&(this._eventHandler=window.requestAnimationFrame(function(){t.handleEvents()}))},o.handleEvents=function(){var t=this,e=this._audioContext.currentTime-this._startTime;this.getAllTracks().forEach(function(n){var i=t.getActiveTimingKey(n,e);null!==i&&t.getCacheEntry(n)!=i&&(t.executeEvent(n,i,e),t.setCacheEntry(n,i))}),this._eventHandler=window.requestAnimationFrame(function(){t.handleEvents()})},o.executeEvent=function(t,e,n){this._timings[t][e](this,e,t,n)},(i=[{key:"volume",get:function(){return this._volume},set:function(t){var e;this._volume=(e=t)<-80?-80:e>5?5:e,this._muted||(this._gainNode.gain.value=n(this._volume))}}])&&function(t,e){for(var n=0;n<e.length;n++){var i=e[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(t,i.key,i)}}(t.prototype,i),t}()}();
|
|
2
|
+
//# sourceMappingURL=misairu.iife.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"misairu.iife.js","sources":["../src/processors/repeat.ts","../src/utilities/audio.ts","../src/misairu.ts","../src/utilities/source.ts"],"sourcesContent":["import { EventFunction, EventTrack, ITrackProcessor } from \"../types\";\r\n\r\nconst REPEAT_NAME_REGEX = new RegExp(/repeat:\\d:\\d:\\d/)\r\n\r\nexport class RepeatTrackProcessor implements ITrackProcessor {\r\n deleteOriginTrack = true;\r\n\r\n matches(name: string): boolean {\r\n return name.match(REPEAT_NAME_REGEX) !== null\r\n }\r\n\r\n process(name: string, track: EventFunction): [string, EventTrack] {\r\n if (typeof track != 'function')\r\n throw Error(`The value of repeat track \"${track}\" is not a function`)\r\n\r\n const repeatTrackArgs = name.split(':')\r\n\r\n if (repeatTrackArgs.length != 4)\r\n throw Error(`The repeat track \"${name}\" does not supply the valid amount of arguments`)\r\n\r\n const startTime = parseFloat(repeatTrackArgs[1])\r\n const interval = parseFloat(repeatTrackArgs[2])\r\n const endTime = parseFloat(repeatTrackArgs[3])\r\n\r\n let time = startTime\r\n const tempTrack = {}\r\n\r\n do {\r\n tempTrack[time.toString()] = track\r\n time += interval\r\n } while (time < endTime)\r\n\r\n return [`repeat-${Math.random().toString(36).substring(7)}`, tempTrack]\r\n }\r\n}","/**\r\n * Method to turn the passed decibel values into volume values for the audio playback\r\n *\r\n * @param db decibel value\r\n * @returns volume value\r\n */\r\nexport function dbToVolume(db: number): number {\r\n return Math.pow(10, db / 20)\r\n}\r\n\r\n/**\r\n * Clamps the volume to a min/max value to prevent accidental oversetting to way too loud measures\r\n */\r\nexport function clampGain(volume: number): number {\r\n if (volume < -80) {\r\n return -80\r\n } else if (volume > 5) {\r\n return 5\r\n }\r\n\r\n return volume\r\n}","import { RepeatTrackProcessor } from './processors/repeat'\r\nimport { EventCache, ITrackProcessor, TimingObject } from './types'\r\nimport { dbToVolume, clampGain } from './utilities/audio'\r\nimport { fetchAudioSource, attachAudioElementSource } from './utilities/source'\r\n\r\n/**\r\n * Main misairu class\r\n */\r\nexport class Misairu {\r\n /**\r\n * The HTML5 AudioContext used for accurately timing our events\r\n */\r\n private readonly _audioContext: AudioContext\r\n\r\n /**\r\n * A source node to play our audio from, either a buffered source from a downloaded media file or a media element source\r\n *\r\n * @default null\r\n */\r\n private _audioSource: HTMLMediaElement | AudioBufferSourceNode | MediaElementAudioSourceNode | null = null\r\n\r\n /**\r\n * A object containing the last executed time key per track to not execute an event on every tick\r\n *\r\n * @default {}\r\n */\r\n private _cache: EventCache = {}\r\n\r\n /**\r\n * Reference to the `requestAnimationFrame` handler\r\n *\r\n * @default null\r\n */\r\n private _eventHandler: number | null = null\r\n\r\n /**\r\n * Gain node from our audio context to control audio volume\r\n */\r\n private readonly _gainNode: GainNode\r\n\r\n /**\r\n * Boolean value describing if this instance is currently muted\r\n */\r\n private _muted = false\r\n\r\n /**\r\n * Boolean value describing if this instance is currently paused\r\n */\r\n private _paused = false\r\n\r\n /**\r\n * The time when event handling was started, based on the audio contexts `currentTime` when `start()` was called\r\n */\r\n private _startTime = 0\r\n\r\n /**\r\n * Object containing all timing tracks and events to be executed\r\n */\r\n private _timings: TimingObject\r\n\r\n /**\r\n * Volume of the current instance\r\n */\r\n private _volume = 0\r\n\r\n /**\r\n * List of (predefined) track processors\r\n */\r\n private _processors: ITrackProcessor[] = [\r\n new RepeatTrackProcessor()\r\n ]\r\n\r\n get volume(): number {\r\n return this._volume\r\n }\r\n\r\n set volume(db: number) {\r\n this._volume = clampGain(db)\r\n\r\n if (!this._muted) {\r\n this._gainNode.gain.value = dbToVolume(this._volume)\r\n }\r\n }\r\n\r\n /**\r\n * misairu constructor\r\n *\r\n * @param audioSource a string or HTML element to be used as audio source\r\n * @param timings a object containing event timing information\r\n */\r\n constructor(audioSource: string | HTMLMediaElement, timings: TimingObject) {\r\n if (timings === null) console.error('You need to specify a timings object')\r\n this._timings = timings\r\n\r\n this._audioContext = new AudioContext()\r\n\r\n this._gainNode = this._audioContext.createGain()\r\n this._gainNode.connect(this._audioContext.destination)\r\n\r\n this.getOptimalAudioSource(audioSource)\r\n\r\n this.processTracks()\r\n }\r\n\r\n /**\r\n * Method to figure out the best course of action to take with the passed audio source\r\n *\r\n * @param audioSource the audio source `Misairu` has been constructed with\r\n * @internal\r\n */\r\n private getOptimalAudioSource(audioSource: string | HTMLMediaElement): void {\r\n if (typeof audioSource == 'string') {\r\n fetchAudioSource(audioSource, this._audioContext).then((audioSource) => {\r\n this._audioSource = audioSource\r\n this._audioSource.connect(this._gainNode)\r\n\r\n document.dispatchEvent(new Event('misairu.ready'))\r\n })\r\n } else if (\r\n typeof audioSource == 'object' &&\r\n (audioSource.tagName == 'AUDIO' || audioSource.tagName == 'VIDEO')\r\n ) {\r\n this._audioSource = attachAudioElementSource(audioSource, this._audioContext)\r\n this._audioSource.connect(this._gainNode)\r\n }\r\n }\r\n\r\n /**\r\n * Mutes the instance audio\r\n *\r\n * @public\r\n */\r\n public mute(): void {\r\n if (!this._muted) {\r\n this._muted = true\r\n this._gainNode.gain.value = 0\r\n }\r\n }\r\n\r\n /**\r\n * Unmutes the instance audio\r\n *\r\n * @public\r\n */\r\n public unmute(): void {\r\n if (this._muted) {\r\n this._muted = false\r\n this._gainNode.gain.value = dbToVolume(this._volume)\r\n }\r\n }\r\n\r\n /**\r\n * Pauses instance playback\r\n *\r\n * @public\r\n */\r\n public pause(): void {\r\n if (!this._paused) {\r\n this._audioContext.suspend()\r\n this._paused = true\r\n }\r\n }\r\n\r\n /**\r\n * Resumes instance playback\r\n *\r\n * @public\r\n */\r\n public unpause(): void {\r\n if (this._paused) {\r\n this._audioContext.resume()\r\n this._paused = false\r\n }\r\n }\r\n\r\n /**\r\n * Set a cache entry for the given track\r\n *\r\n * @param track track to set a cache entry for\r\n * @param entry value of the cache entry\r\n * @internal\r\n */\r\n private setCacheEntry(track: string, entry: string): void {\r\n this._cache[track] = entry\r\n }\r\n\r\n /**\r\n * Get cache entry for the given track\r\n *\r\n * @param track track to get a cache entry for\r\n * @returns a cache entry\r\n * @internal\r\n */\r\n private getCacheEntry(track: string): string {\r\n return this._cache[track]\r\n }\r\n\r\n /**\r\n * Get all tracks from the timing configuration\r\n *\r\n * @returns a list of all track names\r\n * @internal\r\n */\r\n private getAllTracks(): string[] {\r\n return Object.keys(this._timings)\r\n }\r\n\r\n /**\r\n * Returns the current active timing key for a given track\r\n *\r\n * @param track track to get the timing key from\r\n * @param currentTime current playback time\r\n * @returns the current active timing key\r\n * @internal\r\n */\r\n private getActiveTimingKey(track: string, currentTime: number): string {\r\n const timingKeys = Object.keys(this._timings[track])\r\n\r\n const activeTimings = timingKeys.filter((timing) => {\r\n if (currentTime >= parseFloat(timing)) {\r\n return true\r\n }\r\n })\r\n\r\n return activeTimings[activeTimings.length - 1]\r\n }\r\n\r\n /**\r\n * Main method to process special sections in the timing configuration\r\n *\r\n * @internal\r\n */\r\n private processTracks(): void {\r\n this.getAllTracks().forEach((trackName) => {\r\n this._processors.forEach((processor: ITrackProcessor) => {\r\n if (processor.matches(trackName)) {\r\n const [processedTrackName, eventTrack] = processor.process(trackName, this._timings[trackName])\r\n\r\n this._timings[processedTrackName] = eventTrack\r\n\r\n if (processor.deleteOriginTrack) {\r\n delete this._timings[trackName]\r\n }\r\n }\r\n })\r\n })\r\n }\r\n\r\n /**\r\n * Start audio playback and event handling\r\n *\r\n * @public\r\n */\r\n public start(): void {\r\n this._startTime = this._audioContext.currentTime\r\n\r\n ;(this._audioSource as AudioBufferSourceNode).start()\r\n\r\n this.startEventHandling()\r\n }\r\n\r\n /**\r\n * Method to start event handler loop\r\n *\r\n * @internal\r\n */\r\n private startEventHandling(): void {\r\n if (this._eventHandler == null) {\r\n this._eventHandler = window.requestAnimationFrame(() => {\r\n this.handleEvents()\r\n })\r\n }\r\n }\r\n\r\n /**\r\n * Event handler method, is running in a loop using `requestAnimationFrame`\r\n *\r\n * @internal\r\n */\r\n private handleEvents(): void {\r\n const time = this._audioContext.currentTime - this._startTime\r\n\r\n this.getAllTracks().forEach((track) => {\r\n const timingKey = this.getActiveTimingKey(track, time)\r\n\r\n if (timingKey !== null && !(this.getCacheEntry(track) == timingKey)) {\r\n this.executeEvent(track, timingKey, time)\r\n this.setCacheEntry(track, timingKey)\r\n }\r\n })\r\n\r\n this._eventHandler = window.requestAnimationFrame(() => {\r\n this.handleEvents()\r\n })\r\n }\r\n\r\n /**\r\n * Method to execute the event for a given timing key on a given track\r\n *\r\n * @param track the track to execute the event on\r\n * @param timingKey the timing key to execute\r\n * @param time current playback time\r\n * @internal\r\n */\r\n private executeEvent(track: string, timingKey: string, time: number): void {\r\n this._timings[track][timingKey](this, timingKey, track, time)\r\n }\r\n}\r\n","/**\r\n * Method to fetch the external audio file (if the audio source parameter was a string)\r\n * and turning it into a `AudioBufferSourceNode`\r\n *\r\n * @param audioSource the audio source `Misairu` has been constructed with\r\n * @internal\r\n */\r\nexport async function fetchAudioSource(audioSource: string, audioContext: AudioContext): Promise<AudioBufferSourceNode> {\r\n const source = audioContext.createBufferSource()\r\n\r\n const response = await fetch(audioSource)\r\n const arrayBuffer = await response.arrayBuffer()\r\n const buffer = await audioContext.decodeAudioData(arrayBuffer)\r\n\r\n source.buffer = buffer\r\n\r\n return source\r\n}\r\n\r\n/**\r\n * Method to get an `MediaElementAudioSourceNode` from the passed audio source\r\n *\r\n * @param audioSource the audio source `Misairu` has been constructed with\r\n * @internal\r\n */\r\nexport function attachAudioElementSource(audioSource: HTMLMediaElement, audioContext: AudioContext): MediaElementAudioSourceNode {\r\n return audioContext.createMediaElementSource(audioSource)\r\n}"],"names":["REPEAT_NAME_REGEX","RegExp","RepeatTrackProcessor","deleteOriginTrack","matches","name","match","process","track","Error","repeatTrackArgs","split","length","startTime","parseFloat","interval","endTime","time","tempTrack","toString","Math","random","substring","dbToVolume","db","pow","audioSource","timings","_audioContext","_audioSource","_cache","_eventHandler","_gainNode","_muted","_paused","_startTime","_timings","_volume","_processors","console","error","this","AudioContext","createGain","connect","destination","getOptimalAudioSource","processTracks","audioContext","source","createBufferSource","fetch","response","arrayBuffer","decodeAudioData","buffer","fetchAudioSource","then","_this","document","dispatchEvent","Event","tagName","createMediaElementSource","attachAudioElementSource","mute","gain","value","unmute","pause","suspend","unpause","resume","setCacheEntry","entry","getCacheEntry","getAllTracks","Object","keys","getActiveTimingKey","currentTime","activeTimings","filter","timing","forEach","trackName","_this2","processor","start","startEventHandling","window","requestAnimationFrame","_this3","handleEvents","timingKey","_this4","executeEvent","volume"],"mappings":"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"}
|
package/dist/misairu.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var t=new RegExp(/repeat:\d:\d:\d/),e=function(){function e(){this.deleteOriginTrack=!0}var n=e.prototype;return n.matches=function(e){return null!==e.match(t)},n.process=function(t,e){if("function"!=typeof e)throw Error('The value of repeat track "'+e+'" is not a function');var n=t.split(":");if(4!=n.length)throw Error('The repeat track "'+t+'" does not supply the valid amount of arguments');var i=parseFloat(n[1]),o=parseFloat(n[2]),r=parseFloat(n[3]),s=i,a={};do{a[s.toString()]=e,s+=o}while(s<r);return["repeat-"+Math.random().toString(36).substring(7),a]},e}();function n(t){return Math.pow(10,t/20)}module.exports=function(){function t(t,n){this._audioContext=void 0,this._audioSource=null,this._cache={},this._eventHandler=null,this._gainNode=void 0,this._muted=!1,this._paused=!1,this._startTime=0,this._timings=void 0,this._volume=0,this._processors=[new e],null===n&&console.error("You need to specify a timings object"),this._timings=n,this._audioContext=new AudioContext,this._gainNode=this._audioContext.createGain(),this._gainNode.connect(this._audioContext.destination),this.getOptimalAudioSource(t),this.processTracks()}var i,o=t.prototype;return o.getOptimalAudioSource=function(t){var e=this;"string"==typeof t?function(t,e){try{var n=e.createBufferSource();return Promise.resolve(fetch(t)).then(function(t){return Promise.resolve(t.arrayBuffer()).then(function(t){return Promise.resolve(e.decodeAudioData(t)).then(function(t){return n.buffer=t,n})})})}catch(t){return Promise.reject(t)}}(t,this._audioContext).then(function(t){e._audioSource=t,e._audioSource.connect(e._gainNode),document.dispatchEvent(new Event("misairu.ready"))}):"object"!=typeof t||"AUDIO"!=t.tagName&&"VIDEO"!=t.tagName||(this._audioSource=function(t,e){return e.createMediaElementSource(t)}(t,this._audioContext),this._audioSource.connect(this._gainNode))},o.mute=function(){this._muted||(this._muted=!0,this._gainNode.gain.value=0)},o.unmute=function(){this._muted&&(this._muted=!1,this._gainNode.gain.value=n(this._volume))},o.pause=function(){this._paused||(this._audioContext.suspend(),this._paused=!0)},o.unpause=function(){this._paused&&(this._audioContext.resume(),this._paused=!1)},o.setCacheEntry=function(t,e){this._cache[t]=e},o.getCacheEntry=function(t){return this._cache[t]},o.getAllTracks=function(){return Object.keys(this._timings)},o.getActiveTimingKey=function(t,e){var n=Object.keys(this._timings[t]).filter(function(t){if(e>=parseFloat(t))return!0});return n[n.length-1]},o.processTracks=function(){var t=this;this.getAllTracks().forEach(function(e){t._processors.forEach(function(n){if(n.matches(e)){var i=n.process(e,t._timings[e]);t._timings[i[0]]=i[1],n.deleteOriginTrack&&delete t._timings[e]}})})},o.start=function(){this._startTime=this._audioContext.currentTime,this._audioSource.start(),this.startEventHandling()},o.startEventHandling=function(){var t=this;null==this._eventHandler&&(this._eventHandler=window.requestAnimationFrame(function(){t.handleEvents()}))},o.handleEvents=function(){var t=this,e=this._audioContext.currentTime-this._startTime;this.getAllTracks().forEach(function(n){var i=t.getActiveTimingKey(n,e);null!==i&&t.getCacheEntry(n)!=i&&(t.executeEvent(n,i,e),t.setCacheEntry(n,i))}),this._eventHandler=window.requestAnimationFrame(function(){t.handleEvents()})},o.executeEvent=function(t,e,n){this._timings[t][e](this,e,t,n)},(i=[{key:"volume",get:function(){return this._volume},set:function(t){var e;this._volume=(e=t)<-80?-80:e>5?5:e,this._muted||(this._gainNode.gain.value=n(this._volume))}}])&&function(t,e){for(var n=0;n<e.length;n++){var i=e[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(t,i.key,i)}}(t.prototype,i),t}();
|
|
2
|
+
//# sourceMappingURL=misairu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"misairu.js","sources":["../src/processors/repeat.ts","../src/utilities/audio.ts","../src/misairu.ts","../src/utilities/source.ts"],"sourcesContent":["import { EventFunction, EventTrack, ITrackProcessor } from \"../types\";\r\n\r\nconst REPEAT_NAME_REGEX = new RegExp(/repeat:\\d:\\d:\\d/)\r\n\r\nexport class RepeatTrackProcessor implements ITrackProcessor {\r\n deleteOriginTrack = true;\r\n\r\n matches(name: string): boolean {\r\n return name.match(REPEAT_NAME_REGEX) !== null\r\n }\r\n\r\n process(name: string, track: EventFunction): [string, EventTrack] {\r\n if (typeof track != 'function')\r\n throw Error(`The value of repeat track \"${track}\" is not a function`)\r\n\r\n const repeatTrackArgs = name.split(':')\r\n\r\n if (repeatTrackArgs.length != 4)\r\n throw Error(`The repeat track \"${name}\" does not supply the valid amount of arguments`)\r\n\r\n const startTime = parseFloat(repeatTrackArgs[1])\r\n const interval = parseFloat(repeatTrackArgs[2])\r\n const endTime = parseFloat(repeatTrackArgs[3])\r\n\r\n let time = startTime\r\n const tempTrack = {}\r\n\r\n do {\r\n tempTrack[time.toString()] = track\r\n time += interval\r\n } while (time < endTime)\r\n\r\n return [`repeat-${Math.random().toString(36).substring(7)}`, tempTrack]\r\n }\r\n}","/**\r\n * Method to turn the passed decibel values into volume values for the audio playback\r\n *\r\n * @param db decibel value\r\n * @returns volume value\r\n */\r\nexport function dbToVolume(db: number): number {\r\n return Math.pow(10, db / 20)\r\n}\r\n\r\n/**\r\n * Clamps the volume to a min/max value to prevent accidental oversetting to way too loud measures\r\n */\r\nexport function clampGain(volume: number): number {\r\n if (volume < -80) {\r\n return -80\r\n } else if (volume > 5) {\r\n return 5\r\n }\r\n\r\n return volume\r\n}","import { RepeatTrackProcessor } from './processors/repeat'\r\nimport { EventCache, ITrackProcessor, TimingObject } from './types'\r\nimport { dbToVolume, clampGain } from './utilities/audio'\r\nimport { fetchAudioSource, attachAudioElementSource } from './utilities/source'\r\n\r\n/**\r\n * Main misairu class\r\n */\r\nexport class Misairu {\r\n /**\r\n * The HTML5 AudioContext used for accurately timing our events\r\n */\r\n private readonly _audioContext: AudioContext\r\n\r\n /**\r\n * A source node to play our audio from, either a buffered source from a downloaded media file or a media element source\r\n *\r\n * @default null\r\n */\r\n private _audioSource: HTMLMediaElement | AudioBufferSourceNode | MediaElementAudioSourceNode | null = null\r\n\r\n /**\r\n * A object containing the last executed time key per track to not execute an event on every tick\r\n *\r\n * @default {}\r\n */\r\n private _cache: EventCache = {}\r\n\r\n /**\r\n * Reference to the `requestAnimationFrame` handler\r\n *\r\n * @default null\r\n */\r\n private _eventHandler: number | null = null\r\n\r\n /**\r\n * Gain node from our audio context to control audio volume\r\n */\r\n private readonly _gainNode: GainNode\r\n\r\n /**\r\n * Boolean value describing if this instance is currently muted\r\n */\r\n private _muted = false\r\n\r\n /**\r\n * Boolean value describing if this instance is currently paused\r\n */\r\n private _paused = false\r\n\r\n /**\r\n * The time when event handling was started, based on the audio contexts `currentTime` when `start()` was called\r\n */\r\n private _startTime = 0\r\n\r\n /**\r\n * Object containing all timing tracks and events to be executed\r\n */\r\n private _timings: TimingObject\r\n\r\n /**\r\n * Volume of the current instance\r\n */\r\n private _volume = 0\r\n\r\n /**\r\n * List of (predefined) track processors\r\n */\r\n private _processors: ITrackProcessor[] = [\r\n new RepeatTrackProcessor()\r\n ]\r\n\r\n get volume(): number {\r\n return this._volume\r\n }\r\n\r\n set volume(db: number) {\r\n this._volume = clampGain(db)\r\n\r\n if (!this._muted) {\r\n this._gainNode.gain.value = dbToVolume(this._volume)\r\n }\r\n }\r\n\r\n /**\r\n * misairu constructor\r\n *\r\n * @param audioSource a string or HTML element to be used as audio source\r\n * @param timings a object containing event timing information\r\n */\r\n constructor(audioSource: string | HTMLMediaElement, timings: TimingObject) {\r\n if (timings === null) console.error('You need to specify a timings object')\r\n this._timings = timings\r\n\r\n this._audioContext = new AudioContext()\r\n\r\n this._gainNode = this._audioContext.createGain()\r\n this._gainNode.connect(this._audioContext.destination)\r\n\r\n this.getOptimalAudioSource(audioSource)\r\n\r\n this.processTracks()\r\n }\r\n\r\n /**\r\n * Method to figure out the best course of action to take with the passed audio source\r\n *\r\n * @param audioSource the audio source `Misairu` has been constructed with\r\n * @internal\r\n */\r\n private getOptimalAudioSource(audioSource: string | HTMLMediaElement): void {\r\n if (typeof audioSource == 'string') {\r\n fetchAudioSource(audioSource, this._audioContext).then((audioSource) => {\r\n this._audioSource = audioSource\r\n this._audioSource.connect(this._gainNode)\r\n\r\n document.dispatchEvent(new Event('misairu.ready'))\r\n })\r\n } else if (\r\n typeof audioSource == 'object' &&\r\n (audioSource.tagName == 'AUDIO' || audioSource.tagName == 'VIDEO')\r\n ) {\r\n this._audioSource = attachAudioElementSource(audioSource, this._audioContext)\r\n this._audioSource.connect(this._gainNode)\r\n }\r\n }\r\n\r\n /**\r\n * Mutes the instance audio\r\n *\r\n * @public\r\n */\r\n public mute(): void {\r\n if (!this._muted) {\r\n this._muted = true\r\n this._gainNode.gain.value = 0\r\n }\r\n }\r\n\r\n /**\r\n * Unmutes the instance audio\r\n *\r\n * @public\r\n */\r\n public unmute(): void {\r\n if (this._muted) {\r\n this._muted = false\r\n this._gainNode.gain.value = dbToVolume(this._volume)\r\n }\r\n }\r\n\r\n /**\r\n * Pauses instance playback\r\n *\r\n * @public\r\n */\r\n public pause(): void {\r\n if (!this._paused) {\r\n this._audioContext.suspend()\r\n this._paused = true\r\n }\r\n }\r\n\r\n /**\r\n * Resumes instance playback\r\n *\r\n * @public\r\n */\r\n public unpause(): void {\r\n if (this._paused) {\r\n this._audioContext.resume()\r\n this._paused = false\r\n }\r\n }\r\n\r\n /**\r\n * Set a cache entry for the given track\r\n *\r\n * @param track track to set a cache entry for\r\n * @param entry value of the cache entry\r\n * @internal\r\n */\r\n private setCacheEntry(track: string, entry: string): void {\r\n this._cache[track] = entry\r\n }\r\n\r\n /**\r\n * Get cache entry for the given track\r\n *\r\n * @param track track to get a cache entry for\r\n * @returns a cache entry\r\n * @internal\r\n */\r\n private getCacheEntry(track: string): string {\r\n return this._cache[track]\r\n }\r\n\r\n /**\r\n * Get all tracks from the timing configuration\r\n *\r\n * @returns a list of all track names\r\n * @internal\r\n */\r\n private getAllTracks(): string[] {\r\n return Object.keys(this._timings)\r\n }\r\n\r\n /**\r\n * Returns the current active timing key for a given track\r\n *\r\n * @param track track to get the timing key from\r\n * @param currentTime current playback time\r\n * @returns the current active timing key\r\n * @internal\r\n */\r\n private getActiveTimingKey(track: string, currentTime: number): string {\r\n const timingKeys = Object.keys(this._timings[track])\r\n\r\n const activeTimings = timingKeys.filter((timing) => {\r\n if (currentTime >= parseFloat(timing)) {\r\n return true\r\n }\r\n })\r\n\r\n return activeTimings[activeTimings.length - 1]\r\n }\r\n\r\n /**\r\n * Main method to process special sections in the timing configuration\r\n *\r\n * @internal\r\n */\r\n private processTracks(): void {\r\n this.getAllTracks().forEach((trackName) => {\r\n this._processors.forEach((processor: ITrackProcessor) => {\r\n if (processor.matches(trackName)) {\r\n const [processedTrackName, eventTrack] = processor.process(trackName, this._timings[trackName])\r\n\r\n this._timings[processedTrackName] = eventTrack\r\n\r\n if (processor.deleteOriginTrack) {\r\n delete this._timings[trackName]\r\n }\r\n }\r\n })\r\n })\r\n }\r\n\r\n /**\r\n * Start audio playback and event handling\r\n *\r\n * @public\r\n */\r\n public start(): void {\r\n this._startTime = this._audioContext.currentTime\r\n\r\n ;(this._audioSource as AudioBufferSourceNode).start()\r\n\r\n this.startEventHandling()\r\n }\r\n\r\n /**\r\n * Method to start event handler loop\r\n *\r\n * @internal\r\n */\r\n private startEventHandling(): void {\r\n if (this._eventHandler == null) {\r\n this._eventHandler = window.requestAnimationFrame(() => {\r\n this.handleEvents()\r\n })\r\n }\r\n }\r\n\r\n /**\r\n * Event handler method, is running in a loop using `requestAnimationFrame`\r\n *\r\n * @internal\r\n */\r\n private handleEvents(): void {\r\n const time = this._audioContext.currentTime - this._startTime\r\n\r\n this.getAllTracks().forEach((track) => {\r\n const timingKey = this.getActiveTimingKey(track, time)\r\n\r\n if (timingKey !== null && !(this.getCacheEntry(track) == timingKey)) {\r\n this.executeEvent(track, timingKey, time)\r\n this.setCacheEntry(track, timingKey)\r\n }\r\n })\r\n\r\n this._eventHandler = window.requestAnimationFrame(() => {\r\n this.handleEvents()\r\n })\r\n }\r\n\r\n /**\r\n * Method to execute the event for a given timing key on a given track\r\n *\r\n * @param track the track to execute the event on\r\n * @param timingKey the timing key to execute\r\n * @param time current playback time\r\n * @internal\r\n */\r\n private executeEvent(track: string, timingKey: string, time: number): void {\r\n this._timings[track][timingKey](this, timingKey, track, time)\r\n }\r\n}\r\n","/**\r\n * Method to fetch the external audio file (if the audio source parameter was a string)\r\n * and turning it into a `AudioBufferSourceNode`\r\n *\r\n * @param audioSource the audio source `Misairu` has been constructed with\r\n * @internal\r\n */\r\nexport async function fetchAudioSource(audioSource: string, audioContext: AudioContext): Promise<AudioBufferSourceNode> {\r\n const source = audioContext.createBufferSource()\r\n\r\n const response = await fetch(audioSource)\r\n const arrayBuffer = await response.arrayBuffer()\r\n const buffer = await audioContext.decodeAudioData(arrayBuffer)\r\n\r\n source.buffer = buffer\r\n\r\n return source\r\n}\r\n\r\n/**\r\n * Method to get an `MediaElementAudioSourceNode` from the passed audio source\r\n *\r\n * @param audioSource the audio source `Misairu` has been constructed with\r\n * @internal\r\n */\r\nexport function attachAudioElementSource(audioSource: HTMLMediaElement, audioContext: AudioContext): MediaElementAudioSourceNode {\r\n return audioContext.createMediaElementSource(audioSource)\r\n}"],"names":["REPEAT_NAME_REGEX","RegExp","RepeatTrackProcessor","deleteOriginTrack","matches","name","match","process","track","Error","repeatTrackArgs","split","length","startTime","parseFloat","interval","endTime","time","tempTrack","toString","Math","random","substring","dbToVolume","db","pow","audioSource","timings","_audioContext","_audioSource","_cache","_eventHandler","_gainNode","_muted","_paused","_startTime","_timings","_volume","_processors","console","error","this","AudioContext","createGain","connect","destination","getOptimalAudioSource","processTracks","audioContext","source","createBufferSource","fetch","response","arrayBuffer","decodeAudioData","buffer","fetchAudioSource","then","_this","document","dispatchEvent","Event","tagName","createMediaElementSource","attachAudioElementSource","mute","gain","value","unmute","pause","suspend","unpause","resume","setCacheEntry","entry","getCacheEntry","getAllTracks","Object","keys","getActiveTimingKey","currentTime","activeTimings","filter","timing","forEach","trackName","_this2","processor","start","startEventHandling","window","requestAnimationFrame","_this3","handleEvents","timingKey","_this4","executeEvent","volume"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const t=new RegExp(/repeat:\d:\d:\d/);class e{constructor(){this.deleteOriginTrack=!0}matches(e){return null!==e.match(t)}process(t,e){if("function"!=typeof e)throw Error(`The value of repeat track "${e}" is not a function`);const i=t.split(":");if(4!=i.length)throw Error(`The repeat track "${t}" does not supply the valid amount of arguments`);const s=parseFloat(i[1]),n=parseFloat(i[2]),a=parseFloat(i[3]);let o=s;const r={};do{r[o.toString()]=e,o+=n}while(o<a);return[`repeat-${Math.random().toString(36).substring(7)}`,r]}}function i(t){return Math.pow(10,t/20)}class s{get volume(){return this._volume}set volume(t){var e;this._volume=(e=t)<-80?-80:e>5?5:e,this._muted||(this._gainNode.gain.value=i(this._volume))}constructor(t,i){this._audioContext=void 0,this._audioSource=null,this._cache={},this._eventHandler=null,this._gainNode=void 0,this._muted=!1,this._paused=!1,this._startTime=0,this._timings=void 0,this._volume=0,this._processors=[new e],null===i&&console.error("You need to specify a timings object"),this._timings=i,this._audioContext=new AudioContext,this._gainNode=this._audioContext.createGain(),this._gainNode.connect(this._audioContext.destination),this.getOptimalAudioSource(t),this.processTracks()}getOptimalAudioSource(t){"string"==typeof t?async function(t,e){const i=e.createBufferSource(),s=await fetch(t),n=await s.arrayBuffer(),a=await e.decodeAudioData(n);return i.buffer=a,i}(t,this._audioContext).then(t=>{this._audioSource=t,this._audioSource.connect(this._gainNode),document.dispatchEvent(new Event("misairu.ready"))}):"object"!=typeof t||"AUDIO"!=t.tagName&&"VIDEO"!=t.tagName||(this._audioSource=function(t,e){return e.createMediaElementSource(t)}(t,this._audioContext),this._audioSource.connect(this._gainNode))}mute(){this._muted||(this._muted=!0,this._gainNode.gain.value=0)}unmute(){this._muted&&(this._muted=!1,this._gainNode.gain.value=i(this._volume))}pause(){this._paused||(this._audioContext.suspend(),this._paused=!0)}unpause(){this._paused&&(this._audioContext.resume(),this._paused=!1)}setCacheEntry(t,e){this._cache[t]=e}getCacheEntry(t){return this._cache[t]}getAllTracks(){return Object.keys(this._timings)}getActiveTimingKey(t,e){const i=Object.keys(this._timings[t]).filter(t=>{if(e>=parseFloat(t))return!0});return i[i.length-1]}processTracks(){this.getAllTracks().forEach(t=>{this._processors.forEach(e=>{if(e.matches(t)){const[i,s]=e.process(t,this._timings[t]);this._timings[i]=s,e.deleteOriginTrack&&delete this._timings[t]}})})}start(){this._startTime=this._audioContext.currentTime,this._audioSource.start(),this.startEventHandling()}startEventHandling(){null==this._eventHandler&&(this._eventHandler=window.requestAnimationFrame(()=>{this.handleEvents()}))}handleEvents(){const t=this._audioContext.currentTime-this._startTime;this.getAllTracks().forEach(e=>{const i=this.getActiveTimingKey(e,t);null!==i&&this.getCacheEntry(e)!=i&&(this.executeEvent(e,i,t),this.setCacheEntry(e,i))}),this._eventHandler=window.requestAnimationFrame(()=>{this.handleEvents()})}executeEvent(t,e,i){this._timings[t][e](this,e,t,i)}}export{s as default};
|
|
2
|
+
//# sourceMappingURL=misairu.modern.js.map
|