Initial commit
BIN
arm9/data/NotoSansJP-Medium-10.nft2
Normal file
BIN
arm9/data/NotoSansJP-Medium-11.nft2
Normal file
BIN
arm9/data/NotoSansJP-Medium-7_5.nft2
Normal file
BIN
arm9/data/NotoSansJP-Regular-10.nft2
Normal file
7
arm9/gfx/backIcon.grit
Normal file
@@ -0,0 +1,7 @@
|
||||
# tile format
|
||||
-gt
|
||||
|
||||
# graphics bit depth is 4 (16 color)
|
||||
-gB4
|
||||
|
||||
-p!
|
||||
BIN
arm9/gfx/backIcon.png
Normal file
|
After Width: | Height: | Size: 172 B |
7
arm9/gfx/bannerListIcon.grit
Normal file
@@ -0,0 +1,7 @@
|
||||
# tile format
|
||||
-gt
|
||||
|
||||
# graphics bit depth is 4 (16 color)
|
||||
-gB4
|
||||
|
||||
-p!
|
||||
BIN
arm9/gfx/bannerListIcon.png
Normal file
|
After Width: | Height: | Size: 184 B |
5
arm9/gfx/bannerListItemBg0.grit
Normal file
@@ -0,0 +1,5 @@
|
||||
# tile format
|
||||
-gt
|
||||
|
||||
# graphics bit depth is 4 (16 color)
|
||||
-gB4
|
||||
BIN
arm9/gfx/bannerListItemBg0.png
Normal file
|
After Width: | Height: | Size: 256 B |
7
arm9/gfx/bannerListItemBg1.grit
Normal file
@@ -0,0 +1,7 @@
|
||||
# tile format
|
||||
-gt
|
||||
|
||||
# graphics bit depth is 4 (16 color)
|
||||
-gB4
|
||||
|
||||
-p!
|
||||
BIN
arm9/gfx/bannerListItemBg1.png
Normal file
|
After Width: | Height: | Size: 187 B |
7
arm9/gfx/bannerListItemBg2.grit
Normal file
@@ -0,0 +1,7 @@
|
||||
# tile format
|
||||
-gt
|
||||
|
||||
# graphics bit depth is 4 (16 color)
|
||||
-gB4
|
||||
|
||||
-p!
|
||||
BIN
arm9/gfx/bannerListItemBg2.png
Normal file
|
After Width: | Height: | Size: 248 B |
18
arm9/gfx/bottomSheetBg.grit
Normal file
@@ -0,0 +1,18 @@
|
||||
# tile format
|
||||
-gt
|
||||
|
||||
# tile reduction by tiles, palette and hflip/vflip
|
||||
-mRtpf
|
||||
-ma2
|
||||
|
||||
# graphics bit depth is 4 (16 color)
|
||||
-gB4
|
||||
|
||||
-p
|
||||
-pn16
|
||||
|
||||
# map layout standard bg format
|
||||
-mLs
|
||||
|
||||
#-gzl
|
||||
#-mzl
|
||||
BIN
arm9/gfx/bottomSheetBg.png
Normal file
|
After Width: | Height: | Size: 438 B |
3
arm9/gfx/carouselMask.grit
Normal file
@@ -0,0 +1,3 @@
|
||||
-gb
|
||||
-gB8
|
||||
-p!
|
||||
BIN
arm9/gfx/carouselMask.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
5
arm9/gfx/chipFilled.grit
Normal file
@@ -0,0 +1,5 @@
|
||||
# tile format
|
||||
-gt
|
||||
|
||||
# graphics bit depth is 4 (16 color)
|
||||
-gB4
|
||||
BIN
arm9/gfx/chipFilled.png
Normal file
|
After Width: | Height: | Size: 197 B |
7
arm9/gfx/coverflowIcon.grit
Normal file
@@ -0,0 +1,7 @@
|
||||
# tile format
|
||||
-gt
|
||||
|
||||
# graphics bit depth is 4 (16 color)
|
||||
-gB4
|
||||
|
||||
-p!
|
||||
BIN
arm9/gfx/coverflowIcon.png
Normal file
|
After Width: | Height: | Size: 186 B |
3
arm9/gfx/folderCover.grit
Normal file
@@ -0,0 +1,3 @@
|
||||
-gb
|
||||
-gB8
|
||||
-p
|
||||
BIN
arm9/gfx/folderCover.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
7
arm9/gfx/gamesIcon.grit
Normal file
@@ -0,0 +1,7 @@
|
||||
# tile format
|
||||
-gt
|
||||
|
||||
# graphics bit depth is 4 (16 color)
|
||||
-gB4
|
||||
|
||||
-p!
|
||||
BIN
arm9/gfx/gamesIcon.png
Normal file
|
After Width: | Height: | Size: 227 B |
7
arm9/gfx/hGridIcon.grit
Normal file
@@ -0,0 +1,7 @@
|
||||
# tile format
|
||||
-gt
|
||||
|
||||
# graphics bit depth is 4 (16 color)
|
||||
-gB4
|
||||
|
||||
-p!
|
||||
BIN
arm9/gfx/hGridIcon.png
Normal file
|
After Width: | Height: | Size: 173 B |
7
arm9/gfx/heartIcon.grit
Normal file
@@ -0,0 +1,7 @@
|
||||
# tile format
|
||||
-gt
|
||||
|
||||
# graphics bit depth is 4 (16 color)
|
||||
-gB4
|
||||
|
||||
-p!
|
||||
BIN
arm9/gfx/heartIcon.png
Normal file
|
After Width: | Height: | Size: 226 B |
7
arm9/gfx/iconButtonSelector.grit
Normal file
@@ -0,0 +1,7 @@
|
||||
# tile format
|
||||
-gt
|
||||
|
||||
# graphics bit depth is 4 (16 color)
|
||||
-gB4
|
||||
|
||||
-p!
|
||||
BIN
arm9/gfx/iconButtonSelector.png
Normal file
|
After Width: | Height: | Size: 266 B |
3
arm9/gfx/iconButtonSelectorTexture.grit
Normal file
@@ -0,0 +1,3 @@
|
||||
-gb
|
||||
-gB8
|
||||
-p!
|
||||
BIN
arm9/gfx/iconButtonSelectorTexture.png
Normal file
|
After Width: | Height: | Size: 922 B |
7
arm9/gfx/iconCell.grit
Normal file
@@ -0,0 +1,7 @@
|
||||
# tile format
|
||||
-gt
|
||||
|
||||
# graphics bit depth is 4 (16 color)
|
||||
-gB4
|
||||
|
||||
-p!
|
||||
BIN
arm9/gfx/iconCell.png
Normal file
|
After Width: | Height: | Size: 242 B |
5
arm9/gfx/iconCell2.grit
Normal file
@@ -0,0 +1,5 @@
|
||||
# tile format
|
||||
-gt
|
||||
|
||||
# graphics bit depth is 4 (16 color)
|
||||
-gB4
|
||||
BIN
arm9/gfx/iconCell2.png
Normal file
|
After Width: | Height: | Size: 368 B |
5
arm9/gfx/iconCell3.grit
Normal file
@@ -0,0 +1,5 @@
|
||||
# tile format
|
||||
-gt
|
||||
|
||||
# graphics bit depth is 4 (16 color)
|
||||
-gB4
|
||||
BIN
arm9/gfx/iconCell3.png
Normal file
|
After Width: | Height: | Size: 325 B |
7
arm9/gfx/largeDSCardIcon.grit
Normal file
@@ -0,0 +1,7 @@
|
||||
# tile format
|
||||
-gt
|
||||
|
||||
# graphics bit depth is 4 (16 color)
|
||||
-gB4
|
||||
|
||||
-p!
|
||||
BIN
arm9/gfx/largeDSCardIcon.png
Normal file
|
After Width: | Height: | Size: 203 B |
7
arm9/gfx/largeFileIcon.grit
Normal file
@@ -0,0 +1,7 @@
|
||||
# tile format
|
||||
-gt
|
||||
|
||||
# graphics bit depth is 4 (16 color)
|
||||
-gB4
|
||||
|
||||
-p!
|
||||
BIN
arm9/gfx/largeFileIcon.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
7
arm9/gfx/largeFolderIcon.grit
Normal file
@@ -0,0 +1,7 @@
|
||||
# tile format
|
||||
-gt
|
||||
|
||||
# graphics bit depth is 4 (16 color)
|
||||
-gB4
|
||||
|
||||
-p!
|
||||
BIN
arm9/gfx/largeFolderIcon.png
Normal file
|
After Width: | Height: | Size: 204 B |
7
arm9/gfx/listIcon.grit
Normal file
@@ -0,0 +1,7 @@
|
||||
# tile format
|
||||
-gt
|
||||
|
||||
# graphics bit depth is 4 (16 color)
|
||||
-gB4
|
||||
|
||||
-p!
|
||||
BIN
arm9/gfx/listIcon.png
Normal file
|
After Width: | Height: | Size: 160 B |
13
arm9/gfx/mainBg.grit
Normal file
@@ -0,0 +1,13 @@
|
||||
# tile format
|
||||
-gt
|
||||
|
||||
# tile reduction
|
||||
-mR8
|
||||
|
||||
# graphics bit depth is 8 (256 color)
|
||||
-gB8
|
||||
|
||||
-p!
|
||||
|
||||
# map layout standard bg format
|
||||
-mLs
|
||||
BIN
arm9/gfx/mainBg.png
Normal file
|
After Width: | Height: | Size: 631 B |
7
arm9/gfx/moviesIcon.grit
Normal file
@@ -0,0 +1,7 @@
|
||||
# tile format
|
||||
-gt
|
||||
|
||||
# graphics bit depth is 4 (16 color)
|
||||
-gB4
|
||||
|
||||
-p!
|
||||
BIN
arm9/gfx/moviesIcon.png
Normal file
|
After Width: | Height: | Size: 203 B |
7
arm9/gfx/musicIcon.grit
Normal file
@@ -0,0 +1,7 @@
|
||||
# tile format
|
||||
-gt
|
||||
|
||||
# graphics bit depth is 4 (16 color)
|
||||
-gB4
|
||||
|
||||
-p!
|
||||
BIN
arm9/gfx/musicIcon.png
Normal file
|
After Width: | Height: | Size: 206 B |
7
arm9/gfx/picturesIcon.grit
Normal file
@@ -0,0 +1,7 @@
|
||||
# tile format
|
||||
-gt
|
||||
|
||||
# graphics bit depth is 4 (16 color)
|
||||
-gB4
|
||||
|
||||
-p!
|
||||
BIN
arm9/gfx/picturesIcon.png
Normal file
|
After Width: | Height: | Size: 215 B |
7
arm9/gfx/recentIcon.grit
Normal file
@@ -0,0 +1,7 @@
|
||||
# tile format
|
||||
-gt
|
||||
|
||||
# graphics bit depth is 4 (16 color)
|
||||
-gB4
|
||||
|
||||
-p!
|
||||
BIN
arm9/gfx/recentIcon.png
Normal file
|
After Width: | Height: | Size: 222 B |
18
arm9/gfx/scrim.grit
Normal file
@@ -0,0 +1,18 @@
|
||||
# tile format
|
||||
-gt
|
||||
|
||||
# tile reduction by tiles, palette and hflip/vflip
|
||||
-mRtpf
|
||||
-mp1
|
||||
|
||||
# graphics bit depth is 4 (16 color)
|
||||
-gB4
|
||||
|
||||
-p
|
||||
-pn16
|
||||
|
||||
# map layout standard bg format
|
||||
-mLs
|
||||
|
||||
#-gzl
|
||||
#-mzl
|
||||
BIN
arm9/gfx/scrim.png
Normal file
|
After Width: | Height: | Size: 285 B |
7
arm9/gfx/settingsIcon.grit
Normal file
@@ -0,0 +1,7 @@
|
||||
# tile format
|
||||
-gt
|
||||
|
||||
# graphics bit depth is 4 (16 color)
|
||||
-gB4
|
||||
|
||||
-p!
|
||||
BIN
arm9/gfx/settingsIcon.png
Normal file
|
After Width: | Height: | Size: 216 B |
7
arm9/gfx/smallHeartIcon.grit
Normal file
@@ -0,0 +1,7 @@
|
||||
# tile format
|
||||
-gt
|
||||
|
||||
# graphics bit depth is 4 (16 color)
|
||||
-gB4
|
||||
|
||||
-p!
|
||||
BIN
arm9/gfx/smallHeartIcon.png
Normal file
|
After Width: | Height: | Size: 207 B |
7
arm9/gfx/smallHeartIconFilled.grit
Normal file
@@ -0,0 +1,7 @@
|
||||
# tile format
|
||||
-gt
|
||||
|
||||
# graphics bit depth is 4 (16 color)
|
||||
-gB4
|
||||
|
||||
-p!
|
||||
BIN
arm9/gfx/smallHeartIconFilled.png
Normal file
|
After Width: | Height: | Size: 207 B |
7
arm9/gfx/sortNameAscendingIcon.grit
Normal file
@@ -0,0 +1,7 @@
|
||||
# tile format
|
||||
-gt
|
||||
|
||||
# graphics bit depth is 4 (16 color)
|
||||
-gB4
|
||||
|
||||
-p!
|
||||
BIN
arm9/gfx/sortNameAscendingIcon.png
Normal file
|
After Width: | Height: | Size: 204 B |
7
arm9/gfx/sortNameDescendingIcon.grit
Normal file
@@ -0,0 +1,7 @@
|
||||
# tile format
|
||||
-gt
|
||||
|
||||
# graphics bit depth is 4 (16 color)
|
||||
-gB4
|
||||
|
||||
-p!
|
||||
BIN
arm9/gfx/sortNameDescendingIcon.png
Normal file
|
After Width: | Height: | Size: 204 B |
6
arm9/gfx/splashTop.grit
Normal file
@@ -0,0 +1,6 @@
|
||||
-gt
|
||||
-gB8
|
||||
-mRtpf
|
||||
-mp0
|
||||
-p
|
||||
-mLs
|
||||
BIN
arm9/gfx/splashTop.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
3
arm9/gfx/unknownCover.grit
Normal file
@@ -0,0 +1,3 @@
|
||||
-gb
|
||||
-gB8
|
||||
-p
|
||||
BIN
arm9/gfx/unknownCover.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
7
arm9/gfx/unknownIcon.grit
Normal file
@@ -0,0 +1,7 @@
|
||||
# tile format
|
||||
-gt
|
||||
|
||||
# graphics bit depth is 4 (16 color)
|
||||
-gB4
|
||||
|
||||
-p!
|
||||
BIN
arm9/gfx/unknownIcon.png
Normal file
|
After Width: | Height: | Size: 227 B |
7
arm9/gfx/vGridIcon.grit
Normal file
@@ -0,0 +1,7 @@
|
||||
# tile format
|
||||
-gt
|
||||
|
||||
# graphics bit depth is 4 (16 color)
|
||||
-gB4
|
||||
|
||||
-p!
|
||||
BIN
arm9/gfx/vGridIcon.png
Normal file
|
After Width: | Height: | Size: 179 B |
519
arm9/source/App.cpp
Normal file
@@ -0,0 +1,519 @@
|
||||
#include "common.h"
|
||||
#include <algorithm>
|
||||
#include <libtwl/mem/memVram.h>
|
||||
#include <libtwl/gfx/gfx.h>
|
||||
#include <libtwl/gfx/gfxOam.h>
|
||||
#include <libtwl/gfx/gfxPalette.h>
|
||||
#include <libtwl/gfx/gfxBackground.h>
|
||||
#include <libtwl/gfx/gfxStatus.h>
|
||||
#include <libtwl/gfx/gfx3d.h>
|
||||
#include <libtwl/gfx/gfx3dCmd.h>
|
||||
#include <libtwl/sys/sysPower.h>
|
||||
#include "animation/Animator.h"
|
||||
#include "gui/materialDesign.h"
|
||||
#include "themes/material/MaterialColorSchemeFactory.h"
|
||||
#include "core/math/ColorConverter.h"
|
||||
#include "core/math/RgbMixer.h"
|
||||
#include "gui/GraphicsContext.h"
|
||||
#include "romBrowser/views/ChipView.h"
|
||||
#include "picoLoaderBootstrap.h"
|
||||
#include "romBrowser/DisplayMode/RomBrowserDisplayModeFactory.h"
|
||||
#include "romBrowser/Theme/Material/MaterialThemeFileIconFactory.h"
|
||||
#include "romBrowser/views/NdsGameDetailsBottomSheetView.h"
|
||||
#include "romBrowser/views/DisplaySettingsBottomSheetView.h"
|
||||
#include "bgm/AudioStreamPlayer.h"
|
||||
#include "bgm/BgmService.h"
|
||||
#include "themes/ThemeInfoFactory.h"
|
||||
#include "themes/ThemeFactory.h"
|
||||
#include "gui/Gx.h"
|
||||
#include "splashTop.h"
|
||||
#include "App.h"
|
||||
|
||||
#define SPLASH_FRAMES 44
|
||||
|
||||
App::App(IAppSettingsService& appSettingsService, IBgmService& bgmService)
|
||||
: _mainObjPltt(GFX_PLTT_OBJ_MAIN)
|
||||
, _mainObjVram(GFX_OBJ_MAIN)
|
||||
, _mainObjDialogVram(GFX_OBJ_MAIN, 128 * 1024)
|
||||
, _subObjVram(GFX_OBJ_SUB)
|
||||
, _textureVram((vu16*)0x06860000)
|
||||
, _texturePaletteVram((vu16*)0x6880000)
|
||||
, _mainVramContext(nullptr, &_mainObjVram, &_textureVram, &_texturePaletteVram)
|
||||
, _subVramContext(nullptr, &_subObjVram, nullptr, nullptr)
|
||||
, _appSettingsService(appSettingsService)
|
||||
, _bgmService(bgmService)
|
||||
, _inputProvider(&_inputSource)
|
||||
, _inputRepeater(&_inputProvider,
|
||||
InputKey::DpadLeft | InputKey::DpadRight | InputKey::DpadUp | InputKey::DpadDown,
|
||||
25, 8)
|
||||
, _romBrowserController(&appSettingsService, &_ioTaskQueue, &_bgTaskQueue)
|
||||
, _displaySettingsBottomSheetViewModel(&_romBrowserController)
|
||||
, _romBrowserBottomScreenViewModel(&_romBrowserController)
|
||||
, _dialogPresenter(&_focusManager, &_mainObjDialogVram) { }
|
||||
|
||||
void App::InitVramMapping() const
|
||||
{
|
||||
mem_setVramAMapping(MEM_VRAM_AB_TEX_SLOT_1);
|
||||
mem_setVramBMapping(MEM_VRAM_AB_MAIN_OBJ_00000);
|
||||
mem_setVramCMapping(MEM_VRAM_C_SUB_BG_00000);
|
||||
mem_setVramDMapping(MEM_VRAM_D_TEX_SLOT_0);
|
||||
mem_setVramEMapping(MEM_VRAM_E_TEX_PLTT_SLOT_0123);
|
||||
mem_setVramFMapping(MEM_VRAM_FG_MAIN_BG_00000);
|
||||
mem_setVramGMapping(MEM_VRAM_FG_MAIN_BG_04000);
|
||||
mem_setVramHMapping(MEM_VRAM_H_SUB_BG_EXT_PLTT_SLOT_0123);
|
||||
mem_setVramIMapping(MEM_VRAM_I_SUB_OBJ_00000);
|
||||
}
|
||||
|
||||
void App::DisplaySplashScreen() const
|
||||
{
|
||||
dma_ntrCopy32(3, splashTopTiles, GFX_BG_SUB, splashTopTilesLen);
|
||||
dma_ntrCopy32(3, splashTopMap, (u8*)GFX_BG_SUB + 0x3000, splashTopMapLen);
|
||||
mem_setVramHMapping(MEM_VRAM_H_LCDC);
|
||||
dma_ntrCopy32(3, splashTopPal, (void*)0x0689A000, splashTopPalLen);
|
||||
mem_setVramHMapping(MEM_VRAM_H_SUB_BG_EXT_PLTT_SLOT_0123);
|
||||
|
||||
VBlank::Wait();
|
||||
|
||||
sys_setMainEngineToBottomScreen();
|
||||
REG_DISPCNT_SUB = 0x40211015;
|
||||
REG_BG1HOFS_SUB = 0;
|
||||
REG_BG1VOFS_SUB = 0;
|
||||
REG_BG1CNT_SUB = 0x0680;
|
||||
REG_DISPCNT_SUB |= 1 << 9;
|
||||
REG_BLDCNT_SUB = 0x3D42;
|
||||
REG_BLDALPHA_SUB = 0x10;
|
||||
REG_MASTER_BRIGHT_SUB = 0;
|
||||
}
|
||||
|
||||
void App::LoadTheme()
|
||||
{
|
||||
ThemeInfoFactory themeInfoFactory;
|
||||
auto themeInfo = themeInfoFactory.CreateFromThemeFolder(_appSettingsService.GetAppSettings().theme);
|
||||
if (!themeInfo)
|
||||
{
|
||||
LOG_DEBUG("Failed to load theme '%s'. Using fallback theme.\n", _appSettingsService.GetAppSettings().theme.GetString());
|
||||
themeInfo = themeInfoFactory.CreateFallbackTheme();
|
||||
}
|
||||
_theme = ThemeFactory().CreateFromThemeInfo(themeInfo.get());
|
||||
themeInfo.reset();
|
||||
_theme->LoadRomBrowserResources(_mainVramContext, _subVramContext);
|
||||
_topBackground = _theme->CreateRomBrowserTopBackground();
|
||||
_topBackground->LoadResources(*_theme, _subVramContext);
|
||||
_bottomBackground = _theme->CreateRomBrowserBottomBackground();
|
||||
_bottomBackground->LoadResources(*_theme, _mainVramContext);
|
||||
|
||||
_materialThemeFileIconFactory = std::make_unique<MaterialThemeFileIconFactory>(
|
||||
&_theme->GetMaterialColorScheme(), _theme->GetFontRepository());
|
||||
}
|
||||
|
||||
void App::VCountIrq()
|
||||
{
|
||||
_mainObjPltt.VCount();
|
||||
}
|
||||
|
||||
void App::Run()
|
||||
{
|
||||
InitVramMapping();
|
||||
DisplaySplashScreen();
|
||||
gx_init();
|
||||
|
||||
_chipViewVram = ChipView::UploadGraphics(_mainObjVram);
|
||||
_iconButtonViewVram = IconButton2DView::UploadGraphics(_mainObjVram);
|
||||
|
||||
mem_setVramEMapping(MEM_VRAM_E_LCDC);
|
||||
_rgb6Palette.UploadGraphics(_mainVramContext);
|
||||
mem_setVramEMapping(MEM_VRAM_E_TEX_PLTT_SLOT_0123);
|
||||
|
||||
_dialogPresenter.InitVram();
|
||||
|
||||
LoadTheme();
|
||||
|
||||
_ioTaskQueue.StartThread(1, _ioTaskThreadStack, sizeof(_ioTaskThreadStack));
|
||||
_bgTaskQueue.StartThread(2, _bgTaskThreadStack, sizeof(_bgTaskThreadStack));
|
||||
|
||||
StoreVramState(_vramStateBeforeMakeBottomScreenView);
|
||||
|
||||
_romBrowserBottomScreenView = std::make_unique<RomBrowserBottomScreenView>(
|
||||
&_romBrowserBottomScreenViewModel,
|
||||
RomBrowserDisplayModeFactory().GetRomBrowserDisplayMode(
|
||||
_romBrowserController.GetRomBrowserDisplaySettings().layout),
|
||||
_materialThemeFileIconFactory.get(),
|
||||
_theme->GetRomBrowserViewFactory(),
|
||||
&_vblankTextureLoader);
|
||||
_romBrowserBottomScreenView->InitVram(_mainVramContext);
|
||||
|
||||
StoreVramState(_vramStateAfterMakeBottomScreenView);
|
||||
|
||||
const auto& materialColorScheme = _theme->GetMaterialColorScheme();
|
||||
|
||||
auto scrimBlendColor = Rgb<8, 8, 8>(
|
||||
materialColorScheme.inverseOnSurface.r + (materialColorScheme.scrim.r - materialColorScheme.inverseOnSurface.r) * 5 / 16,
|
||||
materialColorScheme.inverseOnSurface.g + (materialColorScheme.scrim.g - materialColorScheme.inverseOnSurface.g) * 5 / 16,
|
||||
materialColorScheme.inverseOnSurface.b + (materialColorScheme.scrim.b - materialColorScheme.inverseOnSurface.b) * 5 / 16);
|
||||
|
||||
RgbMixer::MakeGradientPalette((u16*)GFX_PLTT_BG_MAIN, scrimBlendColor, materialColorScheme.GetColor(md::sys::color::surfaceContainerLow));
|
||||
|
||||
GFX_PLTT_BG_MAIN[0] = ColorConverter::ToGBGR565(materialColorScheme.inverseOnSurface);
|
||||
GFX_PLTT_BG_MAIN[31] = ColorConverter::ToGBGR565(materialColorScheme.scrim);
|
||||
REG_DISPCNT = 0x211F1B;
|
||||
REG_BG0HOFS = 0;
|
||||
REG_BG0VOFS = 0;
|
||||
REG_BG0CNT = 3;
|
||||
|
||||
Gx::MtxMode(GX_MTX_MODE_PROJECTION);
|
||||
mtx43_t orthoMtx =
|
||||
{
|
||||
2048, 0, 0,
|
||||
0, -21845, 0,
|
||||
0, 0, 4096 >> 5,
|
||||
-4096, 4096, 0
|
||||
};
|
||||
Gx::MtxLoad43(&orthoMtx);
|
||||
|
||||
_vcountIrqStarted = false;
|
||||
rtos_disableIrqMask(RTOS_IRQ_VCOUNT);
|
||||
rtos_setIrqFunc(RTOS_IRQ_VCOUNT, [] (u32 mask) { ((App*)gProcessManager.GetRunningProcess())->VCountIrq(); });
|
||||
|
||||
LOG_DEBUG("Amount of main obj vram used: %d\n", _mainObjVram.GetState());
|
||||
|
||||
_ioTaskQueue.Enqueue([this] (const vu8& cancelRequested)
|
||||
{
|
||||
_bgmService.StartBgmFromConfig();
|
||||
return TaskResult<void>::Completed();
|
||||
});
|
||||
|
||||
_fadeAnimator = Animator(16, 0, 16, &md::sys::motion::easing::linear);
|
||||
|
||||
MainLoop();
|
||||
|
||||
_bgmService.StopBgm();
|
||||
rtos_disableIrqMask(RTOS_IRQ_VCOUNT);
|
||||
rtos_setIrqFunc(RTOS_IRQ_VCOUNT, nullptr);
|
||||
}
|
||||
|
||||
void App::MainLoop()
|
||||
{
|
||||
bool fadeIn = true;
|
||||
int fadeWaitFrames = SPLASH_FRAMES;
|
||||
while (true)
|
||||
{
|
||||
Update();
|
||||
Draw();
|
||||
VBlank::Wait();
|
||||
VBlank();
|
||||
if (_exit)
|
||||
{
|
||||
bool fadeComplete = _fadeAnimator.Update();
|
||||
REG_MASTER_BRIGHT = 0x4000 | _fadeAnimator.GetValue();
|
||||
REG_MASTER_BRIGHT_SUB = 0x4000 | _fadeAnimator.GetValue();
|
||||
if (fadeComplete)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (fadeIn)
|
||||
{
|
||||
if (fadeWaitFrames)
|
||||
{
|
||||
fadeWaitFrames--;
|
||||
REG_BLDALPHA_SUB = 16;
|
||||
REG_MASTER_BRIGHT = 0x4010;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool fadeComplete = _fadeAnimator.Update();
|
||||
if (fadeComplete)
|
||||
{
|
||||
fadeIn = false;
|
||||
REG_BLDCNT_SUB = 0;
|
||||
REG_DISPCNT_SUB &= ~(1 << 9);
|
||||
REG_MASTER_BRIGHT = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
int fade = _fadeAnimator.GetValue();
|
||||
REG_BLDALPHA_SUB = ((16 - fade) << 8) | fade;
|
||||
REG_MASTER_BRIGHT = 0x4000 | fade;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void App::Exit()
|
||||
{
|
||||
_fadeAnimator.Goto(16, 16, &md::sys::motion::easing::linear);
|
||||
_exit = true;
|
||||
}
|
||||
|
||||
void App::HandleTrigger(RomBrowserStateTrigger trigger, RomBrowserState newState)
|
||||
{
|
||||
switch (trigger)
|
||||
{
|
||||
case RomBrowserStateTrigger::None:
|
||||
case RomBrowserStateTrigger::Launch:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case RomBrowserStateTrigger::ShowGameInfo:
|
||||
{
|
||||
HandleShowGameInfoTrigger();
|
||||
break;
|
||||
}
|
||||
case RomBrowserStateTrigger::HideGameInfo:
|
||||
{
|
||||
HandleHideGameInfoTrigger();
|
||||
break;
|
||||
}
|
||||
case RomBrowserStateTrigger::ShowDisplaySettings:
|
||||
{
|
||||
HandleShowDisplaySettingsTrigger();
|
||||
break;
|
||||
}
|
||||
case RomBrowserStateTrigger::HideDisplaySettings:
|
||||
{
|
||||
HandleHideDisplaySettingsTrigger();
|
||||
break;
|
||||
}
|
||||
case RomBrowserStateTrigger::Navigate:
|
||||
{
|
||||
HandleNavigateTrigger();
|
||||
break;
|
||||
}
|
||||
case RomBrowserStateTrigger::FolderLoadDone:
|
||||
{
|
||||
HandleFolderLoadDoneTrigger();
|
||||
break;
|
||||
}
|
||||
case RomBrowserStateTrigger::ChangeDisplayMode:
|
||||
{
|
||||
_changeDisplayMode = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void App::HandleShowGameInfoTrigger()
|
||||
{
|
||||
auto gameInfoDialog = std::make_unique<NdsGameDetailsBottomSheetView>(
|
||||
&_romBrowserController, &_theme->GetMaterialColorScheme(), _theme->GetFontRepository());
|
||||
gameInfoDialog->SetGraphics(_chipViewVram);
|
||||
_dialogPresenter.ShowDialog(std::move(gameInfoDialog));
|
||||
}
|
||||
|
||||
void App::HandleHideGameInfoTrigger()
|
||||
{
|
||||
_dialogPresenter.CloseDialog();
|
||||
if (!_dialogPresenter.GetOldFocus())
|
||||
_romBrowserBottomScreenView->Focus(_focusManager);
|
||||
}
|
||||
|
||||
void App::HandleShowDisplaySettingsTrigger()
|
||||
{
|
||||
auto displaySettingsDialog = std::make_unique<DisplaySettingsBottomSheetView>(
|
||||
&_displaySettingsBottomSheetViewModel, &_theme->GetMaterialColorScheme(), _theme->GetFontRepository());
|
||||
displaySettingsDialog->SetGraphics(_iconButtonViewVram);
|
||||
_dialogPresenter.ShowDialog(std::move(displaySettingsDialog));
|
||||
}
|
||||
|
||||
void App::HandleHideDisplaySettingsTrigger()
|
||||
{
|
||||
_dialogPresenter.CloseDialog();
|
||||
if (!_dialogPresenter.GetOldFocus())
|
||||
_romBrowserBottomScreenView->Focus(_focusManager);
|
||||
}
|
||||
|
||||
void App::HandleNavigateTrigger()
|
||||
{
|
||||
if (!_romBrowserBottomScreenView->IsAppBarFocused(_focusManager))
|
||||
_focusManager.Unfocus();
|
||||
}
|
||||
|
||||
void App::HandleFolderLoadDoneTrigger()
|
||||
{
|
||||
_romBrowserTopScreenView.reset();
|
||||
RestoreVramState(_vramStateAfterMakeBottomScreenView);
|
||||
auto displayMode = RomBrowserDisplayModeFactory().GetRomBrowserDisplayMode(
|
||||
_romBrowserController.GetRomBrowserDisplaySettings().layout);
|
||||
_romBrowserTopScreenView = std::make_unique<RomBrowserTopScreenView>(
|
||||
_romBrowserController.GetRomBrowserViewModel(),
|
||||
displayMode,
|
||||
_materialThemeFileIconFactory.get(),
|
||||
_theme->GetRomBrowserViewFactory());
|
||||
_romBrowserTopScreenView->InitVram(_subVramContext);
|
||||
_romBrowserBottomScreenView->RomBrowserViewModelInvalidated(_mainVramContext);
|
||||
if (!_focusManager.GetCurrentFocus())
|
||||
_romBrowserBottomScreenView->Focus(_focusManager);
|
||||
}
|
||||
|
||||
void App::HandleChangeDisplayModeTrigger(RomBrowserState newState)
|
||||
{
|
||||
_dialogPresenter.ClearOldFocus();
|
||||
RestoreVramState(_vramStateBeforeMakeBottomScreenView);
|
||||
auto displayMode = RomBrowserDisplayModeFactory().GetRomBrowserDisplayMode(
|
||||
_romBrowserController.GetRomBrowserDisplaySettings().layout);
|
||||
_romBrowserBottomScreenView = std::make_unique<RomBrowserBottomScreenView>(
|
||||
&_romBrowserBottomScreenViewModel,
|
||||
displayMode,
|
||||
_materialThemeFileIconFactory.get(),
|
||||
_theme->GetRomBrowserViewFactory(),
|
||||
&_vblankTextureLoader);
|
||||
_romBrowserBottomScreenView->InitVram(_mainVramContext);
|
||||
StoreVramState(_vramStateAfterMakeBottomScreenView);
|
||||
_romBrowserTopScreenView = std::make_unique<RomBrowserTopScreenView>(
|
||||
_romBrowserController.GetRomBrowserViewModel(),
|
||||
displayMode,
|
||||
_materialThemeFileIconFactory.get(),
|
||||
_theme->GetRomBrowserViewFactory());
|
||||
_romBrowserTopScreenView->InitVram(_subVramContext);
|
||||
_romBrowserBottomScreenView->RomBrowserViewModelInvalidated(_mainVramContext);
|
||||
if (newState == RomBrowserState::Browser)
|
||||
_romBrowserBottomScreenView->Focus(_focusManager);
|
||||
}
|
||||
|
||||
bool App::IsRomBrowserVisible() const
|
||||
{
|
||||
const auto& stateMachine = _romBrowserController.GetStateMachine();
|
||||
auto curState = stateMachine.GetCurrentState();
|
||||
return curState == RomBrowserState::Browser
|
||||
|| curState == RomBrowserState::GameInfo
|
||||
|| curState == RomBrowserState::DisplaySettings
|
||||
|| curState == RomBrowserState::Launching;
|
||||
}
|
||||
|
||||
void App::Update()
|
||||
{
|
||||
const auto& stateMachine = _romBrowserController.GetStateMachine();
|
||||
_romBrowserController.Update();
|
||||
auto curState = stateMachine.GetCurrentState();
|
||||
if (_changeDisplayMode)
|
||||
{
|
||||
HandleChangeDisplayModeTrigger(curState);
|
||||
_changeDisplayMode = false;
|
||||
}
|
||||
if (stateMachine.HasStateChanged())
|
||||
{
|
||||
HandleTrigger(stateMachine.GetLastTrigger(), curState);
|
||||
}
|
||||
|
||||
bool isRomBrowserVisible = IsRomBrowserVisible();
|
||||
if (isRomBrowserVisible && !_exit && curState != RomBrowserState::Launching)
|
||||
{
|
||||
_focusManager.Update(_inputRepeater);
|
||||
}
|
||||
|
||||
if (_topBackground)
|
||||
_topBackground->Update();
|
||||
if (_bottomBackground)
|
||||
_bottomBackground->Update();
|
||||
|
||||
_dialogPresenter.Update();
|
||||
|
||||
_romBrowserBottomScreenView->Update();
|
||||
if (isRomBrowserVisible)
|
||||
{
|
||||
_romBrowserTopScreenView->Update();
|
||||
_romBrowserController.GetRomBrowserViewModel()->SetIconFrameCounter(
|
||||
_romBrowserController.GetRomBrowserViewModel()->GetIconFrameCounter() + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void App::Draw()
|
||||
{
|
||||
gx_reset();
|
||||
Gx::Viewport(0, 0, 255, 191);
|
||||
Gx::MtxMode(GX_MTX_MODE_POSITION_VECTOR);
|
||||
Gx::MtxIdentity();
|
||||
|
||||
GraphicsContext mainGraphicsContext
|
||||
{
|
||||
&_mainOam,
|
||||
&_mainObjPltt,
|
||||
&_rgb6Palette
|
||||
};
|
||||
GraphicsContext subGraphicsContext
|
||||
{
|
||||
&_subOam,
|
||||
&_subObjPltt,
|
||||
nullptr
|
||||
};
|
||||
|
||||
_mainOam.Clear();
|
||||
_subOam.Clear();
|
||||
_mainObjPltt.Reset();
|
||||
_subObjPltt.Reset();
|
||||
mainGraphicsContext.SetPriority(3);
|
||||
subGraphicsContext.SetPriority(2);
|
||||
|
||||
if (_topBackground)
|
||||
_topBackground->Draw(subGraphicsContext);
|
||||
if (_bottomBackground)
|
||||
_bottomBackground->Draw(mainGraphicsContext);
|
||||
|
||||
if (!_changeDisplayMode && IsRomBrowserVisible())
|
||||
{
|
||||
_romBrowserTopScreenView->Draw(subGraphicsContext);
|
||||
}
|
||||
|
||||
_dialogPresenter.ApplyClipArea(mainGraphicsContext);
|
||||
if (!_changeDisplayMode)
|
||||
{
|
||||
_romBrowserBottomScreenView->Draw(mainGraphicsContext);
|
||||
}
|
||||
mainGraphicsContext.ResetClipArea();
|
||||
|
||||
_dialogPresenter.Draw(mainGraphicsContext);
|
||||
|
||||
_mainObjPltt.EndOfFrame();
|
||||
|
||||
Gx::SwapBuffers(GX_XLU_SORT_MANUAL, GX_DEPTH_MODE_Z);
|
||||
}
|
||||
|
||||
void App::VBlank()
|
||||
{
|
||||
dma_ntrStopDirect(0); // stop hblank dma
|
||||
_inputProvider.Sample();
|
||||
_inputRepeater.Update();
|
||||
_mainOam.Apply(GFX_OAM_MAIN);
|
||||
_subOam.Apply(GFX_OAM_SUB);
|
||||
_subObjPltt.Apply(GFX_PLTT_OBJ_SUB);
|
||||
|
||||
if (!_vcountIrqStarted)
|
||||
{
|
||||
rtos_ackIrqMask(RTOS_IRQ_VCOUNT);
|
||||
rtos_enableIrqMask(RTOS_IRQ_VCOUNT);
|
||||
_vcountIrqStarted = true;
|
||||
}
|
||||
_mainObjPltt.VBlank();
|
||||
|
||||
if (_topBackground)
|
||||
_topBackground->VBlank();
|
||||
if (_bottomBackground)
|
||||
_bottomBackground->VBlank();
|
||||
|
||||
_dialogPresenter.VBlank();
|
||||
|
||||
if (IsRomBrowserVisible())
|
||||
{
|
||||
_romBrowserTopScreenView->VBlank();
|
||||
}
|
||||
_romBrowserBottomScreenView->VBlank();
|
||||
|
||||
_vblankTextureLoader.VBlank();
|
||||
}
|
||||
|
||||
void App::StoreVramState(VramState& vramState) const
|
||||
{
|
||||
vramState._mainObjVramState = _mainObjVram.GetState();
|
||||
vramState._texVramState = _textureVram.GetState();
|
||||
vramState._texPlttVramState = _texturePaletteVram.GetState();
|
||||
vramState._subObjVramState = _subObjVram.GetState();
|
||||
}
|
||||
|
||||
void App::RestoreVramState(const VramState& vramState)
|
||||
{
|
||||
_mainObjVram.SetState(vramState._mainObjVramState);
|
||||
_textureVram.SetState(vramState._texVramState);
|
||||
_texturePaletteVram.SetState(vramState._texPlttVramState);
|
||||
_subObjVram.SetState(vramState._subObjVramState);
|
||||
}
|
||||
129
arm9/source/App.h
Normal file
@@ -0,0 +1,129 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
#include <memory>
|
||||
#include "services/settings/IAppSettingsService.h"
|
||||
#include "bgm/IBgmService.h"
|
||||
#include "services/process/IProcess.h"
|
||||
#include "gui/SimplePaletteManager.h"
|
||||
#include "gui/AdvancedPaletteManager.h"
|
||||
#include "gui/OamManager.h"
|
||||
#include "gui/VramContext.h"
|
||||
#include "gui/AscendingStackVramManager.h"
|
||||
#include "gui/DescendingStackVramManager.h"
|
||||
#include "material/scheme/scheme.h"
|
||||
#include "gui/input/PadInputSource.h"
|
||||
#include "gui/input/SampledInputProvider.h"
|
||||
#include "gui/input/InputRepeater.h"
|
||||
#include "gui/VBlankTextureLoader.h"
|
||||
#include "gui/Rgb6Palette.h"
|
||||
#include "core/task/TaskQueue.h"
|
||||
#include "themes/material/MaterialColorScheme.h"
|
||||
#include "romBrowser/viewModels/RomBrowserBottomScreenViewModel.h"
|
||||
#include "romBrowser/viewModels/DisplaySettingsViewModel.h"
|
||||
#include "romBrowser/views/RomBrowserBottomScreenView.h"
|
||||
#include "romBrowser/views/RomBrowserTopScreenView.h"
|
||||
#include "romBrowser/views/IconButton2DView.h"
|
||||
#include "romBrowser/views/ChipView.h"
|
||||
#include "romBrowser/Theme/Material/MaterialThemeFileIconFactory.h"
|
||||
#include "romBrowser/RomBrowserController.h"
|
||||
#include "DialogPresenter.h"
|
||||
#include "themes/ITheme.h"
|
||||
#include "core/SharedPtr.h"
|
||||
#include "animation/Animator.h"
|
||||
|
||||
class alignas(32) App : public IProcess
|
||||
{
|
||||
public:
|
||||
App(IAppSettingsService& appSettingsService, IBgmService& bgmService);
|
||||
|
||||
void Run() override;
|
||||
void Exit() override;
|
||||
|
||||
private:
|
||||
struct VramState
|
||||
{
|
||||
u32 _subObjVramState;
|
||||
u32 _mainObjVramState;
|
||||
u32 _texVramState;
|
||||
u32 _texPlttVramState;
|
||||
};
|
||||
|
||||
AdvancedPaletteManager<64> _mainObjPltt;
|
||||
OamManager _mainOam;
|
||||
AscendingStackVramManager _mainObjVram;
|
||||
DescendingStackVramManager _mainObjDialogVram;
|
||||
OamManager _subOam;
|
||||
SimplePaletteManager _subObjPltt;
|
||||
AscendingStackVramManager _subObjVram;
|
||||
AscendingStackVramManager _textureVram;
|
||||
AscendingStackVramManager _texturePaletteVram;
|
||||
VBlankTextureLoader _vblankTextureLoader;
|
||||
VramContext _mainVramContext;
|
||||
VramContext _subVramContext;
|
||||
Rgb6Palette _rgb6Palette;
|
||||
Animator<int> _fadeAnimator;
|
||||
|
||||
TaskQueue<32, 32> _ioTaskQueue;
|
||||
u32 _ioTaskThreadStack[2048 / 4];
|
||||
TaskQueue<32, 32> _bgTaskQueue;
|
||||
u32 _bgTaskThreadStack[2048 / 4];
|
||||
|
||||
std::unique_ptr<ITheme> _theme;
|
||||
std::unique_ptr<IThemeBackground> _topBackground;
|
||||
std::unique_ptr<IThemeBackground> _bottomBackground;
|
||||
|
||||
IAppSettingsService& _appSettingsService;
|
||||
IBgmService& _bgmService;
|
||||
volatile bool _exit = false;
|
||||
|
||||
PadInputSource _inputSource;
|
||||
SampledInputProvider _inputProvider;
|
||||
InputRepeater _inputRepeater;
|
||||
|
||||
std::unique_ptr<RomBrowserBottomScreenView> _romBrowserBottomScreenView;
|
||||
std::unique_ptr<RomBrowserTopScreenView> _romBrowserTopScreenView;
|
||||
|
||||
RomBrowserController _romBrowserController;
|
||||
|
||||
DisplaySettingsViewModel _displaySettingsBottomSheetViewModel;
|
||||
|
||||
FocusManager _focusManager;
|
||||
|
||||
std::unique_ptr<MaterialThemeFileIconFactory> _materialThemeFileIconFactory;
|
||||
|
||||
RomBrowserBottomScreenViewModel _romBrowserBottomScreenViewModel;
|
||||
|
||||
DialogPresenter _dialogPresenter;
|
||||
|
||||
VramState _vramStateBeforeMakeBottomScreenView;
|
||||
VramState _vramStateAfterMakeBottomScreenView;
|
||||
bool _changeDisplayMode = false;
|
||||
|
||||
ChipView::VramToken _chipViewVram;
|
||||
IconButton2DView::VramToken _iconButtonViewVram;
|
||||
|
||||
bool _vcountIrqStarted = false;
|
||||
|
||||
void InitVramMapping() const;
|
||||
void DisplaySplashScreen() const;
|
||||
void LoadTheme();
|
||||
void VCountIrq();
|
||||
void HandleTrigger(RomBrowserStateTrigger trigger, RomBrowserState newState);
|
||||
void HandleShowGameInfoTrigger();
|
||||
void HandleHideGameInfoTrigger();
|
||||
void HandleShowDisplaySettingsTrigger();
|
||||
void HandleHideDisplaySettingsTrigger();
|
||||
void HandleNavigateTrigger();
|
||||
void HandleFolderLoadDoneTrigger();
|
||||
void HandleChangeDisplayModeTrigger(RomBrowserState newState);
|
||||
|
||||
bool IsRomBrowserVisible() const;
|
||||
|
||||
void MainLoop();
|
||||
void Update();
|
||||
void Draw();
|
||||
void VBlank();
|
||||
|
||||
void StoreVramState(VramState& vramState) const;
|
||||
void RestoreVramState(const VramState& vramState);
|
||||
};
|
||||
155
arm9/source/DialogPresenter.cpp
Normal file
@@ -0,0 +1,155 @@
|
||||
#include "common.h"
|
||||
#include <libtwl/dma/dmaNitro.h>
|
||||
#include <nds/arm9/background.h>
|
||||
#include "gui/StackVramManager.h"
|
||||
#include "gui/VramContext.h"
|
||||
#include "bottomSheetBg.h"
|
||||
#include "scrim.h"
|
||||
#include "gui/materialDesign.h"
|
||||
#include "gui/GraphicsContext.h"
|
||||
#include "DialogPresenter.h"
|
||||
|
||||
DialogPresenter::DialogPresenter(FocusManager* focusManager, StackVramManager* vramManager)
|
||||
: _focusManager(focusManager), _vramManager(vramManager)
|
||||
, _scrimAnimator(0), _yAnimator(192)
|
||||
{
|
||||
_baseVramState = _vramManager->GetState();
|
||||
}
|
||||
|
||||
void DialogPresenter::ShowDialog(std::unique_ptr<DialogView> dialog)
|
||||
{
|
||||
if (!_nextDialog)
|
||||
_nextDialog = std::move(dialog);
|
||||
}
|
||||
|
||||
void DialogPresenter::CloseDialog()
|
||||
{
|
||||
if (!_currentDialog || _curState != State::BottomSheetVisible)
|
||||
return;
|
||||
|
||||
_newState = State::BottomSheetClosing;
|
||||
}
|
||||
|
||||
void DialogPresenter::Update()
|
||||
{
|
||||
if (_curState != _newState)
|
||||
{
|
||||
_curState = _newState;
|
||||
switch (_curState)
|
||||
{
|
||||
case State::BottomSheetVisible:
|
||||
{
|
||||
_scrimAnimator.Goto(5, md::sys::motion::duration::short2,
|
||||
&md::sys::motion::easing::linear);
|
||||
_yAnimator.Goto(32, md::sys::motion::duration::long2,
|
||||
&md::sys::motion::easing::emphasizedDecelerate);
|
||||
_oldFocus = _focusManager->GetCurrentFocus();
|
||||
_currentDialog->Focus(*_focusManager);
|
||||
break;
|
||||
}
|
||||
case State::BottomSheetClosing:
|
||||
{
|
||||
_scrimAnimator.Goto(0, md::sys::motion::duration::short4,
|
||||
&md::sys::motion::easing::emphasizedAccelerate);
|
||||
_yAnimator.Goto(192, md::sys::motion::duration::short4,
|
||||
&md::sys::motion::easing::emphasizedAccelerate);
|
||||
if (_oldFocus)
|
||||
{
|
||||
_focusManager->Focus(_oldFocus);
|
||||
_oldFocus = nullptr;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
switch (_curState)
|
||||
{
|
||||
case State::Idle:
|
||||
{
|
||||
if (!_currentDialog && _nextDialog)
|
||||
{
|
||||
_currentDialog = std::move(_nextDialog);
|
||||
_initVram = true;
|
||||
_newState = State::BottomSheetVisible;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case State::BottomSheetVisible:
|
||||
{
|
||||
if (!_yAnimator.IsFinished())
|
||||
_yAnimator.Update();
|
||||
break;
|
||||
}
|
||||
case State::BottomSheetClosing:
|
||||
{
|
||||
if (!_yAnimator.IsFinished())
|
||||
{
|
||||
_yAnimator.Update();
|
||||
}
|
||||
else
|
||||
{
|
||||
_newState = State::Idle;
|
||||
_currentDialog.reset();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (_currentDialog)
|
||||
{
|
||||
_currentDialog->SetPosition(_currentDialog->GetPosition().x, _yAnimator.GetValue());
|
||||
_currentDialog->Update();
|
||||
}
|
||||
}
|
||||
|
||||
void DialogPresenter::ApplyClipArea(GraphicsContext& graphicsContext) const
|
||||
{
|
||||
if (_currentDialog)
|
||||
{
|
||||
graphicsContext.SetClipArea(_currentDialog->GetFullyCoveredArea(), true);
|
||||
}
|
||||
}
|
||||
|
||||
void DialogPresenter::VBlank()
|
||||
{
|
||||
REG_BG1VOFS = -_yAnimator.GetValue();
|
||||
|
||||
if (_initVram && _currentDialog)
|
||||
{
|
||||
_vramManager->SetState(_baseVramState);
|
||||
_currentDialog->InitVram(VramContext(nullptr, _vramManager, nullptr, nullptr));
|
||||
_initVram = false;
|
||||
}
|
||||
|
||||
if (_currentDialog)
|
||||
_currentDialog->VBlank();
|
||||
|
||||
if (!_scrimAnimator.IsFinished())
|
||||
{
|
||||
_scrimAnimator.Update();
|
||||
int scrimBlend = _scrimAnimator.GetValue();
|
||||
REG_BLDALPHA = ((16 - scrimBlend) << 8) | scrimBlend;
|
||||
}
|
||||
}
|
||||
|
||||
void DialogPresenter::InitVram()
|
||||
{
|
||||
dma_ntrCopy32(3, scrimTiles, (vu8*)BG_GFX, scrimTilesLen);
|
||||
dma_ntrCopy32(3, bottomSheetBgTiles, (vu8*)BG_GFX + 64, bottomSheetBgTilesLen);
|
||||
dma_ntrCopy32(3, bottomSheetBgMap, (vu8*)BG_GFX + 0x4000, bottomSheetBgMapLen);
|
||||
dma_ntrCopy32(3, scrimMap, (vu8*)BG_GFX + 0x5000, scrimMapLen);
|
||||
|
||||
REG_BG1CNT = BG_32x64 | BG_PRIORITY_1 | BG_COLOR_16 | BG_MAP_BASE(8) | BG_TILE_BASE(0);
|
||||
REG_BG1HOFS = 0;
|
||||
REG_BG1VOFS = 0;
|
||||
|
||||
REG_BG2CNT = BG_32x32 | BG_PRIORITY_2 | BG_COLOR_16 | BG_MAP_BASE(10) | BG_TILE_BASE(0);
|
||||
REG_BG2HOFS = 0;
|
||||
REG_BG2VOFS = 0;
|
||||
|
||||
REG_BLDCNT = 0x3944;
|
||||
REG_BLDALPHA = (16 << 8) | 0;
|
||||
}
|
||||
76
arm9/source/DialogPresenter.h
Normal file
@@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include "animation/Animator.h"
|
||||
#include "gui/views/DialogView.h"
|
||||
|
||||
class StackVramManager;
|
||||
class FocusManager;
|
||||
|
||||
/// @brief Class for displaying dialogs.
|
||||
class DialogPresenter
|
||||
{
|
||||
public:
|
||||
DialogPresenter(FocusManager* focusManager, StackVramManager* vramManager);
|
||||
|
||||
/// @brief Requests to show the given dialog.
|
||||
/// @param dialog The dialog to show.
|
||||
void ShowDialog(std::unique_ptr<DialogView> dialog);
|
||||
|
||||
/// @brief Closes the current dialog.
|
||||
void CloseDialog();
|
||||
|
||||
/// @brief Updates the dialog presenter.
|
||||
void Update();
|
||||
|
||||
/// @brief Applies the clip area of the currently displayed dialog,
|
||||
/// or does nothing if no dialog is being shown.
|
||||
/// @param graphicsContext The graphics context to apply to.
|
||||
void ApplyClipArea(GraphicsContext& graphicsContext) const;
|
||||
|
||||
/// @brief If a dialog is currently being shown, draws the dialog.
|
||||
/// @param graphicsContext The graphics context to use.
|
||||
void Draw(GraphicsContext& graphicsContext)
|
||||
{
|
||||
if (_currentDialog)
|
||||
_currentDialog->Draw(graphicsContext);
|
||||
}
|
||||
|
||||
/// @brief Performs vblank processes for the displayed dialog.
|
||||
void VBlank();
|
||||
|
||||
/// @brief Initializes vram that is needed for showing dialogs.
|
||||
void InitVram();
|
||||
|
||||
/// @brief Clears the focus that was stored when a dialog was opened.
|
||||
void ClearOldFocus()
|
||||
{
|
||||
_oldFocus = nullptr;
|
||||
}
|
||||
|
||||
/// @brief Gets the focus that was stored when a dialog was opened.
|
||||
/// @return The view that was focused when the current dialog was opened.
|
||||
constexpr View* GetOldFocus() const
|
||||
{
|
||||
return _oldFocus;
|
||||
}
|
||||
|
||||
private:
|
||||
enum class State
|
||||
{
|
||||
Idle,
|
||||
BottomSheetVisible,
|
||||
BottomSheetClosing
|
||||
};
|
||||
|
||||
FocusManager* _focusManager;
|
||||
StackVramManager* _vramManager;
|
||||
u32 _baseVramState;
|
||||
std::unique_ptr<DialogView> _currentDialog;
|
||||
std::unique_ptr<DialogView> _nextDialog;
|
||||
bool _initVram = false;
|
||||
View* _oldFocus = nullptr;
|
||||
Animator<int> _scrimAnimator;
|
||||
Animator<int> _yAnimator;
|
||||
State _curState = State::Idle;
|
||||
State _newState = State::Idle;
|
||||
};
|
||||
13
arm9/source/PicoLoaderProcess.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#include "common.h"
|
||||
#include <nds/arm9/video.h>
|
||||
#include "picoLoaderBootstrap.h"
|
||||
#include "PicoLoaderProcess.h"
|
||||
|
||||
void PicoLoaderProcess::Run()
|
||||
{
|
||||
REG_MASTER_BRIGHT = 0x401F;
|
||||
REG_MASTER_BRIGHT_SUB = 0x401F;
|
||||
REG_DISPCNT = 0;
|
||||
REG_DISPCNT_SUB = 0;
|
||||
pload_start();
|
||||
}
|
||||
13
arm9/source/PicoLoaderProcess.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include "services/process/IProcess.h"
|
||||
|
||||
class PicoLoaderProcess : public IProcess
|
||||
{
|
||||
public:
|
||||
void Run() override;
|
||||
|
||||
void Exit() override
|
||||
{
|
||||
// This process never exits
|
||||
}
|
||||
};
|
||||
4
arm9/source/VBlank.cpp
Normal file
@@ -0,0 +1,4 @@
|
||||
#include "common.h"
|
||||
#include "VBlank.h"
|
||||
|
||||
rtos_event_t VBlank::sEvent;
|
||||
25
arm9/source/VBlank.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#include <libtwl/rtos/rtosEvent.h>
|
||||
|
||||
/// @brief Helper class for waiting for vblank.
|
||||
class VBlank
|
||||
{
|
||||
public:
|
||||
static void Init()
|
||||
{
|
||||
rtos_createEvent(&sEvent);
|
||||
}
|
||||
|
||||
static void Wait(bool waitNew = true, bool clearAfter = true)
|
||||
{
|
||||
rtos_waitEvent(&sEvent, waitNew, clearAfter);
|
||||
}
|
||||
|
||||
static void NotifyIrq()
|
||||
{
|
||||
rtos_signalEvent(&sEvent);
|
||||
}
|
||||
|
||||
private:
|
||||
static rtos_event_t sEvent;
|
||||
};
|
||||
35
arm9/source/animation/Animator.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#include "common.h"
|
||||
#include "core/math/fixed.h"
|
||||
#include "Animator.h"
|
||||
|
||||
template <>
|
||||
bool Animator<int>::Update()
|
||||
{
|
||||
if (++_frame >= _duration)
|
||||
{
|
||||
_frame = _duration;
|
||||
_value = _to;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto relativePos = _curve->Compute(_frame * _invDuration);
|
||||
_value = _from + (relativePos.LongMul(_to - _from) + 0.5).Int();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool Animator<fix32<12>>::Update()
|
||||
{
|
||||
if (++_frame >= _duration)
|
||||
{
|
||||
_frame = _duration;
|
||||
_value = _to;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto relativePos = _curve->Compute(_frame * _invDuration);
|
||||
_value = _from + fix32<12>(relativePos.LongMul(_to - _from));
|
||||
|
||||
return false;
|
||||
}
|
||||
50
arm9/source/animation/Animator.h
Normal file
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
#include "Curve.h"
|
||||
|
||||
template <typename T>
|
||||
class Animator
|
||||
{
|
||||
public:
|
||||
constexpr Animator()
|
||||
: _from(), _to(), _value(), _duration(0), _frame(0), _curve(nullptr) { }
|
||||
|
||||
/// @brief Constructs an Animator with the given initialValue.
|
||||
/// @param initialValue The initial value for the animator.
|
||||
constexpr Animator(const T& initialValue)
|
||||
: _from(initialValue), _to(initialValue), _value(initialValue), _duration(0), _frame(0), _curve(nullptr) { }
|
||||
|
||||
/// @brief Constructs an Animator that animates from from to to with the given duration and curve.
|
||||
/// @param from The start value.
|
||||
/// @param to The end value.
|
||||
/// @param duration The duration of the animation.
|
||||
/// @param curve The curve to use for the animation.
|
||||
constexpr Animator(const T& from, const T& to, u32 duration, const Curve* curve)
|
||||
: _from(from), _to(to), _value(_from), _duration(duration), _invDuration(fix32<26>(1) / duration), _frame(0), _curve(curve) { }
|
||||
|
||||
bool Update();
|
||||
|
||||
constexpr const T& GetValue() const { return _value; }
|
||||
constexpr const T& GetTargetValue() const { return _to; }
|
||||
constexpr u32 GetDuration() const { return _duration; }
|
||||
constexpr u32 GetFrame() const { return _frame; }
|
||||
constexpr bool IsFinished() const { return _frame == _duration; }
|
||||
|
||||
/// @brief Restarts this animator from the current value to a new target
|
||||
/// with the given duration and curve.
|
||||
/// @param target The new target value.
|
||||
/// @param duration The duration of the animation.
|
||||
/// @param curve The curve to use.
|
||||
void Goto(const T& target, u32 duration, const Curve* curve)
|
||||
{
|
||||
*this = Animator(_value, target, duration, curve);
|
||||
}
|
||||
|
||||
private:
|
||||
T _from;
|
||||
T _to;
|
||||
T _value;
|
||||
u32 _duration;
|
||||
fix32<26> _invDuration;
|
||||
u32 _frame;
|
||||
const Curve* _curve;
|
||||
};
|
||||
48
arm9/source/animation/CubicBezierCurve.h
Normal file
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
#include "Curve.h"
|
||||
|
||||
class CubicBezierCurve : public Curve
|
||||
{
|
||||
using fx = fix16<13>;
|
||||
|
||||
const fx _cx;
|
||||
const fx _bx;
|
||||
const fx _ax;
|
||||
|
||||
const fx _cy;
|
||||
const fx _by;
|
||||
const fx _ay;
|
||||
|
||||
[[gnu::noinline]]
|
||||
static constexpr fix32<26> SampleCurve(fx ax, fx bx, fx cx, fx t)
|
||||
{
|
||||
return ((ax * t + bx) * t + cx).LongMul(t);
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr CubicBezierCurve(fx p1x, fx p1y, fx p2x, fx p2y)
|
||||
: _cx(3 * p1x)
|
||||
, _bx(3 * (p2x - p1x) - _cx)
|
||||
, _ax(1 - _cx - _bx)
|
||||
, _cy(3 * p1y)
|
||||
, _by(3 * (p2y - p1y) - _cy)
|
||||
, _ay(1 - _cy - _by) { }
|
||||
|
||||
[[gnu::noinline]]
|
||||
fix32<26> Compute(fix32<26> t) const override
|
||||
{
|
||||
fx start = 0.0;
|
||||
fx end = 1.0;
|
||||
while (true)
|
||||
{
|
||||
const fx midpoint = (start + end) >> 1;
|
||||
const fix32<26> estimate = SampleCurve(_ax, _bx, _cx, midpoint);
|
||||
if ((t - estimate).Abs() < 0.001)
|
||||
return SampleCurve(_ay, _by, _cy, midpoint);
|
||||
if (estimate < t)
|
||||
start = midpoint;
|
||||
else
|
||||
end = midpoint;
|
||||
}
|
||||
}
|
||||
};
|
||||
8
arm9/source/animation/Curve.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
|
||||
class Curve
|
||||
{
|
||||
public:
|
||||
virtual fix32<26> Compute(fix32<26> t) const = 0;
|
||||
};
|
||||
20
arm9/source/animation/Interpolator.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
|
||||
class Interpolator
|
||||
{
|
||||
public:
|
||||
template <typename T>
|
||||
static constexpr T InterpolateLinear(T a, T b, T t)
|
||||
{
|
||||
return a + (b - a) * t;
|
||||
}
|
||||
|
||||
template <typename T, u32 FractionBits>
|
||||
static constexpr fixed<T, FractionBits> InterpolateCubic(
|
||||
fixed<T, FractionBits> cf0, fixed<T, FractionBits> cf1,
|
||||
fixed<T, FractionBits> cf2, fixed<T, FractionBits> cf3, fixed<T, FractionBits> t)
|
||||
{
|
||||
return ((((cf0 * t + cf1) * t) + cf2) * t) + cf3;
|
||||
}
|
||||
};
|
||||
11
arm9/source/animation/LinearCurve.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "Curve.h"
|
||||
|
||||
class LinearCurve : public Curve
|
||||
{
|
||||
public:
|
||||
fix32<26> Compute(fix32<26> t) const override
|
||||
{
|
||||
return t;
|
||||
}
|
||||
};
|
||||
48
arm9/source/animation/ThreePointCubicBezierCurve.h
Normal file
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
#include "Curve.h"
|
||||
#include "CubicBezierCurve.h"
|
||||
|
||||
class ThreePointCubicBezierCurve : public Curve
|
||||
{
|
||||
using fx = fix16<13>;
|
||||
|
||||
fix32<26> _midPointX;
|
||||
fix32<26> _midPointY;
|
||||
fix32<26> _invMidPointX;
|
||||
fix32<26> _invOneMinusMidPointX;
|
||||
|
||||
const CubicBezierCurve _firstCurve;
|
||||
const CubicBezierCurve _secondCurve;
|
||||
|
||||
public:
|
||||
constexpr ThreePointCubicBezierCurve(
|
||||
fx a1x, fx a1y, fx b1x, fx b1y,
|
||||
fx midpointx, fx midpointy,
|
||||
fx a2x, fx a2y, fx b2x, fx b2y)
|
||||
: _midPointX(midpointx)
|
||||
, _midPointY(midpointy)
|
||||
, _invMidPointX(fix32<26>(1) / midpointx)
|
||||
, _invOneMinusMidPointX(fix32<26>(1) / (1 - midpointx))
|
||||
, _firstCurve(
|
||||
a1x / midpointx,
|
||||
a1y / midpointy,
|
||||
b1x / midpointx,
|
||||
b1y / midpointy)
|
||||
, _secondCurve(
|
||||
(a2x - midpointx) / (1 - midpointx),
|
||||
(a2y - midpointy) / (1 - midpointy),
|
||||
(b2x - midpointx) / (1 - midpointx),
|
||||
(b2y - midpointy) / (1 - midpointy)) { }
|
||||
|
||||
[[gnu::noinline]]
|
||||
fix32<26> Compute(fix32<26> t) const override
|
||||
{
|
||||
if (t < _midPointX)
|
||||
return _firstCurve.Compute(t * _invMidPointX) * _midPointY;
|
||||
else
|
||||
{
|
||||
fix32<26> scaledT = (t - _midPointX) * _invOneMinusMidPointX;
|
||||
return _secondCurve.Compute(scaledT) * (1 - _midPointY) + _midPointY;
|
||||
}
|
||||
}
|
||||
};
|
||||
140
arm9/source/bgm/AudioStreamPlayer.cpp
Normal file
@@ -0,0 +1,140 @@
|
||||
#include "common.h"
|
||||
#include <nds/arm9/cache.h>
|
||||
#include <libtwl/ipc/ipcFifoSystem.h>
|
||||
#include <libtwl/timer/timer.h>
|
||||
#include <libtwl/rtos/rtosIrq.h>
|
||||
#include "ipcChannels.h"
|
||||
#include "AudioStreamPlayer.h"
|
||||
|
||||
/// @brief Hardware timer used for tracking audio blocks.
|
||||
#define AUDIO_STREAM_PLAYER_TIMER 3
|
||||
|
||||
/// @brief Number of blocks that are not overwritten
|
||||
/// between the read and write pointer to prevent
|
||||
/// touching the current playing position.
|
||||
#define AUDIO_STREAM_PLAYER_SAFETY_BLOCKS 4
|
||||
|
||||
/// @brief Priority of the stream thread.
|
||||
#define AUDIO_STREAM_PLAYER_THREAD_PRIORITY 15
|
||||
|
||||
AudioStreamPlayer* AudioStreamPlayer::sCurrentPlayer = nullptr;
|
||||
|
||||
AudioStreamPlayer::AudioStreamPlayer()
|
||||
{
|
||||
rtos_createMutex(&_mutex);
|
||||
rtos_createEvent(&_event);
|
||||
DC_FlushRange(&_soundStopCmdList, sizeof(_soundStopCmdList));
|
||||
}
|
||||
|
||||
bool AudioStreamPlayer::StartPlaybackIntern(std::unique_ptr<IAudioStream> audioStream)
|
||||
{
|
||||
if (_isPlaying)
|
||||
StopPlaybackIntern();
|
||||
|
||||
_audioStream = std::move(audioStream);
|
||||
|
||||
// fill buffer
|
||||
_readBlock = 0;
|
||||
_writeBlock = 0;
|
||||
for (u32 i = 0; i < AUDIO_STREAM_PLAYER_RING_BLOCKS - 1; i++)
|
||||
{
|
||||
FillRingBlock(i);
|
||||
_writeBlock++;
|
||||
}
|
||||
|
||||
u32 rate = _audioStream->GetSampleRate();
|
||||
u32 timer = -((33513982 + rate) / (rate * 2));
|
||||
_soundStartCmdList.leftSetup.timer = timer;
|
||||
_soundStartCmdList.rightSetup.timer = timer;
|
||||
DC_FlushRange(&_soundStartCmdList, sizeof(_soundStartCmdList));
|
||||
|
||||
sCurrentPlayer = this;
|
||||
|
||||
rtos_disableIrqMask(RTOS_IRQ_TIMER(AUDIO_STREAM_PLAYER_TIMER));
|
||||
rtos_ackIrqMask(RTOS_IRQ_TIMER(AUDIO_STREAM_PLAYER_TIMER));
|
||||
rtos_setIrqFunc(RTOS_IRQ_TIMER(AUDIO_STREAM_PLAYER_TIMER), [] (u32 irqMask)
|
||||
{
|
||||
u32 readBlock = sCurrentPlayer->_readBlock;
|
||||
if (++readBlock == AUDIO_STREAM_PLAYER_RING_BLOCKS)
|
||||
readBlock = 0;
|
||||
sCurrentPlayer->_readBlock = readBlock;
|
||||
rtos_signalEvent(&sCurrentPlayer->_event);
|
||||
});
|
||||
tmr_configure(AUDIO_STREAM_PLAYER_TIMER, TMCNT_H_CLK_SYS_DIV_256, timer << 1, true);
|
||||
|
||||
rtos_createThread(&_thread, AUDIO_STREAM_PLAYER_THREAD_PRIORITY, [] (void* arg)
|
||||
{
|
||||
static_cast<AudioStreamPlayer*>(arg)->ThreadMain();
|
||||
}, this, _threadStack, sizeof(_threadStack));
|
||||
|
||||
tmr_start(AUDIO_STREAM_PLAYER_TIMER);
|
||||
rtos_enableIrqMask(RTOS_IRQ_TIMER(AUDIO_STREAM_PLAYER_TIMER));
|
||||
|
||||
_isPlaying = true;
|
||||
rtos_wakeupThread(&_thread);
|
||||
ipc_sendFifoMessage(IPC_CHANNEL_SOUND, (u32)&_soundStartCmdList);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AudioStreamPlayer::StopPlaybackIntern()
|
||||
{
|
||||
if (!_isPlaying)
|
||||
return;
|
||||
|
||||
_isPlaying = false;
|
||||
rtos_wakeupThread(&_thread);
|
||||
ipc_sendFifoMessage(IPC_CHANNEL_SOUND, (u32)&_soundStopCmdList);
|
||||
tmr_stop(AUDIO_STREAM_PLAYER_TIMER);
|
||||
rtos_disableIrqMask(RTOS_IRQ_TIMER(AUDIO_STREAM_PLAYER_TIMER));
|
||||
rtos_joinThread(&_thread);
|
||||
sCurrentPlayer = nullptr;
|
||||
_audioStream.reset();
|
||||
}
|
||||
|
||||
void AudioStreamPlayer::ThreadMain()
|
||||
{
|
||||
do
|
||||
{
|
||||
bool doUpdate = true;
|
||||
while(_isPlaying && doUpdate)
|
||||
{
|
||||
rtos_lockMutex(&_mutex);
|
||||
{
|
||||
u32 writeBlock = _writeBlock;
|
||||
int freeBlocks = _readBlock - writeBlock - 1;
|
||||
if (freeBlocks < 0)
|
||||
freeBlocks += AUDIO_STREAM_PLAYER_RING_BLOCKS;
|
||||
|
||||
if (freeBlocks > AUDIO_STREAM_PLAYER_SAFETY_BLOCKS)
|
||||
{
|
||||
FillRingBlock(writeBlock);
|
||||
if (++writeBlock == AUDIO_STREAM_PLAYER_RING_BLOCKS)
|
||||
writeBlock = 0;
|
||||
_writeBlock = writeBlock;
|
||||
}
|
||||
else
|
||||
{
|
||||
doUpdate = false;
|
||||
}
|
||||
}
|
||||
rtos_unlockMutex(&_mutex);
|
||||
}
|
||||
|
||||
if (!_isPlaying)
|
||||
break;
|
||||
|
||||
rtos_waitEvent(&_event, false, true);
|
||||
} while(_isPlaying);
|
||||
}
|
||||
|
||||
void AudioStreamPlayer::FillRingBlock(u32 block)
|
||||
{
|
||||
s16* blockPtrL = &_audioRingL[block][0];
|
||||
s16* blockPtrR = &_audioRingR[block][0];
|
||||
|
||||
_audioStream->ReadSamples(blockPtrL, blockPtrR, AUDIO_STREAM_PLAYER_BLOCK_SAMPLES);
|
||||
|
||||
DC_FlushRange(&blockPtrL, AUDIO_STREAM_PLAYER_BLOCK_SAMPLES * sizeof(s16));
|
||||
DC_FlushRange(&blockPtrR, AUDIO_STREAM_PLAYER_BLOCK_SAMPLES * sizeof(s16));
|
||||
}
|
||||
106
arm9/source/bgm/AudioStreamPlayer.h
Normal file
@@ -0,0 +1,106 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
#include <memory>
|
||||
#include <libtwl/rtos/rtosMutex.h>
|
||||
#include <libtwl/rtos/rtosThread.h>
|
||||
#include <libtwl/rtos/rtosEvent.h>
|
||||
#include <../../libtwl7/include/libtwl/sound/soundChannel.h>
|
||||
#include "fat/File.h"
|
||||
#include "soundIpcCommand.h"
|
||||
#include "IAudioStreamPlayer.h"
|
||||
|
||||
#define AUDIO_STREAM_PLAYER_RING_BLOCKS 32
|
||||
#define AUDIO_STREAM_PLAYER_BLOCK_SAMPLES 256
|
||||
|
||||
/// @brief Class implementing an audio stream player.
|
||||
class alignas(32) AudioStreamPlayer : public IAudioStreamPlayer
|
||||
{
|
||||
public:
|
||||
AudioStreamPlayer();
|
||||
|
||||
~AudioStreamPlayer()
|
||||
{
|
||||
StopPlayback();
|
||||
}
|
||||
|
||||
bool StartPlayback(std::unique_ptr<IAudioStream> audioStream) override
|
||||
{
|
||||
bool result;
|
||||
rtos_lockMutex(&_mutex);
|
||||
{
|
||||
result = StartPlaybackIntern(std::move(audioStream));
|
||||
}
|
||||
rtos_unlockMutex(&_mutex);
|
||||
return result;
|
||||
}
|
||||
|
||||
void StopPlayback() override
|
||||
{
|
||||
rtos_lockMutex(&_mutex);
|
||||
{
|
||||
StopPlaybackIntern();
|
||||
}
|
||||
rtos_unlockMutex(&_mutex);
|
||||
}
|
||||
|
||||
private:
|
||||
struct alignas(32) SoundStartCmdList
|
||||
{
|
||||
u32 cmdCount;
|
||||
snd_ipc_cmd_setup_channel_t leftSetup;
|
||||
snd_ipc_cmd_setup_channel_t rightSetup;
|
||||
u32 startChannels;
|
||||
};
|
||||
|
||||
struct alignas(32) SoundStopCmdList
|
||||
{
|
||||
u32 cmdCount;
|
||||
u32 stopChannels;
|
||||
};
|
||||
|
||||
s16 _audioRingL[AUDIO_STREAM_PLAYER_RING_BLOCKS][AUDIO_STREAM_PLAYER_BLOCK_SAMPLES] alignas(32);
|
||||
s16 _audioRingR[AUDIO_STREAM_PLAYER_RING_BLOCKS][AUDIO_STREAM_PLAYER_BLOCK_SAMPLES] alignas(32);
|
||||
SoundStartCmdList _soundStartCmdList alignas(32)
|
||||
{
|
||||
3,
|
||||
{
|
||||
SND_IPC_CMD_SETUP_CHANNEL,
|
||||
0,
|
||||
_audioRingL,
|
||||
0,
|
||||
0,
|
||||
sizeof(_audioRingL) >> 2,
|
||||
SOUNDCNT_VOLUME(127) | SOUNDCNT_MODE_LOOP | SOUNDCNT_FORMAT_PCM16
|
||||
},
|
||||
{
|
||||
SND_IPC_CMD_SETUP_CHANNEL,
|
||||
1,
|
||||
_audioRingR,
|
||||
0,
|
||||
0,
|
||||
sizeof(_audioRingR) >> 2,
|
||||
SOUNDCNT_VOLUME(127) | SOUNDCNT_PAN(127) | SOUNDCNT_MODE_LOOP | SOUNDCNT_FORMAT_PCM16
|
||||
},
|
||||
(0b11 << 8) | SND_IPC_CMD_START_CHANNELS
|
||||
};
|
||||
SoundStopCmdList _soundStopCmdList alignas(32)
|
||||
{
|
||||
1,
|
||||
(0b11 << 8) | SND_IPC_CMD_STOP_CHANNELS
|
||||
};
|
||||
u32 _threadStack[2048 / sizeof(u32)] alignas(32);
|
||||
rtos_mutex_t _mutex;
|
||||
rtos_thread_t _thread;
|
||||
rtos_event_t _event;
|
||||
volatile bool _isPlaying = false;
|
||||
volatile u8 _readBlock;
|
||||
volatile u8 _writeBlock;
|
||||
std::unique_ptr<IAudioStream> _audioStream;
|
||||
|
||||
static AudioStreamPlayer* sCurrentPlayer;
|
||||
|
||||
bool StartPlaybackIntern(std::unique_ptr<IAudioStream> audioStream);
|
||||
void StopPlaybackIntern();
|
||||
void ThreadMain();
|
||||
void FillRingBlock(u32 block);
|
||||
};
|
||||
327
arm9/source/bgm/BcstmAudioStream.cpp
Normal file
@@ -0,0 +1,327 @@
|
||||
#include "common.h"
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include "BcstmAudioStream.h"
|
||||
|
||||
bool BcstmAudioStream::Open(const TCHAR* filePath)
|
||||
{
|
||||
if (_audioFile.Open(filePath, FA_OPEN_EXISTING | FA_READ) != FR_OK)
|
||||
return false;
|
||||
|
||||
return TryLoadBcstm();
|
||||
}
|
||||
|
||||
bool BcstmAudioStream::Open(const FastFileRef& fastFileRef)
|
||||
{
|
||||
_audioFile.Open(fastFileRef, FA_OPEN_EXISTING | FA_READ);
|
||||
return TryLoadBcstm();
|
||||
}
|
||||
|
||||
bool BcstmAudioStream::TryLoadBcstm()
|
||||
{
|
||||
if (_audioFile.CreateClusterTable(_clusterTable, sizeof(_clusterTable)) != FR_OK)
|
||||
return false;
|
||||
|
||||
bcstm_header_t header;
|
||||
u32 bytesRead = 0;
|
||||
if (_audioFile.Read(&header, sizeof(bcstm_header_t), bytesRead) != FR_OK ||
|
||||
bytesRead != sizeof(bcstm_header_t))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_audioFile.Seek(header.infoBlockRef.offset) != FR_OK)
|
||||
return false;
|
||||
|
||||
bcstm_info_t info;
|
||||
if (_audioFile.Read(&info, sizeof(bcstm_info_t), bytesRead) != FR_OK ||
|
||||
bytesRead != sizeof(bcstm_info_t))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_audioFile.Seek(header.infoBlockRef.offset + 8 + info.streamInfoRef.offset) != FR_OK)
|
||||
return false;
|
||||
|
||||
if (_audioFile.Read(&_streamInfo, sizeof(bcstm_info_stream_t), bytesRead) != FR_OK ||
|
||||
bytesRead != sizeof(bcstm_info_stream_t))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_dataOffset = header.dataBlockRef.offset + 8 + _streamInfo.dataRef.offset;
|
||||
|
||||
_channels = std::min<u32>(_streamInfo.nrChannels, 2);
|
||||
|
||||
if (_streamInfo.format == BCSTM_FORMAT_DSP_ADPCM)
|
||||
{
|
||||
u32 tableSize = sizeof(bcstm_ref_table_t) + sizeof(bcstm_ref_t) * (_streamInfo.nrChannels - 1);
|
||||
auto channelInfoRefTab = std::unique_ptr<u8[]>(new u8[tableSize]);
|
||||
|
||||
if (_audioFile.Seek(header.infoBlockRef.offset + 8 + info.channelInfoRef.offset) != FR_OK)
|
||||
return false;
|
||||
|
||||
if (_audioFile.Read(channelInfoRefTab.get(), tableSize, bytesRead) != FR_OK ||
|
||||
bytesRead != tableSize)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto channelInfoRefTabPtr = reinterpret_cast<bcstm_ref_table_t*>(channelInfoRefTab.get());
|
||||
for (u32 i = 0; i < _channels; i++)
|
||||
{
|
||||
u32 offset = header.infoBlockRef.offset + 8 + info.channelInfoRef.offset + channelInfoRefTabPtr->references[i].offset;
|
||||
if (_audioFile.Seek(offset) != FR_OK)
|
||||
return false;
|
||||
|
||||
bcstm_info_channel_t channel;
|
||||
if (_audioFile.Read(&channel, sizeof(bcstm_info_channel_t), bytesRead) != FR_OK ||
|
||||
bytesRead != sizeof(bcstm_info_channel_t))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_audioFile.Seek(offset + channel.codecInfoRef.offset) != FR_OK)
|
||||
return false;
|
||||
|
||||
if (_audioFile.Read(&_dspAdpcmInfo[i], sizeof(bcstm_dspadpcm_t), bytesRead) != FR_OK ||
|
||||
bytesRead != sizeof(bcstm_dspadpcm_t))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < _channels; i++)
|
||||
{
|
||||
_adpcmBlocks[i] = std::unique_ptr<u8[]>(new(cache_align) u8[_streamInfo.blockSize]);
|
||||
if (_streamInfo.format == BCSTM_FORMAT_DSP_ADPCM)
|
||||
{
|
||||
_dspAdpcmContexts[i].coefTable = _dspAdpcmInfo[i].coefs;
|
||||
_dspAdpcmContexts[i].frameData[12] = _dspAdpcmInfo[i].context.last2;
|
||||
_dspAdpcmContexts[i].frameData[13] = _dspAdpcmInfo[i].context.last1;
|
||||
}
|
||||
}
|
||||
|
||||
_blockNumber = 0;
|
||||
_sampleNumberInBlock = 0;
|
||||
|
||||
if (_streamInfo.loop)
|
||||
{
|
||||
_loopBlockNumber = _streamInfo.loopStart / _streamInfo.blockSampleCount;
|
||||
_loopBlockStartSample = _streamInfo.loopStart % _streamInfo.blockSampleCount;
|
||||
|
||||
_loopEndBlockNumber = (_streamInfo.loopEnd - 1) / _streamInfo.blockSampleCount;
|
||||
_loopEndBlockEndSample = (_streamInfo.loopEnd - 1) % _streamInfo.blockSampleCount;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void BcstmAudioStream::Close()
|
||||
{
|
||||
_audioFile.Close();
|
||||
_adpcmBlocks[0].reset();
|
||||
_adpcmBlocks[1].reset();
|
||||
}
|
||||
|
||||
void BcstmAudioStream::ReadSamples(s16* left, s16* right, u32 count)
|
||||
{
|
||||
u32 remaining = count;
|
||||
s16* curLeft = left;
|
||||
s16* curRight = right;
|
||||
do
|
||||
{
|
||||
if (_sampleNumberInBlock == 0)
|
||||
FetchBlock();
|
||||
|
||||
u32 totalSamplesInBlock = GetTotalSamplesInCurrentBlock();
|
||||
u32 samplesToDecode = std::min(totalSamplesInBlock - _sampleNumberInBlock, remaining);
|
||||
DecodeAdpcmSamples(_dspAdpcmContexts[0], curLeft, samplesToDecode);
|
||||
if (_channels == 2)
|
||||
DecodeAdpcmSamples(_dspAdpcmContexts[1], curRight, samplesToDecode);
|
||||
curLeft += samplesToDecode;
|
||||
curRight += samplesToDecode;
|
||||
remaining -= samplesToDecode;
|
||||
_sampleNumberInBlock += samplesToDecode;
|
||||
if (_sampleNumberInBlock == totalSamplesInBlock)
|
||||
{
|
||||
_blockNumber++;
|
||||
_sampleNumberInBlock = 0;
|
||||
}
|
||||
} while (remaining != 0);
|
||||
|
||||
// when mono, copy the samples from the left to the right channel
|
||||
if (_channels == 1)
|
||||
memcpy(right, left, count * sizeof(s16));
|
||||
}
|
||||
|
||||
u32 BcstmAudioStream::GetTotalSamplesInCurrentBlock()
|
||||
{
|
||||
u32 totalSamplesInBlock = _blockNumber == _streamInfo.nrBlocks - 1 ? _streamInfo.lastBlockSampleCount : _streamInfo.blockSampleCount;
|
||||
if (_streamInfo.loop && _blockNumber == _loopEndBlockNumber)
|
||||
totalSamplesInBlock = _loopEndBlockEndSample + 1;
|
||||
return totalSamplesInBlock;
|
||||
}
|
||||
|
||||
void BcstmAudioStream::FetchBlock()
|
||||
{
|
||||
bool looped = false;
|
||||
if (_blockNumber == _streamInfo.nrBlocks || (_streamInfo.loop && _blockNumber == _loopEndBlockNumber + 1))
|
||||
{
|
||||
looped = true;
|
||||
_blockNumber = _streamInfo.loop ? _loopBlockNumber : 0;
|
||||
}
|
||||
|
||||
// fetch block
|
||||
u32 blockOffset = _dataOffset + _streamInfo.blockSize * (_blockNumber * _streamInfo.nrChannels);
|
||||
if (_audioFile.Seek(blockOffset) != FR_OK)
|
||||
return;
|
||||
|
||||
for (u32 i = 0; i < _channels; i++)
|
||||
{
|
||||
u32 blockSize = _blockNumber == _streamInfo.nrBlocks - 1
|
||||
? _streamInfo.lastBlockPaddedSize : _streamInfo.blockSize;
|
||||
u32 bytesRead = 0;
|
||||
if (_audioFile.Read(_adpcmBlocks[i].get(), blockSize, bytesRead) != FR_OK ||
|
||||
bytesRead != blockSize)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_dspAdpcmContexts[i].SetData(_adpcmBlocks[i].get());
|
||||
}
|
||||
|
||||
if (looped)
|
||||
{
|
||||
_sampleNumberInBlock = _loopBlockStartSample;
|
||||
if (_streamInfo.format == BCSTM_FORMAT_DSP_ADPCM)
|
||||
{
|
||||
if (_blockNumber == 0)
|
||||
{
|
||||
for (u32 i = 0; i < _channels; i++)
|
||||
{
|
||||
_dspAdpcmContexts[i].frameData[12] = _dspAdpcmInfo[i].context.last2;
|
||||
_dspAdpcmContexts[i].frameData[13] = _dspAdpcmInfo[i].context.last1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 frameOffset = _loopBlockStartSample % 14u;
|
||||
u32 frame = _loopBlockStartSample / 14u;
|
||||
for (u32 i = 0; i < _channels; i++)
|
||||
{
|
||||
_dspAdpcmContexts[i].SetData(_adpcmBlocks[i].get() + frame * 8);
|
||||
if (frameOffset != 0)
|
||||
{
|
||||
DecodeLoopAdpcmFrame(
|
||||
_dspAdpcmContexts[i],
|
||||
_dspAdpcmInfo[i].loopContext.predictorAndScale,
|
||||
_dspAdpcmInfo[i].loopContext.last1,
|
||||
_dspAdpcmInfo[i].loopContext.last2,
|
||||
_loopBlockStartSample % 14u);
|
||||
}
|
||||
else
|
||||
{
|
||||
_dspAdpcmContexts[i].frameData[12] = _dspAdpcmInfo[i].loopContext.last2;
|
||||
_dspAdpcmContexts[i].frameData[13] = _dspAdpcmInfo[i].loopContext.last1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void dspadpcm_decode(DspAdpcmContext* context);
|
||||
|
||||
void BcstmAudioStream::DecodeAdpcmSamples(DspAdpcmContext& context, s16* dst, u32 count)
|
||||
{
|
||||
do
|
||||
{
|
||||
if (context.frameIdx == 0)
|
||||
dspadpcm_decode(&context);
|
||||
u32 toCopy = std::min<u32>(14 - context.frameIdx, count);
|
||||
memcpy(dst, &context.frameData[context.frameIdx], toCopy * sizeof(s16));
|
||||
context.frameIdx += toCopy;
|
||||
if (context.frameIdx == 14)
|
||||
context.frameIdx = 0;
|
||||
count -= toCopy;
|
||||
dst += toCopy;
|
||||
} while (count != 0);
|
||||
}
|
||||
|
||||
// ITCM_CODE void BcstmAudioStream::DecodeAdpcmFrame(DspAdpcmContext& context)
|
||||
// {
|
||||
// const u8* data = context.data;
|
||||
// u32 scaleCoef = *data++;
|
||||
// u32 scale = (scaleCoef & 0xF) + 11;
|
||||
// u32 coefIdx = scaleCoef >> 4;
|
||||
// s16 coef1 = context.coefTable[coefIdx * 2];
|
||||
// s16 coef2 = context.coefTable[coefIdx * 2 + 1];
|
||||
// s16 last2 = context.frameData[12];
|
||||
// s16 last1 = context.frameData[13];
|
||||
|
||||
// for (u32 j = 0; j < 7; j++)
|
||||
// {
|
||||
// u32 dataValue = *data++;
|
||||
// int high = (int)(dataValue << 24) >> 28;
|
||||
// int val = ((high << scale) + (coef1 * last1 + coef2 * last2) + 1024) >> 11;
|
||||
// int sample = std::clamp(val, -0x8000, 0x7FFF);
|
||||
// context.frameData[j * 2] = sample;
|
||||
// last2 = last1;
|
||||
// last1 = sample;
|
||||
|
||||
// int low = (int)(dataValue << 28) >> 28;
|
||||
// val = ((low << scale) + (coef1 * last1 + coef2 * last2) + 1024) >> 11;
|
||||
// sample = std::clamp(val, -0x8000, 0x7FFF);
|
||||
// context.frameData[j * 2 + 1] = sample;
|
||||
// last2 = last1;
|
||||
// last1 = sample;
|
||||
// }
|
||||
|
||||
// context.frameIdx = 0;
|
||||
// context.data = data;
|
||||
// }
|
||||
|
||||
ITCM_CODE void BcstmAudioStream::DecodeLoopAdpcmFrame(
|
||||
DspAdpcmContext& context, u32 scaleCoef, s16 last1, s16 last2, u32 sampleOffset)
|
||||
{
|
||||
const u8* data = context.data;
|
||||
data += 2 + (sampleOffset >> 1);
|
||||
u32 scale = (scaleCoef & 0xF) + 11;
|
||||
u32 coefIdx = scaleCoef >> 4;
|
||||
s16 coef1 = context.coefTable[coefIdx * 2];
|
||||
s16 coef2 = context.coefTable[coefIdx * 2 + 1];
|
||||
if ((int)sampleOffset - 2 >= 0)
|
||||
context.frameData[sampleOffset - 2] = last2;
|
||||
if ((int)sampleOffset - 1 >= 0)
|
||||
context.frameData[sampleOffset - 1] = last1;
|
||||
|
||||
for (u32 j = sampleOffset; j < 14; j += 2)
|
||||
{
|
||||
u32 dataValue = *data++;
|
||||
if ((j & 1) == 0)
|
||||
{
|
||||
int high = (int)(dataValue << 24) >> 28;
|
||||
int val = ((high << scale) + (coef1 * last1 + coef2 * last2) + 1024) >> 11;
|
||||
int sample = std::clamp(val, -0x8000, 0x7FFF);
|
||||
context.frameData[j] = sample;
|
||||
last2 = last1;
|
||||
last1 = sample;
|
||||
}
|
||||
else
|
||||
{
|
||||
j--;
|
||||
}
|
||||
|
||||
int low = (int)(dataValue << 28) >> 28;
|
||||
int val = ((low << scale) + (coef1 * last1 + coef2 * last2) + 1024) >> 11;
|
||||
int sample = std::clamp(val, -0x8000, 0x7FFF);
|
||||
context.frameData[j + 1] = sample;
|
||||
last2 = last1;
|
||||
last1 = sample;
|
||||
}
|
||||
|
||||
context.frameIdx = sampleOffset;
|
||||
context.data = data;
|
||||
}
|
||||
66
arm9/source/bgm/BcstmAudioStream.h
Normal file
@@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
#include <memory>
|
||||
#include "fat/File.h"
|
||||
#include "IAudioStream.h"
|
||||
#include "bcstm.h"
|
||||
|
||||
struct DspAdpcmContext
|
||||
{
|
||||
const s16* coefTable;
|
||||
const u8* data;
|
||||
s16 frameData[14];
|
||||
u8 frameIdx;
|
||||
|
||||
void SetData(const u8* data)
|
||||
{
|
||||
this->data = data;
|
||||
frameIdx = 0;
|
||||
}
|
||||
};
|
||||
|
||||
class alignas(32) BcstmAudioStream : public IAudioStream
|
||||
{
|
||||
public:
|
||||
/// @brief Opens the given file. Call this function before
|
||||
/// using ReadSamples.
|
||||
/// @param filePath The path of the file to open.
|
||||
/// @return True if the file was successfully opened, or false otherwise.
|
||||
bool Open(const TCHAR* filePath);
|
||||
|
||||
/// @brief Opens the given file. Call this function before
|
||||
/// using ReadSamples.
|
||||
/// @param filePath The path of the file to open.
|
||||
/// @return True if the file was successfully opened, or false otherwise.
|
||||
bool Open(const FastFileRef& fastFileRef);
|
||||
|
||||
void Close() override;
|
||||
void ReadSamples(s16* left, s16* right, u32 count) override;
|
||||
|
||||
u32 GetSampleRate() const override
|
||||
{
|
||||
return _streamInfo.sampleRate;
|
||||
}
|
||||
|
||||
private:
|
||||
File _audioFile alignas(32);
|
||||
DWORD _clusterTable[1024] alignas(32);
|
||||
bcstm_info_stream_t _streamInfo;
|
||||
bcstm_dspadpcm_t _dspAdpcmInfo[2];
|
||||
u32 _dataOffset;
|
||||
std::unique_ptr<u8[]> _adpcmBlocks[2];
|
||||
u32 _channels;
|
||||
u32 _blockNumber;
|
||||
u32 _sampleNumberInBlock;
|
||||
u32 _loopBlockNumber;
|
||||
u32 _loopEndBlockNumber;
|
||||
u32 _loopBlockStartSample;
|
||||
u32 _loopEndBlockEndSample;
|
||||
DspAdpcmContext _dspAdpcmContexts[2];
|
||||
|
||||
bool TryLoadBcstm();
|
||||
u32 GetTotalSamplesInCurrentBlock();
|
||||
void FetchBlock();
|
||||
void DecodeAdpcmSamples(DspAdpcmContext& context, s16* dst, u32 count);
|
||||
void DecodeLoopAdpcmFrame(DspAdpcmContext& context, u32 scaleCoef, s16 last1, s16 last2, u32 sampleOffset);
|
||||
};
|
||||
44
arm9/source/bgm/BgmService.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#include "common.h"
|
||||
#include "core/mini-printf.h"
|
||||
#include "Pcm16FileAudioStream.h"
|
||||
#include "BcstmAudioStream.h"
|
||||
#include "romBrowser/SdFolder.h"
|
||||
#include "romBrowser/SdFolderFactory.h"
|
||||
#include "romBrowser/FileType/NullFileTypeProvider.h"
|
||||
#include "BgmService.h"
|
||||
|
||||
bool BgmService::StartBgm(const TCHAR* filePath)
|
||||
{
|
||||
auto stream = std::make_unique<BcstmAudioStream>();
|
||||
if (!stream->Open(filePath))
|
||||
return false;
|
||||
|
||||
return _audioStreamPlayer->StartPlayback(std::move(stream));
|
||||
}
|
||||
|
||||
void BgmService::StartBgmFromConfig()
|
||||
{
|
||||
TCHAR pathBuffer[128];
|
||||
mini_snprintf(pathBuffer, sizeof(pathBuffer), "/_pico/themes/%s/bgm", _appSettingsService.GetAppSettings().theme.GetString());
|
||||
NullFileTypeProvider fileTypeProvider;
|
||||
auto bgmFolder = SdFolderFactory(&fileTypeProvider).CreateFromPath(pathBuffer);
|
||||
if (!bgmFolder || bgmFolder->GetFileCount() == 0)
|
||||
{
|
||||
StopBgm();
|
||||
return;
|
||||
}
|
||||
u32 bgmToPlay = _randomGenerator.NextU32(bgmFolder->GetFileCount());
|
||||
auto stream = std::make_unique<BcstmAudioStream>();
|
||||
if (!stream->Open(bgmFolder->GetFiles()[bgmToPlay]->GetFastFileRef()))
|
||||
{
|
||||
StopBgm();
|
||||
return;
|
||||
}
|
||||
|
||||
_audioStreamPlayer->StartPlayback(std::move(stream));
|
||||
}
|
||||
|
||||
void BgmService::StopBgm()
|
||||
{
|
||||
_audioStreamPlayer->StopPlayback();
|
||||
}
|
||||
28
arm9/source/bgm/BgmService.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include "IAudioStreamPlayer.h"
|
||||
#include "services/settings/IAppSettingsService.h"
|
||||
#include "rng/RandomGenerator.h"
|
||||
#include "IBgmService.h"
|
||||
|
||||
/// @brief Class implementing a background music service.
|
||||
class BgmService : public IBgmService
|
||||
{
|
||||
public:
|
||||
constexpr BgmService(
|
||||
std::unique_ptr<IAudioStreamPlayer> audioStreamPlayer,
|
||||
IAppSettingsService& appSettingsService,
|
||||
RandomGenerator& randomGenerator)
|
||||
: _audioStreamPlayer(std::move(audioStreamPlayer))
|
||||
, _appSettingsService(appSettingsService)
|
||||
, _randomGenerator(randomGenerator) { }
|
||||
|
||||
bool StartBgm(const TCHAR* filePath) override;
|
||||
void StartBgmFromConfig() override;
|
||||
void StopBgm() override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<IAudioStreamPlayer> _audioStreamPlayer;
|
||||
IAppSettingsService& _appSettingsService;
|
||||
RandomGenerator& _randomGenerator;
|
||||
};
|
||||
24
arm9/source/bgm/IAudioStream.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
/// @brief Interface for an audio stream.
|
||||
class IAudioStream
|
||||
{
|
||||
public:
|
||||
virtual ~IAudioStream() = 0;
|
||||
|
||||
/// @brief Returns the sample rate of the audio stream in Hertz.
|
||||
/// @return The sample rate in Hertz.
|
||||
virtual u32 GetSampleRate() const = 0;
|
||||
|
||||
/// @brief Reads the given number of samples from the audio stream
|
||||
/// to the given buffers for the left and right channel.
|
||||
/// @param left The left channel buffer.
|
||||
/// @param right The right channel buffer.
|
||||
/// @param count The number of samples to read.
|
||||
virtual void ReadSamples(s16* left, s16* right, u32 count) = 0;
|
||||
|
||||
/// @brief Closes the audio stream.
|
||||
virtual void Close() = 0;
|
||||
};
|
||||
|
||||
inline IAudioStream::~IAudioStream() { }
|
||||