| /* | |
| * Copyright 2020 Google LLC | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "modules/audioplayer/SkAudioPlayer.h" | |
| #include "include/core/SkData.h" | |
| #include "oboe/Oboe.h" | |
| #include "stream/MemInputStream.h" | |
| #include "wav/WavStreamReader.h" | |
| namespace { | |
| class OboeAudioPlayer final : public SkAudioPlayer, oboe::AudioStreamCallback { | |
| public: | |
| explicit OboeAudioPlayer(sk_sp<SkData> data) | |
| : fData(std::move(data)) | |
| , fMemInputStream(const_cast<unsigned char *> | |
| (static_cast<const unsigned char *>(fData->data())), static_cast<int32_t>(fData->size())) | |
| { | |
| // wrap data in MemInputStream to parse WAV header | |
| fReader = std::make_unique<parselib::WavStreamReader>(&fMemInputStream); | |
| fReader->parse(); | |
| // set member variables and builder properties using reader | |
| fNumSampleFrames = fReader->getNumSampleFrames(); | |
| oboe::AudioStreamBuilder builder; | |
| builder.setPerformanceMode(oboe::PerformanceMode::LowLatency); | |
| builder.setSharingMode(oboe::SharingMode::Exclusive); | |
| builder.setSampleRate(fReader->getSampleRate()); | |
| builder.setChannelCount(fReader->getNumChannels()); | |
| builder.setCallback(this); | |
| builder.setFormat(oboe::AudioFormat::Float); | |
| // open the stream (must manually close it when done) | |
| fStream = nullptr; | |
| builder.openStream(fStream); | |
| } | |
| private: | |
| oboe::DataCallbackResult | |
| onAudioReady(oboe::AudioStream *oboeStream, void *audioData, int32_t numFrames) override { | |
| // we assume float samples here | |
| float *outBuffer = static_cast<float *>(audioData); | |
| int framesRead = fReader->getDataFloat(outBuffer, numFrames); | |
| fReadFrameIndex += framesRead; | |
| int remainingFrames = numFrames - framesRead; | |
| if (remainingFrames > 0) { | |
| if (fIsLooping) { | |
| // handle wrap around | |
| fReader->positionToAudio(); | |
| fReader->getDataFloat(&outBuffer[framesRead * fReader->getNumChannels()], | |
| remainingFrames); | |
| fReadFrameIndex += remainingFrames; | |
| } else { | |
| // render silence for rest | |
| renderSilence(&outBuffer[framesRead * fReader->getNumChannels()], remainingFrames); | |
| return oboe::DataCallbackResult::Stop; | |
| } | |
| } | |
| return oboe::DataCallbackResult::Continue; | |
| } | |
| void renderSilence(float *start, int numFrames) { | |
| for (int i = 0; i < numFrames * fReader->getNumChannels(); ++i) { | |
| start[i] = 0; | |
| } | |
| } | |
| double onGetDuration() const override { | |
| return fNumSampleFrames * fStream->getChannelCount() / fStream->getSampleRate(); | |
| } | |
| double onGetTime() const override { | |
| return (fReadFrameIndex * fStream->getChannelCount()) / fStream->getSampleRate(); | |
| } | |
| double onSetTime(double t) override { | |
| fReadFrameIndex = (t * fStream->getSampleRate()) / fStream->getChannelCount(); | |
| return onGetTime(); | |
| } | |
| State onSetState(State state) override { | |
| switch (state) { | |
| case State::kPlaying: fStream->start(); break; | |
| case State::kStopped: fStream->close(); break; | |
| case State::kPaused : fStream->pause(); break; | |
| } | |
| return state; | |
| } | |
| // TODO: implement rate function (change sample rate of AudioStream) | |
| float onSetRate(float r) override { | |
| return r; | |
| } | |
| // TODO: implement volume function (multiply each sample by desired amplitude) | |
| float onSetVolume(float v) override { | |
| return v; | |
| } | |
| const sk_sp<SkData> fData; | |
| std::shared_ptr<oboe::AudioStream> fStream; | |
| std::unique_ptr<parselib::WavStreamReader> fReader; | |
| parselib::MemInputStream fMemInputStream; | |
| int32_t fReadFrameIndex {0}; | |
| int fNumSampleFrames; | |
| bool fIsLooping {false}; | |
| }; | |
| } // namespace | |
| std::unique_ptr<SkAudioPlayer> SkAudioPlayer::Make(sk_sp<SkData> src) { | |
| return std::unique_ptr<SkAudioPlayer>(new OboeAudioPlayer(std::move(src))); | |
| } |