audio: run-time mixing and reverberation, with on-screen UI

This commit is contained in:
Zack Buhman 2026-06-03 15:27:34 -05:00
parent 0dbe1a3c7a
commit 76b3edcf1b
33 changed files with 2327 additions and 295 deletions

View File

@ -7,7 +7,7 @@ OBJARCH = elf64-x86-64
UNAME := $(shell uname -s)
OPT += -O3
OPT += -O0
OPT += -march=core-avx2
DEBUG = -g
@ -17,12 +17,13 @@ CXXSTD = -std=gnu++20
CFLAGS += -Wall -Werror
CFLAGS += -Wfatal-errors
CFLAGS += -Wno-error=unused-variable
#CFLAGS += -Wno-error=unused-but-set-variable
CFLAGS += -Wno-error=unused-but-set-variable
CFLAGS += -Wno-format-security
CFLAGS += -Wno-format
CFLAGS += -Wno-error=unused-function
CFLAGS += -Wno-error=array-bounds
CFLAGS += -Wno-unknown-pragmas
CFLAGS += -Wno-vla-cxx-extension
CFLAGS += -fno-strict-aliasing
CFLAGS += -I./include
CFLAGS += -I./data
@ -35,13 +36,14 @@ CFLAGS += -fpic
CFLAGS += -ffunction-sections
CFLAGS += -fdata-sections
#FLAGS += -fstack-protector -fstack-protector-all -fno-omit-frame-pointer -fsanitize=address
FLAGS += -fstack-protector -fstack-protector-all -fno-omit-frame-pointer -fsanitize=address
CXXFLAGS += -fno-exceptions -fno-non-call-exceptions -fno-rtti -fno-threadsafe-statics
LDFLAGS += -lm
#LDFLAGS += -Wl,--gc-sections
#-Wl,--print-gc-sections
ifeq ($(UNAME),Linux)
LDFLAGS += -Wl,-z noexecstack
#LDFLAGS += -Wl,-z noexecstack
endif
ifeq ($(UNAME),Darwin)
LDFLAGS += -framework Foundation -framework Cocoa -framework IOKit -framework AVFoundation -framework CoreVideo -framework CoreAudio -framework CoreMedia -framework CoreHaptics -framework AudioToolbox -framework GameController -framework ForceFeedback -framework Carbon -framework Metal -framework QuartzCore -framework UniformTypeIdentifiers
@ -67,8 +69,10 @@ OBJS = \
src/pack.o \
src/dds/validate.o \
src/vulkan_helper.o \
src/vulkan_state.o \
src/tga/tga.o \
src/font/outline.o \
src/font/bitmap/vulkan.o \
src/renpy/vulkan.o \
src/renpy/composite/vulkan.o \
src/renpy/script.o \
@ -77,7 +81,10 @@ OBJS = \
src/audio.o \
src/poem/birdsong.o \
src/poem/eleanorthehero.o \
src/poem/kiristella.o
src/poem/kiristella.o \
src/ui/vulkan.o \
src/ui/widget.o \
src/ui.o
ZLIB = ../zlib-1.3.2
CFLAGS += -I$(ZLIB)
@ -107,11 +114,14 @@ endif
all: main
CC = clang
CXX = clang++
%.o: %.c
$(CC) $(ARCH) $(CSTD) $(CFLAGS) $(FLAGS) $(OPT) $(DEBUG) -c $< -o $@
%.o: %.cpp
$(CXX) $(ARCH) $(CXXSTD) $(CFLAGS) $(FLAGS) $(OPT) $(DEBUG) -c $< -o $@
$(CXX) $(ARCH) $(CXXSTD) $(CFLAGS) $(CXXFLAGS) $(FLAGS) $(OPT) $(DEBUG) -c $< -o $@
%.o: %.s
$(AS) $< -o $@
@ -132,7 +142,7 @@ all: main
# ./tools/opus_encode $< $@
main: $(OBJS) $(LIBS)
$(CC) $(ARCH) $(LDFLAGS) $(FLAGS) $(OPT) $(DEBUG) $^ -o $@
$(CXX) $(ARCH) $(LDFLAGS) $(FLAGS) $(OPT) $(DEBUG) $^ -o $@
%.spv: %.hlsl
../dxc/bin/dxc -spirv -T lib_6_3 -fspv-target-env=vulkan1.3 $< -Fo $@

Binary file not shown.

View File

@ -1,9 +1,13 @@
shader/font.spv
shader/font/bitmap.spv
data/font/outline/medieval_sharp_24.data
data/font/bitmap/terminus_128x64_6x12.data
shader/renpy.spv
shader/renpy_composite.spv
shader/ui/solid.spv
audio/sfx/Chime.opus.bin
audio/music/MistAmbience.opus.bin
audio/music/Preludium.opus.bin

View File

