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

Unified 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 side-by-side diff with in-line comments
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 »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: examples/audio_play_test/play_tone.cc
diff --git a/examples/audio_play_test/play_tone.cc b/examples/audio_play_test/play_tone.cc
new file mode 100644
index 0000000000000000000000000000000000000000..8b65ef68246644a4ad858d65f4d07156fa54056b
--- /dev/null
+++ b/examples/audio_play_test/play_tone.cc
@@ -0,0 +1,248 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <math.h>
+#include <memory>
+
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/application/application_runner.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+#include "mojo/services/media/audio/interfaces/audio_server.mojom.h"
+#include "mojo/services/media/audio/interfaces/audio_track.mojom.h"
+#include "mojo/services/media/common/cpp/circular_buffer_media_pipe_adapter.h"
+#include "mojo/services/media/common/cpp/linear_transform.h"
+#include "mojo/services/media/common/cpp/local_time.h"
+#include "mojo/services/media/common/interfaces/rate_control.mojom.h"
+
+namespace mojo {
+namespace media {
+namespace audio {
+namespace examples {
+
+static constexpr uint32_t SAMP_FREQ = 48000;
+static constexpr uint32_t CHUNK_USEC = 1000;
+static constexpr uint32_t BUF_LO_WATER_USEC = 50000;
+static constexpr uint32_t BUF_HI_WATER_USEC = BUF_LO_WATER_USEC
+ + (4 * CHUNK_USEC);
+static constexpr uint32_t BUF_DEPTH_USEC = BUF_HI_WATER_USEC
+ + (4 * CHUNK_USEC);
+static constexpr uint32_t FRAME_BYTES = sizeof(int16_t);
+
+static inline constexpr uint32_t USecToBytes(uint64_t usec) {
+ return ((usec * SAMP_FREQ) / 1000000) * FRAME_BYTES;
+}
+
+class PlayToneApp : public ApplicationDelegate {
+ public:
+ void Initialize(ApplicationImpl* app) override;
+
+ private:
+ bool GenerateToneCbk(MediaResult res);
+ void PlayTone(double freq_hz, double amplitude, double duration_sec);
+ void Cleanup();
+
+ AudioServerPtr audio_server_;
+ AudioTrackPtr audio_track_;
+ RateControlPtr rate_control_;
+ std::unique_ptr<CircularBufferMediaPipeAdapter> pipe_;
+
+ bool clock_started_ = false;
+ uint64_t media_time_ = 0;
+ double freq_hz_ = 440.0;
+ double amplitude_ = 1.0;
+};
+
+void PlayToneApp::Initialize(ApplicationImpl* app) {
+ MediaResult result = MediaResult::UNKNOWN_ERROR;
+
+ app->ConnectToService("mojo:audio_server", &audio_server_);
+ audio_server_->CreateTrack(GetProxy(&audio_track_));
+
+ // Query the sink's format capabilities.
+ AudioTrackDescriptorPtr sink_desc;
+ auto desc_cbk = [&sink_desc](AudioTrackDescriptorPtr desc) {
+ sink_desc = desc.Pass();
+ };
+ audio_track_->Describe(AudioTrack::DescribeCallback(desc_cbk));
+
+ // TODO(johngro): this pattern is awkward. We really don't want to be
+ // calling WaitForIncomingResponse, even if we were able supply a timeout.
+ // The best practice would be to defer to a handler for the message we are
+ // expecting to eventually come back.
+ //
+ // But... what if the message never comes back? Perhaps the service is not
+ // implemented properly, or perhaps the service is malicious. We could
+ // queue a delayed message on our run loop which indicates a timeout, but
+ // then what happens when when the response to Describe comes back (as
+ // expected). We don't really have a good way to cancel the "timeout"
+ // message once we have queued it. Maintaining all of the bookkeeping
+ // required to nerf the callback when it happens and is discovered to be
+ // useless is going to get very old, very fast.
+ //
+ // For now, we just do the evil thing and block during init, but I sure do
+ // wish there was something nicer we could do.
+ if (!audio_track_.WaitForIncomingResponse()) {
+ MOJO_LOG(ERROR)
+ << "Failed to fetch sync capabilities; no response received.";
+ Cleanup();
+ return;
+ }
+
+ // TODO(johngro): do something useful with our capabilities description.
+ sink_desc.reset();
+
+ // Grab the rate control interface for our audio renderer.
+ auto get_rc_cbk = [&result](MediaResult res) { result = res; };
+ audio_track_->GetRateControl(GetProxy(&rate_control_), get_rc_cbk);
+ if (!audio_track_.WaitForIncomingResponse()) {
+ MOJO_LOG(ERROR) <<
+ "Failed to fetch rate control interface; no response received.";
+ Cleanup();
+ return;
+ }
+
+ if (result != MediaResult::OK) {
+ MOJO_LOG(ERROR) << "Failed to get rate control interface. (res = "
+ << result << ")";
+ Cleanup();
+ return;
+ }
+
+ // Configure our sink for 16-bit 48KHz mono.
+ AudioTrackConfigurationPtr cfg = AudioTrackConfiguration::New();
+ cfg->max_frames = USecToBytes(BUF_DEPTH_USEC) / FRAME_BYTES;
+
+ LpcmMediaTypeDetailsPtr pcm_cfg = LpcmMediaTypeDetails::New();
+ pcm_cfg->sample_format = LpcmSampleFormat::SIGNED_16;
+ pcm_cfg->samples_per_frame = 1;
+ pcm_cfg->frames_per_second = SAMP_FREQ;
+
+ cfg->media_type = MediaType::New();
+ cfg->media_type->scheme = MediaTypeScheme::LPCM;
+ cfg->media_type->details = MediaTypeDetails::New();
+ cfg->media_type->details->set_lpcm(pcm_cfg.Pass());
+
+ MediaPipePtr pipe;
+ {
+ auto cbk = [&result](MediaResult res) {
+ result = res;
+ };
+ audio_track_->Configure(cfg.Pass(), GetProxy(&pipe), cbk);
+ }
+
+ if (!audio_track_.WaitForIncomingResponse()) {
+ MOJO_LOG(ERROR) << "Failed to configure sink; no response received.";
+ Cleanup();
+ return;
+ }
+
+ if (result != MediaResult::OK) {
+ MOJO_LOG(ERROR) << "Failed to configure sink. (res = "
+ << result << ")";
+ Cleanup();
+ return;
+ }
+
+ // Now that we are configured and have our media pipe, pass its interface to
+ // our circular buffer helper, set up our high/low water marks, register our
+ // callback, and start to buffer our audio.
+ pipe_.reset(new CircularBufferMediaPipeAdapter(pipe.Pass()));
+ pipe_->SetSignalCallback(
+ [this](MediaResult res) -> bool {
+ return GenerateToneCbk(res);
+ });
+ pipe_->SetWatermarks(USecToBytes(BUF_HI_WATER_USEC),
+ USecToBytes(BUF_LO_WATER_USEC));
+}
+
+bool PlayToneApp::GenerateToneCbk(MediaResult res) {
+ using MappedPacket = CircularBufferMediaPipeAdapter::MappedPacket;
+ MappedPacket mapped_pkt;
+
+ MOJO_DCHECK(freq_hz_ > 0.0);
+ MOJO_DCHECK(amplitude_ >= 0.0);
+ MOJO_DCHECK(amplitude_ <= 1.0);
+
+ if (res != MediaResult::OK) {
+ MOJO_LOG(ERROR) << "Fatal error in cbuf (" << res << ").";
+ Cleanup();
+ return false;
+ }
+
+ while (!pipe_->AboveHiWater()) {
+ res = pipe_->CreateMediaPacket(USecToBytes(CHUNK_USEC),
+ false,
+ &mapped_pkt);
+ if (res != MediaResult::OK) {
+ MOJO_LOG(ERROR) << "Unexpected error when creating media packet ("
+ << res << ").";
+ Cleanup();
+ return false;
+ }
+
+ mapped_pkt.packet()->pts = media_time_;
+
+ for (uint32_t i = 0; i < MappedPacket::kMaxRegions; ++i) {
+ int16_t* data = reinterpret_cast<int16_t*>(mapped_pkt.data(i));
+ uint64_t len;
+
+ if (!data) continue;
+ len = mapped_pkt.length(i);
+
+ MOJO_DCHECK(len && !(len % FRAME_BYTES));
+ len /= FRAME_BYTES;
+ for (uint64_t i = 0; i < len; ++i, ++media_time_) {
+ double tmp = ((M_PI * 2.0) / SAMP_FREQ) * freq_hz_ * media_time_;
+ data[i] = std::numeric_limits<int16_t>::max() * amplitude_ * sin(tmp);
+ }
+ }
+
+ res = pipe_->SendMediaPacket(&mapped_pkt);
+ if (res != MediaResult::OK) {
+ MOJO_LOG(ERROR) << "Unexpected error when sending media packet ("
+ << res << ").";
+ pipe_->CancelMediaPacket(&mapped_pkt);
+ Cleanup();
+ return false;
+ }
+ }
+
+ if (!clock_started_) {
+ // In theory, this could be done at compile time using std::ratio, but
+ // std::ratio is prohibited.
+ LinearTransform::Ratio audio_rate(SAMP_FREQ, 1);
+ LinearTransform::Ratio local_time_rate(LocalDuration::period::num,
+ LocalDuration::period::den);
+ LinearTransform::Ratio rate;
+ bool success = LinearTransform::Ratio::Compose(local_time_rate,
+ audio_rate,
+ &rate);
+ MOJO_DCHECK(success); // assert that there was no loss of precision.
+
+ MOJO_LOG(INFO) << "Setting rate " << rate;
+
+ rate_control_->SetRate(rate.numerator, rate.denominator);
+ clock_started_ = true;
+ }
+
+ return true;
+}
+
+void PlayToneApp::Cleanup() {
+ audio_track_.reset();
+ audio_server_.reset();
+ RunLoop::current()->Quit();
+}
+
+} // namespace examples
+} // namespace audio
+} // namespace media
+} // namespace mojo
+
+MojoResult MojoMain(MojoHandle app_request) {
+ mojo::ApplicationRunner runner(new mojo::media::audio::examples::PlayToneApp);
+ return runner.Run(app_request);
+}
« 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