Compare commits
No commits in common. "c1558d99ee889d187d2aa818286ddbf102f5ec92" and "ac6b6138d4ffbbd925c86a948a9d833f2eac1b77" have entirely different histories.
c1558d99ee
...
ac6b6138d4
6
Makefile
6
Makefile
@ -115,12 +115,6 @@ all: main
|
||||
%.pcm: %.wav
|
||||
ffmpeg -loglevel quiet -y -i $< -c:a pcm_s16le -ar 48000 -ac 2 -f s16le $@
|
||||
|
||||
%.pcm: %.ogg
|
||||
ffmpeg -loglevel quiet -y -i $< -c:a pcm_s16le -ar 48000 -ac 2 -f s16le $@
|
||||
|
||||
%.pcm: %.mp3
|
||||
ffmpeg -loglevel quiet -y -i $< -c:a pcm_s16le -ar 48000 -ac 2 -f s16le $@
|
||||
|
||||
%.opus.bin: %.pcm
|
||||
./tools/opus_encode $< $@
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -76,7 +76,6 @@ label start:
|
||||
#voice "n4test.mp3"
|
||||
play TinyForestMinstrels "music/TinyForestMinstrels.ogg"
|
||||
n "Tiny minstrels can be heard amongst the trees"
|
||||
stop TinyForestMinstrels fadeout 5.5
|
||||
|
||||
|
||||
scene bgforest1
|
||||
@ -128,7 +127,7 @@ label start:
|
||||
with Dissolve(1.0)
|
||||
|
||||
|
||||
#voice "n5test.ogg"
|
||||
voice "n5test.ogg"
|
||||
n "As the minstrel mice girls continue along the path, the forest opens up into a beautiful field of flowers"
|
||||
|
||||
play PhrygianButterflies "music/PhrygianButterflies.ogg"
|
||||
|
||||
@ -12,11 +12,4 @@ data/renpy/images/ch/catw.dds
|
||||
data/renpy/images/ch/Eily.dds
|
||||
data/renpy/images/ch/Alice.dds
|
||||
|
||||
audio/sfx/Chime.opus.bin
|
||||
audio/sfx/MistAmbience.opus.bin
|
||||
audio/music/TinyForestMinstrels.opus.bin
|
||||
audio/music/PhrygianButterflies.opus.bin
|
||||
audio/music/Poem1.opus.bin
|
||||
audio/placeholdermeow.opus.bin
|
||||
audio/music/ScaredMice.opus.bin
|
||||
audio/music/WheatFields.opus.bin
|
||||
audio/PhrygianButterflies.opus.bin
|
||||
|
||||
@ -1,11 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "renpy/language.h"
|
||||
|
||||
namespace audio {
|
||||
void init();
|
||||
void load(renpy::language::audio const * const audio, int count);
|
||||
void load();
|
||||
void update();
|
||||
void play(int audio_index);
|
||||
void stop(int audio_index, double fadeout);
|
||||
}
|
||||
|
||||
@ -25,7 +25,6 @@ namespace renpy::language {
|
||||
|
||||
struct audio {
|
||||
char const * const path;
|
||||
double loop_end;
|
||||
};
|
||||
|
||||
struct image {
|
||||
@ -65,11 +64,6 @@ namespace renpy::language {
|
||||
uint32_t audioIndex;
|
||||
};
|
||||
|
||||
struct stop {
|
||||
uint32_t audioIndex;
|
||||
double fadeout;
|
||||
};
|
||||
|
||||
struct _return {
|
||||
};
|
||||
|
||||
@ -98,6 +92,10 @@ namespace renpy::language {
|
||||
struct with {
|
||||
};
|
||||
|
||||
struct stop {
|
||||
uint32_t channelIndex;
|
||||
};
|
||||
|
||||
struct pause {
|
||||
float duration;
|
||||
};
|
||||
|
||||
@ -56,6 +56,7 @@ class Play:
|
||||
channel: lex.Token
|
||||
path: lex.Token
|
||||
fadeout: lex.Token
|
||||
noloop: bool
|
||||
|
||||
__repr__ = lexeme_repr
|
||||
|
||||
@ -262,15 +263,16 @@ def parse_play(tokens, index):
|
||||
if fadeout.type != TT.NUMBER:
|
||||
raise ParseException("expected number", fadeout)
|
||||
index += 2
|
||||
#noloop = False
|
||||
noloop = False
|
||||
if token.type == TT.NOLOOP:
|
||||
#noloop = True
|
||||
noloop = True
|
||||
index += 1
|
||||
|
||||
play = Play(
|
||||
channel = channel,
|
||||
path = path,
|
||||
fadeout = fadeout,
|
||||
noloop = noloop,
|
||||
)
|
||||
return index, play
|
||||
|
||||
@ -478,19 +480,18 @@ def parse_stop(tokens, index):
|
||||
if channel.type != TT.IDENTIFIER:
|
||||
raise ParseException("expected identifier", channel)
|
||||
|
||||
fadeout = tokens[index + 1]
|
||||
if fadeout.type != TT.FADEOUT:
|
||||
raise ParseException("expected fadeout", channel)
|
||||
|
||||
number = tokens[index + 2]
|
||||
if number.type != TT.NUMBER:
|
||||
raise ParseException("expected number", number)
|
||||
|
||||
index += 3
|
||||
index += 1
|
||||
token = tokens[index]
|
||||
fadeout = None
|
||||
if token.type == TT.FADEOUT:
|
||||
fadeout = tokens[index + 1]
|
||||
if fadeout.type != TT.NUMBER:
|
||||
raise ParseException("expected number", fadeout)
|
||||
index += 2
|
||||
|
||||
stop = Stop(
|
||||
channel = channel,
|
||||
fadeout = number
|
||||
fadeout = fadeout
|
||||
)
|
||||
return index, stop
|
||||
|
||||
|
||||
@ -20,7 +20,6 @@ class State:
|
||||
characters_lookup: dict[str, int] # identifier to character index
|
||||
labels_lookup: dict[str, int] # identifier to statement index
|
||||
audio_lookup: dict[str, int]
|
||||
channel_lookup: dict[str, int]
|
||||
string_lookup: dict[str, int]
|
||||
|
||||
global_identifiers: set[str]
|
||||
@ -52,14 +51,6 @@ simple_statement_types = {
|
||||
parse.Hide,
|
||||
}
|
||||
|
||||
loops = {
|
||||
"ScaredMice": 8.0,
|
||||
"PhrygianButterflies": 40.125,
|
||||
"MistAmbience": 22.0,
|
||||
"TinyForestMinstrels": 44.0,
|
||||
"WheatFields": 34.0,
|
||||
}
|
||||
|
||||
def pass1(state, ast):
|
||||
if type(ast) is parse.Image:
|
||||
key = lhs_key(ast.name)
|
||||
@ -84,12 +75,7 @@ def pass1(state, ast):
|
||||
key = lhs_key(ast.name)
|
||||
assert key not in state.labels_lookup
|
||||
state.labels_lookup[key] = len(state.statements)
|
||||
elif type(ast) is parse.Play:
|
||||
if ast.path.lexeme not in state.audio_lookup:
|
||||
index = len(state.audio_lookup)
|
||||
state.audio_lookup[ast.path.lexeme] = index
|
||||
state.channel_lookup[ast.channel.lexeme] = index
|
||||
elif type(ast) is parse.Voice:
|
||||
elif type(ast) in {parse.Play, parse.Voice}:
|
||||
if ast.path.lexeme not in state.audio_lookup:
|
||||
index = len(state.audio_lookup)
|
||||
state.audio_lookup[ast.path.lexeme] = index
|
||||
@ -149,7 +135,7 @@ def pass2_statement(state, pc, statement):
|
||||
if type(statement) is parse.Play:
|
||||
comment = statement.path.lexeme.decode('utf-8')
|
||||
audio_index = state.audio_lookup[statement.path.lexeme]
|
||||
yield f"{{ .type = type::play, .play = {{ .audioIndex = {audio_index} }} }}, // {pc} {comment}"
|
||||
yield f"{{ .type = type::play, .play = {{ .audioIndex = {audio_index}, /* FIXME channel */ }} }}, // {pc} {comment}"
|
||||
elif type(statement) is parse.Scene:
|
||||
key = lhs_key(statement.name)
|
||||
if key in state.images_lookup:
|
||||
@ -204,10 +190,7 @@ def pass2_statement(state, pc, statement):
|
||||
elif type(statement) is parse.Return:
|
||||
yield f"{{ .type = type::_return }}, // {pc}"
|
||||
elif type(statement) is parse.Stop:
|
||||
audio_index = state.channel_lookup[statement.channel.lexeme]
|
||||
fadeout = statement.fadeout.lexeme
|
||||
channel = statement.channel.lexeme.decode('utf-8')
|
||||
yield f"{{ .type = type::stop, .stop = {{ .audioIndex = {audio_index}, .fadeout = {float(fadeout)} }} }}, // {pc} {channel}"
|
||||
yield f"{{ .type = type::stop, .stop = {{ /* FIXME channel */ }} }}, // {pc}"
|
||||
elif type(statement) is parse.Pause:
|
||||
duration = statement.duration.lexeme
|
||||
yield f"{{ .type = type::pause, .pause = {{ .duration = {duration} }} }}, // {pc}"
|
||||
@ -247,7 +230,6 @@ def pass2_characters(state):
|
||||
|
||||
def pass2_audio(state):
|
||||
yield "const language::audio audio[] = {"
|
||||
reverse_channel = {v: k for k, v in state.channel_lookup.items()}
|
||||
for audio, i in sorted(state.audio_lookup.items(), key=lambda kv: kv[1]):
|
||||
orig_path = audio.decode('utf-8')
|
||||
path = orig_path
|
||||
@ -257,12 +239,7 @@ def pass2_audio(state):
|
||||
path = path.removesuffix(".ogg")
|
||||
else:
|
||||
assert False, path
|
||||
name = audio
|
||||
channel_name = reverse_channel[i].decode('utf-8') if i in reverse_channel else None
|
||||
name = f"\"{channel_name}\"" if channel_name is not None else "nullptr"
|
||||
loop = loops[channel_name] if channel_name in loops else 0
|
||||
assert loop < 20_000, loop
|
||||
yield f"{{ .path = \"audio/{path}.opus.bin\", .loop_end = {float(loop)} }}, // {i} {orig_path}"
|
||||
yield f"{{ .path = \"{path}.opus\" }}, // {i} {orig_path}"
|
||||
yield "};"
|
||||
yield "const int audio_length = (sizeof (audio)) / (sizeof (audio[0]));"
|
||||
|
||||
@ -320,7 +297,6 @@ image _internal_flowers = "flowers.png"
|
||||
characters_lookup = dict(),
|
||||
labels_lookup = dict(),
|
||||
audio_lookup = dict(),
|
||||
channel_lookup = dict(),
|
||||
string_lookup = dict(),
|
||||
global_identifiers = set(),
|
||||
)
|
||||
|
||||
215
src/audio.cpp
215
src/audio.cpp
@ -9,49 +9,57 @@
|
||||
#include "audio.h"
|
||||
#include "new.h"
|
||||
#include "minmax.h"
|
||||
#include "renpy/language.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));
|
||||
int const frame_samples = 960; // 20 milliseconds @ 48kHz
|
||||
int const sample_rate = 48000;
|
||||
int const channels = 2;
|
||||
int const sample_size = (sizeof (int16_t));
|
||||
|
||||
static int const max_frame_size = 960 * 3; // 20ms at 48kHz
|
||||
static int const max_packet_size = 1275;
|
||||
|
||||
static int const half_period_samples = sample_rate / 2;
|
||||
static int const half_period_size = half_period_samples * sample_size * channels;
|
||||
int const max_frame_size = 960 * 3; // 20ms at 48kHz
|
||||
int const max_packet_size = 1275;
|
||||
|
||||
//
|
||||
|
||||
static SDL_AudioStream * audio_stream;
|
||||
static SDL_AudioSpec audio_spec;
|
||||
SDL_AudioStream * audio_stream;
|
||||
SDL_AudioSpec audio_spec;
|
||||
|
||||
static OpusDecoder * opus_decoder;
|
||||
OpusDecoder * opus_decoder;
|
||||
|
||||
struct AudioFile {
|
||||
char const * const path;
|
||||
uint32_t loop_end;
|
||||
};
|
||||
|
||||
struct AudioBuffer {
|
||||
renpy::language::audio const * audio;
|
||||
AudioFile * audio_file;
|
||||
int16_t * buf;
|
||||
uint32_t sample_count;
|
||||
};
|
||||
|
||||
struct AudioInstance {
|
||||
int audio_index;
|
||||
AudioBuffer * audio_buffer;
|
||||
uint32_t sample_index;
|
||||
uint32_t tail_index;
|
||||
uint32_t fadeout_end;
|
||||
uint32_t fadeout_index;
|
||||
};
|
||||
|
||||
static AudioBuffer * audio_buffers;
|
||||
static int audio_buffers_count;
|
||||
consteval uint32_t time_to_samples(double m, double s)
|
||||
{
|
||||
return (m * 60.0 + s) * sample_rate;
|
||||
}
|
||||
|
||||
constexpr int max_audio_instances = 128;
|
||||
static AudioInstance audio_instances[max_audio_instances];
|
||||
static int audio_instances_count;
|
||||
AudioFile audio_files[] = {
|
||||
{
|
||||
.path = "audio/PhrygianButterflies.opus.bin",
|
||||
.loop_end = time_to_samples(0, 40.125),
|
||||
},
|
||||
};
|
||||
constexpr int audio_files_count = (sizeof (audio_files)) / (sizeof (audio_files[0]));
|
||||
AudioBuffer audio_buffers[audio_files_count];
|
||||
|
||||
constexpr int max_audio_instances = 16;
|
||||
AudioInstance audio_instances[max_audio_instances];
|
||||
|
||||
void init()
|
||||
{
|
||||
@ -68,8 +76,6 @@ namespace audio {
|
||||
fprintf(stderr, "opus_decoder_create: %s\n", opus_strerror(err));
|
||||
assert(!"opus_decoder_create");
|
||||
}
|
||||
|
||||
audio_instances_count = 0;
|
||||
}
|
||||
|
||||
void decode(char const * const filename, AudioBuffer * audio_buffer)
|
||||
@ -125,149 +131,60 @@ namespace audio {
|
||||
assert(audio_buffer->sample_count / 2);
|
||||
}
|
||||
|
||||
void load(renpy::language::audio const * const audio, int count)
|
||||
void load()
|
||||
{
|
||||
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]);
|
||||
for (int i = 0; i < audio_files_count; i++) {
|
||||
audio_buffers[i].audio_file = &audio_files[i];
|
||||
decode(audio_files[i].path, &audio_buffers[i]);
|
||||
audio_instances[i].audio_buffer = &audio_buffers[i];
|
||||
audio_instances[i].sample_index = 0;
|
||||
audio_instances[i].tail_index = audio_buffers[i].sample_count;
|
||||
}
|
||||
}
|
||||
|
||||
void play(int audio_index)
|
||||
inline static int min(int a, int b)
|
||||
{
|
||||
assert(audio_index >= 0 && audio_index < audio_buffers_count);
|
||||
assert(audio_instances_count < max_audio_instances);
|
||||
|
||||
AudioInstance & instance = audio_instances[audio_instances_count++];
|
||||
|
||||
instance.audio_index = (int)audio_index;
|
||||
instance.audio_buffer = &audio_buffers[audio_index];
|
||||
instance.sample_index = 0;
|
||||
instance.tail_index = audio_buffers[audio_index].sample_count;
|
||||
instance.fadeout_end = 0;
|
||||
instance.fadeout_index = 0;
|
||||
}
|
||||
|
||||
void stop(int audio_index, double fadeout)
|
||||
{
|
||||
assert(audio_index >= 0 && audio_index < audio_buffers_count);
|
||||
|
||||
for (int i = 0; i < audio_instances_count; i++) {
|
||||
if (audio_instances[i].audio_index == audio_index) {
|
||||
if (audio_instances[i].fadeout_end == 0) {
|
||||
fprintf(stderr, "audio: stop instance %d index %d\n", i, audio_index);
|
||||
audio_instances[i].fadeout_end = fadeout * (double)sample_rate;
|
||||
audio_instances[i].fadeout_index = 0;
|
||||
} else {
|
||||
fprintf(stderr, "audio: duplicate stop on instance %d index %d\n", i, audio_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void saturation_add(int16_t * mix_buffer, int32_t value)
|
||||
{
|
||||
int32_t mix_value = *mix_buffer;
|
||||
mix_value += value;
|
||||
if (mix_value > 32767)
|
||||
mix_value = 32767;
|
||||
if (mix_value < -32768)
|
||||
mix_value = -32768;
|
||||
*mix_buffer = mix_value;
|
||||
}
|
||||
|
||||
static inline void remove_instance(int instance_index)
|
||||
{
|
||||
fprintf(stderr, "removed instance %d index %d\n", instance_index, audio_instances[instance_index].audio_index);
|
||||
|
||||
for (int i = instance_index; i < (audio_instances_count - 1); i++) {
|
||||
audio_instances[i] = audio_instances[i + 1];
|
||||
}
|
||||
audio_instances_count -= 1;
|
||||
}
|
||||
|
||||
static inline void update_instance(int16_t * mix_buffer, AudioInstance & instance)
|
||||
{
|
||||
int16_t const * const buf = instance.audio_buffer->buf;
|
||||
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;
|
||||
for (int i = 0; i < half_period_samples; i++) {
|
||||
if (loop_end != 0) {
|
||||
if (instance.sample_index >= loop_end) {
|
||||
instance.sample_index = 0;
|
||||
instance.tail_index = loop_end;
|
||||
}
|
||||
} else if (instance.sample_index >= sample_count) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (instance.fadeout_end != 0 && instance.fadeout_index >= instance.fadeout_end) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert(instance.sample_index < sample_count);
|
||||
assert(instance.tail_index <= sample_count);
|
||||
|
||||
|
||||
double fadeout = 1.0;
|
||||
if (instance.fadeout_end != 0) {
|
||||
fadeout = 1.0 - ((double)instance.fadeout_index / (double)instance.fadeout_end);
|
||||
}
|
||||
|
||||
for (int ch = 0; ch < channels; ch++) {
|
||||
int32_t value = buf[instance.sample_index * channels + ch];
|
||||
if (instance.tail_index != sample_count) {
|
||||
value += buf[instance.tail_index * channels + ch];
|
||||
}
|
||||
saturation_add(&mix_buffer[mix_index * channels + ch], (double)value * fadeout);
|
||||
}
|
||||
instance.sample_index += 1;
|
||||
instance.fadeout_index += 1;
|
||||
if (instance.tail_index != sample_count) {
|
||||
instance.tail_index += 1;
|
||||
}
|
||||
|
||||
mix_index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool should_cull_instance(AudioInstance & instance)
|
||||
{
|
||||
if (instance.audio_buffer->audio->loop_end != 0.0 && instance.sample_index >= instance.audio_buffer->sample_count) {
|
||||
return true;
|
||||
}
|
||||
if (instance.fadeout_end != 0 && instance.fadeout_index >= instance.fadeout_end) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return (a < b) ? a : b;
|
||||
}
|
||||
|
||||
void update()
|
||||
{
|
||||
int half_period_samples = audio_spec.freq / 2;
|
||||
int half_period_size = half_period_samples * sample_size * audio_spec.channels;
|
||||
if (SDL_GetAudioStreamQueued(audio_stream) >= half_period_size)
|
||||
return;
|
||||
|
||||
int16_t mix_buffer[half_period_samples * channels];
|
||||
memset(mix_buffer, 0, (sizeof (mix_buffer)));
|
||||
|
||||
for (int i = 0; i < audio_instances_count; i++) {
|
||||
update_instance(mix_buffer, audio_instances[i]);
|
||||
}
|
||||
AudioInstance & instance = audio_instances[0];
|
||||
int16_t const * const buf = instance.audio_buffer->buf;
|
||||
uint32_t const sample_count = instance.audio_buffer->sample_count;
|
||||
uint32_t const loop_end = instance.audio_buffer->audio_file->loop_end;
|
||||
|
||||
bool culled = true;
|
||||
while (culled) {
|
||||
culled = false;
|
||||
for (int i = 0; i < audio_instances_count; i++) {
|
||||
if (should_cull_instance(audio_instances[i])) {
|
||||
culled = true;
|
||||
remove_instance(i);
|
||||
break;
|
||||
uint32_t mix_index = 0;
|
||||
for (int i = 0; i < half_period_samples; i++) {
|
||||
if (instance.sample_index >= loop_end) {
|
||||
instance.sample_index = 0;
|
||||
instance.tail_index = loop_end;
|
||||
fprintf(stderr, "loop\n");
|
||||
}
|
||||
|
||||
assert(instance.sample_index < sample_count);
|
||||
assert(instance.tail_index <= sample_count);
|
||||
|
||||
for (int ch = 0; ch < channels; ch++) {
|
||||
mix_buffer[mix_index * channels + ch] += buf[instance.sample_index * channels + ch];
|
||||
if (instance.tail_index != sample_count) {
|
||||
mix_buffer[mix_index * channels + ch] += buf[instance.tail_index * channels + ch];
|
||||
}
|
||||
}
|
||||
instance.sample_index += 1;
|
||||
if (instance.tail_index != sample_count) {
|
||||
instance.tail_index += 1;
|
||||
}
|
||||
|
||||
mix_index += 1;
|
||||
}
|
||||
|
||||
SDL_PutAudioStreamData(audio_stream, (void *)mix_buffer, half_period_size);
|
||||
|
||||
@ -24,7 +24,6 @@
|
||||
#include "renpy/vulkan.h"
|
||||
#include "renpy/interpreter.h"
|
||||
#include "renpy/interact.h"
|
||||
#include "renpy/script.h"
|
||||
|
||||
#include "scenes/shadow_test/shadow_test.h"
|
||||
#include "scenes/eidelwind/eidelwind.h"
|
||||
@ -773,7 +772,7 @@ int main()
|
||||
//collada_state.update(0);
|
||||
|
||||
audio::init();
|
||||
audio::load(renpy::script::audio, renpy::script::audio_length);
|
||||
audio::load();
|
||||
|
||||
while (quit == false) {
|
||||
audio::update();
|
||||
|
||||
@ -4,8 +4,6 @@
|
||||
#include "renpy/script.h"
|
||||
#include "renpy/interpreter.h"
|
||||
|
||||
#include "audio.h"
|
||||
|
||||
namespace renpy {
|
||||
void interpreter::reset()
|
||||
{
|
||||
@ -66,12 +64,7 @@ namespace renpy {
|
||||
|
||||
switch (statement.type) {
|
||||
case language::type::play:
|
||||
fprintf(stderr, "interpret_one[%d]: play %d\n", pc, statement.play.audioIndex);
|
||||
audio::play(statement.play.audioIndex);
|
||||
pc += 1;
|
||||
break;
|
||||
case language::type::stop:
|
||||
audio::stop(statement.stop.audioIndex, statement.stop.fadeout);
|
||||
fprintf(stderr, "interpret_one[%d]: play\n", pc);
|
||||
pc += 1;
|
||||
break;
|
||||
case language::type::scene_color:
|
||||
|
||||
@ -194,14 +194,15 @@ const language::character characters[] = {
|
||||
const int characters_length = (sizeof (characters)) / (sizeof (characters[0]));
|
||||
|
||||
const language::audio audio[] = {
|
||||
{ .path = "audio/sfx/Chime.opus.bin", .loop_end = 0.0 }, // 0 sfx/Chime.ogg
|
||||
{ .path = "audio/sfx/MistAmbience.opus.bin", .loop_end = 22.0 }, // 1 sfx/MistAmbience.ogg
|
||||
{ .path = "audio/music/TinyForestMinstrels.opus.bin", .loop_end = 44.0 }, // 2 music/TinyForestMinstrels.ogg
|
||||
{ .path = "audio/music/PhrygianButterflies.opus.bin", .loop_end = 40.125 }, // 3 music/PhrygianButterflies.ogg
|
||||
{ .path = "audio/music/Poem1.opus.bin", .loop_end = 0.0 }, // 4 music/Poem1.ogg
|
||||
{ .path = "audio/placeholdermeow.opus.bin", .loop_end = 0.0 }, // 5 placeholdermeow.mp3
|
||||
{ .path = "audio/music/ScaredMice.opus.bin", .loop_end = 8.0 }, // 6 music/ScaredMice.ogg
|
||||
{ .path = "audio/music/WheatFields.opus.bin", .loop_end = 34.0 }, // 7 music/WheatFields.ogg
|
||||
{ .path = "sfx/Chime.opus" }, // 0 sfx/Chime.ogg
|
||||
{ .path = "sfx/MistAmbience.opus" }, // 1 sfx/MistAmbience.ogg
|
||||
{ .path = "music/TinyForestMinstrels.opus" }, // 2 music/TinyForestMinstrels.ogg
|
||||
{ .path = "n5test.opus" }, // 3 n5test.ogg
|
||||
{ .path = "music/PhrygianButterflies.opus" }, // 4 music/PhrygianButterflies.ogg
|
||||
{ .path = "music/Poem1.opus" }, // 5 music/Poem1.ogg
|
||||
{ .path = "placeholdermeow.opus" }, // 6 placeholdermeow.mp3
|
||||
{ .path = "music/ScaredMice.opus" }, // 7 music/ScaredMice.ogg
|
||||
{ .path = "music/WheatFields.opus" }, // 8 music/WheatFields.ogg
|
||||
};
|
||||
|
||||
const int audio_length = (sizeof (audio)) / (sizeof (audio[0]));
|
||||
@ -221,8 +222,8 @@ const language::image images[] = {
|
||||
const int images_length = (sizeof (images)) / (sizeof (images[0]));
|
||||
|
||||
const language::option options[] = {
|
||||
{ .string = "Complain", .statementIndex = 19 }, // 0
|
||||
{ .string = "Rationalize", .statementIndex = 27 }, // 1
|
||||
{ .string = "Complain", .statementIndex = 18 }, // 0
|
||||
{ .string = "Rationalize", .statementIndex = 26 }, // 1
|
||||
{ .string = "Good idea", .statementIndex = 54 }, // 2
|
||||
{ .string = "I am too tired", .statementIndex = 61 }, // 3
|
||||
{ .string = "Beg for mercy", .statementIndex = 78 }, // 4
|
||||
@ -232,45 +233,45 @@ const language::option options[] = {
|
||||
const int options_length = (sizeof (options)) / (sizeof (options[0]));
|
||||
|
||||
const language::statement statements[] = {
|
||||
{ .type = type::play, .play = { .audioIndex = 0 } }, // 0 sfx/Chime.ogg
|
||||
{ .type = type::play, .play = { .audioIndex = 1 } }, // 1 sfx/MistAmbience.ogg
|
||||
{ .type = type::play, .play = { .audioIndex = 0, /* FIXME channel */ } }, // 0 sfx/Chime.ogg
|
||||
{ .type = type::play, .play = { .audioIndex = 1, /* FIXME channel */ } }, // 1 sfx/MistAmbience.ogg
|
||||
{ .type = type::scene_color, .scene_color = { .color = 0xffffff } }, // 2 bgwhite
|
||||
{ .type = type::dissolve, .dissolve = { .duration = 3.0 } }, // 3
|
||||
{ .type = type::say, .say = { .characterIndex = 4, .stringIndex = 0 } }, // 4 n "Far over the mountains of Almystice"
|
||||
{ .type = type::say, .say = { .characterIndex = 4, .stringIndex = 1 } }, // 5 n "Beyond the tumultuous waters of the Lilac Bay"
|
||||
{ .type = type::say, .say = { .characterIndex = 4, .stringIndex = 2 } }, // 6 n "And across the vast fields of Alysen"
|
||||
{ .type = type::play, .play = { .audioIndex = 2 } }, // 7 music/TinyForestMinstrels.ogg
|
||||
{ .type = type::play, .play = { .audioIndex = 2, /* FIXME channel */ } }, // 7 music/TinyForestMinstrels.ogg
|
||||
{ .type = type::say, .say = { .characterIndex = 4, .stringIndex = 3 } }, // 8 n "Tiny minstrels can be heard amongst the trees"
|
||||
{ .type = type::stop, .stop = { .audioIndex = 2, .fadeout = 5.5 } }, // 9 TinyForestMinstrels
|
||||
{ .type = type::scene, .scene = { .imageIndex = 1 } }, // 10 bgforest1
|
||||
{ .type = type::dissolve, .dissolve = { .duration = 3.0 } }, // 11
|
||||
{ .type = type::show, .show = { .imageIndex = 8, .transformIndex = transform::left } }, // 12 al
|
||||
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 4 } }, // 13 a "Are we almost there?"
|
||||
{ .type = type::show, .show = { .imageIndex = 7, .transformIndex = transform::right } }, // 14 ei
|
||||
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 5 } }, // 15 e "Hmmm... Not really"
|
||||
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 6 } }, // 16 a "How much further have we to go?"
|
||||
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 7 } }, // 17 e "About two more moons"
|
||||
{ .type = type::menu, .menu = { .count = 2, .optionIndex = 0 } }, // 18 "Complain", "Rationalize"
|
||||
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 8 } }, // 19 a "We are still sooo far awayyy"
|
||||
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 9 } }, // 20 e "And it will be even further if you dont stop complaining"
|
||||
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 10 } }, // 21 a "Easy for you to say, all you have to carry is a little memory pipe!"
|
||||
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 11 } }, // 22 a "I'm tired ><"
|
||||
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 12 } }, // 23 e "Don't start whining now!"
|
||||
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 13 } }, // 24 e "You need to remember why we have come all this way"
|
||||
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 14 } }, // 25 a "I understand... I suppose it is for an important purpose"
|
||||
{ .type = type::jump, .jump = { .statementIndex = 29 } }, // 26 internal jump (b'__menu_end', 0)
|
||||
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 14 } }, // 27 a "I understand... I suppose it is for an important purpose"
|
||||
{ .type = type::jump, .jump = { .statementIndex = 29 } }, // 28 internal jump (b'__menu_end', 0)
|
||||
{ .type = type::jump, .jump = { .statementIndex = 30 } }, // 29 mainbranch1
|
||||
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 15 } }, // 30 e "We're almost out of the forest, we can take a little break once we clear the tree line"
|
||||
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 16 } }, // 31 a "Is that where the flora field is?"
|
||||
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 17 } }, // 32 e "Why yes, If I remember correctly, it should be just up ahead"
|
||||
{ .type = type::stop, .stop = { .audioIndex = 2, .fadeout = 5.5 } }, // 33 TinyForestMinstrels
|
||||
{ .type = type::scene_color, .scene_color = { .color = 0xffffff } }, // 34 bgwhite
|
||||
{ .type = type::play, .play = { .audioIndex = 0 } }, // 35 sfx/Chime.ogg
|
||||
{ .type = type::dissolve, .dissolve = { .duration = 1.0 } }, // 36
|
||||
{ .type = type::scene, .scene = { .imageIndex = 1 } }, // 9 bgforest1
|
||||
{ .type = type::dissolve, .dissolve = { .duration = 3.0 } }, // 10
|
||||
{ .type = type::show, .show = { .imageIndex = 8, .transformIndex = transform::left } }, // 11 al
|
||||
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 4 } }, // 12 a "Are we almost there?"
|
||||
{ .type = type::show, .show = { .imageIndex = 7, .transformIndex = transform::right } }, // 13 ei
|
||||
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 5 } }, // 14 e "Hmmm... Not really"
|
||||
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 6 } }, // 15 a "How much further have we to go?"
|
||||
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 7 } }, // 16 e "About two more moons"
|
||||
{ .type = type::menu, .menu = { .count = 2, .optionIndex = 0 } }, // 17 "Complain", "Rationalize"
|
||||
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 8 } }, // 18 a "We are still sooo far awayyy"
|
||||
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 9 } }, // 19 e "And it will be even further if you dont stop complaining"
|
||||
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 10 } }, // 20 a "Easy for you to say, all you have to carry is a little memory pipe!"
|
||||
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 11 } }, // 21 a "I'm tired ><"
|
||||
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 12 } }, // 22 e "Don't start whining now!"
|
||||
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 13 } }, // 23 e "You need to remember why we have come all this way"
|
||||
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 14 } }, // 24 a "I understand... I suppose it is for an important purpose"
|
||||
{ .type = type::jump, .jump = { .statementIndex = 28 } }, // 25 internal jump (b'__menu_end', 0)
|
||||
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 14 } }, // 26 a "I understand... I suppose it is for an important purpose"
|
||||
{ .type = type::jump, .jump = { .statementIndex = 28 } }, // 27 internal jump (b'__menu_end', 0)
|
||||
{ .type = type::jump, .jump = { .statementIndex = 29 } }, // 28 mainbranch1
|
||||
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 15 } }, // 29 e "We're almost out of the forest, we can take a little break once we clear the tree line"
|
||||
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 16 } }, // 30 a "Is that where the flora field is?"
|
||||
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 17 } }, // 31 e "Why yes, If I remember correctly, it should be just up ahead"
|
||||
{ .type = type::stop, .stop = { /* FIXME channel */ } }, // 32
|
||||
{ .type = type::scene_color, .scene_color = { .color = 0xffffff } }, // 33 bgwhite
|
||||
{ .type = type::play, .play = { .audioIndex = 0, /* FIXME channel */ } }, // 34 sfx/Chime.ogg
|
||||
{ .type = type::dissolve, .dissolve = { .duration = 1.0 } }, // 35
|
||||
{ .type = type::voice, .voice = { .audioIndex = 3 } }, // 36 n5test.ogg
|
||||
{ .type = type::say, .say = { .characterIndex = 4, .stringIndex = 18 } }, // 37 n "As the minstrel mice girls continue along the path, the forest opens up into a beautiful field of flowers"
|
||||
{ .type = type::play, .play = { .audioIndex = 3 } }, // 38 music/PhrygianButterflies.ogg
|
||||
{ .type = type::play, .play = { .audioIndex = 4, /* FIXME channel */ } }, // 38 music/PhrygianButterflies.ogg
|
||||
{ .type = type::scene, .scene = { .imageIndex = 3 } }, // 39 bgflower1
|
||||
{ .type = type::dissolve, .dissolve = { .duration = 1.0 } }, // 40
|
||||
{ .type = type::show, .show = { .imageIndex = 7, .transformIndex = transform::right } }, // 41 ei
|
||||
@ -286,26 +287,26 @@ const language::statement statements[] = {
|
||||
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 27 } }, // 51 e "Yah yah"
|
||||
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 28 } }, // 52 e "Anyways, shall I recite a tale?"
|
||||
{ .type = type::menu, .menu = { .count = 2, .optionIndex = 2 } }, // 53 "Good idea", "I am too tired"
|
||||
{ .type = type::stop, .stop = { .audioIndex = 3, .fadeout = 4.2 } }, // 54 PhrygianButterflies
|
||||
{ .type = type::stop, .stop = { /* FIXME channel */ } }, // 54
|
||||
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 29 } }, // 55 a "Why dont you sing the story of Eleanor the Hero!"
|
||||
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 30 } }, // 56 e "Sure"
|
||||
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 31 } }, // 57 a "..."
|
||||
{ .type = type::play, .play = { .audioIndex = 4 } }, // 58 music/Poem1.ogg
|
||||
{ .type = type::play, .play = { .audioIndex = 5, /* FIXME channel */ } }, // 58 music/Poem1.ogg
|
||||
{ .type = type::pause, .pause = { .duration = 40 } }, // 59
|
||||
{ .type = type::jump, .jump = { .statementIndex = 65 } }, // 60 internal jump (b'__menu_end', 1)
|
||||
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 32 } }, // 61 e "Serves you right for scaring those elephant-dogs"
|
||||
{ .type = type::stop, .stop = { .audioIndex = 3, .fadeout = 4.2 } }, // 62 PhrygianButterflies
|
||||
{ .type = type::stop, .stop = { /* FIXME channel */ } }, // 62
|
||||
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 33 } }, // 63 a "They were asking for it, you know"
|
||||
{ .type = type::jump, .jump = { .statementIndex = 65 } }, // 64 internal jump (b'__menu_end', 1)
|
||||
{ .type = type::jump, .jump = { .statementIndex = 66 } }, // 65 mainbranch2
|
||||
{ .type = type::hide, .hide = { .imageIndex = 7 } }, // 66 ei
|
||||
{ .type = type::show, .show = { .imageIndex = 6, .transformIndex = transform::right } }, // 67 catw
|
||||
{ .type = type::show, .show = { .imageIndex = 7, .transformIndex = transform::centerleft } }, // 68 ei
|
||||
{ .type = type::voice, .voice = { .audioIndex = 5 } }, // 69 placeholdermeow.mp3
|
||||
{ .type = type::voice, .voice = { .audioIndex = 6 } }, // 69 placeholdermeow.mp3
|
||||
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 34 } }, // 70 c "Rawrrrr"
|
||||
{ .type = type::hide, .hide = { .imageIndex = 6 } }, // 71 catw
|
||||
{ .type = type::show, .show = { .imageIndex = 5, .transformIndex = transform::right } }, // 72 cat
|
||||
{ .type = type::play, .play = { .audioIndex = 6 } }, // 73 music/ScaredMice.ogg
|
||||
{ .type = type::play, .play = { .audioIndex = 7, /* FIXME channel */ } }, // 73 music/ScaredMice.ogg
|
||||
{ .type = type::say, .say = { .characterIndex = 3, .stringIndex = 35 } }, // 74 mg "AHHHHHHHHHH!!!!!"
|
||||
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 36 } }, // 75 c "Nyanyanyanya"
|
||||
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 37 } }, // 76 c "Well, what do we have here? If it isn't two little meowse girls, all alone amongst the flowers"
|
||||
@ -316,9 +317,9 @@ const language::statement statements[] = {
|
||||
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 40 } }, // 81 e "Please don't eat us, miss kitty cat!!! ><"
|
||||
{ .type = type::jump, .jump = { .statementIndex = 83 } }, // 82 internal jump (b'__menu_end', 2)
|
||||
{ .type = type::jump, .jump = { .statementIndex = 84 } }, // 83 mainbranch3
|
||||
{ .type = type::stop, .stop = { .audioIndex = 6, .fadeout = 2.0 } }, // 84 ScaredMice
|
||||
{ .type = type::stop, .stop = { /* FIXME channel */ } }, // 84
|
||||
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 41 } }, // 85 c "I'm not gonna eat you nyanyanya"
|
||||
{ .type = type::play, .play = { .audioIndex = 2 } }, // 86 music/TinyForestMinstrels.ogg
|
||||
{ .type = type::play, .play = { .audioIndex = 2, /* FIXME channel */ } }, // 86 music/TinyForestMinstrels.ogg
|
||||
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 42 } }, // 87 c "I just want to know what two little meowses are doing so very far away from home"
|
||||
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 43 } }, // 88 c "Also, are you minstrels?"
|
||||
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 44 } }, // 89 e "Y-Yes"
|
||||
@ -340,8 +341,8 @@ const language::statement statements[] = {
|
||||
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 60 } }, // 105 c "Well, no..."
|
||||
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 61 } }, // 106 a "Then why are you traveling to Castle Alysen?"
|
||||
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 62 } }, // 107 c "uhhh"
|
||||
{ .type = type::play, .play = { .audioIndex = 1 } }, // 108 sfx/MistAmbience.ogg
|
||||
{ .type = type::stop, .stop = { .audioIndex = 2, .fadeout = 2.0 } }, // 109 TinyForestMinstrels
|
||||
{ .type = type::play, .play = { .audioIndex = 1, /* FIXME channel */ } }, // 108 sfx/MistAmbience.ogg
|
||||
{ .type = type::stop, .stop = { /* FIXME channel */ } }, // 109
|
||||
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 63 } }, // 110 c "I DONT NEED TO BE PRESSURED BY LITTLE MICE TO SAY ANYTHING!!!!"
|
||||
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 64 } }, // 111 c "GOOD DAY!"
|
||||
{ .type = type::hide, .hide = { .imageIndex = 5 } }, // 112 cat
|
||||
@ -350,7 +351,7 @@ const language::statement statements[] = {
|
||||
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 67 } }, // 115 a "She didn't seem so bad"
|
||||
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 68 } }, // 116 e "Are you kidding? She's a crazy kitty!"
|
||||
{ .type = type::scene_color, .scene_color = { .color = 0xffffff } }, // 117 bgwhite
|
||||
{ .type = type::play, .play = { .audioIndex = 0 } }, // 118 sfx/Chime.ogg
|
||||
{ .type = type::play, .play = { .audioIndex = 0, /* FIXME channel */ } }, // 118 sfx/Chime.ogg
|
||||
{ .type = type::dissolve, .dissolve = { .duration = 3.0 } }, // 119
|
||||
{ .type = type::say, .say = { .characterIndex = 4, .stringIndex = 69 } }, // 120 n "After their encounter with the weird cat, the mice scurry out of the flower field and into the nearby meadow"
|
||||
{ .type = type::scene, .scene = { .imageIndex = 2 } }, // 121 bgforest2
|
||||
@ -363,7 +364,7 @@ const language::statement statements[] = {
|
||||
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 73 } }, // 128 a "Did you hear that?!?!"
|
||||
{ .type = type::show, .show = { .imageIndex = 7, .transformIndex = transform::centerleft } }, // 129 ei
|
||||
{ .type = type::show, .show = { .imageIndex = 5, .transformIndex = transform::right } }, // 130 cat
|
||||
{ .type = type::play, .play = { .audioIndex = 3 } }, // 131 music/PhrygianButterflies.ogg
|
||||
{ .type = type::play, .play = { .audioIndex = 4, /* FIXME channel */ } }, // 131 music/PhrygianButterflies.ogg
|
||||
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 74 } }, // 132 c "Hey there..."
|
||||
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 75 } }, // 133 c "I apologize"
|
||||
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 76 } }, // 134 c "I didn't mean to storm off like that"
|
||||
@ -426,15 +427,15 @@ const language::statement statements[] = {
|
||||
{ .type = type::hide, .hide = { .imageIndex = 5 } }, // 191 cat
|
||||
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 132 } }, // 192 a "Sounds good!"
|
||||
{ .type = type::hide, .hide = { .imageIndex = 8 } }, // 193 al
|
||||
{ .type = type::stop, .stop = { .audioIndex = 3, .fadeout = 2.0 } }, // 194 PhrygianButterflies
|
||||
{ .type = type::stop, .stop = { /* FIXME channel */ } }, // 194
|
||||
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 133 } }, // 195 e "Oh dear!"
|
||||
{ .type = type::hide, .hide = { .imageIndex = 7 } }, // 196 ei
|
||||
{ .type = type::scene_color, .scene_color = { .color = 0xffffff } }, // 197 bgwhite
|
||||
{ .type = type::play, .play = { .audioIndex = 0 } }, // 198 sfx/Chime.ogg
|
||||
{ .type = type::play, .play = { .audioIndex = 0, /* FIXME channel */ } }, // 198 sfx/Chime.ogg
|
||||
{ .type = type::dissolve, .dissolve = { .duration = 2.0 } }, // 199
|
||||
{ .type = type::say, .say = { .characterIndex = 4, .stringIndex = 134 } }, // 200 n "And so the mice girls follow the noble cat further towards their destination"
|
||||
{ .type = type::scene, .scene = { .imageIndex = 4 } }, // 201 bgwheatfield1
|
||||
{ .type = type::play, .play = { .audioIndex = 7 } }, // 202 music/WheatFields.ogg
|
||||
{ .type = type::play, .play = { .audioIndex = 8, /* FIXME channel */ } }, // 202 music/WheatFields.ogg
|
||||
{ .type = type::show, .show = { .imageIndex = 5, .transformIndex = transform::right } }, // 203 cat
|
||||
{ .type = type::dissolve, .dissolve = { .duration = 1.3 } }, // 204
|
||||
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 135 } }, // 205 c "Nya"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user