@ -4,6 +4,105 @@
#include "poem.h"
namespace audio {
constexpr int sample_rate = 48000;
constexpr int channels = 2;
template <int maxDelay>
struct FeedbackCombFilter {
private:
//float * buffer;
float buffer[maxDelay];
int index;
public:
int delay;
float gain0;
float gainM;
FeedbackCombFilter(int delay, float gain0, float gainM);
void reset();
float feed(float value);
};
template <int maxDelay>
struct FeedforwardCombFilter {
private:
//float * buffer;
float buffer[maxDelay];
int index;
public:
int delay;
float gain0;
float gainM;
FeedforwardCombFilter(int delay, float gain0, float gainM);
void reset();
float feed(float value);
};
template <int maxDelay>
struct AllpassFilter {
private:
//float * buffer;
float buffer[maxDelay];
int index;
public:
int delay;
float gain0;
float gainM;
AllpassFilter(int delay, float gain0, float gainM);
void reset();
float feed(float x);
};
using FBCF = FeedbackCombFilter<15000>;
using FFCF = FeedforwardCombFilter<15000>;
using AP = AllpassFilter<2500>;
struct lr { float l; float r; };
struct FBReverb {
static constexpr int cfCount = 4;
static constexpr int apCount = 3;
FBCF cf[cfCount];
AP ap[apCount];
FBReverb();
void reset();
lr feed(float x);
};
struct FFReverb {
static constexpr int __cfCount = 4;
static constexpr int apCount = 3;
FFCF cf[__cfCount];
AP ap[apCount];
FFReverb();
void reset();
lr feed(float x);
};
extern int reverbIndex;
extern FBReverb fbreverb;
extern FFReverb ffreverb;
extern float wetGain;
extern float dryGain;
struct mix_channel {
enum {
music = 0,
poem = 1,
voice = 2,
};
};
constexpr int mixChannelCount = 3;
extern float mixChannelGain[mixChannelCount];
extern int poem_timestamp_index;
extern int poem_line_index;

18
include/font/bitmap.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include "instance_data.h"
namespace font::bitmap {
static inline uint16_2 glyphIndex(int c)
{
assert(c >= 32 && c <= 127);
c -= 32;
int stride = 128 / 6;
int x = c % stride;
int y = c / stride;
return {(uint16_t)x, (uint16_t)y};
}
}

View File

@ -0,0 +1,64 @@
#pragma once
#include "vulkan_state.h"
#include "font/instance_data.h"
namespace font::bitmap {
struct LoadedFont {
VkImage image;
VkDeviceMemory memory;
VkImageView imageView;
};
struct vulkan {
static constexpr int maximumGlyphCount = 1024;
static constexpr VkDeviceSize instanceBufferSize{ maximumGlyphCount * (sizeof (BitmapInstance)) };
VulkanState const * const vk;
VkPipelineLayout pipelineLayout;
VkPipeline pipeline;
::InstanceBuffer instanceBuffer;
BitmapInstance * bitmapInstance[2];
VkDescriptorPool descriptorPool;
static constexpr uint32_t descriptorSetLayoutCount = 1;
VkDescriptorSetLayout descriptorSetLayouts[descriptorSetLayoutCount];
VkDescriptorSet descriptorSet0;
LoadedFont loadedFont;
vulkan(VulkanState const * vk)
: vk(vk)
{
createDescriptorSets();
createPipeline();
loadedFont = loadFont();
writeDescriptorSets(loadedFont.imageView);
createInstanceBuffer(vk->device,
vk->physicalDeviceProperties,
vk->physicalDeviceMemoryProperties,
instanceBufferSize,
&instanceBuffer);
bitmapInstance[0] = (BitmapInstance *)(((ptrdiff_t)instanceBuffer.mappedData) + instanceBuffer.offset[0]);
bitmapInstance[1] = (BitmapInstance *)(((ptrdiff_t)instanceBuffer.mappedData) + instanceBuffer.offset[1]);
}
LoadedFont loadFont();
void createDescriptorSets();
void writeDescriptorSets(VkImageView imageView);
void update(uint32_t frameIndex,
uint32_t glyphCount) const;
void draw(VkCommandBuffer commandBuffer,
uint32_t frameIndex,
uint32_t glyphCount) const;
private:
void createPipeline();
};
}

View File

@ -0,0 +1,15 @@
#pragma once
#include <stdint.h>
#include <assert.h>
#include "mapped_instance_data.h"
namespace font {
struct BitmapInstance {
uint16_2 position;
uint16_2 glyph;
};
static_assert((sizeof (BitmapInstance)) == 2 * 2 + 4);
}

54
include/intstring.h Normal file
View File

@ -0,0 +1,54 @@
#pragma once
#include <stdint.h>
#include <assert.h>
namespace string {
static inline int decLength(int32_t n)
{
if (n >= 1000000000) return 10;
if (n >= 100000000) return 9;
if (n >= 10000000) return 8;
if (n >= 1000000) return 7;
if (n >= 100000) return 6;
if (n >= 10000) return 5;
if (n >= 1000) return 4;
if (n >= 100) return 3;
if (n >= 10) return 2;
return 1;
}
template <typename T, int fixed=0>
static inline int dec(T * c, int len, int32_t n)
{
int index = 0;
if (n < 0) {
c[index++] = '-';
n = -n;
}
int numLength = fixed > 0 ? fixed : decLength(n);
assert(len >= (numLength + index));
for (int i = (numLength - 1); i >= 0; i--) {
const int32_t digit = n % 10;
n = n / 10;
c[index + i] = digit + 48;
}
return numLength + index;
}
template <typename T>
static inline int flt(T * c, int len, float n)
{
int index = 0;
if (n < 0) {
c[index++] = '-';
n = -n;
}
int32_t whole = n;
index += dec<T>(&c[index], len - index, whole);
c[index++] = '.';
int32_t fraction = (int32_t)((n - (float)whole) * 1000.0f);
index += dec<T, 3>(&c[index], len - index, fraction);
return index;
}
}

View File

@ -0,0 +1,22 @@
#pragma once
#include <stdint.h>
struct uint16_2 {
uint16_t x;
uint16_t y;
};
template <typename T>
struct MappedInstanceData {
T * buffer;
int length;
int index;
void append(T const & value)
{
assert(index < length);
buffer[index] = value;
index += 1;
}
};

View File

@ -17,3 +17,11 @@ inline static constexpr T clamp(T n, T minVal, T maxVal)
{
return min(max(n, minVal), maxVal);
}
template <typename T>
static inline T clamp01(T a)
{
if (a < 0.0) return 0.0;
if (a > 1.0) return 1.0;
return a;
}

17
include/mouse.h Normal file
View File

@ -0,0 +1,17 @@
namespace mouse
{
void normalize(int windowWidth, int windowHeight, float mx, float my, float * mxf, float * myf)
{
int canonicalSizeX = 1280;
int canonicalSizeY = 720;
int scaleFactor = 1;
while (canonicalSizeX * (scaleFactor + 1) <= windowWidth && canonicalSizeY * (scaleFactor + 1) <= windowHeight) {
scaleFactor += 1;
}
float scaleFactorInverse = 1.0f / ((float)scaleFactor);
int offsetX = (windowWidth - (canonicalSizeX * scaleFactor)) / 2;
int offsetY = (windowHeight - (canonicalSizeY * scaleFactor)) / 2;
*mxf = ((float)(mx - offsetX)) * scaleFactorInverse;
*myf = ((float)(my - offsetY)) * scaleFactorInverse;
}
}

View File

@ -12,11 +12,11 @@ namespace renpy {
};
static constexpr top_left transforms[] = {
[language::transform::left] = { .top = 192, .left = 0 },
[language::transform::centerleft] = { .top = 192, .left = 240 },
[language::transform::center] = { .top = 192, .left = 416 },
[language::transform::centerright] = { .top = 192, .left = 588 },
[language::transform::right] = { .top = 192, .left = 828 },
/*[language::transform::left] =*/ { .top = 192, .left = 0 },
/*[language::transform::centerleft] =*/ { .top = 192, .left = 240 },
/*[language::transform::center] =*/ { .top = 192, .left = 416 },
/*[language::transform::centerright] =*/ { .top = 192, .left = 588 },
/*[language::transform::right] =*/ { .top = 192, .left = 828 },
};
static constexpr int transformsCount = (sizeof (transforms)) / (sizeof (transforms[0]));

View File

@ -47,7 +47,7 @@ namespace renpy::language {
char const * const path;
double loop_end;
uint32_t audio_flags;
double attenuation;
double gain;
};
struct image {

10
include/ui.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
#include "ui/widget.h"
namespace ui
{
void draw(MappedInstanceData<SolidInstance> & data,
MappedInstanceData<font::BitmapInstance> & fontData);
void update(float mx, float my, bool mLeft, bool mEdge);
}

View File

@ -0,0 +1,16 @@
#pragma once
#include <stdint.h>
#include <assert.h>
#include "mapped_instance_data.h"
namespace ui {
struct SolidInstance {
uint16_2 position;
uint16_2 size;
uint32_t color;
};
static_assert((sizeof (SolidInstance)) == 2 * 2 * 2 + 4);
}

45
include/ui/vulkan.h Normal file
View File

@ -0,0 +1,45 @@
#pragma once
#include "vulkan_state.h"
#include "ui/instance_data.h"
#include "font/instance_data.h"
namespace ui {
struct vulkan {
static constexpr int maximumUIElements = 1024;
static constexpr VkDeviceSize instanceBufferSize{ maximumUIElements * (sizeof (SolidInstance)) };
VulkanState const * const vk;
VkPipelineLayout pipelineLayout;
VkPipeline pipeline;
::InstanceBuffer instanceBuffer;
SolidInstance * solidInstance[2];
vulkan(VulkanState const * vk)
: vk(vk)
{
createPipeline();
createInstanceBuffer(vk->device,
vk->physicalDeviceProperties,
vk->physicalDeviceMemoryProperties,
instanceBufferSize,
&instanceBuffer);
solidInstance[0] = (SolidInstance *)(((ptrdiff_t)instanceBuffer.mappedData) + instanceBuffer.offset[0]);
solidInstance[1] = (SolidInstance *)(((ptrdiff_t)instanceBuffer.mappedData) + instanceBuffer.offset[1]);
}
int update(uint32_t frameIndex,
MappedInstanceData<font::BitmapInstance> & bitmapData) const;
void draw(VkCommandBuffer commandBuffer,
uint32_t frameIndex,
MappedInstanceData<font::BitmapInstance> & bitmapData) const;
private:
void createPipeline();
};
}

151
include/ui/widget.h Normal file
View File

@ -0,0 +1,151 @@
#pragma once
#include "ui/instance_data.h"
#include "font/bitmap.h"
namespace ui::widget
{
struct BoundingBox
{
int left;
int top;
int width;
int height;
bool inside(float x, float y)
{
return (x >= left) && (x <= (left + width))
&& (y >= top) && (y <= (top + height));
}
};
struct Widget {
//virtual void mouseDown(float x, float y) {};
virtual void draw(MappedInstanceData<SolidInstance> & data,
MappedInstanceData<font::BitmapInstance> & fontData) = 0;
virtual void update(float mx, float my, bool mLeft, bool mEdge) = 0;
};
template <int optionCount>
struct Radio : public Widget
{
char const * label;
char const ** boxLabels;
int width;
int height;
int * selected;
BoundingBox box[optionCount];
Radio(char const * label,
char const ** boxLabels,
int left, int top, int width, int height,
int hSpace, int vSpace,
int * selected)
: label(label)
, boxLabels(boxLabels)
, width((optionCount - 1) * hSpace + width)
, height((optionCount - 1) * vSpace + height)
, selected(selected)
{
int x = left;
int y = top;
for (int i = 0; i < optionCount; i++) {
box[i].left = x;
box[i].top = y;
box[i].width = width;
box[i].height = height;
x += hSpace;
y += vSpace;
}
}
void draw(MappedInstanceData<SolidInstance> & data,
MappedInstanceData<font::BitmapInstance> & fontData) override;
void update(float mx, float my, bool mLeft, bool mEdge) override;
};
template <typename T, bool under>
struct Slider : public Widget
{
bool drag;
char const * label;
BoundingBox sliderBorder;
BoundingBox slider;
BoundingBox lminus;
BoundingBox lplus;
BoundingBox rminus;
BoundingBox rplus;
T minValue;
T maxValue;
T minExtent;
T maxExtent;
T * value;
Slider(char const * label,
int left, int top, int width, int height,
T minValue, T maxValue,
T minExtent, T maxExtent,
T * value)
: drag{ false }
, label{ label }
, sliderBorder{ left, top, width, height }
, slider{ left, top + 2, width, height - 4 }
, lminus{ left - 21, top + 3, 8, 8 }
, lplus{ left - 11, top + 3, 8, 8 }
, rminus{ left + width + 3, top + 3, 8, 8 }
, rplus{ left + width + 13, top + 3, 8, 8 }
, minValue{ minValue }
, maxValue{ maxValue }
, minExtent{ minExtent }
, maxExtent{ maxExtent }
, value{ value }
{}
void draw(MappedInstanceData<SolidInstance> & data,
MappedInstanceData<font::BitmapInstance> & fontData) override;
void update(float mx, float my, bool mLeft, bool mEdge) override;
};
struct DelayGainSlider : public Widget
{
char const * label;
Slider<int, true> delay;
Slider<float, true> gain0;
Slider<float, true> gainM;
DelayGainSlider(char const * label,
int left, int top,
int delayMin, int delayMax, int * delayValue,
float gainMin, float gainMax,
float * gain0Value, float * gainMValue)
: label(label)
, delay("delay",
left, top, 150, 14,
delayMin, delayMax,
delayMin, delayMax,
delayValue)
, gain0("gain0",
left, top + 30, 150, 14,
gainMin, gainMax,
gainMin, gainMax,
gain0Value)
, gainM("gainM",
left, top + 60, 150, 14,
gainMin, gainMax,
gainMin, gainMax,
gainMValue)
{}
void draw(MappedInstanceData<SolidInstance> & data,
MappedInstanceData<font::BitmapInstance> & fontData) override;
void update(float mx, float my, bool mLeft, bool mEdge) override;
};
}

View File

@ -94,7 +94,7 @@ void textureTransfer(VkDevice device,
VkDeviceSize nonCoherentAtomSize,
VkPhysicalDeviceMemoryProperties const & physicalDeviceMemoryProperties,
uint32_t imageDataSize,
void * imageData,
void const * imageData,
VkImage image,
uint32_t width,
uint32_t height,
@ -114,3 +114,34 @@ VertexIndex createVertexIndexBuffer(VkDevice device,
uint32_t vertexSize,
void const * indexStart,
uint32_t indexSize);
void createQuadPipeline(VkDevice device,
VkFormat colorFormat,
VkFormat depthFormat,
uint32_t descriptorSetLayoutCount,
VkDescriptorSetLayout const * descriptorSetLayouts,
uint32_t pushConstantRangeCount,
VkPushConstantRange const * pushConstantRanges,
VkShaderModule shaderModule,
uint32_t perInstanceStride,
uint32_t instanceAttributeDescriptionCount,
VkVertexInputAttributeDescription * instanceAttributeDescriptions,
VkPipelineLayout * pipelineLayout,
VkPipeline * pipeline);
VkShaderModule loadShader(VkDevice device,
char const * const path);
struct InstanceBuffer {
VkDeviceSize offset[2];
VkBuffer buffer;
VkDeviceMemory memory;
VkDeviceSize memorySize;
void * mappedData;
};
void createInstanceBuffer(VkDevice device,
VkPhysicalDeviceProperties const & physicalDeviceProperties,
VkPhysicalDeviceMemoryProperties const & physicalDeviceMemoryProperties,
VkDeviceSize bufferSize,
InstanceBuffer * instanceBuffer);

31
include/vulkan_state.h Normal file
View File

@ -0,0 +1,31 @@
#pragma once
#ifdef __APPLE__
#include "vulkan/vulkan.h"
#else
#include "volk/volk.h"
#endif
#include "vulkan_helper.h"
struct VulkanState {
VkInstance instance;
VkDevice device;
VkQueue queue;
VkCommandPool commandPool;
VkPhysicalDeviceProperties physicalDeviceProperties;
VkPhysicalDeviceMemoryProperties physicalDeviceMemoryProperties;
VkFormat colorFormat;
VkFormat depthFormat;
VertexIndex quadVertexIndex;
VulkanState(VkInstance instance,
VkDevice device,
VkQueue queue,
VkCommandPool commandPool,
VkPhysicalDeviceProperties const & physicalDeviceProperties,
VkPhysicalDeviceMemoryProperties const & physicalDeviceMemoryProperties,
VkFormat colorFormat,
VkFormat depthFormat);
};

View File

@ -333,7 +333,7 @@ def pass2_audio(state):
else:
audio_type = "0"
yield f"{{ .path = \"audio/{path}.opus.bin\", .loop_end = {float(loop)}, .audio_flags = {audio_type}, .attenuation = {attenuation} }}, // {i} {orig_path}"
yield f"{{ .path = \"audio/{path}.opus.bin\", .loop_end = {float(loop)}, .audio_flags = {audio_type}, .gain = {attenuation} }}, // {i} {orig_path}"
yield "};"
yield "const int audio_length = (sizeof (audio)) / (sizeof (audio[0]));"

48
shader/font/bitmap.hlsl Normal file
View File

@ -0,0 +1,48 @@
struct GlyphBitmap
{
uint2 Position; // x y, in texels
uint2 Size; // width height
};
// set 0: constant
[[vk::binding(0, 0)]] Texture2D FontTexture;
struct VSInput
{
float2 Position : POSITION0;
float2 Texture : TEXCOORD0;
// per-instance
uint2 InstancePosition : InstancePosition;
uint2 InstanceGlyph : InstanceGlyph;
};
struct VSOutput
{
float4 Position : SV_POSITION;
float2 Texture : NORMAL0;
};
[shader("vertex")]
VSOutput VSMain(VSInput input)
{
//float2 inverseTexel = float2(1.0 / 128.0, 1.0 / 64.0);
float2 inversePixel = float2(1.0 / 1280.0, 1.0 / 720.0);
float2 Size = float2(6, 12);
VSOutput output = (VSOutput)0;
float2 position = (input.Texture * Size + input.InstancePosition) * inversePixel;
output.Position = float4(position * 2.0 - 1.0, 0, 1);
output.Texture = (input.Texture * Size + input.InstanceGlyph * Size);
return output;
}
[shader("pixel")]
float4 PSMain(VSOutput input) : SV_TARGET
{
float4 color = FontTexture.Load(int3(input.Texture, 0));
float c = (color.x == 0) ? 0 : 1;
return float4(1, 1, 1, c.x);
}

38
shader/ui/solid.hlsl Normal file
View File

@ -0,0 +1,38 @@
struct VSInput
{
float2 Position : POSITION0;
float2 Texture : TEXCOORD0;
// per-instance
int2 InstancePosition : InstancePosition;
int2 InstanceSize : InstanceSize;
float4 Color : Color;
};
struct VSOutput
{
float4 Position : SV_POSITION;
float2 Texture : Texture;
float4 Color : Color;
};
[shader("vertex")]
VSOutput VSMain(VSInput input)
{
float2 inversePixel = float2(1.0 / 1280.0, 1.0 / 720.0);
float2 position = (input.Texture * input.InstanceSize + input.InstancePosition) * inversePixel;
VSOutput output = (VSOutput)0;
output.Position = float4(position * 2.0 - 1.0, 0, 1);
output.Texture = input.Texture;
output.Color = input.Color.zyxw;
return output;
}
[shader("pixel")]
float4 PSMain(VSOutput input) : SV_TARGET
{
//return float4(input.Texture, 0, 1);
return float4(input.Color.xyzw);
}

View File

@ -13,10 +13,7 @@
#include "poem.h"
namespace audio {
static int const frame_samples = 960; // 20 milliseconds @ 48kHz
static int const sample_rate = 48000;
static int const channels = 2;
static int const sample_size = (sizeof (int16_t));
static int const max_frame_size = 960 * 3; // 20ms at 48kHz
@ -31,6 +28,79 @@ namespace audio {
uint32_t sample_count;
};
template <int maxDelay>
FeedbackCombFilter<maxDelay>::FeedbackCombFilter(int delay, float gain0, float gainM)
: delay(delay), gain0(gain0), gainM(gainM)
{
//buffer = (float *)malloc((sizeof (float)) * maxDelay);
reset();
}
template <int maxDelay>
void FeedbackCombFilter<maxDelay>::reset()
{
index = 0;
memset(buffer, 0, (sizeof (float)) * maxDelay);
}
template <int maxDelay>
float FeedbackCombFilter<maxDelay>::feed(float value)
{
float y = gain0 * value + gainM * buffer[index];
buffer[index] = y;
index = (index + 1) % delay;
return y;
}
template <int maxDelay>
FeedforwardCombFilter<maxDelay>::FeedforwardCombFilter(int delay, float gain0, float gainM)
: delay(delay), gain0(gain0), gainM(gainM)
{
//buffer = (float *)malloc((sizeof (float)) * maxDelay);
reset();
}
template <int maxDelay>
void FeedforwardCombFilter<maxDelay>::reset()
{
index = 0;
memset(buffer, 0, (sizeof (float)) * maxDelay);
}
template <int maxDelay>
float FeedforwardCombFilter<maxDelay>::feed(float value)
{
float y = gain0 * value + gainM * buffer[index];
buffer[index] = value;
index = (index + 1) % delay;
return y;
}
template <int maxDelay>
AllpassFilter<maxDelay>::AllpassFilter(int delay, float gain0, float gainM)
: delay(delay), gain0(gain0), gainM(gainM)
{
//buffer = (float *)malloc((sizeof (float)) * maxDelay);
reset();
}
template <int maxDelay>
void AllpassFilter<maxDelay>::reset()
{
index = 0;
memset(buffer, 0, (sizeof (float)) * maxDelay);
}
template <int maxDelay>
float AllpassFilter<maxDelay>::feed(float x)
{
float v = x + -gainM * buffer[index];
float y = buffer[index] + gain0 * v;
buffer[index] = v;
index = (index + 1) % delay;
return y;
}
struct AudioInstance {
int audio_index;
AudioBuffer * audio_buffer;
@ -41,13 +111,99 @@ namespace audio {
poem::poem const * poem;
};
FBReverb::FBReverb()
: cf{FBCF(3229, 1.0f, 0.733f), // 1687
FBCF(3079, 1.0f, 0.802f), // 1601
FBCF(3943, 1.0f, 0.753f), // 2053
FBCF(4327, 1.0f, 0.733f), // 2251
}
, ap{AP(661, 0.7f, 0.7f), // 347
AP(257, 0.7f, 0.7f), // 113
AP(71, 0.7f, 0.7f), // 37
}
{ }
void FBReverb::reset()
{
for (int i = 0; i < cfCount; i++)
cf[i].reset();
for (int i = 0; i < apCount; i++)
ap[i].reset();
}
lr FBReverb::feed(float x)
{
for (int i = 0; i < apCount; i++) {
x = ap[i].feed(x);
}
float x0 = cf[0].feed(x);
float x1 = cf[1].feed(x);
float x2 = cf[2].feed(x);
float x3 = cf[3].feed(x);
float s0 = x0 + x2;
float s1 = x1 + x3;
float a = s0 + s1;
float b = s0 - s1;
return {a, b};
}
FFReverb::FFReverb()
: cf{FFCF{9209, 1.0f, 0.742f},
FFCF{9601, 1.0f, 0.733f},
FFCF{10369, 1.0f, 0.715f},
FFCF{11131, 1.0f, 0.697f},
}
, ap{AP{2017, 0.7f, 0.7f},
AP{647, 0.7f, 0.7f},
AP{137, 0.7f, 0.7f},
}
{ }
void FFReverb::reset()
{
for (int i = 0; i < __cfCount; i++) {
printf("reset cf %d\n", i);
cf[i].reset();
}
for (int i = 0; i < apCount; i++)
ap[i].reset();
}
lr FFReverb::feed(float x)
{
for (int i = 0; i < apCount; i++) {
x = ap[i].feed(x);
}
float x0 = cf[0].feed(x);
float x1 = cf[1].feed(x);
float x2 = cf[2].feed(x);
float x3 = cf[3].feed(x);
float s0 = x0 + x1 + x2 + x3;
//float s0 = x0 + x1 + x2;
return {s0, s0};
}
int reverbIndex = 0;
FBReverb fbreverb;
FFReverb ffreverb;
float dryGain = 1.0;
float wetGain = 0.25;
float mixChannelGain[mixChannelCount];
//
static SDL_AudioStream * audio_stream;
static SDL_AudioSpec audio_spec;
static OpusDecoder * opus_decoder;
static AudioBuffer * audio_buffers;
static int audio_buffers_count;
@ -64,17 +220,16 @@ namespace audio {
assert(audio_stream);
SDL_ResumeAudioStreamDevice(audio_stream);
int err;
opus_decoder = opus_decoder_create(sample_rate, channels, &err);
if (err < 0) {
fprintf(stderr, "opus_decoder_create: %s\n", opus_strerror(err));
assert(!"opus_decoder_create");
}
audio_instances_count = 0;
fbreverb.reset();
ffreverb.reset();
for (int i = 0; i < mixChannelCount; i++) {
mixChannelGain[i] = 1.0f;
}
}
void decode(char const * const filename, AudioBuffer * audio_buffer)
void decode(OpusDecoder * opus_decoder, char const * const filename, AudioBuffer * audio_buffer)
{
uint32_t size;
uint8_t const * buf = (uint8_t const *)file::open(filename, &size);
@ -127,14 +282,63 @@ namespace audio {
assert(audio_buffer->sample_count / 2);
}
struct LoadState {
OpusDecoder * opus_decoder;
renpy::language::audio const * audio;
int start;
int count;
};
static int loadAudio(void * data)
{
LoadState * loadState = (LoadState *)data;
for (int i = loadState->start; i < loadState->start + loadState->count; i++) {
audio_buffers[i].audio = &loadState->audio[i];
decode(loadState->opus_decoder, loadState->audio[i].path, &audio_buffers[i]);
}
return 0;
}
void load(renpy::language::audio const * const audio, int count)
{
audio_buffers = NewM<AudioBuffer>(count);
audio_buffers_count = count;
for (int i = 0; i < count; i++) {
audio_buffers[i].audio = &audio[i];
decode(audio[i].path, &audio_buffers[i]);
int core_count = SDL_GetNumLogicalCPUCores();
assert(core_count >= 1);
SDL_Thread ** threads = NewM<SDL_Thread *>(core_count);
LoadState * loadStates = NewM<LoadState>(core_count);
int per_core_count = count / core_count;
int remainder = count % core_count;
int start = 0;
for (int i = 0; i < core_count; i++) {
int this_core_count = per_core_count;
if (remainder) {
this_core_count += 1;
remainder -= 1;
}
int err;
loadStates[i].opus_decoder = opus_decoder_create(sample_rate, channels, &err);
if (err < 0) {
fprintf(stderr, "opus_decoder_create: %s\n", opus_strerror(err));
assert(!"opus_decoder_create");
}
loadStates[i].audio = audio;
loadStates[i].start = start;
loadStates[i].count = this_core_count;
start += this_core_count;
threads[i] = SDL_CreateThread(loadAudio, "loadAudio", &loadStates[i]);
}
for (int i = 0; i < core_count; i++) {
SDL_WaitThread(threads[i], nullptr);
opus_decoder_destroy(loadStates[i].opus_decoder);
}
free(threads);
free(loadStates);
}
void play(int audio_index)
@ -205,6 +409,7 @@ namespace audio {
return 1.0f;
if (v < -1.0f)
return -1.0f;
return v;
}
static inline void remove_instance(int instance_index)
@ -223,6 +428,9 @@ namespace audio {
uint32_t const sample_count = instance.audio_buffer->sample_count;
uint32_t const loop_end = instance.audio_buffer->audio->loop_end * (double)sample_rate;
uint32_t mix_index = 0;
float bufferGain = instance.audio_buffer->audio->gain;
for (int i = 0; i < half_period_samples; i++) {
if (loop_end != 0.0) {
if (instance.sample_index >= loop_end) {
@ -241,23 +449,19 @@ namespace audio {
assert(instance.sample_index < sample_count);
assert(instance.tail_index <= sample_count);
double fadeout = 1.0;
double attenuation = instance.audio_buffer->audio->attenuation;
assert(attenuation != 0.0);
//bool is_music = (instance.audio_buffer->audio->audio_flags & renpy::language::audio::music) != 0;
//if (is_music)
//fprintf(stderr, "attenuation %f\n", attenuation);
float fadeout = 1.0;
if (instance.fadeout_end != 0) {
fadeout = 1.0 - ((double)instance.fadeout_index / (double)instance.fadeout_end);
fadeout = 1.0 - ((float)instance.fadeout_index / (float)instance.fadeout_end);
}
for (int ch = 0; ch < channels; ch++) {
int32_t value = buf[instance.sample_index * channels + ch];
float value = buf[instance.sample_index * channels + ch];
if (instance.tail_index != sample_count) {
value += buf[instance.tail_index * channels + ch];
}
constexpr double scale = 1.0f / 32768.0f;
mix_buffer[mix_index * channels + ch] += (double)value * fadeout * attenuation * scale;
constexpr float scale = 1.0f / 32768.0f;
float output = value * fadeout * bufferGain * scale;
mix_buffer[mix_index * channels + ch] += output;
}
instance.sample_index += 1;
instance.fadeout_index += 1;
@ -308,18 +512,32 @@ namespace audio {
}
}
static inline int getMixChannel(AudioInstance & instance)
{
if (instance.audio_buffer->audio->audio_flags & renpy::language::audio::music) {
return mix_channel::music;
} else if (instance.audio_buffer->audio->audio_flags & renpy::language::audio::poem) {
return mix_channel::poem;
} else {
return mix_channel::voice;
}
}
void update()
{
float mix_buffer[half_period_samples * channels];
float channel_buffer[mixChannelCount][half_period_samples * channels];
static_assert((sizeof (channel_buffer)) == half_period_samples * channels * mixChannelCount * (sizeof (float)));
if (SDL_GetAudioStreamQueued(audio_stream) >= (int)(sizeof (mix_buffer)))
return;
memset(mix_buffer, 0, (sizeof (mix_buffer)));
memset(&mix_buffer[0], 0, (sizeof (mix_buffer)));
memset(&channel_buffer[0][0], 0, (sizeof (channel_buffer)));
poem_playing = nullptr;
for (int i = 0; i < audio_instances_count; i++) {
update_instance(mix_buffer, audio_instances[i]);
update_instance(channel_buffer[getMixChannel(audio_instances[i])], audio_instances[i]);
update_poem(audio_instances[i]);
}
@ -336,6 +554,35 @@ namespace audio {
}
}
// audio configuration "B"
// mono reverberation
for (int i = 0; i < half_period_samples; i++) {
float value = channel_buffer[mix_channel::voice][i * channels + 0];
lr wet;
if (reverbIndex == 0) {
wet = fbreverb.feed(value);
}
else {
wet = ffreverb.feed(value);
}
float left = value * dryGain + wet.l * wetGain;
float right = value * dryGain + wet.r * wetGain;
channel_buffer[mix_channel::voice][i * channels + 0] = left;
channel_buffer[mix_channel::voice][i * channels + 1] = right;
}
for (int i = 0; i < half_period_samples; i++) {
for (int ch = 0; ch < channels; ch++) {
float value = 0;
for (int mixChannel = 0; mixChannel < mixChannelCount; mixChannel++) {
float gain = mixChannelGain[mixChannel];
value += channel_buffer[mixChannel][i * channels + ch] * gain;
}
mix_buffer[i * channels + ch] = clampf(value);
}
}
SDL_PutAudioStreamData(audio_stream, (void *)mix_buffer, (int)(sizeof (mix_buffer)));
}
}

269
src/font/bitmap/vulkan.cpp Normal file
View File

@ -0,0 +1,269 @@
#include <stdio.h>
#ifdef __APPLE__
#include "vulkan/vulkan.h"
#else
#include "volk/volk.h"
#endif
#include "vulkan/vk_enum_string_helper.h"
#include "vulkan_helper.h"
#include "check.h"
#include "file.h"
#include "font/bitmap/vulkan.h"
namespace font::bitmap {
//////////////////////////////////////////////////////////////////////
// pipeline
//////////////////////////////////////////////////////////////////////
void vulkan::createPipeline()
{
uint32_t pushConstantRangeCount = 0;
VkPushConstantRange const * pushConstantRanges = nullptr;
VkShaderModule shaderModule = loadShader(vk->device, "shader/font/bitmap.spv");
uint32_t perInstanceStride = (sizeof (BitmapInstance));
constexpr uint32_t instanceAttributeDescriptionCount = 2;
VkVertexInputAttributeDescription instanceAttributeDescriptions[instanceAttributeDescriptionCount] {
{ // position
.location = 2,
.binding = 1,
.format = VK_FORMAT_R16G16_UINT,
.offset = 0,
},
{ // glyph
.location = 3,
.binding = 1,
.format = VK_FORMAT_R16G16_UINT,
.offset = 4,
},
};
createQuadPipeline(vk->device,
vk->colorFormat,
vk->depthFormat,
descriptorSetLayoutCount,
descriptorSetLayouts,
pushConstantRangeCount,
pushConstantRanges,
shaderModule,
perInstanceStride,
instanceAttributeDescriptionCount,
instanceAttributeDescriptions,
&pipelineLayout,
&pipeline);
}
//////////////////////////////////////////////////////////////////////
// descriptor sets
//////////////////////////////////////////////////////////////////////
void vulkan::createDescriptorSets()
{
//
// pool
//
constexpr int descriptorPoolSizesCount = 1;
VkDescriptorPoolSize descriptorPoolSizes[descriptorPoolSizesCount]{
{
.type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
.descriptorCount = 1,
},
};
VkDescriptorPoolCreateInfo descriptorPoolCreateInfo{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.maxSets = 1,
.poolSizeCount = descriptorPoolSizesCount,
.pPoolSizes = descriptorPoolSizes
};
VK_CHECK(vkCreateDescriptorPool(vk->device, &descriptorPoolCreateInfo, nullptr, &descriptorPool));
//
// (set 0, constant)
//
{
constexpr int bindingCount = 1;
VkDescriptorSetLayoutBinding descriptorSetLayoutBindings[bindingCount]{
{ // font image
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT
},
};
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.bindingCount = bindingCount,
.pBindings = descriptorSetLayoutBindings
};
VK_CHECK(vkCreateDescriptorSetLayout(vk->device, &descriptorSetLayoutCreateInfo, nullptr, &descriptorSetLayouts[0]));
VkDescriptorSetAllocateInfo descriptorSetAllocateInfo{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.descriptorPool = descriptorPool,
.descriptorSetCount = 1,
.pSetLayouts = &descriptorSetLayouts[0]
};
VK_CHECK(vkAllocateDescriptorSets(vk->device, &descriptorSetAllocateInfo, &descriptorSet0));
}
}
//////////////////////////////////////////////////////////////////////
// descriptor set writes
//////////////////////////////////////////////////////////////////////
void vulkan::writeDescriptorSets(VkImageView imageView)
{
constexpr uint32_t writeCount = 1;
VkWriteDescriptorSet writeDescriptorSets[writeCount];
uint32_t writeIndex = 0;
// set0 bindings
VkDescriptorImageInfo terrainDescriptorImageInfo = {
.imageView = imageView,
.imageLayout = VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL
};
writeDescriptorSets[writeIndex++] = {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = descriptorSet0,
.dstBinding = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
.pImageInfo = &terrainDescriptorImageInfo
};
assert(writeIndex == writeCount);
vkUpdateDescriptorSets(vk->device, writeIndex, writeDescriptorSets, 0, nullptr);
}
//////////////////////////////////////////////////////////////////////
// load font
//////////////////////////////////////////////////////////////////////
LoadedFont vulkan::loadFont()
{
uint32_t font_data_size;
void const * font_data = file::open("data/font/bitmap/terminus_128x64_6x12.data", &font_data_size);
assert(font_data != nullptr);
void const * texture_data = font_data;
uint32_t texture_size = font_data_size;
uint32_t texture_width = 128;
uint32_t texture_height = 64;
// transfer texture
VkCommandBuffer commandBuffer{};
VkCommandBufferAllocateInfo commandBufferAllocateInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.commandPool = vk->commandPool,
.commandBufferCount = 1
};
VK_CHECK(vkAllocateCommandBuffers(vk->device, &commandBufferAllocateInfo, &commandBuffer));
VkFenceCreateInfo fenceCreateInfo{
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO
};
VkFence fence{};
VK_CHECK(vkCreateFence(vk->device, &fenceCreateInfo, nullptr, &fence));
void const * imageData = texture_data;
uint32_t imageDataSize = texture_size;
VkFormat format = VK_FORMAT_R8_UNORM;
uint32_t width = texture_width;
uint32_t height = texture_height;
uint32_t levelCount = 1;
uint32_t levelOffset = 0;
VkImage outImage;
VkDeviceMemory outMemory;
VkImageView outImageView;
createImage(vk->device,
vk->physicalDeviceProperties.limits.nonCoherentAtomSize,
vk->physicalDeviceMemoryProperties,
format,
width,
height,
levelCount,
&outImage,
&outMemory,
&outImageView);
textureTransfer(vk->device,
vk->queue,
commandBuffer,
fence,
vk->physicalDeviceProperties.limits.nonCoherentAtomSize,
vk->physicalDeviceMemoryProperties,
imageDataSize,
imageData,
outImage,
width,
height,
levelCount,
&levelOffset);
vkDestroyFence(vk->device, fence, nullptr);
vkFreeCommandBuffers(vk->device,
vk->commandPool,
1,
&commandBuffer);
// return
return {
.image = outImage,
.memory = outMemory,
.imageView = outImageView,
};
}
void vulkan::update(uint32_t frameIndex, uint32_t glyphCount) const
{
constexpr int mappedMemoryRangesCount = 1;
VkMappedMemoryRange mappedMemoryRanges[mappedMemoryRangesCount]{
{
.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
.memory = instanceBuffer.memory,
.offset = instanceBuffer.offset[frameIndex],
.size = (sizeof (BitmapInstance)) * glyphCount,
}
};
alignMappedMemoryRanges(vk->physicalDeviceProperties.limits.nonCoherentAtomSize,
instanceBuffer.memorySize,
mappedMemoryRangesCount,
mappedMemoryRanges);
vkFlushMappedMemoryRanges(vk->device, mappedMemoryRangesCount, mappedMemoryRanges);
}
void vulkan::draw(VkCommandBuffer commandBuffer,
uint32_t frameIndex,
uint32_t glyphCount) const
{
update(frameIndex, glyphCount);
//
uint32_t outputIndex = glyphCount;
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
VkDescriptorSet descriptorSets[1] = {
descriptorSet0,
};
vkCmdBindDescriptorSets(commandBuffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
pipelineLayout,
0, 1, descriptorSets,
0, nullptr);
vkCmdBindIndexBuffer(commandBuffer, vk->quadVertexIndex.buffer, vk->quadVertexIndex.indexOffset, VK_INDEX_TYPE_UINT16);
VkDeviceSize vertexOffsets[2]{ 0, instanceBuffer.offset[frameIndex] };
VkBuffer vertexBuffers[2]{ vk->quadVertexIndex.buffer, instanceBuffer.buffer };
vkCmdBindVertexBuffers(commandBuffer, 0, 2, vertexBuffers, vertexOffsets);
vkCmdDrawIndexed(commandBuffer, 4, outputIndex, 0, 0, 0);
}
}

View File

@ -805,7 +805,7 @@ namespace font::outline {
{
.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
.memory = instanceMemory,
.offset = 0,
.offset = instanceBufferOffset[frameIndex],
.size = (sizeof (GlyphInstance)) * outputIndex,
}
};

View File

@ -20,14 +20,20 @@
#include "shader_data.h"
#include "minmax.h"
#include "view.h"
#include "mouse.h"
#include "font/outline.h"
#include "font/bitmap.h"
#include "font/bitmap/vulkan.h"
#include "renpy/vulkan.h"
#include "renpy/composite/vulkan.h"
#include "renpy/interpreter.h"
#include "renpy/interact.h"
#include "renpy/script.h"
#include "ui/vulkan.h"
#include "ui.h"
#include "audio.h"
VkInstance instance{ VK_NULL_HANDLE };
@ -369,6 +375,8 @@ void offscreenRender(VkCommandBuffer commandBuffer, int frameIndex,
renpy::vulkan const & renpy_state,
renpy::interpreter const & interpreter_state,
font::outline::font const & font_state,
font::bitmap::vulkan const & bitmap_font_state,
ui::vulkan const & ui_state,
VkSurfaceCapabilitiesKHR const & surfaceCapabilities)
{
// barrier
@ -474,6 +482,11 @@ void offscreenRender(VkCommandBuffer commandBuffer, int frameIndex,
font_state.draw(commandBuffer, frameIndex, interpreter_state);
}
MappedInstanceData<font::BitmapInstance> bitmapData{ bitmap_font_state.bitmapInstance[frameIndex],
font::bitmap::vulkan::maximumGlyphCount, 0 };
ui_state.draw(commandBuffer, frameIndex, bitmapData);
bitmap_font_state.draw(commandBuffer, frameIndex, bitmapData.index);
vkCmdEndRendering(commandBuffer);
// barrier
@ -505,13 +518,6 @@ void offscreenRender(VkCommandBuffer commandBuffer, int frameIndex,
}
}
static inline double clamp01(double a)
{
if (a < 0.0) return 0.0;
if (a > 1.0) return 1.0;
return a;
}
void handlePause(renpy::interpreter & interpreter_state,
int64_t const start_time,
double & pause_start,
@ -929,6 +935,15 @@ int main()
minecraft_state.init();
*/
VulkanState vulkan_state(instance,
device,
queue,
commandPool,
physicalDeviceProperties,
physicalDeviceMemoryProperties,
surfaceFormat.format,
depthFormat);
//////////////////////////////////////////////////////////////////////
// initialize font
//////////////////////////////////////////////////////////////////////
@ -945,6 +960,16 @@ int main()
textureSamplers[2]);
font_state.init();
// bitmap font
font::bitmap::vulkan bitmap_font_state(&vulkan_state);
//////////////////////////////////////////////////////////////////////
// ui
//////////////////////////////////////////////////////////////////////
ui::vulkan ui_state(&vulkan_state);
//////////////////////////////////////////////////////////////////////
// initialize renpy
//////////////////////////////////////////////////////////////////////
@ -966,7 +991,14 @@ int main()
//////////////////////////////////////////////////////////////////////
audio::init();
int64_t audio_start;
SDL_GetCurrentTime(&audio_start);
audio::load(renpy::script::audio, renpy::script::audio_length);
int64_t audio_end;
SDL_GetCurrentTime(&audio_end);
double audio_time = (double)(audio_end - audio_start) * (0.000000001);
// 2.17
printf("audio_time %f\n", audio_time);
//////////////////////////////////////////////////////////////////////
// interpreter
@ -975,7 +1007,7 @@ int main()
renpy::interpreter interpreter_state;
//interpreter_state.reset(88);
//interpreter_state.reset(427);
interpreter_state.reset(0);
interpreter_state.reset(347);
//while (interpreter_state.pc < 543) {
/*
while (interpreter_state.pc < 26) {
@ -1054,7 +1086,8 @@ int main()
bool useGamepad = false;
uint32_t whichGamepad = 0;
SDL_Thread * audio_thread = SDL_CreateThread(mainAudio, "audio", &quit);
SDL_Thread * audio_thread = nullptr;
audio_thread = SDL_CreateThread(mainAudio, "audio", &quit);
while (quit == false) {
//////////////////////////////////////////////////////////////////////
@ -1125,7 +1158,10 @@ int main()
float mx;
float my;
uint32_t mouseFlags = SDL_GetMouseState(&mx, &my);
static bool mLastLeft = false;
bool mLeft = (mouseFlags & SDL_BUTTON_LMASK) != 0;
bool mEdge = (mLeft && (mLeft != mLastLeft));
mLastLeft = mLeft;
bool gUp = false;
bool gDown = false;
bool gAccept = false;
@ -1140,6 +1176,13 @@ int main()
}
}
float mxf;
float myf;
mouse::normalize(surfaceCapabilities.currentExtent.width,
surfaceCapabilities.currentExtent.height,
mx, my, &mxf, &myf);
ui::update(mxf, myf, mLeft, mEdge);
renpy::update(interpreter_state, mx, my, mLeft,
gUp, gDown, gAccept, useGamepad,
surfaceCapabilities.currentExtent.width,
@ -1329,10 +1372,10 @@ int main()
//////////////////////////////////////////////////////////////////////
if (interpreter_state.pause.dissolve) {
offscreenRender(commandBuffer, frameIndex, mx, my, 2, true, renpy_state, interpreter_state, font_state, surfaceCapabilities);
offscreenRender(commandBuffer, frameIndex, mx, my, 2, true, renpy_state, interpreter_state, font_state, bitmap_font_state, ui_state, surfaceCapabilities);
} else {
offscreenRender(commandBuffer, frameIndex, mx, my, 0, true, renpy_state, interpreter_state, font_state, surfaceCapabilities);
offscreenRender(commandBuffer, frameIndex, mx, my, 1, false, renpy_state, interpreter_state, font_state, surfaceCapabilities);
offscreenRender(commandBuffer, frameIndex, mx, my, 0, true, renpy_state, interpreter_state, font_state, bitmap_font_state, ui_state, surfaceCapabilities);
offscreenRender(commandBuffer, frameIndex, mx, my, 1, false, renpy_state, interpreter_state, font_state, bitmap_font_state, ui_state, surfaceCapabilities);
}
//////////////////////////////////////////////////////////////////////
@ -1541,6 +1584,7 @@ int main()
}
}
if (audio_thread != nullptr)
SDL_WaitThread(audio_thread, nullptr);
VK_CHECK(vkDeviceWaitIdle(device));

View File

@ -285,241 +285,241 @@ const language::character characters[] = {
const int characters_length = (sizeof (characters)) / (sizeof (characters[0]));
const language::audio audio[] = {
{ .path = "audio/music/MistAmbience.opus.bin", .loop_end = 22.0, .audio_flags = audio::music, .attenuation = 1.0 }, // 0 music/MistAmbience.ogg
{ .path = "audio/sfx/Chime.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 1 sfx/Chime.ogg
{ .path = "audio/nara/n1.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 2 nara/n1.ogg
{ .path = "audio/nara/n2.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 3 nara/n2.ogg
{ .path = "audio/nara/n3.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 4 nara/n3.ogg
{ .path = "audio/music/TinyForestMinstrels.opus.bin", .loop_end = 44.0, .audio_flags = audio::music, .attenuation = 0.45 }, // 5 music/TinyForestMinstrels.ogg
{ .path = "audio/nara/n4.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 6 nara/n4.ogg
{ .path = "audio/alice/a1.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 7 alice/a1.ogg
{ .path = "audio/eily/e1.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 8 eily/e1.ogg
{ .path = "audio/alice/a2.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 9 alice/a2.ogg
{ .path = "audio/eily/e2.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 10 eily/e2.ogg
{ .path = "audio/alice/a3.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 11 alice/a3.ogg
{ .path = "audio/eily/e3.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 12 eily/e3.ogg
{ .path = "audio/alice/a4.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 13 alice/a4.ogg
{ .path = "audio/alice/a5.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 14 alice/a5.ogg
{ .path = "audio/eily/e4.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 15 eily/e4.ogg
{ .path = "audio/eily/e5.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 16 eily/e5.ogg
{ .path = "audio/alice/a6.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 17 alice/a6.ogg
{ .path = "audio/eily/e6.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 18 eily/e6.ogg
{ .path = "audio/alice/a7.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 19 alice/a7.ogg
{ .path = "audio/eily/e7.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 20 eily/e7.ogg
{ .path = "audio/nara/n5.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 21 nara/n5.ogg
{ .path = "audio/music/PhrygianButterflies.opus.bin", .loop_end = 40.2, .audio_flags = audio::music, .attenuation = 0.5 }, // 22 music/PhrygianButterflies.ogg
{ .path = "audio/eily/e8.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 23 eily/e8.ogg
{ .path = "audio/alice/a8.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 24 alice/a8.ogg
{ .path = "audio/eily/e9.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 25 eily/e9.ogg
{ .path = "audio/eily/e10.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 26 eily/e10.ogg
{ .path = "audio/alice/a9.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 27 alice/a9.ogg
{ .path = "audio/eily/e11.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 28 eily/e11.ogg
{ .path = "audio/eily/e12.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 29 eily/e12.ogg
{ .path = "audio/alice/a10.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 30 alice/a10.ogg
{ .path = "audio/eily/e13.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 31 eily/e13.ogg
{ .path = "audio/eily/e14.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 32 eily/e14.ogg
{ .path = "audio/alice/a11.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 33 alice/a11.ogg
{ .path = "audio/eily/e15.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 34 eily/e15.ogg
{ .path = "audio/poem/EleanorTheHero.opus.bin", .loop_end = 0.0, .audio_flags = audio::poem, .attenuation = 1.0 }, // 35 poem/EleanorTheHero.ogg
{ .path = "audio/eily/e16.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 36 eily/e16.ogg
{ .path = "audio/alice/a12.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 37 alice/a12.ogg
{ .path = "audio/leona/c1.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 38 leona/c1.ogg
{ .path = "audio/music/ScaredMice.opus.bin", .loop_end = 8.0, .audio_flags = audio::music, .attenuation = 1.0 }, // 39 music/ScaredMice.ogg
{ .path = "audio/mousegirls/mg1.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 40 mousegirls/mg1.ogg
{ .path = "audio/leona/c2.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 41 leona/c2.ogg
{ .path = "audio/leona/c3.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 42 leona/c3.ogg
{ .path = "audio/alice/a14.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 43 alice/a14.ogg
{ .path = "audio/eily/e18.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 44 eily/e18.ogg
{ .path = "audio/eily/e19.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 45 eily/e19.ogg
{ .path = "audio/leona/c4.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 46 leona/c4.ogg
{ .path = "audio/leona/c5.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 47 leona/c5.ogg
{ .path = "audio/leona/c6.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 48 leona/c6.ogg
{ .path = "audio/eily/e20.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 49 eily/e20.ogg
{ .path = "audio/alice/a15.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 50 alice/a15.ogg
{ .path = "audio/eily/e21.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 51 eily/e21.ogg
{ .path = "audio/leona/c7.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 52 leona/c7.ogg
{ .path = "audio/leona/c8.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 53 leona/c8.ogg
{ .path = "audio/eily/e22.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 54 eily/e22.ogg
{ .path = "audio/leona/c9.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 55 leona/c9.ogg
{ .path = "audio/eily/e23.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 56 eily/e23.ogg
{ .path = "audio/leona/c10.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 57 leona/c10.ogg
{ .path = "audio/eily/e24.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 58 eily/e24.ogg
{ .path = "audio/leona/c11.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 59 leona/c11.ogg
{ .path = "audio/eily/e25.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 60 eily/e25.ogg
{ .path = "audio/leona/c12.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 61 leona/c12.ogg
{ .path = "audio/leona/c13.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 62 leona/c13.ogg
{ .path = "audio/eily/e26.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 63 eily/e26.ogg
{ .path = "audio/eily/e27.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 64 eily/e27.ogg
{ .path = "audio/leona/c14.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 65 leona/c14.ogg
{ .path = "audio/alice/a16.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 66 alice/a16.ogg
{ .path = "audio/leona/c15.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 67 leona/c15.ogg
{ .path = "audio/leona/c16.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 68 leona/c16.ogg
{ .path = "audio/leona/c17.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 69 leona/c17.ogg
{ .path = "audio/alice/a17.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 70 alice/a17.ogg
{ .path = "audio/eily/e28.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 71 eily/e28.ogg
{ .path = "audio/alice/a18.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 72 alice/a18.ogg
{ .path = "audio/eily/e29.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 73 eily/e29.ogg
{ .path = "audio/nara/n6.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 74 nara/n6.ogg
{ .path = "audio/eily/e30.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 75 eily/e30.ogg
{ .path = "audio/alice/a19.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 76 alice/a19.ogg
{ .path = "audio/eily/e31.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 77 eily/e31.ogg
{ .path = "audio/alice/a20.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 78 alice/a20.ogg
{ .path = "audio/leona/c18.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 79 leona/c18.ogg
{ .path = "audio/eily/e32.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 80 eily/e32.ogg
{ .path = "audio/alice/a21.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 81 alice/a21.ogg
{ .path = "audio/eily/e33.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 82 eily/e33.ogg
{ .path = "audio/leona/c19.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 83 leona/c19.ogg
{ .path = "audio/leona/c20.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 84 leona/c20.ogg
{ .path = "audio/leona/c21.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 85 leona/c21.ogg
{ .path = "audio/alice/a22.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 86 alice/a22.ogg
{ .path = "audio/leona/c22.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 87 leona/c22.ogg
{ .path = "audio/leona/c23.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 88 leona/c23.ogg
{ .path = "audio/alice/a23.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 89 alice/a23.ogg
{ .path = "audio/eily/e34.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 90 eily/e34.ogg
{ .path = "audio/alice/a24.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 91 alice/a24.ogg
{ .path = "audio/eily/e35.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 92 eily/e35.ogg
{ .path = "audio/leona/c24.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 93 leona/c24.ogg
{ .path = "audio/leona/c25.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 94 leona/c25.ogg
{ .path = "audio/leona/c26.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 95 leona/c26.ogg
{ .path = "audio/eily/e36.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 96 eily/e36.ogg
{ .path = "audio/eily/e37.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 97 eily/e37.ogg
{ .path = "audio/eily/e38.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 98 eily/e38.ogg
{ .path = "audio/eily/e39.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 99 eily/e39.ogg
{ .path = "audio/eily/e40.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 100 eily/e40.ogg
{ .path = "audio/eily/e41.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 101 eily/e41.ogg
{ .path = "audio/eily/e42.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 102 eily/e42.ogg
{ .path = "audio/alice/a25.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 103 alice/a25.ogg
{ .path = "audio/eily/e43.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 104 eily/e43.ogg
{ .path = "audio/alice/a26.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 105 alice/a26.ogg
{ .path = "audio/eily/e44.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 106 eily/e44.ogg
{ .path = "audio/eily/e45.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 107 eily/e45.ogg
{ .path = "audio/alice/a27.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 108 alice/a27.ogg
{ .path = "audio/eily/e46.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 109 eily/e46.ogg
{ .path = "audio/alice/a28.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 110 alice/a28.ogg
{ .path = "audio/leona/c27.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 111 leona/c27.ogg
{ .path = "audio/eily/e47.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 112 eily/e47.ogg
{ .path = "audio/alice/a29.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 113 alice/a29.ogg
{ .path = "audio/leona/c28.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 114 leona/c28.ogg
{ .path = "audio/leona/c29.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 115 leona/c29.ogg
{ .path = "audio/eily/e48.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 116 eily/e48.ogg
{ .path = "audio/leona/c30.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 117 leona/c30.ogg
{ .path = "audio/alice/a30.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 118 alice/a30.ogg
{ .path = "audio/eily/e49.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 119 eily/e49.ogg
{ .path = "audio/eily/e50.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 120 eily/e50.ogg
{ .path = "audio/eily/e51.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 121 eily/e51.ogg
{ .path = "audio/eily/e52.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 122 eily/e52.ogg
{ .path = "audio/eily/e53.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 123 eily/e53.ogg
{ .path = "audio/eily/e54.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 124 eily/e54.ogg
{ .path = "audio/alice/a31.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 125 alice/a31.ogg
{ .path = "audio/leona/c31.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 126 leona/c31.ogg
{ .path = "audio/leona/c32.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 127 leona/c32.ogg
{ .path = "audio/alice/a32.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 128 alice/a32.ogg
{ .path = "audio/leona/c33.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 129 leona/c33.ogg
{ .path = "audio/mousegirls/mg2.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 130 mousegirls/mg2.ogg
{ .path = "audio/leona/c34.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 131 leona/c34.ogg
{ .path = "audio/leona/c35.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 132 leona/c35.ogg
{ .path = "audio/alice/a33.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 133 alice/a33.ogg
{ .path = "audio/eily/e55.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 134 eily/e55.ogg
{ .path = "audio/leona/c36.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 135 leona/c36.ogg
{ .path = "audio/alice/a34.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 136 alice/a34.ogg
{ .path = "audio/eily/e56.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 137 eily/e56.ogg
{ .path = "audio/nara/n7.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 138 nara/n7.ogg
{ .path = "audio/music/WheatFields.opus.bin", .loop_end = 34.0, .audio_flags = audio::music, .attenuation = 1.0 }, // 139 music/WheatFields.ogg
{ .path = "audio/leona/c37.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 140 leona/c37.ogg
{ .path = "audio/alice/a35.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 141 alice/a35.ogg
{ .path = "audio/eily/e57.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 142 eily/e57.ogg
{ .path = "audio/leona/c38.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 143 leona/c38.ogg
{ .path = "audio/leona/c39.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 144 leona/c39.ogg
{ .path = "audio/eily/e58.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 145 eily/e58.ogg
{ .path = "audio/alice/a36.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 146 alice/a36.ogg
{ .path = "audio/eily/e59.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 147 eily/e59.ogg
{ .path = "audio/eily/e60.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 148 eily/e60.ogg
{ .path = "audio/leona/c40.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 149 leona/c40.ogg
{ .path = "audio/eily/e61.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 150 eily/e61.ogg
{ .path = "audio/leona/c41.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 151 leona/c41.ogg
{ .path = "audio/leona/c42.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 152 leona/c42.ogg
{ .path = "audio/alice/a37.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 153 alice/a37.ogg
{ .path = "audio/eily/e62.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 154 eily/e62.ogg
{ .path = "audio/leona/c43.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 155 leona/c43.ogg
{ .path = "audio/eily/e63.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 156 eily/e63.ogg
{ .path = "audio/leona/c44.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 157 leona/c44.ogg
{ .path = "audio/leona/c45.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 158 leona/c45.ogg
{ .path = "audio/leona/c46.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 159 leona/c46.ogg
{ .path = "audio/eily/e64.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 160 eily/e64.ogg
{ .path = "audio/leona/c47.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 161 leona/c47.ogg
{ .path = "audio/eily/e65.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 162 eily/e65.ogg
{ .path = "audio/leona/c48.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 163 leona/c48.ogg
{ .path = "audio/leona/c49.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 164 leona/c49.ogg
{ .path = "audio/leona/c50.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 165 leona/c50.ogg
{ .path = "audio/mousegirls/mg3.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 166 mousegirls/mg3.ogg
{ .path = "audio/leona/c51.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 167 leona/c51.ogg
{ .path = "audio/leona/c52.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 168 leona/c52.ogg
{ .path = "audio/leona/c53.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 169 leona/c53.ogg
{ .path = "audio/poem/KiriStella.opus.bin", .loop_end = 0.0, .audio_flags = audio::poem, .attenuation = 1.0 }, // 170 poem/KiriStella.ogg
{ .path = "audio/leona/c54.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 171 leona/c54.ogg
{ .path = "audio/leona/c55.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 172 leona/c55.ogg
{ .path = "audio/alice/a39.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 173 alice/a39.ogg
{ .path = "audio/eily/e67.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 174 eily/e67.ogg
{ .path = "audio/eily/e68.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 175 eily/e68.ogg
{ .path = "audio/leona/c56.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 176 leona/c56.ogg
{ .path = "audio/nara/n8.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 177 nara/n8.ogg
{ .path = "audio/nara/n9.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 178 nara/n9.ogg
{ .path = "audio/nara/n10.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 179 nara/n10.ogg
{ .path = "audio/hera/h1.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 180 hera/h1.ogg
{ .path = "audio/music/Preludium.opus.bin", .loop_end = 58.2, .audio_flags = audio::music, .attenuation = 0.45 }, // 181 music/Preludium.ogg
{ .path = "audio/hera/h2.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 182 hera/h2.ogg
{ .path = "audio/leona/c57.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 183 leona/c57.ogg
{ .path = "audio/hera/h3.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 184 hera/h3.ogg
{ .path = "audio/eily/e69.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 185 eily/e69.ogg
{ .path = "audio/hera/h4.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 186 hera/h4.ogg
{ .path = "audio/sfx/Glass.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 187 sfx/Glass.ogg
{ .path = "audio/alice/a40.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 188 alice/a40.ogg
{ .path = "audio/alice/a41.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 189 alice/a41.ogg
{ .path = "audio/leona/c58.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 190 leona/c58.ogg
{ .path = "audio/bird/b1.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 191 bird/b1.ogg
{ .path = "audio/bird/b2.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 192 bird/b2.ogg
{ .path = "audio/bird/b3.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 193 bird/b3.ogg
{ .path = "audio/leona/c59.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 194 leona/c59.ogg
{ .path = "audio/eily/e70.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 195 eily/e70.ogg
{ .path = "audio/bird/b4.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 196 bird/b4.ogg
{ .path = "audio/bird/b5.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 197 bird/b5.ogg
{ .path = "audio/leona/c60.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 198 leona/c60.ogg
{ .path = "audio/leona/c61.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 199 leona/c61.ogg
{ .path = "audio/bird/b6.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 200 bird/b6.ogg
{ .path = "audio/bird/b7.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 201 bird/b7.ogg
{ .path = "audio/leona/c0.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 202 leona/c0.ogg
{ .path = "audio/bird/b8.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 203 bird/b8.ogg
{ .path = "audio/bird/b9.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 204 bird/b9.ogg
{ .path = "audio/bird/b10.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 205 bird/b10.ogg
{ .path = "audio/leona/c62.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 206 leona/c62.ogg
{ .path = "audio/bird/b11.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 207 bird/b11.ogg
{ .path = "audio/bird/b12.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 208 bird/b12.ogg
{ .path = "audio/bird/b13.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 209 bird/b13.ogg
{ .path = "audio/leona/c63.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 210 leona/c63.ogg
{ .path = "audio/bird/b14.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 211 bird/b14.ogg
{ .path = "audio/leona/c64.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 212 leona/c64.ogg
{ .path = "audio/bird/b15.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 213 bird/b15.ogg
{ .path = "audio/leona/c65.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 214 leona/c65.ogg
{ .path = "audio/leona/c66.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 215 leona/c66.ogg
{ .path = "audio/bird/b16.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 216 bird/b16.ogg
{ .path = "audio/bird/b17.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 217 bird/b17.ogg
{ .path = "audio/alice/a42.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 218 alice/a42.ogg
{ .path = "audio/eily/e71.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 219 eily/e71.ogg
{ .path = "audio/eily/e72.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 220 eily/e72.ogg
{ .path = "audio/eily/e73.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 221 eily/e73.ogg
{ .path = "audio/eily/e74.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 222 eily/e74.ogg
{ .path = "audio/eily/e75.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 223 eily/e75.ogg
{ .path = "audio/eily/e76.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 224 eily/e76.ogg
{ .path = "audio/bird/b18.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 225 bird/b18.ogg
{ .path = "audio/leona/c67.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 226 leona/c67.ogg
{ .path = "audio/alice/a43.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 227 alice/a43.ogg
{ .path = "audio/eily/e77.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 228 eily/e77.ogg
{ .path = "audio/bird/b19.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 229 bird/b19.ogg
{ .path = "audio/bird/b20.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 230 bird/b20.ogg
{ .path = "audio/poem/BirdSong.opus.bin", .loop_end = 0.0, .audio_flags = audio::poem, .attenuation = 1.0 }, // 231 poem/BirdSong.ogg
{ .path = "audio/nara/n11.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 232 nara/n11.ogg
{ .path = "audio/nara/n12.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 233 nara/n12.ogg
{ .path = "audio/nara/n13.opus.bin", .loop_end = 0.0, .audio_flags = 0, .attenuation = 1.0 }, // 234 nara/n13.ogg
{ .path = "audio/music/MistAmbience.opus.bin", .loop_end = 22.0, .audio_flags = audio::music, .gain = 1.0 }, // 0 music/MistAmbience.ogg
{ .path = "audio/sfx/Chime.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 1 sfx/Chime.ogg
{ .path = "audio/nara/n1.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 2 nara/n1.ogg
{ .path = "audio/nara/n2.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 3 nara/n2.ogg
{ .path = "audio/nara/n3.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 4 nara/n3.ogg
{ .path = "audio/music/TinyForestMinstrels.opus.bin", .loop_end = 44.0, .audio_flags = audio::music, .gain = 0.45 }, // 5 music/TinyForestMinstrels.ogg
{ .path = "audio/nara/n4.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 6 nara/n4.ogg
{ .path = "audio/alice/a1.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 7 alice/a1.ogg
{ .path = "audio/eily/e1.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 8 eily/e1.ogg
{ .path = "audio/alice/a2.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 9 alice/a2.ogg
{ .path = "audio/eily/e2.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 10 eily/e2.ogg
{ .path = "audio/alice/a3.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 11 alice/a3.ogg
{ .path = "audio/eily/e3.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 12 eily/e3.ogg
{ .path = "audio/alice/a4.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 13 alice/a4.ogg
{ .path = "audio/alice/a5.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 14 alice/a5.ogg
{ .path = "audio/eily/e4.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 15 eily/e4.ogg
{ .path = "audio/eily/e5.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 16 eily/e5.ogg
{ .path = "audio/alice/a6.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 17 alice/a6.ogg
{ .path = "audio/eily/e6.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 18 eily/e6.ogg
{ .path = "audio/alice/a7.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 19 alice/a7.ogg
{ .path = "audio/eily/e7.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 20 eily/e7.ogg
{ .path = "audio/nara/n5.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 21 nara/n5.ogg
{ .path = "audio/music/PhrygianButterflies.opus.bin", .loop_end = 40.2, .audio_flags = audio::music, .gain = 0.5 }, // 22 music/PhrygianButterflies.ogg
{ .path = "audio/eily/e8.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 23 eily/e8.ogg
{ .path = "audio/alice/a8.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 24 alice/a8.ogg
{ .path = "audio/eily/e9.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 25 eily/e9.ogg
{ .path = "audio/eily/e10.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 26 eily/e10.ogg
{ .path = "audio/alice/a9.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 27 alice/a9.ogg
{ .path = "audio/eily/e11.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 28 eily/e11.ogg
{ .path = "audio/eily/e12.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 29 eily/e12.ogg
{ .path = "audio/alice/a10.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 30 alice/a10.ogg
{ .path = "audio/eily/e13.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 31 eily/e13.ogg
{ .path = "audio/eily/e14.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 32 eily/e14.ogg
{ .path = "audio/alice/a11.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 33 alice/a11.ogg
{ .path = "audio/eily/e15.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 34 eily/e15.ogg
{ .path = "audio/poem/EleanorTheHero.opus.bin", .loop_end = 0.0, .audio_flags = audio::poem, .gain = 1.0 }, // 35 poem/EleanorTheHero.ogg
{ .path = "audio/eily/e16.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 36 eily/e16.ogg
{ .path = "audio/alice/a12.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 37 alice/a12.ogg
{ .path = "audio/leona/c1.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 38 leona/c1.ogg
{ .path = "audio/music/ScaredMice.opus.bin", .loop_end = 8.0, .audio_flags = audio::music, .gain = 1.0 }, // 39 music/ScaredMice.ogg
{ .path = "audio/mousegirls/mg1.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 40 mousegirls/mg1.ogg
{ .path = "audio/leona/c2.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 41 leona/c2.ogg
{ .path = "audio/leona/c3.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 42 leona/c3.ogg
{ .path = "audio/alice/a14.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 43 alice/a14.ogg
{ .path = "audio/eily/e18.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 44 eily/e18.ogg
{ .path = "audio/eily/e19.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 45 eily/e19.ogg
{ .path = "audio/leona/c4.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 46 leona/c4.ogg
{ .path = "audio/leona/c5.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 47 leona/c5.ogg
{ .path = "audio/leona/c6.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 48 leona/c6.ogg
{ .path = "audio/eily/e20.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 49 eily/e20.ogg
{ .path = "audio/alice/a15.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 50 alice/a15.ogg
{ .path = "audio/eily/e21.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 51 eily/e21.ogg
{ .path = "audio/leona/c7.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 52 leona/c7.ogg
{ .path = "audio/leona/c8.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 53 leona/c8.ogg
{ .path = "audio/eily/e22.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 54 eily/e22.ogg
{ .path = "audio/leona/c9.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 55 leona/c9.ogg
{ .path = "audio/eily/e23.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 56 eily/e23.ogg
{ .path = "audio/leona/c10.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 57 leona/c10.ogg
{ .path = "audio/eily/e24.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 58 eily/e24.ogg
{ .path = "audio/leona/c11.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 59 leona/c11.ogg
{ .path = "audio/eily/e25.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 60 eily/e25.ogg
{ .path = "audio/leona/c12.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 61 leona/c12.ogg
{ .path = "audio/leona/c13.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 62 leona/c13.ogg
{ .path = "audio/eily/e26.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 63 eily/e26.ogg
{ .path = "audio/eily/e27.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 64 eily/e27.ogg
{ .path = "audio/leona/c14.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 65 leona/c14.ogg
{ .path = "audio/alice/a16.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 66 alice/a16.ogg
{ .path = "audio/leona/c15.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 67 leona/c15.ogg
{ .path = "audio/leona/c16.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 68 leona/c16.ogg
{ .path = "audio/leona/c17.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 69 leona/c17.ogg
{ .path = "audio/alice/a17.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 70 alice/a17.ogg
{ .path = "audio/eily/e28.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 71 eily/e28.ogg
{ .path = "audio/alice/a18.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 72 alice/a18.ogg
{ .path = "audio/eily/e29.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 73 eily/e29.ogg
{ .path = "audio/nara/n6.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 74 nara/n6.ogg
{ .path = "audio/eily/e30.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 75 eily/e30.ogg
{ .path = "audio/alice/a19.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 76 alice/a19.ogg
{ .path = "audio/eily/e31.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 77 eily/e31.ogg
{ .path = "audio/alice/a20.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 78 alice/a20.ogg
{ .path = "audio/leona/c18.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 79 leona/c18.ogg
{ .path = "audio/eily/e32.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 80 eily/e32.ogg
{ .path = "audio/alice/a21.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 81 alice/a21.ogg
{ .path = "audio/eily/e33.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 82 eily/e33.ogg
{ .path = "audio/leona/c19.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 83 leona/c19.ogg
{ .path = "audio/leona/c20.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 84 leona/c20.ogg
{ .path = "audio/leona/c21.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 85 leona/c21.ogg
{ .path = "audio/alice/a22.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 86 alice/a22.ogg
{ .path = "audio/leona/c22.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 87 leona/c22.ogg
{ .path = "audio/leona/c23.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 88 leona/c23.ogg
{ .path = "audio/alice/a23.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 89 alice/a23.ogg
{ .path = "audio/eily/e34.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 90 eily/e34.ogg
{ .path = "audio/alice/a24.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 91 alice/a24.ogg
{ .path = "audio/eily/e35.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 92 eily/e35.ogg
{ .path = "audio/leona/c24.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 93 leona/c24.ogg
{ .path = "audio/leona/c25.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 94 leona/c25.ogg
{ .path = "audio/leona/c26.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 95 leona/c26.ogg
{ .path = "audio/eily/e36.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 96 eily/e36.ogg
{ .path = "audio/eily/e37.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 97 eily/e37.ogg
{ .path = "audio/eily/e38.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 98 eily/e38.ogg
{ .path = "audio/eily/e39.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 99 eily/e39.ogg
{ .path = "audio/eily/e40.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 100 eily/e40.ogg
{ .path = "audio/eily/e41.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 101 eily/e41.ogg
{ .path = "audio/eily/e42.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 102 eily/e42.ogg
{ .path = "audio/alice/a25.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 103 alice/a25.ogg
{ .path = "audio/eily/e43.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 104 eily/e43.ogg
{ .path = "audio/alice/a26.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 105 alice/a26.ogg
{ .path = "audio/eily/e44.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 106 eily/e44.ogg
{ .path = "audio/eily/e45.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 107 eily/e45.ogg
{ .path = "audio/alice/a27.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 108 alice/a27.ogg
{ .path = "audio/eily/e46.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 109 eily/e46.ogg
{ .path = "audio/alice/a28.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 110 alice/a28.ogg
{ .path = "audio/leona/c27.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 111 leona/c27.ogg
{ .path = "audio/eily/e47.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 112 eily/e47.ogg
{ .path = "audio/alice/a29.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 113 alice/a29.ogg
{ .path = "audio/leona/c28.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 114 leona/c28.ogg
{ .path = "audio/leona/c29.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 115 leona/c29.ogg
{ .path = "audio/eily/e48.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 116 eily/e48.ogg
{ .path = "audio/leona/c30.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 117 leona/c30.ogg
{ .path = "audio/alice/a30.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 118 alice/a30.ogg
{ .path = "audio/eily/e49.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 119 eily/e49.ogg
{ .path = "audio/eily/e50.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 120 eily/e50.ogg
{ .path = "audio/eily/e51.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 121 eily/e51.ogg
{ .path = "audio/eily/e52.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 122 eily/e52.ogg
{ .path = "audio/eily/e53.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 123 eily/e53.ogg
{ .path = "audio/eily/e54.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 124 eily/e54.ogg
{ .path = "audio/alice/a31.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 125 alice/a31.ogg
{ .path = "audio/leona/c31.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 126 leona/c31.ogg
{ .path = "audio/leona/c32.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 127 leona/c32.ogg
{ .path = "audio/alice/a32.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 128 alice/a32.ogg
{ .path = "audio/leona/c33.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 129 leona/c33.ogg
{ .path = "audio/mousegirls/mg2.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 130 mousegirls/mg2.ogg
{ .path = "audio/leona/c34.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 131 leona/c34.ogg
{ .path = "audio/leona/c35.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 132 leona/c35.ogg
{ .path = "audio/alice/a33.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 133 alice/a33.ogg
{ .path = "audio/eily/e55.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 134 eily/e55.ogg
{ .path = "audio/leona/c36.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 135 leona/c36.ogg
{ .path = "audio/alice/a34.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 136 alice/a34.ogg
{ .path = "audio/eily/e56.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 137 eily/e56.ogg
{ .path = "audio/nara/n7.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 138 nara/n7.ogg
{ .path = "audio/music/WheatFields.opus.bin", .loop_end = 34.0, .audio_flags = audio::music, .gain = 1.0 }, // 139 music/WheatFields.ogg
{ .path = "audio/leona/c37.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 140 leona/c37.ogg
{ .path = "audio/alice/a35.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 141 alice/a35.ogg
{ .path = "audio/eily/e57.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 142 eily/e57.ogg
{ .path = "audio/leona/c38.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 143 leona/c38.ogg
{ .path = "audio/leona/c39.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 144 leona/c39.ogg
{ .path = "audio/eily/e58.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 145 eily/e58.ogg
{ .path = "audio/alice/a36.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 146 alice/a36.ogg
{ .path = "audio/eily/e59.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 147 eily/e59.ogg
{ .path = "audio/eily/e60.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 148 eily/e60.ogg
{ .path = "audio/leona/c40.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 149 leona/c40.ogg
{ .path = "audio/eily/e61.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 150 eily/e61.ogg
{ .path = "audio/leona/c41.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 151 leona/c41.ogg
{ .path = "audio/leona/c42.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 152 leona/c42.ogg
{ .path = "audio/alice/a37.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 153 alice/a37.ogg
{ .path = "audio/eily/e62.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 154 eily/e62.ogg
{ .path = "audio/leona/c43.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 155 leona/c43.ogg
{ .path = "audio/eily/e63.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 156 eily/e63.ogg
{ .path = "audio/leona/c44.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 157 leona/c44.ogg
{ .path = "audio/leona/c45.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 158 leona/c45.ogg
{ .path = "audio/leona/c46.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 159 leona/c46.ogg
{ .path = "audio/eily/e64.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 160 eily/e64.ogg
{ .path = "audio/leona/c47.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 161 leona/c47.ogg
{ .path = "audio/eily/e65.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 162 eily/e65.ogg
{ .path = "audio/leona/c48.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 163 leona/c48.ogg
{ .path = "audio/leona/c49.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 164 leona/c49.ogg
{ .path = "audio/leona/c50.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 165 leona/c50.ogg
{ .path = "audio/mousegirls/mg3.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 166 mousegirls/mg3.ogg
{ .path = "audio/leona/c51.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 167 leona/c51.ogg
{ .path = "audio/leona/c52.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 168 leona/c52.ogg
{ .path = "audio/leona/c53.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 169 leona/c53.ogg
{ .path = "audio/poem/KiriStella.opus.bin", .loop_end = 0.0, .audio_flags = audio::poem, .gain = 1.0 }, // 170 poem/KiriStella.ogg
{ .path = "audio/leona/c54.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 171 leona/c54.ogg
{ .path = "audio/leona/c55.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 172 leona/c55.ogg
{ .path = "audio/alice/a39.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 173 alice/a39.ogg
{ .path = "audio/eily/e67.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 174 eily/e67.ogg
{ .path = "audio/eily/e68.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 175 eily/e68.ogg
{ .path = "audio/leona/c56.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 176 leona/c56.ogg
{ .path = "audio/nara/n8.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 177 nara/n8.ogg
{ .path = "audio/nara/n9.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 178 nara/n9.ogg
{ .path = "audio/nara/n10.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 179 nara/n10.ogg
{ .path = "audio/hera/h1.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 180 hera/h1.ogg
{ .path = "audio/music/Preludium.opus.bin", .loop_end = 58.2, .audio_flags = audio::music, .gain = 0.45 }, // 181 music/Preludium.ogg
{ .path = "audio/hera/h2.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 182 hera/h2.ogg
{ .path = "audio/leona/c57.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 183 leona/c57.ogg
{ .path = "audio/hera/h3.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 184 hera/h3.ogg
{ .path = "audio/eily/e69.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 185 eily/e69.ogg
{ .path = "audio/hera/h4.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 186 hera/h4.ogg
{ .path = "audio/sfx/Glass.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 187 sfx/Glass.ogg
{ .path = "audio/alice/a40.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 188 alice/a40.ogg
{ .path = "audio/alice/a41.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 189 alice/a41.ogg
{ .path = "audio/leona/c58.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 190 leona/c58.ogg
{ .path = "audio/bird/b1.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 191 bird/b1.ogg
{ .path = "audio/bird/b2.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 192 bird/b2.ogg
{ .path = "audio/bird/b3.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 193 bird/b3.ogg
{ .path = "audio/leona/c59.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 194 leona/c59.ogg
{ .path = "audio/eily/e70.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 195 eily/e70.ogg
{ .path = "audio/bird/b4.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 196 bird/b4.ogg
{ .path = "audio/bird/b5.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 197 bird/b5.ogg
{ .path = "audio/leona/c60.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 198 leona/c60.ogg
{ .path = "audio/leona/c61.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 199 leona/c61.ogg
{ .path = "audio/bird/b6.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 200 bird/b6.ogg
{ .path = "audio/bird/b7.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 201 bird/b7.ogg
{ .path = "audio/leona/c0.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 202 leona/c0.ogg
{ .path = "audio/bird/b8.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 203 bird/b8.ogg
{ .path = "audio/bird/b9.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 204 bird/b9.ogg
{ .path = "audio/bird/b10.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 205 bird/b10.ogg
{ .path = "audio/leona/c62.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 206 leona/c62.ogg
{ .path = "audio/bird/b11.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 207 bird/b11.ogg
{ .path = "audio/bird/b12.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 208 bird/b12.ogg
{ .path = "audio/bird/b13.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 209 bird/b13.ogg
{ .path = "audio/leona/c63.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 210 leona/c63.ogg
{ .path = "audio/bird/b14.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 211 bird/b14.ogg
{ .path = "audio/leona/c64.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 212 leona/c64.ogg
{ .path = "audio/bird/b15.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 213 bird/b15.ogg
{ .path = "audio/leona/c65.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 214 leona/c65.ogg
{ .path = "audio/leona/c66.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 215 leona/c66.ogg
{ .path = "audio/bird/b16.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 216 bird/b16.ogg
{ .path = "audio/bird/b17.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 217 bird/b17.ogg
{ .path = "audio/alice/a42.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 218 alice/a42.ogg
{ .path = "audio/eily/e71.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 219 eily/e71.ogg
{ .path = "audio/eily/e72.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 220 eily/e72.ogg
{ .path = "audio/eily/e73.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 221 eily/e73.ogg
{ .path = "audio/eily/e74.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 222 eily/e74.ogg
{ .path = "audio/eily/e75.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 223 eily/e75.ogg
{ .path = "audio/eily/e76.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 224 eily/e76.ogg
{ .path = "audio/bird/b18.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 225 bird/b18.ogg
{ .path = "audio/leona/c67.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 226 leona/c67.ogg
{ .path = "audio/alice/a43.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 227 alice/a43.ogg
{ .path = "audio/eily/e77.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 228 eily/e77.ogg
{ .path = "audio/bird/b19.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 229 bird/b19.ogg
{ .path = "audio/bird/b20.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 230 bird/b20.ogg
{ .path = "audio/poem/BirdSong.opus.bin", .loop_end = 0.0, .audio_flags = audio::poem, .gain = 1.0 }, // 231 poem/BirdSong.ogg
{ .path = "audio/nara/n11.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 232 nara/n11.ogg
{ .path = "audio/nara/n12.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 233 nara/n12.ogg
{ .path = "audio/nara/n13.opus.bin", .loop_end = 0.0, .audio_flags = 0, .gain = 1.0 }, // 234 nara/n13.ogg
};
const int audio_length = (sizeof (audio)) / (sizeof (audio[0]));

View File

@ -640,7 +640,7 @@ namespace renpy {
{
.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
.memory = instanceMemory,
.offset = 0,
.offset = instanceBufferOffset[frameIndex],
.size = (sizeof (ImageInstance)) * outputIndex,
}
};

169
src/ui.cpp Normal file
View File

@ -0,0 +1,169 @@
#include "ui.h"
#include "audio.h"
namespace ui
{
constexpr int top = 75;
constexpr int ySpace = 120;
constexpr int yMixSpace = 60;
constexpr int inputMixLeft = 100;
constexpr int allpassLeft = 400;
constexpr int combLeft = 700;
constexpr int outputMixLeft = 1000;
#define BREVERB audio::fbreverb
#define FREVERB audio::ffreverb
widget::DelayGainSlider fap[3] {
widget::DelayGainSlider("allpass 0",
allpassLeft, top + ySpace * 0,
1, 2500, &FREVERB.ap[0].delay,
0.0, 1.0, &FREVERB.ap[0].gain0, &FREVERB.ap[0].gainM),
widget::DelayGainSlider("allpass 1",
allpassLeft, top + ySpace * 1,
1, 2500, &FREVERB.ap[1].delay,
0.0, 1.0, &FREVERB.ap[1].gain0, &FREVERB.ap[1].gainM),
widget::DelayGainSlider("allpass 2",
allpassLeft, top + ySpace * 2,
1, 2500, &FREVERB.ap[2].delay,
0.0, 1.0, &FREVERB.ap[2].gain0, &FREVERB.ap[2].gainM),
};
widget::DelayGainSlider fcomb[4] {
widget::DelayGainSlider("comb 0",
combLeft, top + ySpace * 0,
100, 15000, &FREVERB.cf[0].delay,
0.0, 1.0, &FREVERB.cf[0].gain0, &FREVERB.cf[0].gainM),
widget::DelayGainSlider("comb 1",
combLeft, top + ySpace * 1,
100, 15000, &FREVERB.cf[1].delay,
0.0, 1.0, &FREVERB.cf[1].gain0, &FREVERB.cf[1].gainM),
widget::DelayGainSlider("comb 2",
combLeft, top + ySpace * 2,
100, 15000, &FREVERB.cf[2].delay,
0.0, 1.0, &FREVERB.cf[2].gain0, &FREVERB.cf[2].gainM),
widget::DelayGainSlider("comb 3",
combLeft, top + ySpace * 3,
100, 15000, &FREVERB.cf[3].delay,
0.0, 1.0, &FREVERB.cf[3].gain0, &FREVERB.cf[3].gainM),
};
widget::DelayGainSlider bap[3] {
widget::DelayGainSlider("allpass 0",
allpassLeft, top + ySpace * 0,
1, 2500, &BREVERB.ap[0].delay,
0.0, 1.0, &BREVERB.ap[0].gain0, &BREVERB.ap[0].gainM),
widget::DelayGainSlider("allpass 1",
allpassLeft, top + ySpace * 1,
1, 2500, &BREVERB.ap[1].delay,
0.0, 1.0, &BREVERB.ap[1].gain0, &BREVERB.ap[1].gainM),
widget::DelayGainSlider("allpass 2",
allpassLeft, top + ySpace * 2,
1, 2500, &BREVERB.ap[2].delay,
0.0, 1.0, &BREVERB.ap[2].gain0, &BREVERB.ap[2].gainM),
};
widget::DelayGainSlider bcomb[4] {
widget::DelayGainSlider("comb 0",
combLeft, top + ySpace * 0,
100, 15000, &BREVERB.cf[0].delay,
0.0, 1.0, &BREVERB.cf[0].gain0, &BREVERB.cf[0].gainM),
widget::DelayGainSlider("comb 1",
combLeft, top + ySpace * 1,
100, 15000, &BREVERB.cf[1].delay,
0.0, 1.0, &BREVERB.cf[1].gain0, &BREVERB.cf[1].gainM),
widget::DelayGainSlider("comb 2",
combLeft, top + ySpace * 2,
100, 15000, &BREVERB.cf[2].delay,
0.0, 1.0, &BREVERB.cf[2].gain0, &BREVERB.cf[2].gainM),
widget::DelayGainSlider("comb 3",
combLeft, top + ySpace * 3,
100, 15000, &BREVERB.cf[3].delay,
0.0, 1.0, &BREVERB.cf[3].gain0, &BREVERB.cf[3].gainM),
};
widget::Slider<float, false> dryGain("dry gain",
outputMixLeft, top + yMixSpace * 0, 150, 14,
0.0, 1.0, 0.0, 1.0,
&audio::dryGain);
widget::Slider<float, false> wetGain("wet gain",
outputMixLeft, top + yMixSpace * 1, 150, 14,
0.0, 1.0, 0.0, 1.0,
&audio::wetGain);
widget::Slider<float, false> voiceGain("voice gain",
inputMixLeft, top + yMixSpace * 0, 150, 14,
0.0, 1.0, 0.0, 1.0,
&audio::mixChannelGain[audio::mix_channel::voice]);
widget::Slider<float, false> poemGain("poem gain",
inputMixLeft, top + yMixSpace * 1, 150, 14,
0.0, 1.0, 0.0, 1.0,
&audio::mixChannelGain[audio::mix_channel::poem]);
widget::Slider<float, false> musicGain("music gain",
inputMixLeft, top + yMixSpace * 2, 150, 14,
0.0, 1.0, 0.0, 1.0,
&audio::mixChannelGain[audio::mix_channel::music]);
const char * reverberatorNames[] = {
"feed-back",
"feed-forward",
};
widget::Radio<2> reverberators("reverberator",
reverberatorNames,
950, 200, 100, 40,
120, 0,
&audio::reverbIndex);
void draw(MappedInstanceData<SolidInstance> & data,
MappedInstanceData<font::BitmapInstance> & fontData)
{
data.append({
{ (uint16_t)0, (uint16_t)0 },
{ (uint16_t)1280, (uint16_t)720 },
0x80000000,
});
widget::DelayGainSlider * ap = (audio::reverbIndex == 0) ? bap : fap;
widget::DelayGainSlider * comb = (audio::reverbIndex == 0) ? bcomb : fcomb;
for (int i = 0; i < 3; i++)
ap[i].draw(data, fontData);
for (int i = 0; i < 4; i++)
comb[i].draw(data, fontData);
dryGain.draw(data, fontData);
wetGain.draw(data, fontData);
voiceGain.draw(data, fontData);
poemGain.draw(data, fontData);
musicGain.draw(data, fontData);
reverberators.draw(data, fontData);
}
void update(float mx, float my, bool mLeft, bool mEdge)
{
widget::DelayGainSlider * ap = (audio::reverbIndex == 0) ? bap : fap;
widget::DelayGainSlider * comb = (audio::reverbIndex == 0) ? bcomb : fcomb;
for (int i = 0; i < 3; i++)
ap[i].update(mx, my, mLeft, mEdge);
for (int i = 0; i < 4; i++)
comb[i].update(mx, my, mLeft, mEdge);
dryGain.update(mx, my, mLeft, mEdge);
wetGain.update(mx, my, mLeft, mEdge);
voiceGain.update(mx, my, mLeft, mEdge);
poemGain.update(mx, my, mLeft, mEdge);
musicGain.update(mx, my, mLeft, mEdge);
reverberators.update(mx, my, mLeft, mEdge);
}
}

118
src/ui/vulkan.cpp Normal file
View File

@ -0,0 +1,118 @@
#include <stdio.h>
#ifdef __APPLE__
#include "vulkan/vulkan.h"
#else
#include "volk/volk.h"
#endif
#include "vulkan_helper.h"
#include "ui/vulkan.h"
#include "ui/instance_data.h"
#include "ui.h"
#include "font/bitmap.h"
namespace ui {
void vulkan::createPipeline()
{
uint32_t descriptorSetLayoutCount = 0;
VkDescriptorSetLayout const * descriptorSetLayouts = nullptr;
uint32_t pushConstantRangeCount = 0;
VkPushConstantRange const * pushConstantRanges = nullptr;
VkShaderModule shaderModule = loadShader(vk->device, "shader/ui/solid.spv");
uint32_t perInstanceStride = (sizeof (SolidInstance));
constexpr uint32_t instanceAttributeDescriptionCount = 3;
VkVertexInputAttributeDescription instanceAttributeDescriptions[instanceAttributeDescriptionCount] {
{ // position
.location = 2,
.binding = 1,
.format = VK_FORMAT_R16G16_UINT,
.offset = 0,
},
{ // size
.location = 3,
.binding = 1,
.format = VK_FORMAT_R16G16_UINT,
.offset = 4,
},
{ // color
.location = 4,
.binding = 1,
.format = VK_FORMAT_R8G8B8A8_UNORM,
.offset = 8,
},
};
createQuadPipeline(vk->device,
vk->colorFormat,
vk->depthFormat,
descriptorSetLayoutCount,
descriptorSetLayouts,
pushConstantRangeCount,
pushConstantRanges,
shaderModule,
perInstanceStride,
instanceAttributeDescriptionCount,
instanceAttributeDescriptions,
&pipelineLayout,
&pipeline);
}
int vulkan::update(uint32_t frameIndex, MappedInstanceData<font::BitmapInstance> & bitmapData) const
{
MappedInstanceData<SolidInstance> solidData{ solidInstance[frameIndex], maximumUIElements, 0 };
ui::draw(solidData, bitmapData);
//////////////////////////////////////////////////////////////////////
// flush
//////////////////////////////////////////////////////////////////////
constexpr int mappedMemoryRangesCount = 1;
VkMappedMemoryRange mappedMemoryRanges[mappedMemoryRangesCount]{
{
.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
.memory = instanceBuffer.memory,
.offset = instanceBuffer.offset[frameIndex],
.size = (sizeof (SolidInstance)) * solidData.index,
}
};
alignMappedMemoryRanges(vk->physicalDeviceProperties.limits.nonCoherentAtomSize,
instanceBuffer.memorySize,
mappedMemoryRangesCount,
mappedMemoryRanges);
vkFlushMappedMemoryRanges(vk->device, mappedMemoryRangesCount, mappedMemoryRanges);
return solidData.index;
}
void vulkan::draw(VkCommandBuffer commandBuffer,
uint32_t frameIndex,
MappedInstanceData<font::BitmapInstance> & bitmapData) const
{
int outputIndex = update(frameIndex, bitmapData);
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
/*
VkDescriptorSet descriptorSets[1] = {
descriptorSet0,
};
vkCmdBindDescriptorSets(commandBuffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
pipelineLayout,
0, 1, descriptorSets,
0, nullptr);
*/
vkCmdBindIndexBuffer(commandBuffer, vk->quadVertexIndex.buffer, vk->quadVertexIndex.indexOffset, VK_INDEX_TYPE_UINT16);
VkDeviceSize vertexOffsets[2]{ 0, instanceBuffer.offset[frameIndex] };
VkBuffer vertexBuffers[2]{ vk->quadVertexIndex.buffer, instanceBuffer.buffer };
vkCmdBindVertexBuffers(commandBuffer, 0, 2, vertexBuffers, vertexOffsets);
vkCmdDrawIndexed(commandBuffer, 4, outputIndex, 0, 0, 0);
}
}

221
src/ui/widget.cpp Normal file
View File

@ -0,0 +1,221 @@
#include <string.h>
#include <stdio.h>
#include "ui/widget.h"
#include "intstring.h"
#include "minmax.h"
namespace ui::widget
{
static void drawBoundingBox(MappedInstanceData<SolidInstance> & data,
BoundingBox const & bb, uint32_t color)
{
if ((color & 0xff000000) == 0)
color |= 0xff000000;
data.append({
{ (uint16_t)bb.left, (uint16_t)bb.top },
{ (uint16_t)bb.width, (uint16_t)bb.height },
color,
});
}
static inline void drawGlyph(MappedInstanceData<font::BitmapInstance> & fontData,
int x, int y,
int c)
{
fontData.append({ {(uint16_t)(x), (uint16_t)(y)}, font::bitmap::glyphIndex(c) });
}
template<typename T>
inline void drawNumberCentered(MappedInstanceData<font::BitmapInstance> & fontData,
int x, int y,
T number);
template<>
inline void drawNumberCentered<int>(MappedInstanceData<font::BitmapInstance> & fontData,
int x, int y,
int number)
{
char buf[16];
int numLength = string::dec(buf, 16, number);
int width = numLength * 6;
int left = x - (width / 2);
for (int i = 0; i < numLength; i++) {
fontData.append({ {(uint16_t)(left + 6 * i), (uint16_t)(y)}, font::bitmap::glyphIndex(buf[i]) });
}
}
template<>
inline void drawNumberCentered<float>(MappedInstanceData<font::BitmapInstance> & fontData,
int x, int y,
float number)
{
char buf[64];
int numLength = string::flt(buf, 64, number);
int width = numLength * 6;
int left = x - (width / 2);
for (int i = 0; i < numLength; i++) {
fontData.append({ {(uint16_t)(left + 6 * i), (uint16_t)(y)}, font::bitmap::glyphIndex(buf[i]) });
}
}
static inline void drawStringCentered(MappedInstanceData<font::BitmapInstance> & fontData,
int x, int y,
const char * string)
{
int length = strlen(string);
int width = length * 6;
int left = x - (width / 2);
for (int i = 0; i < length; i++) {
fontData.append({ {(uint16_t)(left + 6 * i), (uint16_t)(y)}, font::bitmap::glyphIndex(string[i]) });
}
}
static inline void drawStringLeft(MappedInstanceData<font::BitmapInstance> & fontData,
int x, int y,
const char * string)
{
int length = strlen(string);
int left = x;
for (int i = 0; i < length; i++) {
fontData.append({ {(uint16_t)(left + 6 * i), (uint16_t)(y)}, font::bitmap::glyphIndex(string[i]) });
}
}
static inline void drawStringRight(MappedInstanceData<font::BitmapInstance> & fontData,
int x, int y,
const char * string)
{
int length = strlen(string);
int width = length * 6;
int left = x - width;
for (int i = 0; i < length; i++) {
fontData.append({ {(uint16_t)(left + 6 * i), (uint16_t)(y)}, font::bitmap::glyphIndex(string[i]) });
}
}
template <typename T, bool under>
void Slider<T, under>::draw(MappedInstanceData<SolidInstance> & data,
MappedInstanceData<font::BitmapInstance> & fontData)
{
drawBoundingBox(data, sliderBorder, 0x545454);
float sliderInterp = (float)(*value - minExtent) / (float)(maxExtent - minExtent);
int sliderWidth = (float)slider.width * clamp01(sliderInterp);
drawBoundingBox(data, {
slider.left,
slider.top,
sliderWidth,
slider.height,
}, 0x800000);
drawBoundingBox(data, lminus, 0x008080);
drawGlyph(fontData, lminus.left + 2, lminus.top - 3, '-');
drawBoundingBox(data, lplus, 0x008080);
drawGlyph(fontData, lplus.left + 2, lplus.top - 3, '+');
drawBoundingBox(data, rminus, 0x008000);
drawGlyph(fontData, rminus.left + 2, lminus.top - 3, '-');
drawBoundingBox(data, rplus, 0x008000);
drawGlyph(fontData, rplus.left + 2, lplus.top - 3, '+');
constexpr int yOffset = under ? 14 : -16;
drawNumberCentered<T>(fontData, slider.left, slider.top + yOffset, minExtent);
drawNumberCentered<T>(fontData, slider.left + slider.width, slider.top + yOffset, maxExtent);
drawNumberCentered<T>(fontData, slider.left + slider.width / 2, slider.top + yOffset, *value);
drawStringRight(fontData, slider.left - 25, slider.top - 1, label);
//drawStringRight(fontData, slider.left - 25, slider.top + 5, label);
//drawStringLeft(fontData, slider.left + slider.width + 26, slider.top - 1, label);
}
template <typename T, bool under>
void Slider<T, under>::update(float mx, float my, bool mLeft, bool mEdge)
{
if (!mLeft) {
drag = false;
return;
}
if (mEdge) {
double eWidth2 = (double)(maxExtent - minExtent) / 2.0f;
double vWidth2 = (double)(maxValue - minValue) / 2.0f;
double width2 = min(eWidth2, vWidth2);
if (lminus.inside(mx, my)) {
minExtent = clamp<double>(minExtent + width2, minValue, maxValue);
} else if (lplus.inside(mx, my)) {
minExtent = clamp<double>(minExtent - width2, minValue, maxValue);
} else if (rminus.inside(mx, my)) {
maxExtent = clamp<double>(maxExtent - width2, minValue, maxValue);
} else if (rplus.inside(mx, my)) {
maxExtent = clamp<double>(maxExtent + width2, minValue, maxValue);
} else if (slider.inside(mx, my)) {
drag = true;
}
}
if (drag) {
float lerp = clamp01((mx - (float)slider.left) / (float)(slider.width));
float valueScale = maxExtent - minExtent;
*value = (T)(lerp * valueScale + minExtent);
}
}
template struct Slider<int, true>;
template struct Slider<int, false>;
template struct Slider<float, true>;
template struct Slider<float, false>;
void DelayGainSlider::draw(MappedInstanceData<SolidInstance> & data,
MappedInstanceData<font::BitmapInstance> & fontData)
{
drawStringCentered(fontData, delay.slider.left + delay.slider.width / 2, delay.slider.top - 16, label);
delay.draw(data, fontData);
gain0.draw(data, fontData);
gainM.draw(data, fontData);
}
void DelayGainSlider::update(float mx, float my, bool mLeft, bool mEdge)
{
delay.update(mx, my, mLeft, mEdge);
gain0.update(mx, my, mLeft, mEdge);
gainM.update(mx, my, mLeft, mEdge);
}
template <int optionCount>
void Radio<optionCount>::draw(MappedInstanceData<SolidInstance> & data,
MappedInstanceData<font::BitmapInstance> & fontData)
{
drawStringCentered(fontData, box[0].left + width / 2, box[0].top - 16, label);
for (int i = 0; i < optionCount; i++) {
uint32_t color = (i == *selected) ? 0xa0800080 : 0x80300030;
drawBoundingBox(data, box[i], color);
drawStringCentered(fontData, box[i].left + box[i].width / 2, box[0].top + box[i].height / 2 - 6, boxLabels[i]);
}
}
template <int optionCount>
void Radio<optionCount>::update(float mx, float my, bool mLeft, bool mEdge)
{
if (!mEdge)
return;
for (int i = 0; i < optionCount; i++) {
if (box[i].inside(mx, my)) {
*selected = i;
break;
}
}
}
template struct widget::Radio<2>;
}

View File

@ -9,7 +9,6 @@
#include "volk/volk.h"
#endif
#include "vulkan/vulkan.h"
#include "vulkan/vk_enum_string_helper.h"
#include "minmax.h"
@ -134,7 +133,7 @@ void textureTransfer(VkDevice device,
VkDeviceSize nonCoherentAtomSize,
VkPhysicalDeviceMemoryProperties const & physicalDeviceMemoryProperties,
uint32_t imageDataSize,
void * imageData,
void const * imageData,
VkImage image,
uint32_t width,
uint32_t height,
@ -491,3 +490,244 @@ VertexIndex createVertexIndexBuffer(VkDevice device,
return vertexIndex;
}
void createQuadPipeline(VkDevice device,
VkFormat colorFormat,
VkFormat depthFormat,
uint32_t descriptorSetLayoutCount,
VkDescriptorSetLayout const * descriptorSetLayouts,
uint32_t pushConstantRangeCount,
VkPushConstantRange const * pushConstantRanges,
VkShaderModule shaderModule,
uint32_t perInstanceStride,
uint32_t instanceAttributeDescriptionCount,
VkVertexInputAttributeDescription * instanceAttributeDescriptions,
VkPipelineLayout * pipelineLayout,
VkPipeline * pipeline)
{
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = descriptorSetLayoutCount,
.pSetLayouts = descriptorSetLayouts,
.pushConstantRangeCount = pushConstantRangeCount,
.pPushConstantRanges = pushConstantRanges
};
VK_CHECK(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, pipelineLayout));
VkPipelineInputAssemblyStateCreateInfo inputAssemblyState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP
};
VkPipelineShaderStageCreateInfo shaderStages[2]{
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_VERTEX_BIT,
.module = shaderModule,
.pName = "VSMain"
},
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
.module = shaderModule,
.pName = "PSMain"
}
};
VkPipelineViewportStateCreateInfo viewportState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.viewportCount = 1,
.scissorCount = 1
};
constexpr uint32_t dynamicStateCount = 2;
VkDynamicState dynamicStates[dynamicStateCount]{
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR,
};
VkPipelineDynamicStateCreateInfo dynamicState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.dynamicStateCount = dynamicStateCount,
.pDynamicStates = dynamicStates
};
VkPipelineDepthStencilStateCreateInfo depthStencilState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
.depthTestEnable = VK_FALSE,
.depthWriteEnable = VK_FALSE,
.depthCompareOp = VK_COMPARE_OP_ALWAYS,
.stencilTestEnable = VK_FALSE,
.front = {
.failOp = VK_STENCIL_OP_REPLACE,
.passOp = VK_STENCIL_OP_REPLACE,
.depthFailOp = VK_STENCIL_OP_REPLACE,
.compareOp = VK_COMPARE_OP_ALWAYS,
.compareMask = 0x01,
.writeMask = 0x01,
.reference = 1,
},
.back = {
.failOp = VK_STENCIL_OP_REPLACE,
.passOp = VK_STENCIL_OP_REPLACE,
.depthFailOp = VK_STENCIL_OP_REPLACE,
.compareOp = VK_COMPARE_OP_ALWAYS,
.compareMask = 0x01,
.writeMask = 0x01,
.reference = 1,
},
};
VkPipelineRenderingCreateInfo renderingCreateInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO,
.colorAttachmentCount = 1,
.pColorAttachmentFormats = &colorFormat,
.depthAttachmentFormat = depthFormat,
.stencilAttachmentFormat = depthFormat
};
VkPipelineColorBlendAttachmentState blendAttachment{
.blendEnable = VK_TRUE,
.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA,
.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
.colorBlendOp = VK_BLEND_OP_ADD,
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
.alphaBlendOp = VK_BLEND_OP_ADD,
.colorWriteMask = 0xF,
};
VkPipelineColorBlendStateCreateInfo colorBlendState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.attachmentCount = 1,
.pAttachments = &blendAttachment
};
VkPipelineRasterizationStateCreateInfo rasterizationState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
//.cullMode = VK_CULL_MODE_BACK_BIT,
.cullMode = VK_CULL_MODE_NONE,
.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE,
.lineWidth = 1.0f
};
VkPipelineMultisampleStateCreateInfo multisampleState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT
};
constexpr uint32_t perVertexStride = 4 * 2;
constexpr int vertexBindingDescriptionsCount = 2;
VkVertexInputBindingDescription vertexBindingDescriptions[vertexBindingDescriptionsCount]{
{
.binding = 0,
.stride = perVertexStride,
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX
},
{
.binding = 1,
.stride = perInstanceStride,
.inputRate = VK_VERTEX_INPUT_RATE_INSTANCE
},
};
uint32_t vertexAttributeDescriptionsCount = 2u + instanceAttributeDescriptionCount;
VkVertexInputAttributeDescription vertexAttributeDescriptions[vertexAttributeDescriptionsCount];
// per-vertex
vertexAttributeDescriptions[0] = { // position
.location = 0,
.binding = 0,
.format = VK_FORMAT_R16G16_SFLOAT,
.offset = 0,
};
vertexAttributeDescriptions[1] = { // texture
.location = 1,
.binding = 0,
.format = VK_FORMAT_R16G16_SFLOAT,
.offset = 4,
};
memcpy(&vertexAttributeDescriptions[2], instanceAttributeDescriptions, instanceAttributeDescriptionCount * (sizeof (VkVertexInputAttributeDescription)));
VkPipelineVertexInputStateCreateInfo vertexInputState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.vertexBindingDescriptionCount = vertexBindingDescriptionsCount,
.pVertexBindingDescriptions = vertexBindingDescriptions,
.vertexAttributeDescriptionCount = vertexAttributeDescriptionsCount,
.pVertexAttributeDescriptions = vertexAttributeDescriptions,
};
VkGraphicsPipelineCreateInfo pipelineCreateInfos[1]{
{
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = &renderingCreateInfo,
.stageCount = 2,
.pStages = shaderStages,
.pVertexInputState = &vertexInputState,
.pInputAssemblyState = &inputAssemblyState,
.pViewportState = &viewportState,
.pRasterizationState = &rasterizationState,
.pMultisampleState = &multisampleState,
.pDepthStencilState = &depthStencilState,
.pColorBlendState = &colorBlendState,
.pDynamicState = &dynamicState,
.layout = *pipelineLayout
},
};
VK_CHECK(vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, pipelineCreateInfos, nullptr, pipeline));
}
VkShaderModule loadShader(VkDevice device,
char const * const path)
{
uint32_t shaderSize;
void const * shaderStart = file::open(path, &shaderSize);
VkShaderModuleCreateInfo shaderModuleCreateInfo{
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.codeSize = shaderSize,
.pCode = (uint32_t *)shaderStart
};
VkShaderModule shaderModule;
VK_CHECK(vkCreateShaderModule(device, &shaderModuleCreateInfo, nullptr, &shaderModule));
return shaderModule;
}
void createInstanceBuffer(VkDevice device,
VkPhysicalDeviceProperties const & physicalDeviceProperties,
VkPhysicalDeviceMemoryProperties const & physicalDeviceMemoryProperties,
VkDeviceSize bufferSize,
InstanceBuffer * instanceBuffer)
{
instanceBuffer->memorySize = bufferSize * 2;
instanceBuffer->offset[0] = bufferSize * 0;
instanceBuffer->offset[1] = bufferSize * 1;
// create buffer
VkBufferCreateInfo bufferCreateInfo{
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = instanceBuffer->memorySize,
.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE
};
VK_CHECK(vkCreateBuffer(device, &bufferCreateInfo, nullptr, &instanceBuffer->buffer));
// allocate memory
VkMemoryRequirements memoryRequirements;
vkGetBufferMemoryRequirements(device, instanceBuffer->buffer, &memoryRequirements);
VkMemoryPropertyFlags memoryPropertyFlags{ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT };
VkMemoryAllocateFlags memoryAllocateFlags{};
VkDeviceSize stride;
allocateFromMemoryRequirements(device,
physicalDeviceProperties.limits.nonCoherentAtomSize,
physicalDeviceMemoryProperties,
memoryRequirements,
memoryPropertyFlags,
memoryAllocateFlags,
1,
&instanceBuffer->memory,
&stride);
VK_CHECK(vkBindBufferMemory(device, instanceBuffer->buffer, instanceBuffer->memory, 0));
// map memory
VK_CHECK(vkMapMemory(device, instanceBuffer->memory, 0, VK_WHOLE_SIZE, 0, (void **)&instanceBuffer->mappedData));
}

