-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgain-matrix.js
126 lines (113 loc) · 3.47 KB
/
gain-matrix.js
1
2
3
4
5
6
7
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
import React from "react";
import { render } from "react-dom";
import {
RAudioContext,
RBufferSource,
RExtensible,
RGain,
RPipeline,
RSplit,
RSplitChannels,
} from "../index.js";
class GainMatrix extends RExtensible {
constructor(props) {
super(props);
const gains = new Array(props.channelCount || 2).fill(
new Array(props.channelCount || 2).fill(1)
);
this.state = { gains };
this.makeRow = this.makeRow.bind(this);
}
onGainInput(e) {
const [x, y] = e.target.name.split("").map((v) => parseInt(v));
const gains = this.state.gains.slice().map((arr) => arr.slice());
gains[x][y] = e.target.value;
this.setState({ gains });
}
makeRow(row, rowIndex) {
return (
<RSplit key={rowIndex}>
{row.map((cellGain, columnIndex) => (
<RPipeline key={columnIndex}>
<RGain
name={`gain${rowIndex}${columnIndex}`}
gain={cellGain}
connectToChannel={columnIndex}
/>
<form>
<label htmlFor={`label-${rowIndex}${columnIndex}`}>
{`Row: ${rowIndex} - Column: ${columnIndex}`}
</label>
<input
type="range"
min="0"
max="1"
step="any"
defaultValue="1"
id={`label-${rowIndex}${columnIndex}`}
name={`${rowIndex}${columnIndex}`}
onChange={this.onGainInput.bind(this)}
/>
<hr />
</form>
</RPipeline>
))}
</RSplit>
);
}
renderGraph() {
return (
<RSplitChannels channelCount={this.props.channelCount}>
{this.state.gains.map(this.makeRow)}
</RSplitChannels>
);
}
}
export default class GainMatrixExample extends React.Component {
constructor() {
super();
this.state = {
buffer: null,
};
}
componentDidMount() {
// In Safari decodeAudioData doesn't return a promise
// so we need to run this as both a callback and a promise handler
const loadBuffer = (buffer) => buffer && this.setState({ buffer });
fetch("/assets/audio/clarinet.mp3")
.then((res) => res.arrayBuffer())
.then((ab) => this.audioContext.decodeAudioData(ab, loadBuffer, null))
.then(loadBuffer);
}
render() {
return (
<RAudioContext debug={false} onInit={(ctx) => (this.audioContext = ctx)}>
<article>
<h1>Gain Matrix</h1>
<p>
This example (courtesy of{" "}
<a href="http://github.com/tomjnixon">Tom Nixon</a> from BBC
R&D) shows how we can create complex multichannel graphs using{" "}
<code>RSplitChannels</code> and explicit{" "}
<code>connectToChannel</code> props.
</p>
<p>
Each channel of the stereo input signal is routed to both channels
of the output signal and each branch is processed by a separate{" "}
<code>RGain</code>. This kind of graph is particularly useful when
binauralising audio.
</p>
<p>
Stereo audio recording by Freesound user{" "}
<a href="https://freesound.org/people/debudding/">debudding</a>{" "}
(Public Domain).
</p>
</article>
<RPipeline>
<RBufferSource buffer={this.state.buffer} loop start={0} />
<GainMatrix channelCount={2} />
</RPipeline>
</RAudioContext>
);
}
}