audio player (for mac)
Change-Id: I491df748d22dfa566000c265a61f62f81755e5a0 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/309792 Reviewed-by: Florin Malita <fmalita@chromium.org> Commit-Queue: Mike Reed <reed@google.com>
This commit is contained in:
parent
984d49667a
commit
8a8be22597
7
BUILD.gn
7
BUILD.gn
@ -459,6 +459,7 @@ optional("fontmgr_mac_ct") {
|
||||
# AppKit symbols NSFontWeightXXX may be dlsym'ed.
|
||||
"AppKit.framework",
|
||||
"ApplicationServices.framework",
|
||||
"AVFoundation.framework",
|
||||
]
|
||||
}
|
||||
|
||||
@ -2500,6 +2501,7 @@ if (skia_enable_tools) {
|
||||
test_app("viewer") {
|
||||
is_shared_library = is_android
|
||||
sources = [
|
||||
"src/ports/SkAudioPlayer.cpp",
|
||||
"tools/viewer/AnimTimer.h",
|
||||
"tools/viewer/BisectSlide.cpp",
|
||||
"tools/viewer/GMSlide.cpp",
|
||||
@ -2552,6 +2554,11 @@ if (skia_enable_tools) {
|
||||
"//third_party/spirv-tools:spvtools_val",
|
||||
]
|
||||
}
|
||||
if (is_mac) {
|
||||
sources += [ "src/ports/SkAudioPlayer_mac.mm" ]
|
||||
} else {
|
||||
sources += [ "src/ports/SkAudioPlayer_none.cpp" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@ samples_sources = [
|
||||
"$_samplecode/SampleAnimatedText.cpp",
|
||||
"$_samplecode/SampleArc.cpp",
|
||||
"$_samplecode/SampleAtlas.cpp",
|
||||
"$_samplecode/SampleAudio.cpp",
|
||||
"$_samplecode/SampleBackdropBounds.cpp",
|
||||
"$_samplecode/SampleBitmapRect.cpp",
|
||||
"$_samplecode/SampleCamera.cpp",
|
||||
|
63
include/ports/SkAudioPlayer.h
Normal file
63
include/ports/SkAudioPlayer.h
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2020 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "include/core/SkData.h"
|
||||
#include <memory>
|
||||
|
||||
class SK_API SkAudioPlayer {
|
||||
public:
|
||||
virtual ~SkAudioPlayer();
|
||||
|
||||
// Returns null on failure (possibly unknown format?)
|
||||
static std::unique_ptr<SkAudioPlayer> Make(sk_sp<SkData>);
|
||||
|
||||
// in seconds
|
||||
double duration() const { return this->onGetDuration(); }
|
||||
double time() const { return this->onGetTime(); } // 0...duration()
|
||||
double setTime(double); // returns actual time
|
||||
|
||||
double normalizedTime() const { return this->time() / this->duration(); }
|
||||
double setNormalizedTime(double t);
|
||||
|
||||
enum class State {
|
||||
kPlaying,
|
||||
kStopped,
|
||||
kPaused,
|
||||
};
|
||||
State state() const { return fState; }
|
||||
float volume() const { return fVolume; } // 0...1
|
||||
float rate() const { return fRate; } // multiplier (e.g. 1.0 is default)
|
||||
|
||||
State setState(State); // returns actual State
|
||||
float setRate(float); // returns actual rate
|
||||
float setVolume(float); // returns actual volume
|
||||
|
||||
void play() { this->setState(State::kPlaying); }
|
||||
void pause() { this->setState(State::kPaused); }
|
||||
void stop() { this->setState(State::kStopped); }
|
||||
|
||||
bool isPlaying() const { return this->state() == State::kPlaying; }
|
||||
bool isPaused() const { return this->state() == State::kPaused; }
|
||||
bool isStopped() const { return this->state() == State::kStopped; }
|
||||
|
||||
protected:
|
||||
SkAudioPlayer() {} // only called by subclasses
|
||||
|
||||
virtual double onGetDuration() const = 0;
|
||||
virtual double onGetTime() const = 0;
|
||||
virtual double onSetTime(double) = 0;
|
||||
|
||||
virtual State onSetState(State) = 0;
|
||||
virtual float onSetRate(float) = 0;
|
||||
virtual float onSetVolume(float) = 0;
|
||||
|
||||
private:
|
||||
State fState = State::kStopped;
|
||||
float fRate = 1.0f;
|
||||
float fVolume = 1.0f;
|
||||
};
|
95
samplecode/SampleAudio.cpp
Normal file
95
samplecode/SampleAudio.cpp
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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 "include/core/SkCanvas.h"
|
||||
#include "include/core/SkData.h"
|
||||
#include "include/ports/SkAudioPlayer.h"
|
||||
#include "samplecode/Sample.h"
|
||||
#include "src/core/SkUtils.h"
|
||||
#include "tools/Resources.h"
|
||||
|
||||
class AudioView : public Sample {
|
||||
std::unique_ptr<SkAudioPlayer> fPlayer;
|
||||
SkRect fBar;
|
||||
|
||||
public:
|
||||
AudioView() {}
|
||||
|
||||
protected:
|
||||
SkString name() override { return SkString("Audio"); }
|
||||
|
||||
void onOnceBeforeDraw() override {
|
||||
auto data = SkData::MakeFromFileName("/Users/reed/skia/mp3/scott-joplin-peacherine-rag.mp3");
|
||||
if (data) {
|
||||
fPlayer = SkAudioPlayer::Make(data);
|
||||
|
||||
SkDebugf("make: dur:%g time%g state:%d",
|
||||
fPlayer->duration(),
|
||||
fPlayer->time(),
|
||||
fPlayer->state());
|
||||
}
|
||||
|
||||
fBar = { 10, 10, 510, 30 };
|
||||
}
|
||||
|
||||
void onDrawContent(SkCanvas* canvas) override {
|
||||
if (!fPlayer) {
|
||||
return;
|
||||
}
|
||||
|
||||
SkPaint p;
|
||||
p.setColor(0xFFCCCCCC);
|
||||
canvas->drawRect(fBar, p);
|
||||
|
||||
p.setColor(fPlayer->isPlaying() ? SK_ColorBLUE : 0xFF8888FF);
|
||||
SkRect r = fBar;
|
||||
r.fRight = r.fLeft + (float)fPlayer->normalizedTime() * r.width();
|
||||
canvas->drawRect(r, p);
|
||||
}
|
||||
|
||||
bool onChar(SkUnichar c) override {
|
||||
if (c == ' ') {
|
||||
switch (fPlayer->state()) {
|
||||
case SkAudioPlayer::State::kPlaying: fPlayer->pause(); break;
|
||||
case SkAudioPlayer::State::kPaused: fPlayer->play(); break;
|
||||
case SkAudioPlayer::State::kStopped: fPlayer->play(); break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return this->INHERITED::onChar(c);
|
||||
}
|
||||
|
||||
Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) override {
|
||||
if (fPlayer && fBar.contains(x, y)) {
|
||||
bool wasPlaying = fPlayer->isPlaying();
|
||||
if (wasPlaying) {
|
||||
fPlayer->pause();
|
||||
}
|
||||
return new Click([this, wasPlaying](Click* click) {
|
||||
if (fBar.contains(click->fCurr.fX, click->fCurr.fY)) {
|
||||
fPlayer->setNormalizedTime((click->fCurr.fX - fBar.fLeft) / fBar.width());
|
||||
}
|
||||
|
||||
if (click->fState == skui::InputState::kUp) {
|
||||
if (wasPlaying) {
|
||||
fPlayer->play();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool onAnimate(double /*nanos*/) override {
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
typedef Sample INHERITED;
|
||||
};
|
||||
DEF_SAMPLE( return new AudioView; )
|
57
src/ports/SkAudioPlayer.cpp
Normal file
57
src/ports/SkAudioPlayer.cpp
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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 "include/ports/SkAudioPlayer.h"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
SkAudioPlayer::~SkAudioPlayer() {}
|
||||
|
||||
double SkAudioPlayer::setTime(double t) {
|
||||
t = std::min(std::max(t, 0.0), this->duration());
|
||||
if (!std::isfinite(t)) {
|
||||
t = this->time();
|
||||
}
|
||||
if (t != this->time()) {
|
||||
t = this->onSetTime(t);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
double SkAudioPlayer::setNormalizedTime(double t) {
|
||||
this->setTime(t * this->duration());
|
||||
return this->normalizedTime();
|
||||
}
|
||||
|
||||
SkAudioPlayer::State SkAudioPlayer::setState(State s) {
|
||||
if (s != fState) {
|
||||
fState = this->onSetState(s);
|
||||
}
|
||||
return fState;
|
||||
}
|
||||
|
||||
float SkAudioPlayer::setRate(float r) {
|
||||
r = std::min(std::max(r, 0.f), 1.f);
|
||||
if (!std::isfinite(r)) {
|
||||
r = fRate;
|
||||
}
|
||||
if (r != fRate) {
|
||||
fRate = this->onSetRate(r);
|
||||
}
|
||||
return fRate;
|
||||
}
|
||||
|
||||
float SkAudioPlayer::setVolume(float v) {
|
||||
v = std::min(std::max(v, 0.f), 1.f);
|
||||
if (!std::isfinite(v)) {
|
||||
v = fVolume;
|
||||
}
|
||||
if (v != fVolume) {
|
||||
fVolume = this->onSetVolume(v);
|
||||
}
|
||||
return fVolume;
|
||||
}
|
83
src/ports/SkAudioPlayer_mac.mm
Normal file
83
src/ports/SkAudioPlayer_mac.mm
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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 "include/core/SkData.h"
|
||||
#include "include/ports/SkAudioPlayer.h"
|
||||
|
||||
#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
|
||||
|
||||
#ifdef SK_BUILD_FOR_MAC
|
||||
#include <AVFoundation/AVFoundation.h>
|
||||
#endif
|
||||
|
||||
#ifdef SK_BUILD_FOR_IOS
|
||||
// ???
|
||||
#endif
|
||||
|
||||
class SkAudioPlayer_Mac : public SkAudioPlayer {
|
||||
public:
|
||||
SkAudioPlayer_Mac(AVAudioPlayer* player, sk_sp<SkData> data)
|
||||
: fPlayer(player)
|
||||
, fData(std::move(data))
|
||||
{
|
||||
fPlayer.enableRate = YES;
|
||||
[fPlayer prepareToPlay];
|
||||
}
|
||||
|
||||
~SkAudioPlayer_Mac() override {
|
||||
// [fPlayer release];
|
||||
}
|
||||
|
||||
double onGetDuration() const override { return [fPlayer duration]; }
|
||||
|
||||
double onGetTime() const override { return fPlayer.currentTime; }
|
||||
|
||||
double onSetTime(double t) override {
|
||||
bool wasPlaying = this->isPlaying();
|
||||
if (wasPlaying) {
|
||||
[fPlayer pause];
|
||||
}
|
||||
fPlayer.currentTime = t;
|
||||
if (wasPlaying) {
|
||||
[fPlayer play];
|
||||
}
|
||||
return fPlayer.currentTime;
|
||||
}
|
||||
|
||||
|
||||
State onSetState(State state) override {
|
||||
switch (state) {
|
||||
case State::kPlaying: [fPlayer play]; break;
|
||||
case State::kStopped: [fPlayer stop]; break;
|
||||
case State::kPaused: [fPlayer pause]; break;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
float onSetRate(float r) override { fPlayer.rate = r; return r; }
|
||||
|
||||
float onSetVolume(float v) override { fPlayer.volume = v; return v; }
|
||||
|
||||
private:
|
||||
AVAudioPlayer* fPlayer;
|
||||
sk_sp<SkData> fData; // we hold this onbehalf of the player's NSData
|
||||
};
|
||||
|
||||
std::unique_ptr<SkAudioPlayer> SkAudioPlayer::Make(sk_sp<SkData> src) {
|
||||
// The NSData does not own the actual buffer (src), but our subclass will
|
||||
|
||||
NSData* data = [[NSData alloc] initWithBytesNoCopy:(void*)src->data() length:src->size()];
|
||||
AVAudioPlayer* player = [[AVAudioPlayer alloc] initWithData:data error:nil];
|
||||
[data release];
|
||||
|
||||
if (player) {
|
||||
return std::unique_ptr<SkAudioPlayer>(new SkAudioPlayer_Mac(player, std::move(src)));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#endif
|
13
src/ports/SkAudioPlayer_none.cpp
Normal file
13
src/ports/SkAudioPlayer_none.cpp
Normal file
@ -0,0 +1,13 @@
|
||||
/*
|
||||
* 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 "include/core/SkData.h"
|
||||
#include "include/ports/SkAudioPlayer.h"
|
||||
|
||||
std::unique_ptr<SkAudioPlayer> SkAudioPlayer::Make(sk_sp<SkData> src) {
|
||||
return nullptr;
|
||||
}
|
Loading…
Reference in New Issue
Block a user