Implements playing music.
This commit is contained in:
parent
7a13e62bf1
commit
ea6fbd572e
@ -146,6 +146,7 @@
|
|||||||
</Link>
|
</Link>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ClCompile Include="src\AudioDevice.cc" />
|
||||||
<ClCompile Include="src\Font.cc" />
|
<ClCompile Include="src\Font.cc" />
|
||||||
<ClCompile Include="src\Game.cc" />
|
<ClCompile Include="src\Game.cc" />
|
||||||
<ClCompile Include="src\HiraganaMatcher.cc" />
|
<ClCompile Include="src\HiraganaMatcher.cc" />
|
||||||
@ -158,6 +159,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="src\HiraganaMatcher.h" />
|
<ClInclude Include="src\HiraganaMatcher.h" />
|
||||||
|
<ClInclude Include="src\iAudioDevice.h" />
|
||||||
<ClInclude Include="src\iInputMatcher.h" />
|
<ClInclude Include="src\iInputMatcher.h" />
|
||||||
<ClInclude Include="src\common.h" />
|
<ClInclude Include="src\common.h" />
|
||||||
<ClInclude Include="src\ElementStore.h" />
|
<ClInclude Include="src\ElementStore.h" />
|
||||||
@ -175,6 +177,9 @@
|
|||||||
<ClInclude Include="src\iWritable.h" />
|
<ClInclude Include="src\iWritable.h" />
|
||||||
<ClInclude Include="src\Logger.h" />
|
<ClInclude Include="src\Logger.h" />
|
||||||
<ClInclude Include="src\Lua.h" />
|
<ClInclude Include="src\Lua.h" />
|
||||||
|
<ClInclude Include="src\MusicElement.h" />
|
||||||
|
<ClInclude Include="src\AudioDevice.h" />
|
||||||
|
<ClInclude Include="src\MusicElementFactory.h" />
|
||||||
<ClInclude Include="src\OffsetClock.h" />
|
<ClInclude Include="src\OffsetClock.h" />
|
||||||
<ClInclude Include="src\Period.h" />
|
<ClInclude Include="src\Period.h" />
|
||||||
<ClInclude Include="src\PlayScene.h" />
|
<ClInclude Include="src\PlayScene.h" />
|
||||||
@ -198,6 +203,7 @@
|
|||||||
<ClInclude Include="thirdparty\lua.hpp" />
|
<ClInclude Include="thirdparty\lua.hpp" />
|
||||||
<ClInclude Include="thirdparty\luaconf.h" />
|
<ClInclude Include="thirdparty\luaconf.h" />
|
||||||
<ClInclude Include="thirdparty\lualib.h" />
|
<ClInclude Include="thirdparty\lualib.h" />
|
||||||
|
<ClInclude Include="thirdparty\miniaudio.h" />
|
||||||
<ClInclude Include="thirdparty\stb_truetype.h" />
|
<ClInclude Include="thirdparty\stb_truetype.h" />
|
||||||
<ClInclude Include="src\Win32Console.h" />
|
<ClInclude Include="src\Win32Console.h" />
|
||||||
<ClInclude Include="thirdparty\utf8.h" />
|
<ClInclude Include="thirdparty\utf8.h" />
|
||||||
|
@ -45,6 +45,9 @@
|
|||||||
<ClCompile Include="src\ResultScene.cc">
|
<ClCompile Include="src\ResultScene.cc">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="src\AudioDevice.cc">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="src\iConsole.h">
|
<ClInclude Include="src\iConsole.h">
|
||||||
@ -179,6 +182,21 @@
|
|||||||
<ClInclude Include="src\ResultScene.h">
|
<ClInclude Include="src\ResultScene.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="thirdparty\miniaudio.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="src\MusicElement.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="src\AudioDevice.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="src\iAudioDevice.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="src\MusicElementFactory.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Library Include="thirdparty\lua5.1.lib" />
|
<Library Include="thirdparty\lua5.1.lib" />
|
||||||
|
69
src/AudioDevice.cc
Normal file
69
src/AudioDevice.cc
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#define MINIAUDIO_IMPLEMENTATION
|
||||||
|
#include "AudioDevice.h"
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
|
||||||
|
gj::AudioDevice::AudioDevice() {
|
||||||
|
ma_device_config config = ma_device_config_init(ma_device_type_playback);
|
||||||
|
config.playback.format = kFormat;
|
||||||
|
config.playback.channels = kChannel;
|
||||||
|
config.sampleRate = kSampleRate;
|
||||||
|
config.dataCallback = Callback_;
|
||||||
|
config.pUserData = this;
|
||||||
|
|
||||||
|
if (ma_device_init(nullptr, &config, &ma_) != MA_SUCCESS) {
|
||||||
|
Abort("AudioDevice error");
|
||||||
|
}
|
||||||
|
ma_device_start(&ma_);
|
||||||
|
}
|
||||||
|
|
||||||
|
gj::AudioDevice::~AudioDevice() {
|
||||||
|
ma_device_stop(&ma_);
|
||||||
|
ma_device_uninit(&ma_);
|
||||||
|
|
||||||
|
/* the worker thread has been exited so no lock is necessary anymore */
|
||||||
|
|
||||||
|
if (playing_) {
|
||||||
|
ma_decoder_uninit(&dec_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gj::AudioDevice::PlayMusic(const std::string& path) {
|
||||||
|
std::lock_guard _(mtx_);
|
||||||
|
|
||||||
|
if (playing_) {
|
||||||
|
ma_decoder_uninit(&dec_);
|
||||||
|
}
|
||||||
|
ma_decoder_config config = ma_decoder_config_init(kFormat, kChannel, kSampleRate);
|
||||||
|
if (ma_decoder_init_file(path.c_str(), &config, &dec_) != MA_SUCCESS) {
|
||||||
|
Abort("AudioDevice decoder error: "+path);
|
||||||
|
}
|
||||||
|
playing_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gj::AudioDevice::StopMusic() {
|
||||||
|
std::lock_guard _(mtx_);
|
||||||
|
if (!playing_) return;
|
||||||
|
|
||||||
|
ma_decoder_uninit(&dec_);
|
||||||
|
playing_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gj::AudioDevice::Callback_(ma_device* ma, void* out, const void* in, ma_uint32 framecnt) {
|
||||||
|
AudioDevice* dev = reinterpret_cast<AudioDevice*>(ma->pUserData);
|
||||||
|
std::lock_guard _(dev->mtx_);
|
||||||
|
|
||||||
|
float* dst = reinterpret_cast<float*>(out);
|
||||||
|
|
||||||
|
size_t wrote = 0;
|
||||||
|
if (dev->playing_) {
|
||||||
|
const ma_uint64 n = ma_decoder_read_pcm_frames(&dev->dec_, dst, framecnt);
|
||||||
|
if (n < framecnt) {
|
||||||
|
dev->playing_ = false;
|
||||||
|
ma_decoder_uninit(&dev->dec_);
|
||||||
|
}
|
||||||
|
wrote += n;
|
||||||
|
}
|
||||||
|
dev->time_.fetch_add(framecnt);
|
||||||
|
}
|
56
src/AudioDevice.h
Normal file
56
src/AudioDevice.h
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#define NOMINMAX /* miniaudio includes windows.h */
|
||||||
|
#include "thirdparty/miniaudio.h"
|
||||||
|
#undef NOMINMAX
|
||||||
|
|
||||||
|
#include "iAudioDevice.h"
|
||||||
|
#include "iClock.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace gj {
|
||||||
|
|
||||||
|
|
||||||
|
class AudioDevice : public iAudioDevice, public iClock {
|
||||||
|
public:
|
||||||
|
static constexpr auto kFormat = ma_format_f32;
|
||||||
|
static constexpr auto kChannel = 2;
|
||||||
|
static constexpr auto kSampleRate = 48000;
|
||||||
|
|
||||||
|
AudioDevice(AudioDevice&&) = default;
|
||||||
|
AudioDevice(const AudioDevice&) = default;
|
||||||
|
|
||||||
|
AudioDevice& operator=(AudioDevice&&) = default;
|
||||||
|
AudioDevice& operator=(const AudioDevice&) = default;
|
||||||
|
|
||||||
|
AudioDevice();
|
||||||
|
~AudioDevice();
|
||||||
|
|
||||||
|
void PlayMusic(const std::string& path) override;
|
||||||
|
void StopMusic() override;
|
||||||
|
|
||||||
|
uint64_t now() const override {
|
||||||
|
return time_.load() * 1000 / kSampleRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::mutex mtx_;
|
||||||
|
|
||||||
|
ma_device ma_{0};
|
||||||
|
|
||||||
|
bool playing_ = false;
|
||||||
|
ma_decoder dec_{0};
|
||||||
|
|
||||||
|
std::atomic<uint64_t> time_;
|
||||||
|
|
||||||
|
static void Callback_(ma_device* ma, void* out, const void* in, ma_uint32 framecnt);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -11,9 +11,7 @@ gj::Game::Game(gj::Game::Param&& p) :
|
|||||||
gj::PlayScene::Param param;
|
gj::PlayScene::Param param;
|
||||||
param.alloc = alloc_;
|
param.alloc = alloc_;
|
||||||
param.clock = &clock_;
|
param.clock = &clock_;
|
||||||
param.logger = &logger_;
|
param.audio = p.audio;
|
||||||
param.w = w_;
|
|
||||||
param.h = h_;
|
|
||||||
param.score = "test"; /* TODO test */
|
param.score = "test"; /* TODO test */
|
||||||
scene_ = alloc_->MakeUniq<gj::iScene, gj::PlayScene>(std::move(param));
|
scene_ = alloc_->MakeUniq<gj::iScene, gj::PlayScene>(std::move(param));
|
||||||
}
|
}
|
@ -5,6 +5,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "Frame.h"
|
#include "Frame.h"
|
||||||
|
#include "iAudioDevice.h"
|
||||||
#include "iDrawable.h"
|
#include "iDrawable.h"
|
||||||
#include "iWritable.h"
|
#include "iWritable.h"
|
||||||
#include "iScene.h"
|
#include "iScene.h"
|
||||||
@ -22,6 +23,7 @@ class Game : public iDrawable, public iWritable {
|
|||||||
|
|
||||||
struct Param {
|
struct Param {
|
||||||
iAllocator* alloc;
|
iAllocator* alloc;
|
||||||
|
iAudioDevice* audio;
|
||||||
const iClock* clock;
|
const iClock* clock;
|
||||||
|
|
||||||
uint32_t w, h;
|
uint32_t w, h;
|
||||||
@ -44,6 +46,7 @@ class Game : public iDrawable, public iWritable {
|
|||||||
|
|
||||||
UniqPtr<iScene> next = scene_->Update(frame_);
|
UniqPtr<iScene> next = scene_->Update(frame_);
|
||||||
if (next) {
|
if (next) {
|
||||||
|
/* This could cause an empty frame during scene change but I think it's not so big problem. */
|
||||||
scene_ = std::move(next);
|
scene_ = std::move(next);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -58,6 +61,7 @@ class Game : public iDrawable, public iWritable {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
iAllocator* alloc_;
|
iAllocator* alloc_;
|
||||||
|
|
||||||
TickingClock clock_;
|
TickingClock clock_;
|
||||||
|
|
||||||
Logger logger_;
|
Logger logger_;
|
||||||
|
47
src/MusicElement.h
Normal file
47
src/MusicElement.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#include "iAudioDevice.h"
|
||||||
|
#include "iElement.h"
|
||||||
|
#include "iElementDriver.h"
|
||||||
|
#include "Texture.h"
|
||||||
|
|
||||||
|
namespace gj {
|
||||||
|
|
||||||
|
|
||||||
|
class MusicElement : public iElement {
|
||||||
|
public:
|
||||||
|
MusicElement() = delete;
|
||||||
|
MusicElement(MusicElement&&) = delete;
|
||||||
|
MusicElement(const MusicElement&) = delete;
|
||||||
|
|
||||||
|
MusicElement& operator=(MusicElement&&) = delete;
|
||||||
|
MusicElement& operator=(const MusicElement&) = delete;
|
||||||
|
|
||||||
|
MusicElement(const Period& p, iAudioDevice* audio, const std::string& name) :
|
||||||
|
iElement(p), audio_(audio), path_("res/music/"+name) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void Update(Frame& frame, double t) override {
|
||||||
|
if (first_) {
|
||||||
|
audio_->PlayMusic(path_);
|
||||||
|
first_ = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Finalize() override {
|
||||||
|
if (!first_) {
|
||||||
|
audio_->StopMusic();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
iAudioDevice* audio_;
|
||||||
|
std::string path_;
|
||||||
|
|
||||||
|
bool first_ = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
38
src/MusicElementFactory.h
Normal file
38
src/MusicElementFactory.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "iAllocator.h"
|
||||||
|
#include "iAudioDevice.h"
|
||||||
|
#include "iElementFactory.h"
|
||||||
|
#include "MusicElement.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace gj {
|
||||||
|
|
||||||
|
class MusicElementFactory : public iElementFactory {
|
||||||
|
public:
|
||||||
|
MusicElementFactory(MusicElementFactory&&) = delete;
|
||||||
|
MusicElementFactory(const MusicElementFactory&) = delete;
|
||||||
|
|
||||||
|
MusicElementFactory& operator=(MusicElementFactory&&) = delete;
|
||||||
|
MusicElementFactory& operator=(const MusicElementFactory&) = delete;
|
||||||
|
|
||||||
|
MusicElementFactory(iAllocator* alloc, iAudioDevice* audio) :
|
||||||
|
alloc_(alloc), audio_(audio) {
|
||||||
|
}
|
||||||
|
|
||||||
|
UniqPtr<iElement> Create(Param&& param) override {
|
||||||
|
if (param.custom.size() != 1) return nullptr;
|
||||||
|
|
||||||
|
const std::string name = std::get<std::string>(param.custom[0]);
|
||||||
|
|
||||||
|
return alloc_->MakeUniq<iElement, MusicElement>(param.period, audio_, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
iAllocator* alloc_;
|
||||||
|
iAudioDevice* audio_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -3,26 +3,27 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "GlyphElementFactory.h"
|
#include "GlyphElementFactory.h"
|
||||||
#include "InputWindowElementFactory.h"
|
#include "InputWindowElementFactory.h"
|
||||||
|
#include "MusicElementFactory.h"
|
||||||
#include "ResultScene.h"
|
#include "ResultScene.h"
|
||||||
|
|
||||||
|
|
||||||
gj::PlayScene::PlayScene(Param&& p) :
|
gj::PlayScene::PlayScene(Param&& p) :
|
||||||
alloc_(p.alloc), logger_(p.logger), w_(p.w), h_(p.h),
|
alloc_(p.alloc), audio_(p.audio),
|
||||||
clock_(p.clock), store_(&clock_, 256) {
|
clock_(p.clock), store_(&clock_, 256) {
|
||||||
|
|
||||||
GlyphElementFactory glyph(alloc_);
|
GlyphElementFactory glyph(alloc_);
|
||||||
InputWindowElementFactory inputWin(alloc_, &sb_);
|
InputWindowElementFactory inputWin(alloc_, &sb_);
|
||||||
|
MusicElementFactory music(alloc_, audio_);
|
||||||
|
|
||||||
sb_.title = ConvertStrToWstr(p.score);
|
sb_.title = ConvertStrToWstr(p.score);
|
||||||
|
|
||||||
Lua::FactoryMap map;
|
Lua::FactoryMap map = {
|
||||||
map["Glyph"] = &glyph;
|
{ "Glyph", &glyph },
|
||||||
map["InputWin"] = &inputWin;
|
{ "InputWin", &inputWin },
|
||||||
|
{ "Music", &music },
|
||||||
|
};
|
||||||
lua_ = alloc_->MakeUniq<Lua>(
|
lua_ = alloc_->MakeUniq<Lua>(
|
||||||
alloc_, &store_, map, "res/score/" + p.score + ".lua");
|
alloc_, &store_, map, "res/score/" + p.score + ".lua");
|
||||||
|
|
||||||
logger_->Print(L"PlayScene init");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include "ElementStore.h"
|
#include "ElementStore.h"
|
||||||
#include "Frame.h"
|
#include "Frame.h"
|
||||||
#include "iAllocator.h"
|
#include "iAllocator.h"
|
||||||
|
#include "iAudioDevice.h"
|
||||||
#include "iLogger.h"
|
#include "iLogger.h"
|
||||||
#include "iScene.h"
|
#include "iScene.h"
|
||||||
#include "Lua.h"
|
#include "Lua.h"
|
||||||
@ -18,11 +19,9 @@ class PlayScene : public iScene {
|
|||||||
public:
|
public:
|
||||||
struct Param {
|
struct Param {
|
||||||
iAllocator* alloc;
|
iAllocator* alloc;
|
||||||
iLogger* logger;
|
iAudioDevice* audio;
|
||||||
const iClock* clock;
|
const iClock* clock;
|
||||||
|
|
||||||
uint32_t w, h;
|
|
||||||
|
|
||||||
std::string score;
|
std::string score;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -39,9 +38,7 @@ class PlayScene : public iScene {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
iAllocator* alloc_;
|
iAllocator* alloc_;
|
||||||
iLogger* logger_;
|
iAudioDevice* audio_;
|
||||||
|
|
||||||
uint32_t w_, h_;
|
|
||||||
|
|
||||||
OffsetClock clock_;
|
OffsetClock clock_;
|
||||||
|
|
||||||
|
26
src/iAudioDevice.h
Normal file
26
src/iAudioDevice.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
|
||||||
|
namespace gj {
|
||||||
|
|
||||||
|
|
||||||
|
class iAudioDevice {
|
||||||
|
public:
|
||||||
|
iAudioDevice(iAudioDevice&&) = default;
|
||||||
|
iAudioDevice(const iAudioDevice&) = default;
|
||||||
|
|
||||||
|
iAudioDevice& operator=(iAudioDevice&&) = default;
|
||||||
|
iAudioDevice& operator=(const iAudioDevice&) = default;
|
||||||
|
|
||||||
|
iAudioDevice() = default;
|
||||||
|
|
||||||
|
virtual ~iAudioDevice() = default;
|
||||||
|
|
||||||
|
virtual void PlayMusic(const std::string& path) = 0;
|
||||||
|
virtual void StopMusic() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -6,6 +6,7 @@
|
|||||||
#undef NOMINMAX
|
#undef NOMINMAX
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "AudioDevice.h"
|
||||||
#include "Font.h"
|
#include "Font.h"
|
||||||
#include "Game.h"
|
#include "Game.h"
|
||||||
#include "LinearAllocator.h"
|
#include "LinearAllocator.h"
|
||||||
@ -25,12 +26,16 @@ int main() {
|
|||||||
gj::Win32Console console(&alloc, kWidth, kHeight);
|
gj::Win32Console console(&alloc, kWidth, kHeight);
|
||||||
console.Show();
|
console.Show();
|
||||||
|
|
||||||
|
gj::AudioDevice audio;
|
||||||
|
|
||||||
gj::Game::Param param;
|
gj::Game::Param param;
|
||||||
param.alloc = &alloc;
|
param.alloc = &alloc;
|
||||||
param.clock = &gj::SystemClock::instance();
|
param.clock = &audio;
|
||||||
|
param.audio = &audio;
|
||||||
param.w = kWidth;
|
param.w = kWidth;
|
||||||
param.h = kHeight;
|
param.h = kHeight;
|
||||||
gj::Game game(std::move(param));
|
gj::Game game(std::move(param));
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
game.Update(console.TakeInput());
|
game.Update(console.TakeInput());
|
||||||
{
|
{
|
||||||
|
70273
thirdparty/miniaudio.h
vendored
Normal file
70273
thirdparty/miniaudio.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user