scene/logo: simultaneous jingle and animation

This commit is contained in:
Zack Buhman 2025-06-28 13:36:24 -05:00
parent 5eaadbb1c9
commit 9b2d91e2e6
17 changed files with 235 additions and 95 deletions

Binary file not shown.

BIN
pcm/jingle.adpcm Normal file

Binary file not shown.

15
pcm/jingle.adpcm.h Normal file
View File

@ -0,0 +1,15 @@
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
extern uint32_t _binary_pcm_jingle_adpcm_start __asm("_binary_pcm_jingle_adpcm_start");
extern uint32_t _binary_pcm_jingle_adpcm_end __asm("_binary_pcm_jingle_adpcm_end");
extern uint32_t _binary_pcm_jingle_adpcm_size __asm("_binary_pcm_jingle_adpcm_size");
#ifdef __cplusplus
}
#endif

BIN
pcm/jingle.ogg Normal file

Binary file not shown.

BIN
pcm/jingle.pcm Normal file

Binary file not shown.

View File

@ -20,11 +20,9 @@
#include "holly/video_output.hpp"
#include "framebuffer.hpp"
#include "scene/scene.hpp"
#include "scene/tracker/scene.hpp"
#include "scene/logo/scene.hpp"
#include "graphics.hpp"
#include "texture.hpp"
#include "scene/scene.hpp"
static volatile int ta_in_use = 0;
static volatile int core_in_use = 0;
@ -32,13 +30,6 @@ static volatile int next_frame = 0;
static volatile int framebuffer_ix = 0;
static volatile int next_frame_ix = 0;
static const scene::scene scenes[] = {
scene::tracker::scene,
scene::logo::scene,
};
static const scene::scene * current_scene = &scenes[1];
void graphics_interrupt(uint32_t istnrm)
{
if (istnrm & istnrm::v_blank_in) {
@ -103,11 +94,11 @@ void graphics_init()
transfer_palettes();
}
void graphics_scene_init()
void graphics_scene_init(const opb_size * opb_size)
{
region_array_multipass(framebuffer.tile_width(),
framebuffer.tile_height(),
&current_scene->opb_size,
opb_size,
ta_cont_count,
texture_memory_alloc.region_array.start,
texture_memory_alloc.object_list.start);
@ -117,7 +108,7 @@ void graphics_event(ta_parameter_writer& writer)
{
writer.offset = 0;
current_scene->transfer(writer);
scene::current_scene->transfer(writer);
while (ta_in_use);
while (core_in_use);
@ -126,8 +117,8 @@ void graphics_event(ta_parameter_writer& writer)
texture_memory_alloc.isp_tsp_parameters.end,
texture_memory_alloc.object_list.start,
texture_memory_alloc.object_list.end,
current_scene->opb_size.total(),
current_scene->ta_alloc,
scene::current_scene->opb_size.total(),
scene::current_scene->ta_alloc,
framebuffer.tile_width(),
framebuffer.tile_height());
ta_polygon_converter_writeback(writer.buf, writer.offset);

View File

@ -3,10 +3,11 @@
#include <stdint.h>
#include "holly/ta_parameter.hpp"
#include "holly/region_array.hpp"
constexpr int ta_cont_count = 1;
void graphics_interrupt(uint32_t istnrm);
void graphics_init();
void graphics_event(ta_parameter_writer& writer);
void graphics_scene_init();
void graphics_scene_init(const opb_size * opb_size);

View File

@ -12,6 +12,8 @@
#include "graphics.hpp"
#include "interpreter.hpp"
#include "sound.hpp"
#include "scene/scene.hpp"
#include "scene/logo/sound.hpp"
#include "xm/milkypack01.xm.h"
#include "xm.h"
@ -51,12 +53,8 @@ void vbr600()
aica::tactl_tima::TACTL(0) // increment once every sample
| aica::tactl_tima::TIMA(0xffff) // interrupt after 1 counts
;
//interpreter::interrupt();
} else if (sh7091.CCN.EXPEVT == 0 && sh7091.CCN.INTEVT == 0x400) { // TMU0
sh7091.TMU.TCR0
= tmu::tcr0::UNIE
| tmu::tcr0::tpsc::p_phi_256; // clear underflow
scene::logo::sound::interrupt();
//interpreter::interrupt();
} else {
serial::string("vbr600\n");
@ -197,7 +195,7 @@ void load_xm(float clock_multiplier)
sample_data,
sample_data_length);
interpreter::init(clock_multiplier);
sound_init(sample_data, sample_data_ix);
sound::transfer(sample_data, sample_data_ix);
printf("tick_rate %d\n", state.tick_rate);
}
@ -235,13 +233,13 @@ void main()
{
serial::init(0);
//const float tmu_clock_multiplier = 195.32;
const float aica_clock_multiplier = 44.1;
load_xm(aica_clock_multiplier);
sound::init();
graphics_init();
graphics_scene_init();
scene::scene_init();
//const float aica_clock_multiplier = 44.1;
//load_xm(aica_clock_multiplier);
test_pattern();
//test_pattern();
interrupt_init();
//channel_sandbox_defaults();

View File

@ -4,6 +4,7 @@
#include "ta_parameter.hpp"
#include "scene/logo/scene.hpp"
#include "scene/logo/sound.hpp"
#include "texture.hpp"
#include "framebuffer.hpp"
@ -18,7 +19,7 @@ static vec3 screen_transform(const vec3& v)
static inline float light_intensity(vec3 n, vec3 l)
{
float ambient = 0.2f;
float ambient = 0.4f;
float diffuse_strength = 0.7f;
float n_dot_l = dot(n, l);
@ -31,7 +32,7 @@ static inline float light_intensity(vec3 n, vec3 l)
vec3 light_vec = (vec3){-1, -1, -1} - (vec3){0, 0, 0};
static void render_mesh(ta_parameter_writer& writer, const mesh& mesh, const mat4x4& trans, bool wireframe)
static void render_mesh(ta_parameter_writer& writer, const mesh& mesh, const mat4x4& trans, float base_intensity, bool wireframe)
{
if (wireframe) {
global_polygon_untextured(writer,
@ -57,7 +58,7 @@ static void render_mesh(ta_parameter_writer& writer, const mesh& mesh, const mat
normal_cache[i] = normal_multiply(trans, mesh.polygon_normal[i]);
}
const int green = 0x00ff00;
const int green = (int)(255.f * base_intensity) << 8;
for (int i = 0; i < mesh.polygons_length; i++) {
const polygon& p = mesh.polygons[i];
@ -118,26 +119,37 @@ namespace scene::logo {
.punch_through = 0
},
.transfer = transfer,
.init = init
};
static int tick = 0;
static int last_tick = 0;
struct keyframe {
float rx;
float ry;
float s;
float i; // intensity
float rx; // rotate_x
float ry; // rotate_y
float s; // scale
float duration;
};
const struct keyframe keyframes[] = {
{
.i = 0,
.rx = 0,
.ry = 0,
.s = 0.1,
.duration = 1.0 / (5 * 60),
.ry = pi,
.s = 0.01,
.duration = 1.0 / (5.2 * 60),
},
{
.i = 1,
.rx = 0,
.ry = pi,
.s = 0.1,
.duration = 1.0 / (4.5 * 60),
},
{
.i = 1,
.rx = pi / 4,
.ry = pi + pi / 4,
.s = 0.7,
@ -145,6 +157,8 @@ namespace scene::logo {
},
};
const int keyframes_length = (sizeof (keyframes)) / (sizeof (keyframes[0]));
static inline float clamp(float f)
{
if (f > 1.0)
@ -159,14 +173,16 @@ namespace scene::logo {
{
float ratio = clamp(dt * a.duration);
float di = b.i - a.i;
float drx = b.rx - a.rx;
float dry = b.ry - a.ry;
float drs = b.s - a.s;
float ds = b.s - a.s;
return {
.i = a.i + di * ratio,
.rx = a.rx + drx * ratio,
.ry = a.ry + dry * ratio,
.s = a.s + drs * ratio,
.s = a.s + ds * ratio,
.duration = a.duration,
};
}
@ -176,7 +192,17 @@ namespace scene::logo {
vec3 t = {framebuffer.px_width / 2.f, framebuffer.px_height / 2.f, 0};
float s = framebuffer.px_height / 3.f;
keyframe k = interpolate(keyframes[0], keyframes[1], tick - last_tick);
static int keyframe_ix = 0;
float dt = tick - last_tick;
if (dt * keyframes[keyframe_ix].duration >= 1) {
if (keyframe_ix < (keyframes_length - 2)) {
last_tick = tick;
dt = 0;
keyframe_ix += 1;
}
}
keyframe k = interpolate(keyframes[keyframe_ix], keyframes[keyframe_ix + 1], dt);
mat4x4 trans
= translate(t)
@ -189,13 +215,20 @@ namespace scene::logo {
* rotate_y(k.ry)
* scale((vec3){-1, -1, 1});
render_mesh(writer, mesh_thirty_two, trans, tick < (6 * 60));
render_mesh(writer, mesh_bit, trans, tick < (7 * 60));
render_mesh(writer, mesh_jam, trans, tick < (8 * 60));
render_mesh(writer, mesh_thirty_two, trans, k.i, tick < (9.85 * 60));
if (keyframe_ix > 0) {
render_mesh(writer, mesh_bit, trans, k.i, tick < (10.85 * 60));
render_mesh(writer, mesh_jam, trans, k.i, tick < (11.85 * 60));
}
writer.append<ta_global_parameter::end_of_list>() =
ta_global_parameter::end_of_list(para_control::para_type::end_of_list);
tick += 1;
}
void init()
{
::scene::logo::sound::init();
}
}

View File

@ -7,5 +7,7 @@ namespace scene::logo {
void transfer(ta_parameter_writer& writer);
void init();
extern const struct scene::scene scene;
}

74
src/scene/logo/sound.cpp Normal file
View File

@ -0,0 +1,74 @@
#include "aica/aica.hpp"
#include "../../sound.hpp"
#include "scene/logo/sound.hpp"
#include "pcm/jingle.adpcm.h"
#include "printf/printf.h"
namespace scene::logo::sound {
const static void * start = reinterpret_cast<void *>(&_binary_pcm_jingle_adpcm_start);
const static int size = reinterpret_cast<int>(&_binary_pcm_jingle_adpcm_size);
const static int sample_count = size * 2;
const static int loop_length = 65528;
const static int segment_count = sample_count / loop_length;
void init()
{
printf("init\n");
::sound::transfer(start, size);
wait(); aica_sound.common.afsel_mslc_mobuf
= aica::afsel_mslc_mobuf::AFSEL(0)
| aica::afsel_mslc_mobuf::MSLC(0);
wait(); aica_sound.channel[0].LPCTL(1);
wait(); aica_sound.channel[0].PCMS(2); // adpcm
wait(); aica_sound.channel[0].LSA(0);
wait(); aica_sound.channel[0].LEA(loop_length);
wait(); aica_sound.channel[0].D2R(0x0);
wait(); aica_sound.channel[0].D1R(0x0);
wait(); aica_sound.channel[0].RR(0x1f);
wait(); aica_sound.channel[0].AR(0x1f);
wait(); aica_sound.channel[0].OCT(0);
wait(); aica_sound.channel[0].FNS(0);
wait(); aica_sound.channel[0].DISDL(0xf);
wait(); aica_sound.channel[0].DIPAN(0x0);
wait(); aica_sound.channel[0].KYONB(0);
wait(); aica_sound.channel[0].KYONEX(1);
}
void interrupt()
{
static int segment = 0;
wait();
int lp_sgc_eg = aica_sound.common.lp_sgc_eg;
if (aica::lp_sgc_eg::SGC(lp_sgc_eg) == 3) { // release
if (segment != 0)
return;
printf("start\n");
wait(); aica_sound.channel[0].SA(0);
wait(); aica_sound.channel[0].KYONB(1);
wait(); aica_sound.channel[0].KYONEX(1);
return;
}
if (aica::lp_sgc_eg::LP(lp_sgc_eg)) {
segment += 1;
if (segment >= segment_count) {
wait(); aica_sound.channel[0].KYONB(0);
wait(); aica_sound.channel[0].KYONEX(1);
return;
}
printf("loop %d\n", segment);
int sa = 0 + (loop_length / 2) * segment;
wait(); aica_sound.channel[0].SA(sa);
}
}
}

8
src/scene/logo/sound.hpp Normal file
View File

@ -0,0 +1,8 @@
#pragma once
namespace scene::logo::sound {
void init();
void interrupt();
}

22
src/scene/scene.cpp Normal file
View File

@ -0,0 +1,22 @@
#include "graphics.hpp"
#include "scene/scene.hpp"
#include "scene/tracker/scene.hpp"
#include "scene/logo/scene.hpp"
static const scene::scene scenes[] = {
scene::tracker::scene,
scene::logo::scene,
};
namespace scene {
const scene * current_scene = &scenes[1];
void scene_init()
{
graphics_scene_init(&current_scene->opb_size);
if (current_scene->init != nullptr)
current_scene->init();
}
}

View File

@ -1,13 +1,18 @@
#pragma once
#include "holly/ta_parameter.hpp"
#include "holly/region_array.hpp"
namespace scene {
struct scene {
const uint32_t ta_alloc;
const struct opb_size opb_size;
void (* const transfer)(ta_parameter_writer& writer);
};
struct scene {
const uint32_t ta_alloc;
const struct opb_size opb_size;
void (* const transfer)(ta_parameter_writer& writer);
void (* const init)();
};
extern const scene * current_scene;
void scene_init();
}

View File

@ -17,7 +17,7 @@
#include "assert.h"
void g2_aica_dma(uint32_t g2_address, uint32_t system_address, int length)
static void g2_aica_dma(uint32_t g2_address, uint32_t system_address, int length)
{
using namespace dmac;
@ -44,7 +44,7 @@ void g2_aica_dma(uint32_t g2_address, uint32_t system_address, int length)
g2_if.ADST = 1; // start G2-AICA-DMA
}
void g2_aica_dma_wait_complete()
static void g2_aica_dma_wait_complete()
{
// wait for maple DMA completion
while ((system.ISTNRM & istnrm::end_of_dma_aica_dma) == 0);
@ -52,7 +52,7 @@ void g2_aica_dma_wait_complete()
assert(g2_if.ADST == 0);
}
void writeback(void const * const buf, uint32_t size)
static void writeback(void const * const buf, uint32_t size)
{
uint8_t const * const buf8 = reinterpret_cast<uint8_t const * const>(buf);
@ -67,12 +67,18 @@ void writeback(void const * const buf, uint32_t size)
static uint8_t __attribute__((aligned(32))) zero[0x28c0] = {};
void sound_init(uint8_t * sample_data, int sample_data_ix)
namespace sound {
void init()
{
printf("sound::init\n");
wait(); aica_sound.common.vreg_armrst = aica::vreg_armrst::ARMRST(1);
wait(); aica_sound.common.dmea0_mrwinh = aica::dmea0_mrwinh::MRWINH(0b0111);
system.ISTNRM = istnrm::end_of_dma_aica_dma;
writeback(zero, (sizeof (zero)));
// slot/common: 00700000 - 007028c0 (excludes vreg_armrst)
g2_aica_dma((uint32_t)0x00700000, (int)zero, 0x28c0);
g2_aica_dma_wait_complete();
@ -81,34 +87,6 @@ void sound_init(uint8_t * sample_data, int sample_data_ix)
g2_aica_dma((uint32_t)0x00703000, (int)zero, 0x15e0);
g2_aica_dma_wait_complete();
for (int i = 0; i < 16; i++) {
serial::hexlify(&sample_data[i * 16], 16);
}
printf("transfer %08x %08x %d\n", (int)aica_wave_memory, (int)sample_data, sample_data_ix);
// wave memory
int size = (sample_data_ix + 31) & (~31);
writeback(sample_data, size);
system.ISTERR = 0xffffffff;
g2_aica_dma((int)aica_wave_memory, (int)sample_data, size);
g2_aica_dma_wait_complete();
printf("sar0 %08x\n", sh7091.DMAC.SAR0);
printf("dar0 %08x\n", sh7091.DMAC.DAR0);
printf("dmatcr0 %08x\n", sh7091.DMAC.DMATCR0);
printf("chcr0 %08x\n", sh7091.DMAC.CHCR0);
printf("isterr %08x\n", system.ISTERR);
for (int i = 0; i < 16; i++) {
volatile uint8_t * s = &((volatile uint8_t*)aica_wave_memory)[i * 16];
for (int j = 0; j < 16; j++) {
wait();
serial::hexlify(s[j]);
serial::character(' ');
}
serial::character('\n');
}
wait(); aica_sound.common.dmea0_mrwinh = aica::dmea0_mrwinh::MRWINH(0b0001);
for (int i = 0; i < 64; i++) {
@ -142,6 +120,8 @@ void sound_init(uint8_t * sample_data, int sample_data_ix)
wait(); aica_sound.channel[i].LPOFF(1);
}
wait(); aica_sound.channel[0].KYONEX(1);
wait(); aica_sound.common.mono_mem8mb_dac18b_ver_mvol =
aica::mono_mem8mb_dac18b_ver_mvol::MONO(0) // enable panpots
| aica::mono_mem8mb_dac18b_ver_mvol::MEM8MB(0) // 16Mbit SDRAM
@ -149,19 +129,6 @@ void sound_init(uint8_t * sample_data, int sample_data_ix)
| aica::mono_mem8mb_dac18b_ver_mvol::MVOL(0xc) // volume
;
/*
sh7091.TMU.TSTR = 0; // stop all timers
sh7091.TMU.TCOR0 = tick_rate / 2;
sh7091.TMU.TOCR = tmu::tocr::tcoe::tclk_is_external_clock_or_input_capture;
sh7091.TMU.TCR0
= tmu::tcr0::UNIE
| tmu::tcr0::tpsc::p_phi_256; // 256 / 50MHz = 5.12 μs ; underflows in ~1 hour
sh7091.TMU.TCNT0 = 0;
sh7091.TMU.TSTR = tmu::tstr::str0::counter_start;
sh7091.INTC.IPRA = intc::ipra::TMU0(1);
*/
wait(); aica_sound.common.tactl_tima =
aica::tactl_tima::TACTL(0) // increment once every sample
| aica::tactl_tima::TIMA(0xffff) // interrupt after 1 counts
@ -170,3 +137,16 @@ void sound_init(uint8_t * sample_data, int sample_data_ix)
wait(); aica_sound.common.mcieb = (1 << 6); // interrupt timer A
wait(); aica_sound.common.mcire = (1 << 6); // interrupt timer A
}
void transfer(const void * sample_data, int sample_data_ix)
{
printf("aica transfer 0x%08x 0x%08x 0x%x\n", (int)aica_wave_memory, (int)sample_data, sample_data_ix);
int size = (sample_data_ix + 31) & (~31);
writeback(sample_data, size);
g2_aica_dma((int)aica_wave_memory, (int)sample_data, size);
g2_aica_dma_wait_complete();
}
}

View File

@ -13,4 +13,10 @@ static inline void wait()
};
}
void sound_init(uint8_t * sample_data, int sample_data_ix);
namespace sound {
void init();
void transfer(const void * sample_data, int sample_data_ix);
}

View File

@ -5,6 +5,9 @@ TEXTURE_OBJ = \
font/tandy1k.data.o \
model/32bitlogo/colors.data.o
PCM_OBJ = \
pcm/jingle.adpcm.o
XM_PLAYER_OBJ = \
$(LIB)/holly/core.o \
$(LIB)/holly/region_array.o \
@ -26,10 +29,12 @@ XM_PLAYER_OBJ = \
src/framebuffer.o \
src/texture.o \
src/graphics_primitive.o \
src/scene/scene.o \
src/scene/tracker/scene.o \
src/scene/tracker/channel_status.o \
src/scene/tracker/notes.o \
src/scene/logo/scene.o
src/scene/logo/scene.o \
src/scene/logo/sound.o
xm_player.elf: LDSCRIPT = $(LIB)/main.lds
xm_player.elf: $(START_OBJ) $(XM_PLAYER_OBJ) $(TEXTURE_OBJ) $(XM_OBJ) $(LIBGCC)
xm_player.elf: $(START_OBJ) $(XM_PLAYER_OBJ) $(TEXTURE_OBJ) $(XM_OBJ) $(PCM_OBJ) $(LIBGCC)