Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(575)

Side by Side Diff: media/audio/sounds/audio_stream_handler.cc

Issue 115693004: Added volume adjust sound behind the flag. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Split of WavAudioHandler is rolled back. Created 7 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "media/audio/sounds/audio_stream_handler.h" 5 #include "media/audio/sounds/audio_stream_handler.h"
6 6
7 #include <string> 7 #include <string>
8 8
9 #include "base/cancelable_callback.h"
9 #include "base/logging.h" 10 #include "base/logging.h"
10 #include "base/message_loop/message_loop_proxy.h" 11 #include "base/message_loop/message_loop_proxy.h"
12 #include "base/time/time.h"
11 #include "media/audio/audio_manager.h" 13 #include "media/audio/audio_manager.h"
12 #include "media/audio/audio_manager_base.h" 14 #include "media/audio/audio_manager_base.h"
13 #include "media/base/channel_layout.h" 15 #include "media/base/channel_layout.h"
14 16
15 namespace media { 17 namespace media {
16 18
17 namespace { 19 namespace {
18 20
19 // Volume percent. 21 // Volume percent.
20 const double kOutputVolumePercent = 0.8; 22 const double kOutputVolumePercent = 0.8;
21 23
22 // The number of frames each OnMoreData() call will request. 24 // Delay between end of sound and call to AudioOutputStream::Stop().
23 const int kDefaultFrameCount = 1024; 25 const int kStopStreamDelaySec = 3;
24 26
25 AudioStreamHandler::TestObserver* g_observer_for_testing = NULL; 27 AudioStreamHandler::TestObserver* g_observer_for_testing = NULL;
26 AudioOutputStream::AudioSourceCallback* g_audio_source_for_testing = NULL; 28 AudioOutputStream::AudioSourceCallback* g_audio_source_for_testing = NULL;
27 29
28 } // namespace 30 } // namespace
29 31
30 class AudioStreamHandler::AudioStreamContainer 32 class AudioStreamHandler::AudioStreamContainer
31 : public AudioOutputStream::AudioSourceCallback { 33 : public AudioOutputStream::AudioSourceCallback {
32 public: 34 public:
33 AudioStreamContainer(const WavAudioHandler& wav_audio, 35 AudioStreamContainer(const WavAudioHandler& wav_audio)
34 const AudioParameters& params)
35 : stream_(NULL), 36 : stream_(NULL),
36 wav_audio_(wav_audio), 37 wav_audio_(wav_audio),
37 params_(params), 38 cursor_(0),
38 cursor_(0) { 39 replay_scheduled_(false),
DaleCurtis 2013/12/18 21:20:21 I think instead of all this replay_scheduled_ and
ygorshenin1 2013/12/19 15:42:29 * added a lock around internal state; * is_stream_
DaleCurtis 2013/12/19 19:44:08 I'm only suggesting the timer is used to Stop() th
ygorshenin1 2013/12/20 09:05:05 You don't need to press a key 5 times per second,
39 } 40 is_stream_idle_(true),
41 schedule_replay_request_for_testing_(0) {}
40 42
41 virtual ~AudioStreamContainer() { 43 virtual ~AudioStreamContainer() {
42 DCHECK(AudioManager::Get()->GetMessageLoop()->BelongsToCurrentThread()); 44 DCHECK(AudioManager::Get()->GetMessageLoop()->BelongsToCurrentThread());
45 stop_stream_closure_.Cancel();
46 StopAndCloseStream();
43 } 47 }
44 48
45 void Play() { 49 void Play() {
46 DCHECK(AudioManager::Get()->GetMessageLoop()->BelongsToCurrentThread()); 50 DCHECK(AudioManager::Get()->GetMessageLoop()->BelongsToCurrentThread());
47 51
48 if (!stream_) { 52 if (!stream_) {
49 stream_ = AudioManager::Get()->MakeAudioOutputStreamProxy(params_, 53 stream_ = AudioManager::Get()->MakeAudioOutputStreamProxy(
50 std::string(), 54 wav_audio_.params(), std::string(), std::string());
51 std::string());
52 if (!stream_ || !stream_->Open()) { 55 if (!stream_ || !stream_->Open()) {
53 LOG(ERROR) << "Failed to open an output stream."; 56 LOG(ERROR) << "Failed to open an output stream.";
54 return; 57 return;
55 } 58 }
59 cursor_ = 0;
56 stream_->SetVolume(kOutputVolumePercent); 60 stream_->SetVolume(kOutputVolumePercent);
61 } else if (is_stream_idle_) {
62 // If stream exists and idle, just stop the stream and start again.
63 cursor_ = 0;
64 StopStream();
57 } else { 65 } else {
58 // TODO (ygorshenin@): implement smart stream rewind. 66 // Stream exists and not idle, so let the current sound to
59 stream_->Stop(); 67 // finish playing. If less than |wav_audio_.duration()| / 2
68 // until the end (including all replay requests), then replay is
69 // sheduled. The main purpose of this strategy is to smooth
70 // sound reproduction when multiple requests are made, for
71 // instance, when user presses volume(up|down) button for a long
72 // time.
73 if (ReplayRequestCouldBeScheduled())
74 replay_scheduled_ = true;
75 return;
60 } 76 }
61 77
62 cursor_ = 0; 78 last_play_start_time_ = base::TimeTicks::Now();
79 replay_scheduled_ = false;
80 is_stream_idle_ = false;
63 if (g_audio_source_for_testing) 81 if (g_audio_source_for_testing)
64 stream_->Start(g_audio_source_for_testing); 82 stream_->Start(g_audio_source_for_testing);
65 else 83 else
66 stream_->Start(this); 84 stream_->Start(this);
67 85
68 if (g_observer_for_testing) 86 if (g_observer_for_testing)
69 g_observer_for_testing->OnPlay(); 87 g_observer_for_testing->OnPlay();
70 } 88 }
71 89
72 void Stop() { 90 void Stop() {
73 DCHECK(AudioManager::Get()->GetMessageLoop()->BelongsToCurrentThread()); 91 DCHECK(AudioManager::Get()->GetMessageLoop()->BelongsToCurrentThread());
74 if (!stream_) 92 StopStream();
75 return;
76 stream_->Stop();
77 stream_->Close();
78 stream_ = NULL;
79
80 if (g_observer_for_testing)
81 g_observer_for_testing->OnStop(cursor_);
82 } 93 }
83 94
84 private: 95 private:
96 friend class AudioStreamHandler;
97
85 // AudioOutputStream::AudioSourceCallback overrides: 98 // AudioOutputStream::AudioSourceCallback overrides:
86 // Following methods could be called from *ANY* thread. 99 // Following methods could be called from *ANY* thread.
87 virtual int OnMoreData(AudioBus* dest, 100 virtual int OnMoreData(AudioBus* dest,
88 AudioBuffersState /* state */) OVERRIDE { 101 AudioBuffersState /* state */) OVERRIDE {
102 if (is_stream_idle_)
103 return 0;
104
89 size_t bytes_written = 0; 105 size_t bytes_written = 0;
90 if (wav_audio_.AtEnd(cursor_) || 106 if (wav_audio_.AtEnd(cursor_) ||
91 !wav_audio_.CopyTo(dest, cursor_, &bytes_written)) { 107 !wav_audio_.CopyTo(dest, cursor_, &bytes_written)) {
92 AudioManager::Get()->GetMessageLoop()->PostTask( 108 // If there are replay requests, |cursor_| is reset and will
93 FROM_HERE, 109 // continue to play from the start. Otherwise, stream will be
94 base::Bind(&AudioStreamContainer::Stop, base::Unretained(this))); 110 // stopped (not closed!) in |kStopStreamDelaySec| seconds,
111 // unless new Play() requests arrive during this time period.
112 if (replay_scheduled_) {
113 cursor_ = 0;
114 last_play_start_time_ = base::TimeTicks::Now();
115 replay_scheduled_ = false;
116 if (g_observer_for_testing)
117 g_observer_for_testing->OnReplay();
118 } else {
119 is_stream_idle_ = true;
120 stop_stream_closure_.Reset(base::Bind(&AudioStreamContainer::StopStream,
121 base::Unretained(this)));
122 AudioManager::Get()->GetMessageLoop()->PostDelayedTask(
123 FROM_HERE,
124 stop_stream_closure_.callback(),
125 base::TimeDelta::FromSeconds(kStopStreamDelaySec));
126 }
95 return 0; 127 return 0;
96 } 128 }
97 cursor_ += bytes_written; 129 cursor_ += bytes_written;
98 130
99 return dest->frames(); 131 return dest->frames();
100 } 132 }
101 133
102 virtual int OnMoreIOData(AudioBus* /* source */, 134 virtual int OnMoreIOData(AudioBus* /* source */,
103 AudioBus* dest, 135 AudioBus* dest,
104 AudioBuffersState state) OVERRIDE { 136 AudioBuffersState state) OVERRIDE {
105 return OnMoreData(dest, state); 137 return OnMoreData(dest, state);
106 } 138 }
107 139
108 virtual void OnError(AudioOutputStream* /* stream */) OVERRIDE { 140 virtual void OnError(AudioOutputStream* /* stream */) OVERRIDE {
109 LOG(ERROR) << "Error during system sound reproduction."; 141 LOG(ERROR) << "Error during system sound reproduction.";
110 } 142 }
111 143
144 void StopStream() {
145 stop_stream_closure_.Cancel();
146 replay_scheduled_ = false;
147 if (stream_)
148 stream_->Stop();
149 if (g_observer_for_testing)
150 g_observer_for_testing->OnStop(cursor_);
151 }
152
153 void StopAndCloseStream() {
154 StopStream();
155 if (stream_) {
156 stream_->Close();
157 stream_ = NULL;
158 }
159 }
160
161 bool ReplayRequestCouldBeScheduled() {
162 if (schedule_replay_request_for_testing_) {
163 --schedule_replay_request_for_testing_;
164 return true;
165 }
166 const base::TimeDelta delta =
167 base::TimeTicks::Now() - last_play_start_time_;
168 return 2 * delta > wav_audio_.duration() && !replay_scheduled_;
169 }
170
171 void AllowReplayOnceForTesting() {
172 ++schedule_replay_request_for_testing_;
173 }
174
112 AudioOutputStream* stream_; 175 AudioOutputStream* stream_;
113 176
114 const WavAudioHandler wav_audio_; 177 WavAudioHandler wav_audio_;
115 const AudioParameters params_;
116 178
117 size_t cursor_; 179 size_t cursor_;
118 180
181 // True if replay of the current sound is scheduled.
182 bool replay_scheduled_;
183
184 base::CancelableClosure stop_stream_closure_;
185 bool is_stream_idle_;
186
187 // Last time sound started to play from the start.
188 base::TimeTicks last_play_start_time_;
189
190 int schedule_replay_request_for_testing_;
191
119 DISALLOW_COPY_AND_ASSIGN(AudioStreamContainer); 192 DISALLOW_COPY_AND_ASSIGN(AudioStreamContainer);
120 }; 193 };
121 194
122 AudioStreamHandler::AudioStreamHandler(const base::StringPiece& wav_data) 195 AudioStreamHandler::AudioStreamHandler(const base::StringPiece& wav_data)
123 : wav_audio_(wav_data), 196 : wav_audio_(wav_data),
124 initialized_(false) { 197 initialized_(false) {
125 AudioManager* manager = AudioManager::Get(); 198 AudioManager* manager = AudioManager::Get();
126 if (!manager) { 199 if (!manager) {
127 LOG(ERROR) << "Can't get access to audio manager."; 200 LOG(ERROR) << "Can't get access to audio manager.";
128 return; 201 return;
129 } 202 }
130 AudioParameters params(AudioParameters::AUDIO_PCM_LOW_LATENCY, 203 if (!wav_audio_.params().IsValid()) {
131 GuessChannelLayout(wav_audio_.num_channels()),
132 wav_audio_.sample_rate(),
133 wav_audio_.bits_per_sample(),
134 kDefaultFrameCount);
135 if (!params.IsValid()) {
136 LOG(ERROR) << "Audio params are invalid."; 204 LOG(ERROR) << "Audio params are invalid.";
137 return; 205 return;
138 } 206 }
139 stream_.reset(new AudioStreamContainer(wav_audio_, params)); 207 stream_.reset(new AudioStreamContainer(wav_audio_));
140 initialized_ = true; 208 initialized_ = true;
141 } 209 }
142 210
143 AudioStreamHandler::~AudioStreamHandler() { 211 AudioStreamHandler::~AudioStreamHandler() {
144 DCHECK(CalledOnValidThread()); 212 DCHECK(CalledOnValidThread());
145 AudioManager::Get()->GetMessageLoop()->PostTask( 213 AudioManager::Get()->GetMessageLoop()->PostTask(
146 FROM_HERE, 214 FROM_HERE,
147 base::Bind(&AudioStreamContainer::Stop, base::Unretained(stream_.get()))); 215 base::Bind(&AudioStreamContainer::Stop, base::Unretained(stream_.get())));
148 AudioManager::Get()->GetMessageLoop()->DeleteSoon(FROM_HERE, 216 AudioManager::Get()->GetMessageLoop()->DeleteSoon(FROM_HERE,
149 stream_.release()); 217 stream_.release());
(...skipping 28 matching lines...) Expand all
178 void AudioStreamHandler::SetObserverForTesting(TestObserver* observer) { 246 void AudioStreamHandler::SetObserverForTesting(TestObserver* observer) {
179 g_observer_for_testing = observer; 247 g_observer_for_testing = observer;
180 } 248 }
181 249
182 // static 250 // static
183 void AudioStreamHandler::SetAudioSourceForTesting( 251 void AudioStreamHandler::SetAudioSourceForTesting(
184 AudioOutputStream::AudioSourceCallback* source) { 252 AudioOutputStream::AudioSourceCallback* source) {
185 g_audio_source_for_testing = source; 253 g_audio_source_for_testing = source;
186 } 254 }
187 255
256 void AudioStreamHandler::AllowReplayOnceForTesting() {
257 AudioManager::Get()->GetMessageLoop()->PostTask(
258 FROM_HERE,
259 base::Bind(&AudioStreamContainer::AllowReplayOnceForTesting,
260 base::Unretained(stream_.get())));
261 }
262
188 } // namespace media 263 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698