43
src/vulkan_state.cpp Normal file
View File

@ -0,0 +1,43 @@
#include "vulkan_state.h"
static const _Float16 vertexData[] = {
// x y u v
(_Float16)-1.0, (_Float16)-1.0, (_Float16)0.0, (_Float16)0.0,
(_Float16)1.0, (_Float16)-1.0, (_Float16)1.0, (_Float16)0.0,
(_Float16)-1.0, (_Float16)1.0, (_Float16)0.0, (_Float16)1.0,
(_Float16)1.0, (_Float16)1.0, (_Float16)1.0, (_Float16)1.0,
};
static const uint32_t vertexSize = (sizeof (vertexData));
static const uint16_t indexData[] = {
0, 1, 2, 3,
};
static const uint32_t indexSize = (sizeof (indexData));
VulkanState::VulkanState(VkInstance instance,
VkDevice device,
VkQueue queue,
VkCommandPool commandPool,
VkPhysicalDeviceProperties const & physicalDeviceProperties,
VkPhysicalDeviceMemoryProperties const & physicalDeviceMemoryProperties,
VkFormat colorFormat,
VkFormat depthFormat)
: instance(instance)
, device(device)
, queue(queue)
, commandPool(commandPool)
, physicalDeviceProperties(physicalDeviceProperties)
, physicalDeviceMemoryProperties(physicalDeviceMemoryProperties)
, colorFormat(colorFormat)
, depthFormat(depthFormat)
{
void const * vertexStart = (void const *)vertexData;
void const * indexStart = (void const *)indexData;
quadVertexIndex = createVertexIndexBuffer(device,
physicalDeviceProperties,
physicalDeviceMemoryProperties,
vertexStart, vertexSize,
indexStart, indexSize);
}