OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
xiyuan
2014/07/31 19:22:23
nit: 'use strict';
rkc
2014/07/31 23:12:53
Done.
| |
5 /** | |
6 * Function to convert an array of bytes to a base64 string | |
7 * @param {string} bytes String containing the bytes we want to convert. | |
xiyuan
2014/07/31 19:22:23
nit: Think |bytes| should be an Uint8Array (or we
rkc
2014/07/31 23:12:53
This is the form this comes to us from Nacl. It is
xiyuan
2014/08/01 19:24:27
Let's put a TODO for this. Ideally, nacl should se
rkc
2014/08/01 21:21:12
Done.
| |
8 * @return {string} String containing the base64 representation. | |
9 */ | |
10 function bytesToBase64(bytes) { | |
11 var bstr = ''; | |
12 for (var i = 0; i < bytes.length; ++i) | |
13 bstr += String.fromCharCode(bytes[i]); | |
14 return btoa(bstr); | |
15 } | |
16 | |
17 /** | |
18 * Function to convert a string to an array of bytes. | |
19 * @param {string} str String to convert. | |
20 * @return {Array} Array containing the string. | |
21 */ | |
22 function stringToArray(str) { | |
23 var buffer = []; | |
24 for (var i = 0; i < str.length; ++i) | |
25 buffer[i] = str.charCodeAt(i); | |
26 return buffer; | |
27 } | |
28 | |
29 /** | |
30 * Creates a whispernet encoder. | |
31 * @constructor | |
32 * @param {Object} params Dictionary of parameters used to initialize the | |
33 * whispernet encoder. | |
34 * @param {Object} whisperNacl The NaclBridge object to use to communicate with | |
35 * the whispernet wrapper. | |
36 */ | |
37 function WhisperEncoder(params, whisperNacl) { | |
38 params = params || {}; | |
39 this.repetitions_ = params.repetitions || 3; | |
40 | |
41 this.whisperNacl_ = whisperNacl; | |
42 this.whisperNacl_.addListener(this.onNaclMessage_.bind(this)); | |
43 | |
44 var symbolCoder = {}; | |
45 symbolCoder.sample_rate = params.sampleRate || 48000.0; | |
46 symbolCoder.upsampling_factor = params.bitsPerSample || 16; | |
47 symbolCoder.desired_carrier_frequency = params.carrierFrequency || 18500.0; | |
48 symbolCoder.bits_per_symbol = 4; | |
49 symbolCoder.min_cycles_per_frame = 4; | |
50 symbolCoder.baseband_decimation_factor = 4; | |
51 | |
52 var msg = { | |
53 type: 'initialize_encoder', | |
54 symbol_coder: symbolCoder, | |
55 encoder_params: { | |
56 bytes_per_token: 6, | |
57 include_parity_symbol: true, | |
58 single_sideband: true | |
59 } | |
60 }; | |
61 this.whisperNacl_.send(JSON.stringify(msg)); | |
62 } | |
63 | |
64 /** | |
65 * Method to encode a token. | |
66 * @param {string} token Token to encode. | |
67 * @param {boolean} raw Whether we should return the encoded samples in raw | |
68 * format or as a Wave file. | |
69 */ | |
70 WhisperEncoder.prototype.encode = function(token, raw) { | |
71 var msg = { | |
72 type: 'encode_token', | |
73 // Trying to send the token in binary form to Nacl doesn't work correctly. | |
74 // We end up with the correct string + a bunch of extra characters. This is | |
75 // true of returning a binary string too; hence we communicate back and | |
76 // forth by converting the bytes into an array of integers. | |
77 token: stringToArray(token), | |
78 repetitions: this.repetitions_, | |
79 return_raw_samples: raw | |
80 }; | |
81 this.whisperNacl_.send(JSON.stringify(msg)); | |
82 }; | |
83 | |
84 /** | |
85 * Method to set the callback for encoded audio data received from the encoder | |
86 * when we finish encoding a token. | |
87 * @param {function(string, ArrayBuffer)} callback Callback which will receive | |
88 * the audio samples. | |
89 */ | |
90 WhisperEncoder.prototype.onAudioData = function(callback) { | |
xiyuan
2014/07/31 19:22:23
nit: onAudioData -> setAudioDataCallback or some n
rkc
2014/07/31 23:12:53
Done.
| |
91 this.audioDataCallback_ = callback; | |
92 }; | |
93 | |
94 /** | |
95 * Method to handle messages from the whispernet NaCl wrapper. | |
96 * @param {Event} e Event from the whispernet wrapper. | |
97 * @private | |
98 */ | |
99 WhisperEncoder.prototype.onNaclMessage_ = function(e) { | |
100 var msg = e.data; | |
101 if (msg.type == 'encode_token_response') { | |
102 this.audioDataCallback_(bytesToBase64(msg.token), msg.samples); | |
103 } | |
104 }; | |
105 | |
106 /** | |
107 * Creates a whispernet decoder. | |
108 * @constructor | |
109 * @param {Object} params Dictionary of parameters used to initialize the | |
110 * whispernet decoder. | |
111 * @param {Object} whisperNacl The NaclBridge object to use to communicate with | |
112 * the whispernet wrapper. | |
113 */ | |
114 function WhisperDecoder(params, whisperNacl) { | |
115 params = params || {}; | |
116 | |
117 this.whisperNacl_ = whisperNacl; | |
118 this.whisperNacl_.addListener(this.onNaclMessage_.bind(this)); | |
119 | |
120 var msg = { | |
121 type: 'initialize_decoder', | |
122 num_channels: params.channels, | |
123 symbol_coder: { | |
124 sample_rate: params.sampleRate || 48000.0, | |
125 upsampling_factor: params.bitsPerSample || 16, | |
126 desired_carrier_frequency: params.carrierFrequency || 18500.0, | |
127 bits_per_symbol: 4, | |
128 min_cycles_per_frame: 4, | |
129 baseband_decimation_factor: 4 | |
130 }, | |
131 decoder_params: { | |
132 bytes_per_token: 6, | |
133 include_parity_symbol: true, | |
134 max_candidates: 1, | |
135 broadcaster_stopped_threshold_in_seconds: 10 | |
136 }, | |
137 acquisition_params: { | |
138 max_buffer_duration_in_seconds: 3 | |
139 } | |
140 }; | |
141 this.whisperNacl_.send(JSON.stringify(msg)); | |
142 } | |
143 | |
144 /** | |
145 * Method to request the decoder to wipe its internal buffer. | |
146 */ | |
147 WhisperDecoder.prototype.wipeDecoder = function() { | |
148 var msg = { | |
149 type: 'wipe_decode_buffer' | |
150 }; | |
151 this.whisperNacl_.send(JSON.stringify(msg)); | |
152 }; | |
153 | |
154 /** | |
155 * Method to request the decoder to detect a broadcast. | |
156 */ | |
157 WhisperDecoder.prototype.detectBroadcast = function() { | |
158 var msg = { | |
159 type: 'detect_broadcast' | |
160 }; | |
161 this.whisperNacl_.send(JSON.stringify(msg)); | |
162 }; | |
163 | |
164 /** | |
165 * Method to request the decoder to process samples. | |
166 * @param {ArrayBuffer} samples Array of samples to process. | |
167 */ | |
168 WhisperDecoder.prototype.processSamples = function(samples) { | |
169 // For sample processing, the Nacl module doesn't expect any frills in the | |
170 // message, just send the samples directly. | |
171 this.whisperNacl_.send(samples); | |
172 }; | |
173 | |
174 /** | |
175 * Method to set the callback for decoded tokens received from the decoder. | |
176 * @param {function(!Array.string)} callback Callback to receive the list of | |
177 * decoded tokens. | |
178 */ | |
179 WhisperDecoder.prototype.onReceive = function(callback) { | |
xiyuan
2014/07/31 19:22:23
similar here. onReceive -> setReceiveCallback.
rkc
2014/07/31 23:12:53
Done.
| |
180 this.tokenCallback_ = callback; | |
181 }; | |
182 | |
183 /** | |
184 * Method to set the callback for receiving the detect callback status received | |
185 * from the decoder. | |
186 * @param {function()} callback Callback to set to receive the detect broadcast | |
187 * status. | |
188 */ | |
189 WhisperDecoder.prototype.onDetectBroadcast = function(callback) { | |
190 this.detectBroadcastCallback_ = callback; | |
191 }; | |
192 | |
193 /** | |
194 * Method to handle messages from the whispernet NaCl wrapper. | |
195 * @param {Event} e Event from the whispernet wrapper. | |
196 * @private | |
197 */ | |
198 WhisperDecoder.prototype.onNaclMessage_ = function(e) { | |
199 var msg = e.data; | |
200 if (msg.type == 'decode_tokens_response') { | |
201 this.handleCandidates_(JSON.parse(msg.tokens)); | |
202 } else if (msg.type == 'detect_broadcast_response') { | |
203 this.detectBroadcastCallback_(msg.detected); | |
204 } | |
205 }; | |
206 | |
207 /** | |
208 * Method to receive tokens from the decoder and process and forward them to the | |
209 * token callback registered with us. | |
210 * @param {!Array.string} candidates Array of token candidates. | |
211 * @private | |
212 */ | |
213 WhisperDecoder.prototype.handleCandidates_ = function(candidates) { | |
214 if (!this.tokenCallback_ || !candidates || candidates.length == 0) | |
215 return; | |
216 | |
217 var returnCandidates = []; | |
218 for (var i = 0; i < candidates.length; ++i) | |
219 returnCandidates[i] = bytesToBase64(candidates[i]); | |
220 this.tokenCallback_(returnCandidates); | |
221 }; | |
OLD | NEW |