Skip to content

Commit 1da2c52

Browse files
authored
Merge pull request #1 from SEPIA-Framework/dev
v0.9.7 stable
2 parents edc52f4 + c8d3fcd commit 1da2c52

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+4323
-744
lines changed

.github/FUNDING.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
github: [fquirin]

README.md

Lines changed: 274 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,283 @@
11
# SEPIA Web-Audio Library
22

3-
## -- Currently in BETA --
4-
5-
Modular, cross-browser library to record and process audio using audio-worklets, web-workers and script-processors (as fallback).
6-
Can be used to chain different modules (worklets AND workers) to one audio pipeline.
3+
Modular, cross-browser library to record and process audio using **audio-worklets**, **web-workers** and script-processors (as fallback).
4+
Can be used to **chain different modules (worklets AND workers) to one audio pipeline**.
5+
Thanks to the worklet + worker combination most of the audio **processing can be done in the background** without messing with the main UI-thread!
6+
7+
The main focus of this library is speech recording and processing (see: [SEPIA Client app](https://github.com/SEPIA-Framework/sepia-html-client-app)),
8+
but you can quickly add modules for many other use-cases as well (contributions welcome ^^).
79

810
Available modules:
911

10-
- Resampler using Speex codec (WASM module)
11-
- Voice-Activity-Detection (VAD) via WebRTC-VAD
12-
- Custom SEPIA VAD module using Meyda to analyze bark-scale, MFCC and more
13-
- Wave Encoder with lookback-buffer
14-
- Porcupine Wake-Word detector
12+
- **Resampler** using Speex codec (WASM module)
13+
- **Voice-Activity-Detection** (VAD) via WebRTC-VAD
14+
- Custom SEPIA VAD module using Meyda to analyze bark-scale, **MFCC** and more
15+
- **Wave Encoder** with lookback-buffer
16+
- Porcupine **Wake-Word detector** (including: "Computer", "Jarvis", "Hey SEPIA" and more)
17+
- [SEPIA STT Server](https://github.com/SEPIA-Framework/sepia-stt-server) WebSocket module for **speech recognition** (see STT Server for demo)
1518
- more to come ...
19+
20+
Check out the [screenshots](screenshots) section for some test-page impressions :-).
21+
22+
## Quick-Start - Voice Recorder
23+
24+
<p align="center">
25+
<img src="screenshots/voice-recorder-demo.png" alt="SEPIA Voice Recorder Demo"/>
26+
</p>
27+
28+
Efficiently resampling audio to 16000 Hz and creating 16Bit mono samples for speech recognition was one of the primary objectives when building this library.
29+
While you can put together your own audio pipeline to do that (see below) there is a very convenient plugin available that does the job for you.
30+
In this quick-start guide you will learn the basics to use the **'SepiaVoiceRecorder'**.
31+
32+
The first step is to import the required files and set the correct path to the modules folder. You will find more details about this step in the tutorial below.
33+
In this example the required files in 'modules' are `speex-resample-switch.js`, `wave-encoder-worker.js` and `shared/ring-buffer.min.js`:
34+
```html
35+
<script type="text/javascript" src="test/sepia-web-audio.min.js"></script>
36+
<script type="text/javascript" src="test/sepia-recorder.min.js"></script>
37+
<script>
38+
SepiaFW.webAudio.defaultProcessorOptions.moduleFolder = "test/modules";
39+
</script>
40+
```
41+
42+
Note that we chose to copy the minified core library (from 'dist'), the recorder plugin and the modules folder (from 'src') to a folder named 'test'.
43+
Now we can create our recorder:
44+
```javascript
45+
//catch some core events:
46+
SepiaVoiceRecorder.onProcessorReady = console.log; //add your own handler here
47+
SepiaVoiceRecorder.onProcessorInitError = console.error;
48+
49+
//events to process data:
50+
SepiaVoiceRecorder.onResamplerData = function(data){
51+
//here you can process raw data, e.g.:
52+
//RMS volume: data.rms
53+
//Raw 16Bit mono buffer: data.samples[0]
54+
}
55+
SepiaVoiceRecorder.onWaveEncoderAudioData = function(waveData){
56+
//when we get encoded WAV data we can add it as audio element to our page:
57+
SepiaVoiceRecorder.addAudioElementToPage(document.body, waveData, "audio/wav");
58+
}
59+
60+
//create the recorder:
61+
SepiaVoiceRecorder.create({
62+
targetSampleRate: 16000, //16kHz is actually the voice recorder default
63+
gain: 3.0, //we can amplify the signal using the gain option
64+
recordingLimitMs: 30000, //Total recording limit ms
65+
});
66+
```
67+
68+
Everything is prepared. To finally start recording you should wait for the ready event and then call:
69+
```javascript
70+
//to start:
71+
SepiaVoiceRecorder.start();
72+
73+
//to stop:
74+
SepiaVoiceRecorder.stop();
75+
```
76+
77+
That's it :-).
78+
79+
Of cause there are many more events, options and features available ^^. Please check out the [voice-recorder-demo.html](voice-recorder-demo.html) for a more complex recorder setup including VAD etc..
80+
81+
82+
## Tutorial - Building Audio Pipelines
83+
84+
Full code for this tutorial can be found at: [tutorial-code-page.html](tutorial-code-page.html).
85+
Please check out the extensive [modules-demo.html](modules-demo.html) and the test pages for more examples.
86+
87+
88+
### Part 1: Basic setup and single buffer module
89+
90+
#### Import library and modules
91+
92+
Copy `sepia-web-audio.js` to your project and import the library using the `<head>` section of your HTML page:
93+
94+
```html
95+
<script type="text/javascript" src="src/sepia-web-audio.js"></script>
96+
```
97+
98+
Copy the audio modules you are planning to use, for part 1 of the tutorial we only need 'buffer-switch.js'. When you're done set the correct path to your modules folder:
99+
100+
```html
101+
<script>
102+
SepiaFW.webAudio.defaultProcessorOptions.moduleFolder = "src/modules";
103+
</script>
104+
```
105+
106+
Note: In this example we've used a folder called `src` for the library and modules folder but you can choose whatever you like.
107+
Note2: Currently there is no Javascript modules support yet. Feel free to create a request via the issues section if you need it ;-).
108+
109+
#### Create the audio processor
110+
111+
After importing the library you should see `SepiaFW.webAudio` in your scope. We have used this already in the head to set the modules folder.
112+
The `Processor` class is our main interface to handle the audio pipeline, but first we need to define the modules we want to use.
113+
Modules come in two main flavours, "switch" and "worker" with the main difference that switches are based on 'AudioWorklet' and can pass through data on a lower level (more details later).
114+
115+
For this first example we only look at raw microphone data:
116+
117+
```javascript
118+
var myModules = [];
119+
120+
function bufferCallback(data){
121+
//handle samples here using: data.samples
122+
}
123+
myModules.push({
124+
name: 'buffer-switch',
125+
settings: {
126+
onmessage: bufferCallback,
127+
options: {
128+
processorOptions: {
129+
bufferSize: 512, //size of samples generated
130+
passThroughMode: 0, //0: none, 1: original (float32 array)
131+
}
132+
}
133+
}
134+
});
135+
```
136+
137+
After the module is set up we can create the processor:
138+
139+
```javascript
140+
var processor = new SepiaFW.webAudio.Processor({
141+
onaudiostart: console.log,
142+
onaudioend: console.log,
143+
onrelease: console.log,
144+
onerror: console.error,
145+
modules: myModules
146+
147+
}, function(info){
148+
//Processor ready
149+
console.log(info); //Use 'info' to get details about source, sample-rate etc.
150+
151+
}, function(err){
152+
//Initialization error
153+
console.error(err);
154+
});
155+
```
156+
157+
#### Start, stop and release the processor
158+
159+
As soon as the ready event fired we can start processing with `processor.start()`, check `onaudiostart` (defined in processor options) and wait for data in `bufferCallback` (defined in module).
160+
161+
After we're done we stop with `processor.stop()` and look out for `onaudioend`.
162+
163+
If we don't want to restart later we can close the processor and clean up resources with `processor.release()`.
164+
165+
166+
### Part 2: Resample input and record raw 16Bit PCM mono audio (WAV)
167+
168+
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).
169+
To make this happen we will replace the buffer module from earlier with a resampler and wave encoder module.
170+
If you haven't done already please copy `speex-resample-switch.js`, `wave-encoder-worker.js` and `shared/ring-buffer.min.js` to your modules folder.
171+
172+
NOTE: Some browsers are actually able to natively resampling for us ^^. The resampler module will simply skip transformation in this case but it might be preferable to prevent native resampling to retain full control over the quality and speed.
173+
`SepiaFW.webAudio.isNativeStreamResamplingSupported` will be 'true' when the lib is imported because we can't test for the feature but set to 'false' after the first failed attempt of native resampling!
174+
175+
We create the resampler first and we use the "switch" version (not the "worker", this is preferred if 'AudioWorklet' is supported):
176+
177+
```javascript
178+
SepiaFW.webAudio.tryNativeStreamResampling = false; //global option (remain in control of resampling)
179+
180+
var myModules = [];
181+
var targetSampleRate = 16000; //this is the sample-rate we want
182+
var bufferSize = 512; //size of samples generated
183+
184+
function resamplerCallback(data){
185+
//data will include e.g.: data.samples and data.rms (volume)
186+
}
187+
var resampler = {
188+
name: 'speex-resample-switch',
189+
settings: {
190+
onmessage: resamplerCallback,
191+
sendToModules: [], //[moduleIndex] - filled below with index of wave-encoder module
192+
options: {
193+
processorOptions: {
194+
targetSampleRate: targetSampleRate,
195+
bufferSize: bufferSize,
196+
resampleQuality: 5, //1 (low quality) - 10 (best quality)
197+
calculateRmsVolume: true, //the resampler can calculate RMS signal volume
198+
gain: 1.0, //we can amplify the signal here
199+
passThroughMode: 0 //0: none - only switch in our pipe atm
200+
}
201+
}
202+
}
203+
};
204+
```
205+
206+
Next we create the wave-encoder module:
207+
208+
```javascript
209+
function waveEncoderCallback(data){
210+
//can be used to track capture state and get final WAV
211+
//check: data.gate, data.output.wav, data.output.buffer
212+
}
213+
var waveEncoder = {
214+
name: 'wave-encoder',
215+
type: 'worker',
216+
handle: {}, //will be updated on init. with ref. to node.
217+
settings: {
218+
onmessage: waveEncoderCallback,
219+
options: {
220+
setup: {
221+
inputSampleRate: targetSampleRate, //input of this will be ...
222+
inputSampleSize: bufferSize, //... output of resampler
223+
lookbackBufferMs: 0, //(experimental) ignore for now
224+
recordBufferLimitMs: 6000, //we can apply recording limit as milliseconds
225+
//recordBufferLimitKb: 600, //... or as kilobytes (default ~5MB)
226+
isFloat32: false //resampler gives int16 - use e.g. for buffer module
227+
}
228+
}
229+
}
230+
};
231+
```
232+
233+
Now we combine both modules to our audio pipeline. To tell the wave-encoder what input to use we combine the modules like this:
234+
235+
```javascript
236+
myModules.push(resampler); //index 1
237+
myModules.push(waveEncoder); //index 2
238+
239+
//connect resampler output to wave-encoder input:
240+
resampler.settings.sendToModules.push(2);
241+
```
242+
243+
We create the processor the same way as before (`var processor = new SepiaFW.webAudio.Processor({...});`) and call `processor.start()` when ready.
244+
The wave-encoder will receive data now but only start capturing when we explicitly tell it to using the module message interface.
245+
The same interface is used to request the captured data after we stop processing:
246+
247+
```javascript
248+
//start recording
249+
waveEncoder.handle.sendToModule({gate: "open"});
250+
251+
//wait some time then stop recording (this will trigger automatically after 'recordBufferLimitMs')
252+
waveEncoder.handle.sendToModule({gate: "close"});
253+
254+
//finally get the data (this is possible until processor is released)
255+
waveEncoder.handle.sendToModule({request: {get: "wave"}}); //encoded WAV
256+
waveEncoder.handle.sendToModule({request: {get: "buffer"}}); //raw int16 buffer
257+
```
258+
259+
Using the module interface an optimized 'waveEncoderCallback' (defined in module above) could look like this:
260+
261+
```javascript
262+
//modified 'waveEncoderCallback':
263+
function waveEncoderCallback(data){
264+
if (data.gate && data.gate.isOpen === false){
265+
//stop processor
266+
processor.stop();
267+
//get data
268+
waveEncoder.handle.sendToModule({request: {get: "wave"}});
269+
waveEncoder.handle.sendToModule({request: {get: "buffer"}});
270+
}
271+
if (data.output && data.output.wav){
272+
//just for fun, add WAV to page:
273+
var targetEle = document.body;
274+
var blobType = "audio/wav";
275+
SepiaFW.webAudio.addAudioElementToPage(targetEle, data.output.wav, blobType);
276+
}
277+
}
278+
```
279+
280+
... TO BE CONTINUED
16281

17282
# Resources (see LICENSE as well)
18283

dist/sepia-recorder.min.js

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/sepia-web-audio.min.js

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)