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