1 |
|
---|
2 | //var org.greenstone.meandre.audio.util.MixType = {
|
---|
3 |
|
---|
4 | var MixType = {
|
---|
5 | LeftOnly: { value: 0, name: "Left only" },
|
---|
6 | RightOnly: { value: 1, name: "Right only" },
|
---|
7 | LeftRightAdd: { value: 2, name: "Left and Right add" },
|
---|
8 | LeftRightSub: { value: 3, name: "Left and Right subtract" }
|
---|
9 | };
|
---|
10 |
|
---|
11 |
|
---|
12 | var AudioMixer = function(numSamples,channels) {
|
---|
13 |
|
---|
14 | this.getset = {};
|
---|
15 |
|
---|
16 | this.numSamples = numSamples;
|
---|
17 | this.channels = 2;
|
---|
18 |
|
---|
19 | }
|
---|
20 |
|
---|
21 | AudioMixer.inherits(Component);
|
---|
22 |
|
---|
23 | //----------------------------- OVERVIEW -----------------------------------------------------
|
---|
24 |
|
---|
25 | AudioMixer.staticfield('Component', {
|
---|
26 |
|
---|
27 | creator: "David Bainbridge (based on AudioFileRead by Kris West)",
|
---|
28 |
|
---|
29 | description : "Overview: Takes a Signal object with file location metadata as input, "
|
---|
30 | + "reads in the corresponding audio file and streams out frames of data from that file."
|
---|
31 | + "Detailed Description: This module reads in audio files in a number of formats,"
|
---|
32 | + "enframes the data and streams out the frames. Mixing options include: LeftOnly, RightOnly, "
|
---|
33 | + "LeftRightAdd, LeftRightSub."
|
---|
34 | + "The frame size, overlap percentage and sample rate of the of the audio frames are appended to"
|
---|
35 | + "the metadata of the Signal object, which is \'passed through\' for later (optional) use but other components. "
|
---|
36 | + "The \"Read Frame Size\" parameter can be used to control how often data is read from "
|
---|
37 | + "disk."
|
---|
38 | + "Currently supported audio file formats include MP3 (.mp3) and Wave (.wav).",
|
---|
39 |
|
---|
40 | name: "Audio Signal Mixer",
|
---|
41 |
|
---|
42 | tags: "signal reader",
|
---|
43 |
|
---|
44 | // dependency: [ "foundry-abstracts.jar", "seasr-commons-1.4.9.jar" ]
|
---|
45 | });
|
---|
46 |
|
---|
47 | //------------------------------ INPUTS ------------------------------------------------------
|
---|
48 |
|
---|
49 | AudioMixer.staticfield('ComponentInputArray', [
|
---|
50 | /*
|
---|
51 | {
|
---|
52 | name: "signal",
|
---|
53 | description: "A Signal object with \"fileLocation\" metadata, indicating the "
|
---|
54 | + "location of the corresponding audio file."
|
---|
55 | }
|
---|
56 | */
|
---|
57 |
|
---|
58 | {
|
---|
59 | name: "rawInput",
|
---|
60 | description: "The raw float32 input from the underlying (decompressed if required) audio file"
|
---|
61 | }
|
---|
62 |
|
---|
63 |
|
---|
64 | ]);
|
---|
65 |
|
---|
66 |
|
---|
67 | //------------------------------ PROPERTIES --------------------------------------------------
|
---|
68 |
|
---|
69 | AudioMixer.staticfield('ComponentPropertyArray', [
|
---|
70 | /*
|
---|
71 | {
|
---|
72 | name: "outputFrameLength",
|
---|
73 | defaultValue: "0.023219954648526078",
|
---|
74 | description: "The length of audio frames to output, measured in seconds. This "
|
---|
75 | + "will be multiplied by the samplerate of the audio file and "
|
---|
76 | + "rounded to calculate the length in samples of the output "
|
---|
77 | + "vectors. This ensures that constant time resolution can be "
|
---|
78 | + "maintained at different sample rates. The default setting (0.023219...) "
|
---|
79 | + "produces 1024 sample frames at 44.1kHz and 512 at 22.05kHz."
|
---|
80 | },
|
---|
81 |
|
---|
82 | {
|
---|
83 | name: "overlapPercent",
|
---|
84 | defaultValue: "0.5",
|
---|
85 | description: "The percentage to overlap concurrent output frames by (0.0-1.0)"
|
---|
86 | },
|
---|
87 |
|
---|
88 | {
|
---|
89 | name: "mixType",
|
---|
90 | defaultValue: "LeftRightAdd",
|
---|
91 | description: "Controls how the Signal in should be mix. Assuming a stero "
|
---|
92 | + "input, then choices are: LeftOnly, RightOnly, LeftRightAdd, LeftRightSub."
|
---|
93 | + "In the case of mono input, the signal is passed straight through unchanged."
|
---|
94 | },
|
---|
95 |
|
---|
96 |
|
---|
97 | {
|
---|
98 | name: "readFrameSize",
|
---|
99 | defaultValue: "102400",
|
---|
100 | description: "Controls frame size of data read in by this module. If possible, "
|
---|
101 | + "this frame size will be used by the AudioReader responsible for "
|
---|
102 | + "the file otherwise buffering will be used to achieve the correct "
|
---|
103 | + "framesize. This parameter is used to control how often data is "
|
---|
104 | + "read from disk."
|
---|
105 | },
|
---|
106 |
|
---|
107 |
|
---|
108 | {
|
---|
109 | name: "limitAudioFrames",
|
---|
110 | defaultValue: "false",
|
---|
111 | description: "Controls whether the number of frames that are output is limited or not."
|
---|
112 | },
|
---|
113 |
|
---|
114 |
|
---|
115 | {
|
---|
116 | name: "maxOutFrames",
|
---|
117 | defaultValue: "10000",
|
---|
118 | description: "The maxium number of frames to output."
|
---|
119 | }
|
---|
120 | */
|
---|
121 | ]);
|
---|
122 |
|
---|
123 |
|
---|
124 | //------------------------------ OUTPUTS -----------------------------------------------------
|
---|
125 |
|
---|
126 | AudioMixer.staticfield('ComponentOutputArray', [
|
---|
127 |
|
---|
128 | {
|
---|
129 | name: "mixedFrames",
|
---|
130 | description: "Audio frames that are some combination of left and/or right channels "
|
---|
131 | + "as set by the mixType property"
|
---|
132 | },
|
---|
133 | /*
|
---|
134 | {
|
---|
135 | name: "signal",
|
---|
136 | description: "The Signal Object used to read the file, with"
|
---|
137 | + " \"frameSize\", \"overlapSize\" and \"sampleRate\" metadata set."
|
---|
138 | }
|
---|
139 | */
|
---|
140 |
|
---|
141 | ]);
|
---|
142 |
|
---|
143 | AudioMixer.prototype.defaults = function() {
|
---|
144 |
|
---|
145 | /** enum representing how the left and right audio in channels are to be treated
|
---|
146 | */
|
---|
147 | getset.mixType = MixType.LeftRightAdd;
|
---|
148 |
|
---|
149 | /** The length of audio frames output, measured in seconds. */
|
---|
150 | getset.outputFrameLength = (512.0 / 22050.0);
|
---|
151 |
|
---|
152 | /** The desired read frame size. This parameter can be used to adjust the number
|
---|
153 | * of disk accesses per output frame.
|
---|
154 | */
|
---|
155 | getset.readFrameSize = 1024;
|
---|
156 |
|
---|
157 | /** The percentage to overlap concurrent frames.
|
---|
158 | */
|
---|
159 | getset.overlapPercent = 0.5;
|
---|
160 |
|
---|
161 | /** The maximum number of frames to output.
|
---|
162 | */
|
---|
163 | getset.maxOutFrames = 10000;
|
---|
164 | /** Determines whether number of frames is limited
|
---|
165 | */
|
---|
166 | getset.limitAudioFrames = false;
|
---|
167 |
|
---|
168 | }
|
---|
169 |
|
---|
170 |
|
---|
171 | AudioMixer.prototype.initializeCallBack = function(ccp) {
|
---|
172 |
|
---|
173 | // typeof(ccp) => ComponentContextProperties
|
---|
174 |
|
---|
175 | // Put these into ComponentPropertyArray
|
---|
176 |
|
---|
177 | this.concatLimit = 8;
|
---|
178 | this.concatCount = 0;
|
---|
179 |
|
---|
180 | this.concatSignal = new Float32Array(this.numSamples * this.concatLimit); // mono
|
---|
181 |
|
---|
182 | }
|
---|
183 |
|
---|
184 |
|
---|
185 | AudioMixer.prototype.executeCallBack = function(cc) {
|
---|
186 |
|
---|
187 | // typeof(cc) => ComponentContext
|
---|
188 |
|
---|
189 | // get the "[D" input
|
---|
190 |
|
---|
191 | //console.log("*** AudioMixer::executeCallBack(): away to request 'rawInput'");
|
---|
192 |
|
---|
193 | var fb = cc.getDataComponentFromInput("rawInput");
|
---|
194 | /*
|
---|
195 | var mess = "";
|
---|
196 | for (var i=0; i<fb.length; i+=200) {
|
---|
197 | mess += fb[i] + ", ";
|
---|
198 | }
|
---|
199 | console.log("**** AudioMixer: data samples = " + mess);
|
---|
200 | */
|
---|
201 |
|
---|
202 | // Mix number of channels down to one channel
|
---|
203 |
|
---|
204 | var p =0;
|
---|
205 | for (var i=0; i<this.numSamples; i++ ) {
|
---|
206 |
|
---|
207 | var sv = 0;
|
---|
208 | for (var c=0; c<this.channels; c++) {
|
---|
209 | sv += fb[p];
|
---|
210 | p++;
|
---|
211 | }
|
---|
212 | // signal[i] = sv / channels;
|
---|
213 | this.concatSignal[(this.concatCount*this.numSamples)+i] = sv / this.channels;
|
---|
214 | }
|
---|
215 |
|
---|
216 | this.concatCount++;
|
---|
217 |
|
---|
218 | //console.log("*** concatCount = " + this.concatCount);
|
---|
219 | //console.log("*** concatLimit = " + this.concatLimit);
|
---|
220 |
|
---|
221 | if (this.concatCount==this.concatLimit) {
|
---|
222 | // send this mono-mixed concatenated array on its way
|
---|
223 |
|
---|
224 | this.concatCount=0;
|
---|
225 | cc.pushDataComponentToOutput("mixedFrames", this.concatSignal);
|
---|
226 | }
|
---|
227 | }
|
---|
228 |
|
---|
229 |
|
---|
230 | AudioMixer.prototype.disposeCallBack = function(ccp) {
|
---|
231 |
|
---|
232 | // typeof(ccp) => ComponentContextProperties
|
---|
233 |
|
---|
234 | this.concatSignal = null;
|
---|
235 |
|
---|
236 | }
|
---|
237 |
|
---|
238 |
|
---|
239 |
|
---|
240 |
|
---|
241 |
|
---|