blob: 6e5769237a4d9493bbe20a05b16f567446e1d736 [file] [log] [blame]
/*
* Copyright 2020 Google Inc.
*
* 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)));
}