Home | History | Annotate | Download | only in audio
      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 
      5 #include "components/copresence/handlers/audio/audio_directive_handler.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/logging.h"
      9 #include "base/memory/scoped_ptr.h"
     10 #include "base/strings/string_util.h"
     11 #include "base/time/time.h"
     12 #include "components/copresence/mediums/audio/audio_player.h"
     13 #include "components/copresence/mediums/audio/audio_recorder.h"
     14 #include "components/copresence/proto/data.pb.h"
     15 #include "media/base/audio_bus.h"
     16 
     17 namespace {
     18 
     19 // UrlSafe is defined as:
     20 // '/' represented by a '_' and '+' represented by a '-'
     21 // TODO(rkc): Move this processing to the whispernet wrapper.
     22 std::string FromUrlSafe(std::string token) {
     23   base::ReplaceChars(token, "-", "+", &token);
     24   base::ReplaceChars(token, "_", "/", &token);
     25   return token;
     26 }
     27 
     28 const int kSampleExpiryTimeMs = 60 * 60 * 1000;  // 60 minutes.
     29 const int kMaxSamples = 10000;
     30 
     31 }  // namespace
     32 
     33 namespace copresence {
     34 
     35 // Public methods.
     36 
     37 AudioDirectiveHandler::AudioDirectiveHandler(
     38     const AudioRecorder::DecodeSamplesCallback& decode_cb,
     39     const AudioDirectiveHandler::EncodeTokenCallback& encode_cb)
     40     : player_audible_(NULL),
     41       player_inaudible_(NULL),
     42       recorder_(NULL),
     43       decode_cb_(decode_cb),
     44       encode_cb_(encode_cb),
     45       samples_cache_audible_(
     46           base::TimeDelta::FromMilliseconds(kSampleExpiryTimeMs),
     47           kMaxSamples),
     48       samples_cache_inaudible_(
     49           base::TimeDelta::FromMilliseconds(kSampleExpiryTimeMs),
     50           kMaxSamples) {
     51 }
     52 
     53 AudioDirectiveHandler::~AudioDirectiveHandler() {
     54   if (player_audible_)
     55     player_audible_->Finalize();
     56   if (player_inaudible_)
     57     player_inaudible_->Finalize();
     58   if (recorder_)
     59     recorder_->Finalize();
     60 }
     61 
     62 void AudioDirectiveHandler::Initialize() {
     63   player_audible_ = new AudioPlayer();
     64   player_audible_->Initialize();
     65 
     66   player_inaudible_ = new AudioPlayer();
     67   player_inaudible_->Initialize();
     68 
     69   recorder_ = new AudioRecorder(decode_cb_);
     70   recorder_->Initialize();
     71 }
     72 
     73 void AudioDirectiveHandler::AddInstruction(const TokenInstruction& instruction,
     74                                            const std::string& op_id,
     75                                            base::TimeDelta ttl) {
     76   switch (instruction.token_instruction_type()) {
     77     case TRANSMIT:
     78       DVLOG(2) << "Audio Transmit Directive received. Token: "
     79                << instruction.token_id()
     80                << " with TTL=" << ttl.InMilliseconds();
     81       switch (instruction.medium()) {
     82         case AUDIO_ULTRASOUND_PASSBAND:
     83           transmits_list_inaudible_.AddDirective(op_id, ttl);
     84           PlayToken(instruction.token_id(), false);
     85           break;
     86         case AUDIO_AUDIBLE_DTMF:
     87           transmits_list_audible_.AddDirective(op_id, ttl);
     88           PlayToken(instruction.token_id(), true);
     89           break;
     90         default:
     91           NOTREACHED();
     92       }
     93       break;
     94     case RECEIVE:
     95       DVLOG(2) << "Audio Receive Directive received. TTL="
     96                << ttl.InMilliseconds();
     97       receives_list_.AddDirective(op_id, ttl);
     98       ProcessNextReceive();
     99       break;
    100     case UNKNOWN_TOKEN_INSTRUCTION_TYPE:
    101     default:
    102       LOG(WARNING) << "Unknown Audio Transmit Directive received.";
    103   }
    104 }
    105 
    106 void AudioDirectiveHandler::RemoveInstructions(const std::string& op_id) {
    107   transmits_list_audible_.RemoveDirective(op_id);
    108   transmits_list_inaudible_.RemoveDirective(op_id);
    109   receives_list_.RemoveDirective(op_id);
    110 
    111   ProcessNextTransmit();
    112   ProcessNextReceive();
    113 }
    114 
    115 // Private methods.
    116 
    117 void AudioDirectiveHandler::ProcessNextTransmit() {
    118   // If we have an active directive for audible or inaudible audio, ensure that
    119   // we are playing our respective token; if we do not have a directive, then
    120   // make sure we aren't playing. This is duplicate code, but for just two
    121   // elements, it has hard to make a case for processing a loop instead.
    122 
    123   scoped_ptr<AudioDirective> audible_transmit(
    124       transmits_list_audible_.GetActiveDirective());
    125   if (audible_transmit && !player_audible_->IsPlaying() &&
    126       samples_cache_audible_.HasKey(current_token_audible_)) {
    127     DVLOG(3) << "Playing audible for op_id: " << audible_transmit->op_id;
    128     player_audible_->Play(
    129         samples_cache_audible_.GetValue(current_token_audible_));
    130     stop_audible_playback_timer_.Start(
    131         FROM_HERE,
    132         audible_transmit->end_time - base::Time::Now(),
    133         this,
    134         &AudioDirectiveHandler::ProcessNextTransmit);
    135   } else if (!audible_transmit && player_audible_->IsPlaying()) {
    136     DVLOG(3) << "Stopping audible playback.";
    137     current_token_audible_.clear();
    138     stop_audible_playback_timer_.Stop();
    139     player_audible_->Stop();
    140   }
    141 
    142   scoped_ptr<AudioDirective> inaudible_transmit(
    143       transmits_list_inaudible_.GetActiveDirective());
    144   if (inaudible_transmit && !player_inaudible_->IsPlaying() &&
    145       samples_cache_inaudible_.HasKey(current_token_inaudible_)) {
    146     DVLOG(3) << "Playing inaudible for op_id: " << inaudible_transmit->op_id;
    147     player_inaudible_->Play(
    148         samples_cache_inaudible_.GetValue(current_token_inaudible_));
    149     stop_inaudible_playback_timer_.Start(
    150         FROM_HERE,
    151         inaudible_transmit->end_time - base::Time::Now(),
    152         this,
    153         &AudioDirectiveHandler::ProcessNextTransmit);
    154   } else if (!inaudible_transmit && player_inaudible_->IsPlaying()) {
    155     DVLOG(3) << "Stopping inaudible playback.";
    156     current_token_inaudible_.clear();
    157     stop_inaudible_playback_timer_.Stop();
    158     player_inaudible_->Stop();
    159   }
    160 }
    161 
    162 void AudioDirectiveHandler::ProcessNextReceive() {
    163   scoped_ptr<AudioDirective> receive(receives_list_.GetActiveDirective());
    164 
    165   if (receive && !recorder_->IsRecording()) {
    166     DVLOG(3) << "Recording for op_id: " << receive->op_id;
    167     recorder_->Record();
    168     stop_recording_timer_.Start(FROM_HERE,
    169                                 receive->end_time - base::Time::Now(),
    170                                 this,
    171                                 &AudioDirectiveHandler::ProcessNextReceive);
    172   } else if (!receive && recorder_->IsRecording()) {
    173     DVLOG(3) << "Stopping Recording";
    174     stop_recording_timer_.Stop();
    175     recorder_->Stop();
    176   }
    177 }
    178 
    179 void AudioDirectiveHandler::PlayToken(const std::string token, bool audible) {
    180   std::string valid_token = FromUrlSafe(token);
    181 
    182   // If the token has been encoded already, use the cached samples.
    183   if (audible && samples_cache_audible_.HasKey(valid_token)) {
    184     current_token_audible_ = token;
    185     ProcessNextTransmit();
    186   } else if (!audible && samples_cache_inaudible_.HasKey(valid_token)) {
    187     current_token_inaudible_ = token;
    188     ProcessNextTransmit();
    189   } else {
    190     // Otherwise, encode the token and then play it.
    191     encode_cb_.Run(valid_token,
    192                    audible,
    193                    base::Bind(&AudioDirectiveHandler::PlayEncodedToken,
    194                               base::Unretained(this)));
    195   }
    196 }
    197 
    198 void AudioDirectiveHandler::PlayEncodedToken(
    199     const std::string& token,
    200     bool audible,
    201     const scoped_refptr<media::AudioBusRefCounted>& samples) {
    202   DVLOG(3) << "Token " << token << "[audible:" << audible << "] encoded.";
    203   if (audible) {
    204     samples_cache_audible_.Add(token, samples);
    205     current_token_audible_ = token;
    206     // Force process transmits to pick up the new token.
    207     if (player_audible_->IsPlaying())
    208       player_audible_->Stop();
    209   } else {
    210     samples_cache_inaudible_.Add(token, samples);
    211     current_token_inaudible_ = token;
    212     // Force process transmits to pick up the new token.
    213     if (player_inaudible_->IsPlaying())
    214       player_inaudible_->Stop();
    215   }
    216 
    217   ProcessNextTransmit();
    218 }
    219 
    220 }  // namespace copresence
    221