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

Side by Side Diff: examples/audio_play_test/play_tone.cc

Issue 1406393004: Add two demos which exercise the audio server. (Closed) Base URL: https://github.com/domokit/mojo.git@change6
Patch Set: fix android build Created 5 years, 1 month 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
« no previous file with comments | « examples/audio_play_test/BUILD.gn ('k') | examples/audio_play_test/play_wav.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2015 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 <math.h>
6 #include <memory>
7
8 #include "mojo/public/c/system/main.h"
9 #include "mojo/public/cpp/application/application_delegate.h"
10 #include "mojo/public/cpp/application/application_impl.h"
11 #include "mojo/public/cpp/application/application_runner.h"
12 #include "mojo/public/cpp/utility/run_loop.h"
13 #include "mojo/services/media/audio/interfaces/audio_server.mojom.h"
14 #include "mojo/services/media/audio/interfaces/audio_track.mojom.h"
15 #include "mojo/services/media/common/cpp/circular_buffer_media_pipe_adapter.h"
16 #include "mojo/services/media/common/cpp/linear_transform.h"
17 #include "mojo/services/media/common/cpp/local_time.h"
18 #include "mojo/services/media/common/interfaces/rate_control.mojom.h"
19
20 namespace mojo {
21 namespace media {
22 namespace audio {
23 namespace examples {
24
25 static constexpr uint32_t SAMP_FREQ = 48000;
26 static constexpr uint32_t CHUNK_USEC = 1000;
27 static constexpr uint32_t BUF_LO_WATER_USEC = 50000;
28 static constexpr uint32_t BUF_HI_WATER_USEC = BUF_LO_WATER_USEC
29 + (4 * CHUNK_USEC);
30 static constexpr uint32_t BUF_DEPTH_USEC = BUF_HI_WATER_USEC
31 + (4 * CHUNK_USEC);
32 static constexpr uint32_t FRAME_BYTES = sizeof(int16_t);
33
34 static inline constexpr uint32_t USecToBytes(uint64_t usec) {
35 return ((usec * SAMP_FREQ) / 1000000) * FRAME_BYTES;
36 }
37
38 class PlayToneApp : public ApplicationDelegate {
39 public:
40 void Initialize(ApplicationImpl* app) override;
41
42 private:
43 bool GenerateToneCbk(MediaResult res);
44 void PlayTone(double freq_hz, double amplitude, double duration_sec);
45 void Cleanup();
46
47 AudioServerPtr audio_server_;
48 AudioTrackPtr audio_track_;
49 RateControlPtr rate_control_;
50 std::unique_ptr<CircularBufferMediaPipeAdapter> pipe_;
51
52 bool clock_started_ = false;
53 uint64_t media_time_ = 0;
54 double freq_hz_ = 440.0;
55 double amplitude_ = 1.0;
56 };
57
58 void PlayToneApp::Initialize(ApplicationImpl* app) {
59 MediaResult result = MediaResult::UNKNOWN_ERROR;
60
61 app->ConnectToService("mojo:audio_server", &audio_server_);
62 audio_server_->CreateTrack(GetProxy(&audio_track_));
63
64 // Query the sink's format capabilities.
65 AudioTrackDescriptorPtr sink_desc;
66 auto desc_cbk = [&sink_desc](AudioTrackDescriptorPtr desc) {
67 sink_desc = desc.Pass();
68 };
69 audio_track_->Describe(AudioTrack::DescribeCallback(desc_cbk));
70
71 // TODO(johngro): this pattern is awkward. We really don't want to be
72 // calling WaitForIncomingResponse, even if we were able supply a timeout.
73 // The best practice would be to defer to a handler for the message we are
74 // expecting to eventually come back.
75 //
76 // But... what if the message never comes back? Perhaps the service is not
77 // implemented properly, or perhaps the service is malicious. We could
78 // queue a delayed message on our run loop which indicates a timeout, but
79 // then what happens when when the response to Describe comes back (as
80 // expected). We don't really have a good way to cancel the "timeout"
81 // message once we have queued it. Maintaining all of the bookkeeping
82 // required to nerf the callback when it happens and is discovered to be
83 // useless is going to get very old, very fast.
84 //
85 // For now, we just do the evil thing and block during init, but I sure do
86 // wish there was something nicer we could do.
87 if (!audio_track_.WaitForIncomingResponse()) {
88 MOJO_LOG(ERROR)
89 << "Failed to fetch sync capabilities; no response received.";
90 Cleanup();
91 return;
92 }
93
94 // TODO(johngro): do something useful with our capabilities description.
95 sink_desc.reset();
96
97 // Grab the rate control interface for our audio renderer.
98 auto get_rc_cbk = [&result](MediaResult res) { result = res; };
99 audio_track_->GetRateControl(GetProxy(&rate_control_), get_rc_cbk);
100 if (!audio_track_.WaitForIncomingResponse()) {
101 MOJO_LOG(ERROR) <<
102 "Failed to fetch rate control interface; no response received.";
103 Cleanup();
104 return;
105 }
106
107 if (result != MediaResult::OK) {
108 MOJO_LOG(ERROR) << "Failed to get rate control interface. (res = "
109 << result << ")";
110 Cleanup();
111 return;
112 }
113
114 // Configure our sink for 16-bit 48KHz mono.
115 AudioTrackConfigurationPtr cfg = AudioTrackConfiguration::New();
116 cfg->max_frames = USecToBytes(BUF_DEPTH_USEC) / FRAME_BYTES;
117
118 LpcmMediaTypeDetailsPtr pcm_cfg = LpcmMediaTypeDetails::New();
119 pcm_cfg->sample_format = LpcmSampleFormat::SIGNED_16;
120 pcm_cfg->samples_per_frame = 1;
121 pcm_cfg->frames_per_second = SAMP_FREQ;
122
123 cfg->media_type = MediaType::New();
124 cfg->media_type->scheme = MediaTypeScheme::LPCM;
125 cfg->media_type->details = MediaTypeDetails::New();
126 cfg->media_type->details->set_lpcm(pcm_cfg.Pass());
127
128 MediaPipePtr pipe;
129 {
130 auto cbk = [&result](MediaResult res) {
131 result = res;
132 };
133 audio_track_->Configure(cfg.Pass(), GetProxy(&pipe), cbk);
134 }
135
136 if (!audio_track_.WaitForIncomingResponse()) {
137 MOJO_LOG(ERROR) << "Failed to configure sink; no response received.";
138 Cleanup();
139 return;
140 }
141
142 if (result != MediaResult::OK) {
143 MOJO_LOG(ERROR) << "Failed to configure sink. (res = "
144 << result << ")";
145 Cleanup();
146 return;
147 }
148
149 // Now that we are configured and have our media pipe, pass its interface to
150 // our circular buffer helper, set up our high/low water marks, register our
151 // callback, and start to buffer our audio.
152 pipe_.reset(new CircularBufferMediaPipeAdapter(pipe.Pass()));
153 pipe_->SetSignalCallback(
154 [this](MediaResult res) -> bool {
155 return GenerateToneCbk(res);
156 });
157 pipe_->SetWatermarks(USecToBytes(BUF_HI_WATER_USEC),
158 USecToBytes(BUF_LO_WATER_USEC));
159 }
160
161 bool PlayToneApp::GenerateToneCbk(MediaResult res) {
162 using MappedPacket = CircularBufferMediaPipeAdapter::MappedPacket;
163 MappedPacket mapped_pkt;
164
165 MOJO_DCHECK(freq_hz_ > 0.0);
166 MOJO_DCHECK(amplitude_ >= 0.0);
167 MOJO_DCHECK(amplitude_ <= 1.0);
168
169 if (res != MediaResult::OK) {
170 MOJO_LOG(ERROR) << "Fatal error in cbuf (" << res << ").";
171 Cleanup();
172 return false;
173 }
174
175 while (!pipe_->AboveHiWater()) {
176 res = pipe_->CreateMediaPacket(USecToBytes(CHUNK_USEC),
177 false,
178 &mapped_pkt);
179 if (res != MediaResult::OK) {
180 MOJO_LOG(ERROR) << "Unexpected error when creating media packet ("
181 << res << ").";
182 Cleanup();
183 return false;
184 }
185
186 mapped_pkt.packet()->pts = media_time_;
187
188 for (uint32_t i = 0; i < MappedPacket::kMaxRegions; ++i) {
189 int16_t* data = reinterpret_cast<int16_t*>(mapped_pkt.data(i));
190 uint64_t len;
191
192 if (!data) continue;
193 len = mapped_pkt.length(i);
194
195 MOJO_DCHECK(len && !(len % FRAME_BYTES));
196 len /= FRAME_BYTES;
197 for (uint64_t i = 0; i < len; ++i, ++media_time_) {
198 double tmp = ((M_PI * 2.0) / SAMP_FREQ) * freq_hz_ * media_time_;
199 data[i] = std::numeric_limits<int16_t>::max() * amplitude_ * sin(tmp);
200 }
201 }
202
203 res = pipe_->SendMediaPacket(&mapped_pkt);
204 if (res != MediaResult::OK) {
205 MOJO_LOG(ERROR) << "Unexpected error when sending media packet ("
206 << res << ").";
207 pipe_->CancelMediaPacket(&mapped_pkt);
208 Cleanup();
209 return false;
210 }
211 }
212
213 if (!clock_started_) {
214 // In theory, this could be done at compile time using std::ratio, but
215 // std::ratio is prohibited.
216 LinearTransform::Ratio audio_rate(SAMP_FREQ, 1);
217 LinearTransform::Ratio local_time_rate(LocalDuration::period::num,
218 LocalDuration::period::den);
219 LinearTransform::Ratio rate;
220 bool success = LinearTransform::Ratio::Compose(local_time_rate,
221 audio_rate,
222 &rate);
223 MOJO_DCHECK(success); // assert that there was no loss of precision.
224
225 MOJO_LOG(INFO) << "Setting rate " << rate;
226
227 rate_control_->SetRate(rate.numerator, rate.denominator);
228 clock_started_ = true;
229 }
230
231 return true;
232 }
233
234 void PlayToneApp::Cleanup() {
235 audio_track_.reset();
236 audio_server_.reset();
237 RunLoop::current()->Quit();
238 }
239
240 } // namespace examples
241 } // namespace audio
242 } // namespace media
243 } // namespace mojo
244
245 MojoResult MojoMain(MojoHandle app_request) {
246 mojo::ApplicationRunner runner(new mojo::media::audio::examples::PlayToneApp);
247 return runner.Run(app_request);
248 }
OLDNEW
« no previous file with comments | « examples/audio_play_test/BUILD.gn ('k') | examples/audio_play_test/play_wav.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698