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.
8 * Function to convert an array of bytes to a base64 string
9 * TODO(rkc): Change this to use a Uint8array instead of a string.
10 * @param {string} bytes String containing the bytes we want to convert.
11 * @return {string} String containing the base64 representation.
13 function bytesToBase64(bytes) {
15 for (var i = 0; i < bytes.length; ++i)
16 bstr += String.fromCharCode(bytes[i]);
17 return btoa(bstr).replace(/=/g, '');
21 * Function to convert a string to an array of bytes.
22 * @param {string} str String to convert.
23 * @return {Array} Array containing the string.
25 function stringToArray(str) {
27 for (var i = 0; i < str.length; ++i)
28 buffer[i] = str.charCodeAt(i);
33 * Creates a whispernet encoder.
35 * @param {Object} params Audio parameters for the whispernet encoder.
36 * @param {Object} whisperNacl The NaclBridge object, used to communicate with
37 * the whispernet wrapper.
38 * @param {string} clientId A string identifying the requester.
40 function WhisperEncoder(params, whisperNacl, clientId) {
41 this.whisperNacl_ = whisperNacl;
42 this.whisperNacl_.addListener(this.onNaclMessage_.bind(this));
43 this.clientId_ = clientId;
46 type: 'initialize_encoder',
51 this.whisperNacl_.send(msg);
55 * Method to encode a token.
56 * @param {Object} params Encode token parameters object.
58 WhisperEncoder.prototype.encode = function(params) {
59 // Pad the token before decoding it.
60 var token = params.token.token;
61 while (token.length % 4 > 0)
66 client_id: this.clientId_,
67 // Trying to send the token in binary form to Nacl doesn't work correctly.
68 // We end up with the correct string + a bunch of extra characters. This is
69 // true of returning a binary string too; hence we communicate back and
70 // forth by converting the bytes into an array of integers.
71 token: stringToArray(atob(token)),
72 repetitions: params.repetitions,
73 use_dtmf: params.token.audible,
74 use_crc: params.tokenParams.crc,
75 use_parity: params.tokenParams.parity
78 this.whisperNacl_.send(msg);
82 * Method to handle messages from the whispernet NaCl wrapper.
83 * @param {Event} e Event from the whispernet wrapper.
86 WhisperEncoder.prototype.onNaclMessage_ = function(e) {
88 if (msg.type == 'encode_token_response' && msg.client_id == this.clientId_) {
89 chrome.copresencePrivate.sendSamples(this.clientId_,
90 { token: bytesToBase64(msg.token), audible: msg.audible }, msg.samples);
95 * Creates a whispernet decoder.
97 * @param {Object} params Audio parameters for the whispernet decoder.
98 * @param {Object} whisperNacl The NaclBridge object, used to communicate with
99 * the whispernet wrapper.
100 * @param {string} clientId A string identifying the requester.
102 function WhisperDecoder(params, whisperNacl, clientId) {
103 this.whisperNacl_ = whisperNacl;
104 this.whisperNacl_.addListener(this.onNaclMessage_.bind(this));
105 this.clientId_ = clientId;
108 type: 'initialize_decoder',
112 this.whisperNacl_.send(msg);
116 * Method to request the decoder to process samples.
117 * @param {Object} params Process samples parameters object.
119 WhisperDecoder.prototype.processSamples = function(params) {
121 type: 'decode_tokens',
122 client_id: this.clientId_,
123 data: params.samples,
125 decode_audible: params.decodeAudible,
126 token_length_dtmf: params.audibleTokenParams.length,
127 crc_dtmf: params.audibleTokenParams.crc,
128 parity_dtmf: params.audibleTokenParams.parity,
130 decode_inaudible: params.decodeInaudible,
131 token_length_dsss: params.inaudibleTokenParams.length,
132 crc_dsss: params.inaudibleTokenParams.crc,
133 parity_dsss: params.inaudibleTokenParams.parity,
136 this.whisperNacl_.send(msg);
140 * Method to handle messages from the whispernet NaCl wrapper.
141 * @param {Event} e Event from the whispernet wrapper.
144 WhisperDecoder.prototype.onNaclMessage_ = function(e) {
146 if (msg.type == 'decode_tokens_response' && msg.client_id == this.clientId_) {
147 this.handleCandidates_(msg.tokens, msg.audible);
152 * Method to receive tokens from the decoder and process and forward them to the
153 * token callback registered with us.
154 * @param {!Array.string} candidates Array of token candidates.
155 * @param {boolean} audible Whether the received candidates are from the audible
159 WhisperDecoder.prototype.handleCandidates_ = function(candidates, audible) {
160 if (!candidates || candidates.length == 0)
163 var returnCandidates = [];
164 for (var i = 0; i < candidates.length; ++i) {
165 returnCandidates[i] = { token: bytesToBase64(candidates[i]),
168 chrome.copresencePrivate.sendFound(this.clientId_, returnCandidates);