Skip to content

Commit 46141a2

Browse files
committed
added full code for tutorial
1 parent 6fe7778 commit 46141a2

File tree

3 files changed

+199
-6
lines changed

3 files changed

+199
-6
lines changed

README.md

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@ Available modules:
1818

1919
## Tutorial
2020

21-
UNDER CONSTRUCTION - Please check out the test pages for more examples.
21+
UNDER CONSTRUCTION - Please check out the test pages for more examples.
22+
23+
Full tutorial code can be found at: [tutorial-test-page.html](tutorial-test-page.html)
2224

23-
### Basic interface
25+
### Part 1: Basic setup and single buffer module
2426

2527
#### Import library and modules
2628

@@ -97,7 +99,7 @@ After we're done we stop with `processor.stop()` and look out for `onaudioend`.
9799

98100
If we don't want to restart later we can close the processor and clean up resources with `processor.release()`.
99101

100-
### Resample input and record raw 16Bit PCM mono audio (WAV)
102+
### Part 2: Resample input and record raw 16Bit PCM mono audio (WAV)
101103

102104
A very common use-case for this library is to resample microphone input and encode it as 16Bit PCM mono data (which is basically the default WAV file format).
103105
To make this happen we will replace the buffer module from earlier with a resampler and wave encoder module.
@@ -188,11 +190,18 @@ waveEncoder.handle.sendToModule({request: {get: "wave"}}); //encoded WAV
188190
waveEncoder.handle.sendToModule({request: {get: "buffer"}}); //raw int16 buffer
189191
```
190192

191-
Just for fun we can add the generated WAV to our page (body) like this:
193+
Using the module interface an optimized 'waveEncoderCallback' (defined in module above) could look like this:
192194

193195
```javascript
194196
//modified 'waveEncoderCallback':
195197
function waveEncoderCallback(data){
198+
if (data.gate && data.gate.isOpen === false){
199+
//stop processor
200+
processor.stop();
201+
//get data
202+
waveEncoder.handle.sendToModule({request: {get: "wave"}});
203+
waveEncoder.handle.sendToModule({request: {get: "buffer"}});
204+
}
196205
if (data.output && data.output.wav){
197206
//just for fun, add WAV to page:
198207
var targetEle = document.body;

src/sepia-web-audio.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ if (!(typeof SepiaFW == "object")){
178178
if (!mainAudioContext || mainAudioContext.state == "closed"){
179179
//TODO: clean up old context and sources?
180180
mainAudioContext = WebAudio.createAudioContext(options, ignoreOptions);
181-
if (options.startSuspended){
181+
if (options.startSuspended == undefined || options.startSuspended){
182182
try { await mainAudioContext.resume(); } catch(error){}; //TODO: prevent quirky stuff on e.g. iOS
183183
await mainAudioContext.suspend();
184184
}else{
@@ -826,7 +826,7 @@ if (!(typeof SepiaFW == "object")){
826826
if (!asyncCreateOrUpdateAudioContext){
827827
asyncCreateOrUpdateAudioContext = async function(forceNew, ignoreOptions){
828828
var audioContext = WebAudio.createAudioContext(options, ignoreOptions);
829-
if (options.startSuspended){
829+
if (options.startSuspended == undefined || options.startSuspended){
830830
try { await audioContext.resume(); } catch (error){}; //TODO: prevent quirky stuff on e.g. iOS
831831
await audioContext.suspend();
832832
}else{

tutorial-test-page.html

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
<!doctype html>
2+
<html lang="en-us">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1">
7+
<title>SEPIA Web Audio Input-Output Test</title>
8+
9+
<script type="text/javascript" src="src/sepia-web-audio.js?v=0.9.6"></script>
10+
<script>
11+
//set correct modules folder
12+
SepiaFW.webAudio.defaultProcessorOptions.moduleFolder = "src/modules";
13+
</script>
14+
15+
<link rel="stylesheet" type="text/css" href="tests.css?v=0.9.6">
16+
<style></style>
17+
</head>
18+
<body>
19+
<div id="mainView">
20+
<h1>SEPIA Web Audio Processor - Input/Output Tests</h1>
21+
<div>
22+
<button onclick="tutorialPart1();">Tutorial Part 1</button>
23+
<button onclick="tutorialPart2();">Tutorial Part 2</button>
24+
</div>
25+
</div>
26+
<script type='text/javascript'>
27+
28+
//Tutorial Part 1
29+
function tutorialPart1(){
30+
var myModules = [];
31+
32+
function bufferCallback(data){
33+
//handle samples here using: data.samples
34+
console.log("bufferCallback", data);
35+
}
36+
myModules.push({
37+
name: 'buffer-switch',
38+
settings: {
39+
onmessage: bufferCallback,
40+
options: {
41+
processorOptions: {
42+
bufferSize: 512, //size of samples generated
43+
passThroughMode: 0, //0: none, 1: original (float32 array)
44+
}
45+
}
46+
}
47+
});
48+
49+
var processor = new SepiaFW.webAudio.Processor({
50+
onaudiostart: function(){ console.log("onaudiostart", arguments); },
51+
onaudioend: function(){ console.log("onaudioend", arguments); },
52+
onrelease: function(){ console.log("onrelease", arguments); },
53+
onerror: function(){ console.error("onerror", arguments); },
54+
modules: myModules
55+
56+
}, function(info){
57+
//Processor ready
58+
console.log(info); //Use 'info' to get details about source, sample-rate etc.
59+
//start
60+
processor.start();
61+
//wait 3s
62+
setTimeout(function(){
63+
//stop
64+
processor.stop();
65+
//wait 3s
66+
setTimeout(function(){
67+
processor.release();
68+
}, 3000);
69+
}, 3000);
70+
71+
}, function(err){
72+
//Initialization error
73+
console.error(err);
74+
});
75+
}
76+
77+
//Tutorial Part 2
78+
function tutorialPart2(){
79+
SepiaFW.webAudio.tryNativeStreamResampling = false; //global option
80+
81+
var myModules = [];
82+
var targetSampleRate = 16000; //this is the sample-rate we want
83+
var bufferSize = 512; //size of samples generated
84+
85+
function resamplerCallback(data){
86+
//data will include e.g.: data.samples and data.rms (volume)
87+
console.log("resamplerCallback", data);
88+
}
89+
var resampler = {
90+
name: 'speex-resample-switch',
91+
settings: {
92+
onmessage: resamplerCallback,
93+
sendToModules: [], //[moduleIndex] - filled below with index of wave-encoder module
94+
options: {
95+
processorOptions: {
96+
targetSampleRate: targetSampleRate,
97+
bufferSize: bufferSize,
98+
resampleQuality: 5, //1 (low quality) - 10 (best quality)
99+
calculateRmsVolume: true, //the resampler can calculate RMS signal volume
100+
gain: 5.0, //we can amplify the signal here
101+
passThroughMode: 0 //0: none - only switch in our pipe atm
102+
}
103+
}
104+
}
105+
};
106+
107+
function waveEncoderCallback(data){
108+
//can be used to track capture state and get final WAV
109+
//check: data.gate, data.output.wav, data.output.buffer
110+
console.log("waveEncoderCallback", data);
111+
if (data.gate && data.gate.isOpen === false){
112+
//stop processor
113+
processor.stop();
114+
//get data
115+
waveEncoder.handle.sendToModule({request: {get: "wave"}});
116+
waveEncoder.handle.sendToModule({request: {get: "buffer"}});
117+
//release after 3s
118+
setTimeout(function(){
119+
processor.release();
120+
}, 3000);
121+
}
122+
if (data.output && data.output.wav){
123+
//just for fun, add WAV to page:
124+
var targetEle = document.body;
125+
var blobType = "audio/wav";
126+
SepiaFW.webAudio.addAudioElementToPage(targetEle, data.output.wav, blobType);
127+
}
128+
}
129+
var waveEncoder = {
130+
name: 'wave-encoder',
131+
type: 'worker',
132+
handle: {}, //will be updated on init. with ref. to node.
133+
settings: {
134+
onmessage: waveEncoderCallback,
135+
options: {
136+
setup: {
137+
inputSampleRate: targetSampleRate, //input of this will be ...
138+
inputSampleSize: bufferSize, //... output of resampler
139+
lookbackBufferMs: 0, //(experimental) ignore for now
140+
recordBufferLimitMs: 8000, //we can apply recording limit as milliseconds
141+
//recordBufferLimitKb: 600, //... or as kilobytes (default ~5MB)
142+
isFloat32: false //resampler gives int16 - use e.g. for buffer module
143+
}
144+
}
145+
}
146+
};
147+
148+
myModules.push(resampler); //index 1
149+
myModules.push(waveEncoder); //index 2
150+
151+
//connect resampler output to wave-encoder input:
152+
resampler.settings.sendToModules.push(2);
153+
154+
var processor = new SepiaFW.webAudio.Processor({
155+
onaudiostart: function(){
156+
console.log("onaudiostart", arguments);
157+
waveEncoder.handle.sendToModule({gate: "open"});
158+
},
159+
onaudioend: function(){
160+
console.log("onaudioend", arguments);
161+
},
162+
onrelease: function(){ console.log("onrelease", arguments); },
163+
onerror: function(){ console.error("onerror", arguments); },
164+
modules: myModules
165+
166+
}, function(info){
167+
//Processor ready
168+
console.log(info); //Use 'info' to get details about source, sample-rate etc.
169+
//start
170+
processor.start();
171+
//wait 4s
172+
setTimeout(function(){
173+
//stop recorder
174+
waveEncoder.handle.sendToModule({gate: "close"});
175+
}, 4000);
176+
177+
}, function(err){
178+
//Initialization error
179+
console.error(err);
180+
});
181+
}
182+
</script>
183+
</body>
184+
</html>

0 commit comments

Comments
 (0)