1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2020 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "modules/audioplayer/SkAudioPlayer.h"
9*c8dee2aaSAndroid Build Coastguard Worker
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkData.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "oboe/Oboe.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "stream/MemInputStream.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "wav/WavStreamReader.h"
14*c8dee2aaSAndroid Build Coastguard Worker
15*c8dee2aaSAndroid Build Coastguard Worker namespace {
16*c8dee2aaSAndroid Build Coastguard Worker
17*c8dee2aaSAndroid Build Coastguard Worker class OboeAudioPlayer final : public SkAudioPlayer, oboe::AudioStreamCallback {
18*c8dee2aaSAndroid Build Coastguard Worker public:
OboeAudioPlayer(sk_sp<SkData> data)19*c8dee2aaSAndroid Build Coastguard Worker explicit OboeAudioPlayer(sk_sp<SkData> data)
20*c8dee2aaSAndroid Build Coastguard Worker : fData(std::move(data))
21*c8dee2aaSAndroid Build Coastguard Worker , fMemInputStream(const_cast<unsigned char *>
22*c8dee2aaSAndroid Build Coastguard Worker (static_cast<const unsigned char *>(fData->data())), static_cast<int32_t>(fData->size()))
23*c8dee2aaSAndroid Build Coastguard Worker {
24*c8dee2aaSAndroid Build Coastguard Worker // wrap data in MemInputStream to parse WAV header
25*c8dee2aaSAndroid Build Coastguard Worker fReader = std::make_unique<parselib::WavStreamReader>(&fMemInputStream);
26*c8dee2aaSAndroid Build Coastguard Worker fReader->parse();
27*c8dee2aaSAndroid Build Coastguard Worker // set member variables and builder properties using reader
28*c8dee2aaSAndroid Build Coastguard Worker fNumSampleFrames = fReader->getNumSampleFrames();
29*c8dee2aaSAndroid Build Coastguard Worker
30*c8dee2aaSAndroid Build Coastguard Worker oboe::AudioStreamBuilder builder;
31*c8dee2aaSAndroid Build Coastguard Worker builder.setPerformanceMode(oboe::PerformanceMode::LowLatency);
32*c8dee2aaSAndroid Build Coastguard Worker builder.setSharingMode(oboe::SharingMode::Exclusive);
33*c8dee2aaSAndroid Build Coastguard Worker builder.setSampleRate(fReader->getSampleRate());
34*c8dee2aaSAndroid Build Coastguard Worker builder.setChannelCount(fReader->getNumChannels());
35*c8dee2aaSAndroid Build Coastguard Worker builder.setCallback(this);
36*c8dee2aaSAndroid Build Coastguard Worker builder.setFormat(oboe::AudioFormat::Float);
37*c8dee2aaSAndroid Build Coastguard Worker
38*c8dee2aaSAndroid Build Coastguard Worker // open the stream (must manually close it when done)
39*c8dee2aaSAndroid Build Coastguard Worker fStream = nullptr;
40*c8dee2aaSAndroid Build Coastguard Worker builder.openStream(fStream);
41*c8dee2aaSAndroid Build Coastguard Worker }
42*c8dee2aaSAndroid Build Coastguard Worker
43*c8dee2aaSAndroid Build Coastguard Worker private:
44*c8dee2aaSAndroid Build Coastguard Worker oboe::DataCallbackResult
onAudioReady(oboe::AudioStream * oboeStream,void * audioData,int32_t numFrames)45*c8dee2aaSAndroid Build Coastguard Worker onAudioReady(oboe::AudioStream *oboeStream, void *audioData, int32_t numFrames) override {
46*c8dee2aaSAndroid Build Coastguard Worker // we assume float samples here
47*c8dee2aaSAndroid Build Coastguard Worker float *outBuffer = static_cast<float *>(audioData);
48*c8dee2aaSAndroid Build Coastguard Worker int framesRead = fReader->getDataFloat(outBuffer, numFrames);
49*c8dee2aaSAndroid Build Coastguard Worker fReadFrameIndex += framesRead;
50*c8dee2aaSAndroid Build Coastguard Worker int remainingFrames = numFrames - framesRead;
51*c8dee2aaSAndroid Build Coastguard Worker if (remainingFrames > 0) {
52*c8dee2aaSAndroid Build Coastguard Worker if (fIsLooping) {
53*c8dee2aaSAndroid Build Coastguard Worker // handle wrap around
54*c8dee2aaSAndroid Build Coastguard Worker fReader->positionToAudio();
55*c8dee2aaSAndroid Build Coastguard Worker fReader->getDataFloat(&outBuffer[framesRead * fReader->getNumChannels()],
56*c8dee2aaSAndroid Build Coastguard Worker remainingFrames);
57*c8dee2aaSAndroid Build Coastguard Worker fReadFrameIndex += remainingFrames;
58*c8dee2aaSAndroid Build Coastguard Worker } else {
59*c8dee2aaSAndroid Build Coastguard Worker // render silence for rest
60*c8dee2aaSAndroid Build Coastguard Worker renderSilence(&outBuffer[framesRead * fReader->getNumChannels()], remainingFrames);
61*c8dee2aaSAndroid Build Coastguard Worker return oboe::DataCallbackResult::Stop;
62*c8dee2aaSAndroid Build Coastguard Worker }
63*c8dee2aaSAndroid Build Coastguard Worker }
64*c8dee2aaSAndroid Build Coastguard Worker return oboe::DataCallbackResult::Continue;
65*c8dee2aaSAndroid Build Coastguard Worker }
66*c8dee2aaSAndroid Build Coastguard Worker
renderSilence(float * start,int numFrames)67*c8dee2aaSAndroid Build Coastguard Worker void renderSilence(float *start, int numFrames) {
68*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < numFrames * fReader->getNumChannels(); ++i) {
69*c8dee2aaSAndroid Build Coastguard Worker start[i] = 0;
70*c8dee2aaSAndroid Build Coastguard Worker }
71*c8dee2aaSAndroid Build Coastguard Worker }
onGetDuration() const72*c8dee2aaSAndroid Build Coastguard Worker double onGetDuration() const override {
73*c8dee2aaSAndroid Build Coastguard Worker return fNumSampleFrames * fStream->getChannelCount() / fStream->getSampleRate();
74*c8dee2aaSAndroid Build Coastguard Worker }
75*c8dee2aaSAndroid Build Coastguard Worker
onGetTime() const76*c8dee2aaSAndroid Build Coastguard Worker double onGetTime() const override {
77*c8dee2aaSAndroid Build Coastguard Worker return (fReadFrameIndex * fStream->getChannelCount()) / fStream->getSampleRate();
78*c8dee2aaSAndroid Build Coastguard Worker }
79*c8dee2aaSAndroid Build Coastguard Worker
onSetTime(double t)80*c8dee2aaSAndroid Build Coastguard Worker double onSetTime(double t) override {
81*c8dee2aaSAndroid Build Coastguard Worker fReadFrameIndex = (t * fStream->getSampleRate()) / fStream->getChannelCount();
82*c8dee2aaSAndroid Build Coastguard Worker return onGetTime();
83*c8dee2aaSAndroid Build Coastguard Worker }
84*c8dee2aaSAndroid Build Coastguard Worker
onSetState(State state)85*c8dee2aaSAndroid Build Coastguard Worker State onSetState(State state) override {
86*c8dee2aaSAndroid Build Coastguard Worker switch (state) {
87*c8dee2aaSAndroid Build Coastguard Worker case State::kPlaying: fStream->start(); break;
88*c8dee2aaSAndroid Build Coastguard Worker case State::kStopped: fStream->close(); break;
89*c8dee2aaSAndroid Build Coastguard Worker case State::kPaused : fStream->pause(); break;
90*c8dee2aaSAndroid Build Coastguard Worker }
91*c8dee2aaSAndroid Build Coastguard Worker
92*c8dee2aaSAndroid Build Coastguard Worker return state;
93*c8dee2aaSAndroid Build Coastguard Worker }
94*c8dee2aaSAndroid Build Coastguard Worker
95*c8dee2aaSAndroid Build Coastguard Worker
96*c8dee2aaSAndroid Build Coastguard Worker // TODO: implement rate function (change sample rate of AudioStream)
onSetRate(float r)97*c8dee2aaSAndroid Build Coastguard Worker float onSetRate(float r) override {
98*c8dee2aaSAndroid Build Coastguard Worker return r;
99*c8dee2aaSAndroid Build Coastguard Worker }
100*c8dee2aaSAndroid Build Coastguard Worker
101*c8dee2aaSAndroid Build Coastguard Worker // TODO: implement volume function (multiply each sample by desired amplitude)
onSetVolume(float v)102*c8dee2aaSAndroid Build Coastguard Worker float onSetVolume(float v) override {
103*c8dee2aaSAndroid Build Coastguard Worker return v;
104*c8dee2aaSAndroid Build Coastguard Worker }
105*c8dee2aaSAndroid Build Coastguard Worker
106*c8dee2aaSAndroid Build Coastguard Worker const sk_sp<SkData> fData;
107*c8dee2aaSAndroid Build Coastguard Worker std::shared_ptr<oboe::AudioStream> fStream;
108*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<parselib::WavStreamReader> fReader;
109*c8dee2aaSAndroid Build Coastguard Worker parselib::MemInputStream fMemInputStream;
110*c8dee2aaSAndroid Build Coastguard Worker int32_t fReadFrameIndex {0};
111*c8dee2aaSAndroid Build Coastguard Worker int fNumSampleFrames;
112*c8dee2aaSAndroid Build Coastguard Worker bool fIsLooping {false};
113*c8dee2aaSAndroid Build Coastguard Worker };
114*c8dee2aaSAndroid Build Coastguard Worker
115*c8dee2aaSAndroid Build Coastguard Worker } // namespace
116*c8dee2aaSAndroid Build Coastguard Worker
Make(sk_sp<SkData> src)117*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<SkAudioPlayer> SkAudioPlayer::Make(sk_sp<SkData> src) {
118*c8dee2aaSAndroid Build Coastguard Worker return std::unique_ptr<SkAudioPlayer>(new OboeAudioPlayer(std::move(src)));
119*c8dee2aaSAndroid Build Coastguard Worker }
120