tone-stream 1.5.0 → 1.7.0
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/.editorconfig +9 -0
- package/examples/{silence_filler.js → stay_alive.js} +5 -3
- package/index.js +162 -152
- package/package.json +4 -4
package/.editorconfig
ADDED
|
@@ -10,9 +10,11 @@ const format = {
|
|
|
10
10
|
channels: 1
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
const
|
|
13
|
+
const opts = {
|
|
14
|
+
stay_alive: true,
|
|
15
|
+
}
|
|
14
16
|
|
|
15
|
-
const ts = new ToneStream(format,
|
|
17
|
+
const ts = new ToneStream(format, opts)
|
|
16
18
|
|
|
17
19
|
const s = new Speaker(format)
|
|
18
20
|
|
|
@@ -44,7 +46,7 @@ assert(b.length == 140)
|
|
|
44
46
|
s.write(b)
|
|
45
47
|
|
|
46
48
|
b = ts.read(12000)
|
|
47
|
-
assert(b.length == 12000) // this will be filled with
|
|
49
|
+
assert(b.length == 12000) // this will be filled with silence (zero) after all items are consumed.
|
|
48
50
|
s.write(b)
|
|
49
51
|
|
|
50
52
|
setTimeout(() => {}, 2000)
|
package/index.js
CHANGED
|
@@ -1,159 +1,169 @@
|
|
|
1
|
-
const { Readable } = require(
|
|
1
|
+
const { Readable } = require("stream");
|
|
2
2
|
|
|
3
|
-
const SpecReadStream = require(
|
|
3
|
+
const SpecReadStream = require("spec-read-stream");
|
|
4
4
|
|
|
5
|
-
const DTMF = require(
|
|
5
|
+
const DTMF = require("./lib/dtmf");
|
|
6
6
|
|
|
7
7
|
class ToneStream extends Readable {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
8
|
+
constructor(format, opts) {
|
|
9
|
+
super();
|
|
10
|
+
|
|
11
|
+
if (format) {
|
|
12
|
+
this.sampleRate = format.sampleRate;
|
|
13
|
+
this.bitDepth = format.bitDepth;
|
|
14
|
+
this.channels = format.channels;
|
|
15
|
+
} else {
|
|
16
|
+
// default
|
|
17
|
+
this.sampleRate = 8000;
|
|
18
|
+
this.bitDepth = 16;
|
|
19
|
+
this.channels = 1;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
this.opts = opts;
|
|
23
|
+
|
|
24
|
+
this.amplitude = 2 ** this.bitDepth / 2 - 1;
|
|
25
|
+
|
|
26
|
+
this.specReadStream = new SpecReadStream();
|
|
27
|
+
|
|
28
|
+
this.currentSample = 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
add(spec) {
|
|
32
|
+
this.specReadStream.add(spec);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
concat(specs) {
|
|
36
|
+
specs.forEach((spec) => {
|
|
37
|
+
this.specReadStream.add(spec);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
on(evt, cb) {
|
|
42
|
+
super.on(evt, cb);
|
|
43
|
+
|
|
44
|
+
if (evt == "empty") {
|
|
45
|
+
this.specReadStream.on(evt, cb);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
_read(n) {
|
|
50
|
+
//console.log(`_read(${n})`)
|
|
51
|
+
|
|
52
|
+
let sampleSize = this.bitDepth / 8;
|
|
53
|
+
let blockAlign = sampleSize * this.channels;
|
|
54
|
+
|
|
55
|
+
let numSamples = Math.floor(n / blockAlign);
|
|
56
|
+
|
|
57
|
+
let end = this.currentSample + numSamples;
|
|
58
|
+
|
|
59
|
+
let buf = Buffer.alloc(numSamples * blockAlign);
|
|
60
|
+
|
|
61
|
+
let setter = buf["writeInt" + this.bitDepth + "LE"].bind(buf);
|
|
62
|
+
|
|
63
|
+
var specs = this.specReadStream.read(numSamples);
|
|
64
|
+
//console.log(`specs=${specs}`)
|
|
65
|
+
|
|
66
|
+
var buf_idx = 0;
|
|
67
|
+
|
|
68
|
+
if (!specs) {
|
|
69
|
+
if (this.opts && this.opts.stay_alive) {
|
|
70
|
+
for (var j = 0; j < numSamples * this.channels; j++) {
|
|
71
|
+
let offset = j * sampleSize * this.channels;
|
|
72
|
+
setter(0, offset);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
this.push(buf);
|
|
76
|
+
return;
|
|
77
|
+
} else {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
let actualSamples = specs.reduce((total, spec) => {
|
|
83
|
+
return total + spec[0];
|
|
84
|
+
}, 0);
|
|
85
|
+
//console.log(`actualSamples=${actualSamples}`)
|
|
86
|
+
|
|
87
|
+
for (var i = 0; i < specs.length; i++) {
|
|
88
|
+
var spec = specs[i];
|
|
89
|
+
//console.log(`spec=${spec}`)
|
|
90
|
+
|
|
91
|
+
var nSamples = spec[0];
|
|
92
|
+
var spec_val = spec[1];
|
|
93
|
+
|
|
94
|
+
var val_calculator = (() => {
|
|
95
|
+
if (spec_val == "s") {
|
|
96
|
+
// silence
|
|
97
|
+
return () => {
|
|
98
|
+
return 0;
|
|
99
|
+
};
|
|
100
|
+
} else if (typeof spec_val == "number") {
|
|
101
|
+
var freq = spec_val;
|
|
102
|
+
let t = (Math.PI * 2 * freq) / this.sampleRate;
|
|
103
|
+
|
|
104
|
+
return (amplitude, currentSample) => {
|
|
105
|
+
return Math.round(amplitude * Math.sin(t * currentSample)); // sine wave
|
|
106
|
+
};
|
|
107
|
+
} else if (
|
|
108
|
+
typeof spec_val == "string" &&
|
|
109
|
+
spec_val.startsWith("DTMF:")
|
|
110
|
+
) {
|
|
111
|
+
var tone = DTMF[spec_val.split(":")[1]];
|
|
112
|
+
var lo = tone[0];
|
|
113
|
+
var hi = tone[1];
|
|
114
|
+
|
|
115
|
+
let t_lo = (Math.PI * 2 * lo) / this.sampleRate;
|
|
116
|
+
let t_hi = (Math.PI * 2 * hi) / this.sampleRate;
|
|
117
|
+
|
|
118
|
+
return (amplitude, currentSample) => {
|
|
119
|
+
return (
|
|
120
|
+
(Math.round(amplitude * Math.sin(t_lo * currentSample)) +
|
|
121
|
+
Math.round(amplitude * Math.sin(t_hi * currentSample))) /
|
|
122
|
+
2
|
|
123
|
+
);
|
|
124
|
+
};
|
|
125
|
+
} else {
|
|
126
|
+
throw `invalid spec ${spec}`;
|
|
127
|
+
}
|
|
128
|
+
})();
|
|
129
|
+
|
|
130
|
+
for (var j = 0; j < nSamples; j++) {
|
|
131
|
+
for (let channel = 0; channel < this.channels; channel++) {
|
|
132
|
+
let val = val_calculator(this.amplitude, this.currentSample) / 2; // halve amplitude
|
|
133
|
+
let offset =
|
|
134
|
+
buf_idx * sampleSize * this.channels + channel * sampleSize;
|
|
135
|
+
setter(val, offset);
|
|
136
|
+
buf_idx++;
|
|
137
|
+
this.currentSample++;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (typeof this.filler == "number") {
|
|
143
|
+
while (numSamples > actualSamples) {
|
|
144
|
+
//console.log("adding filler")
|
|
145
|
+
for (let channel = 0; channel < this.channels; channel++) {
|
|
146
|
+
let offset = (actualSamples - 1) * sampleSize * this.channels;
|
|
147
|
+
setter(this.filler, offset);
|
|
148
|
+
this.currentSample++;
|
|
149
|
+
actualSamples++;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (numSamples > actualSamples) {
|
|
155
|
+
//console.log(`slicing buf from ${numSamples} to ${actualSamples}`)
|
|
156
|
+
//console.log(`${actualSamples * sampleSize * this.channels}`)
|
|
157
|
+
buf = buf.slice(0, actualSamples * sampleSize * this.channels);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
//console.log("pushing")
|
|
161
|
+
//console.log(buf.length)
|
|
162
|
+
this.push(buf);
|
|
163
|
+
}
|
|
154
164
|
}
|
|
155
165
|
|
|
156
166
|
module.exports = {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
}
|
|
167
|
+
ToneStream,
|
|
168
|
+
utils: require("./lib/utils.js"),
|
|
169
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tone-stream",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "A simple audio tone stream library",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": {
|
|
@@ -22,13 +22,13 @@
|
|
|
22
22
|
"license": "MIT",
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"lodash": "^4.17.21",
|
|
25
|
-
"speaker": "^0.5.1"
|
|
25
|
+
"speaker": "^0.5.1",
|
|
26
|
+
"wav": "^1.0.2"
|
|
26
27
|
},
|
|
27
28
|
"dependencies": {
|
|
28
29
|
"dtmf-detection-stream": "^1.10.0",
|
|
29
30
|
"morse-node": "^0.1.1",
|
|
30
31
|
"note-to-frequency": "^1.4.1",
|
|
31
|
-
"spec-read-stream": "^1.2.
|
|
32
|
-
"wav": "^1.0.2"
|
|
32
|
+
"spec-read-stream": "^1.2.8"
|
|
33
33
|
}
|
|
34
34
|
}
|