firmware: put tasks into dedicated files with clean API, prepare story title image and sounds

This commit is contained in:
Anthony Rabine 2023-07-29 23:58:55 +02:00
parent cebd349af4
commit 084af988cb
21 changed files with 744 additions and 329 deletions

Binary file not shown.

BIN
art/pack/dragon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

View file

@ -33,12 +33,14 @@ set(OST_SRCS
system/sdcard.c
system/ff_diskio_sdcard.c
system/debug.c
system/picture.c
system/filesystem.c
system/sdcard.c
system/qor.c
system/qor_armv6m.s
system/audio_player.c
system/vm_task.c
system/hmi_task.c
system/fs_task.c
system/ff/ff.c
system/ff/ffsystem.c
system/ff/ff_stubs.c

View file

@ -4,6 +4,19 @@
#include "ost_hal.h"
#include "filesystem.h"
#include "mini_qoi.h"
#ifdef OST_USE_FF_LIBRARY
#include "ff.h"
#include "diskio.h"
typedef FIL file_t;
#else
// Use standard library
typedef FILE *file_t;
typedef int FRESULT;
#define F_OK
#endif
// SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h,
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
@ -151,3 +164,130 @@ bool filesystem_read_index_file(ost_context_t *ctx)
debug_printf("ERROR: index.ost not found\r\n");
}
}
static mqoi_dec_t dec;
static mqoi_desc_t desc;
static uint8_t bmpImage[256];
static color_t line[320];
file_t file_open(const char *filename)
{
#ifdef OST_USE_FF_LIBRARY
file_t fil;
FRESULT fr = f_open(&fil, filename, FA_READ);
if (fr != FR_OK)
{
debug_printf("ERROR: f_open %d\n\r", (int)fr);
}
return fil;
#else
return fopen(filename, "r");
#endif
}
void filesystem_display_image(const char *filename)
{
file_t fil;
unsigned int br;
fil = file_open(filename);
uint32_t imgW, imgH;
mqoi_desc_init(&desc);
// 1. Read header
f_read(&fil, &desc.magic[0], sizeof(mqoi_desc_t) - 1, &br);
uint8_t errn = mqoi_desc_verify(&desc, &imgW, &imgH);
if (errn)
{
debug_printf("Invalid image, code %d\n", errn);
return;
}
debug_printf("Image dimensions: %d, %d\n", imgW, imgH);
uint32_t start, end, pxCount = 0;
volatile mqoi_rgba_t *px;
mqoi_dec_init(&dec, imgW * imgH);
// Serial.println("starting decode...");
int index = 256; // force refill first time
int x = 0;
int y = 0;
while (!mqoi_dec_done(&dec))
{
if (index >= sizeof(bmpImage))
{
// refill buffer
f_read(&fil, bmpImage, sizeof(bmpImage), &br);
index = 0;
}
mqoi_dec_push(&dec, bmpImage[index++]);
while ((px = mqoi_dec_pop(&dec)) != NULL)
{
pxCount++;
line[x].r = px->r;
line[x].g = px->g;
line[x].b = px->b;
x++;
if (x >= 320)
{
ost_display_draw_h_line_rgb888(y, line);
x = 0;
y++;
}
}
}
f_close(&fil);
}
#define FS_MAX_SIZE_READ 256
void filesystem_load_rom(uint8_t *mem, const char *filename)
{
file_t fil;
FILINFO fno;
unsigned int br;
FRESULT fr = f_stat(filename, &fno);
if (fr == FR_OK)
{
int total_size = fno.fsize;
fil = file_open(filename);
int copied_size = 0;
do
{
int read_size = total_size > FS_MAX_SIZE_READ ? FS_MAX_SIZE_READ : total_size;
f_read(&fil, &mem[copied_size], read_size, &br);
if (br == read_size)
{
total_size -= read_size;
copied_size += read_size;
}
else
{
debug_printf("Read file error\n");
total_size = 0; // force exit
return;
}
} while (total_size > 0);
f_close(&fil);
}
}

View file

@ -11,5 +11,7 @@ typedef struct
bool filesystem_read_index_file(ost_context_t *ctx);
void filesystem_mount();
void filesystem_display_image(const char *filename);
void filesystem_load_rom(uint8_t *mem, const char *filename);
#endif // FILESYSTEM_H

192
software/system/fs_task.c Normal file
View file

@ -0,0 +1,192 @@
/**
* @file fs_task.c
*
* @author your name (you@domain.com)
* @brief
* @version 0.1
* @date 2023-07-29
*
* @copyright Copyright (c) 2023
*
*/
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include "ost_hal.h"
#include "debug.h"
#include "qor.h"
#include "audio_player.h"
#include "filesystem.h"
#include "system.h"
#include "vm_task.h"
#include "fs_task.h"
// ===========================================================================================================
// DEFINITIONS
// ===========================================================================================================
typedef struct
{
uint8_t ev;
} ost_audio_event_t;
typedef enum
{
FS_WAIT_FOR_EVENT,
FS_PLAY_SOUND,
FS_DISPLAY_IMAGE,
FS_LOAD_INDEX,
FS_LOAD_STORY
} fs_state_t;
typedef struct
{
fs_state_t ev;
uint8_t *mem;
char *filename;
} ost_fs_event_t;
// ===========================================================================================================
// PRIVATE GLOBAL VARIABLES
// ===========================================================================================================
static qor_tcb_t FsTcb;
static uint32_t FsStack[4096];
static qor_mbox_t AudioMailBox;
static ost_audio_event_t wake_up;
static ost_audio_event_t AudioQueue[10];
static int dbg_state = 0;
static fs_state_t FsState = FS_WAIT_FOR_EVENT;
static qor_mbox_t FsMailBox;
static ost_fs_event_t FsEventQueue[10];
static ost_context_t OstContext;
// ===========================================================================================================
// FILE SYSTEM TASK
// ===========================================================================================================
// End of DMA transfer callback
static void audio_callback(void)
{
dbg_state = 1 - dbg_state;
qor_mbox_notify(&AudioMailBox, (void **)&wake_up, QOR_MBOX_OPTION_SEND_BACK);
}
void show_duration(uint32_t millisecondes)
{
uint32_t minutes, secondes, reste;
// Calcul des minutes, secondes et millisecondes
minutes = millisecondes / (60 * 1000);
reste = millisecondes % (60 * 1000);
secondes = reste / 1000;
reste = reste % 1000;
// Affichage du temps
debug_printf("Temps : %d minutes, %d secondes, %d millisecondes\r\n", minutes, secondes, reste);
}
void play_sound_file(const char *filename)
{
debug_printf("\r\n-------------------------------------------------------\r\nPlaying: out2.wav\r\n");
ost_system_stopwatch_start();
ost_audio_play(filename);
ost_audio_event_t *e = NULL;
int isPlaying = 0;
int count = 0;
uint32_t res = 0;
do
{
uint32_t res = qor_mbox_wait(&AudioMailBox, (void **)&e, 300); // On devrait recevoir un message toutes les 3ms (durée d'envoi d'un buffer I2S)
if (res == QOR_MBOX_OK)
{
isPlaying = ost_audio_process();
}
count++;
} while (isPlaying);
uint32_t executionTime = ost_system_stopwatch_stop();
ost_audio_stop();
debug_printf("\r\nPackets: %d\r\n", count);
show_duration(executionTime);
}
void FsTask(void *args)
{
ost_fs_event_t *fs_ev = NULL;
uint32_t res = 0;
while (1)
{
switch (FsState)
{
FS_PLAY_SOUND:
play_sound_file(fs_ev->filename);
FsState = FS_WAIT_FOR_EVENT;
break;
FS_DISPLAY_IMAGE:
filesystem_display_image(fs_ev->filename);
FsState = FS_WAIT_FOR_EVENT;
break;
FS_LOAD_INDEX:
filesystem_read_index_file(&OstContext);
FsState = FS_WAIT_FOR_EVENT;
break;
FS_LOAD_STORY:
filesystem_load_rom(fs_ev->mem, fs_ev->filename);
// ROM loaded, execute story
FsState = FS_WAIT_FOR_EVENT;
break;
FS_WAIT_FOR_EVENT:
default:
res = qor_mbox_wait(&FsMailBox, (void **)&fs_ev, 1000);
if (res == QOR_MBOX_OK)
{
// valid event, accept it
FsState = fs_ev->ev;
}
break;
}
// for (;;)
// {
// ost_hal_gpio_set(OST_GPIO_DEBUG_LED, 0);
// qor_sleep(500);
// ost_hal_gpio_set(OST_GPIO_DEBUG_LED, 1);
// qor_sleep(500);
// }
}
}
void fs_task_scan_index()
{
static ost_fs_event_t ScanIndexEv = {
.ev = FS_LOAD_INDEX,
.filename = NULL};
qor_mbox_notify(&FsMailBox, (void **)&ScanIndexEv, QOR_MBOX_OPTION_SEND_BACK);
}
void fs_task_initialize()
{
qor_mbox_init(&AudioMailBox, (void **)&AudioQueue, 10);
qor_mbox_init(&FsMailBox, (void **)&FsEventQueue, 10);
ost_audio_register_callback(audio_callback);
qor_create_thread(&FsTcb, FsTask, FsStack, sizeof(FsStack) / sizeof(FsStack[0]), FS_TASK_PRIORITY, "FsTask");
}

View file

@ -0,0 +1,5 @@
#ifndef FS_TASK_H
#define FS_TASK_H
void fs_task_initialize();
#endif // FS_TASK_H

View file

@ -0,0 +1,78 @@
/**
* @file hmi_task.c
*
* @author your name (you@domain.com)
* @brief
* @version 0.1
* @date 2023-07-29
*
* @copyright Copyright (c) 2023
*
*/
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include "ost_hal.h"
#include "debug.h"
#include "qor.h"
#include "audio_player.h"
#include "filesystem.h"
#include "system.h"
#include "vm_task.h"
#include "fs_task.h"
// ===========================================================================================================
// DEFINITIONS
// ===========================================================================================================
typedef struct
{
uint8_t ev;
} ost_hmi_event_t;
// ===========================================================================================================
// GLOBAL STORY VARIABLES
// ===========================================================================================================
static qor_tcb_t HmiTcb;
static uint32_t HmiStack[4096];
static qor_mbox_t HmiMailBox;
static ost_hmi_event_t HmiEvent;
static ost_hmi_event_t HmiQueue[10];
// ===========================================================================================================
// HMI TASK (user interface, buttons manager, LCD)
// ===========================================================================================================
void HmiTask(void *args)
{
ost_hmi_event_t *e = NULL;
// filesystem_display_image("/ba869e4b-03d6-4249-9202-85b4cec767a7/images/bird.qoi");
// Start by scanning the index file
while (1)
{
uint32_t res = qor_mbox_wait(&HmiMailBox, (void **)&e, 1000);
if (res == QOR_MBOX_OK)
{
}
else
{
debug_printf("H"); // pour le debug only
}
}
}
void hmi_task_initialize()
{
qor_mbox_init(&HmiMailBox, (void **)&HmiQueue, 10);
qor_create_thread(&HmiTcb, HmiTask, HmiStack, sizeof(HmiStack) / sizeof(HmiStack[0]), HMI_TASK_PRIORITY, "HmiTask"); // less priority is the HMI (user inputs and LCD)
}

View file

@ -0,0 +1,6 @@
#ifndef HMI_TASK_H
#define HMI_TASK_H
void hmi_task_initialize();
#endif // HMI_TASK_H

View file

@ -2,7 +2,7 @@
#include "ost_hal.h"
#include "debug.h"
#include "filesystem.h"
#include "picture.h"
#include "qor.h"
#include "rotary-button.h"
@ -12,331 +12,16 @@
#include <stdbool.h>
#include <stdlib.h>
#include "audio_player.h"
#include "chip32_vm.h"
#include "mini_qoi.h"
#ifdef OST_USE_FF_LIBRARY
#include "ff.h"
#include "diskio.h"
typedef FIL file_t;
#else
// Use standard library
typedef FILE *file_t;
typedef int FRESULT;
#define F_OK
#endif
file_t file_open(const char *filename)
{
#ifdef OST_USE_FF_LIBRARY
file_t fil;
FRESULT fr = f_open(&fil, filename, FA_READ);
if (fr != FR_OK)
{
debug_printf("ERROR: f_open %d\n\r", (int)fr);
}
return fil;
#else
return fopen(filename, "r");
#endif
}
#include "system.h"
#include "hmi_task.h"
#include "vm_task.h"
#include "fs_task.h"
void ost_hal_panic()
{
}
// ===========================================================================================================
// GLOBAL STORY VARIABLES
// ===========================================================================================================
static ost_context_t OstContext;
static mqoi_dec_t dec;
static mqoi_desc_t desc;
/*
if (pixel == info_header.width)
{
// enough pixels to write a line to the screen
ost_display_draw_h_line(pos.y, decompressed, palette);
// debug_printf("POS Y: %d", pos.y);
memset(decompressed, 0, sizeof(decompressed));
// ili9341_write(&pos, decompressed);
// next line...
pos.y++;
totalPixels += info_header.width;
pixel = 0;
nblines++;
}
*/
static uint8_t bmpImage[256];
static color_t line[320];
void display_image(const char *filename)
{
file_t fil;
unsigned int br;
fil = file_open(filename);
uint32_t imgW, imgH;
mqoi_desc_init(&desc);
// 1. Read header
f_read(&fil, &desc.magic[0], sizeof(mqoi_desc_t) - 1, &br);
uint8_t errn = mqoi_desc_verify(&desc, &imgW, &imgH);
if (errn)
{
debug_printf("Invalid image, code %d\n", errn);
return;
}
debug_printf("Image dimensions: %d, %d\n", imgW, imgH);
uint32_t start, end, pxCount = 0;
volatile mqoi_rgba_t *px;
mqoi_dec_init(&dec, imgW * imgH);
// Serial.println("starting decode...");
int index = 256; // force refill first time
int x = 0;
int y = 0;
while (!mqoi_dec_done(&dec))
{
if (index >= sizeof(bmpImage))
{
// refill buffer
f_read(&fil, bmpImage, sizeof(bmpImage), &br);
index = 0;
}
mqoi_dec_push(&dec, bmpImage[index++]);
while ((px = mqoi_dec_pop(&dec)) != NULL)
{
pxCount++;
line[x].r = px->r;
line[x].g = px->g;
line[x].b = px->b;
x++;
if (x >= 320)
{
ost_display_draw_h_line_rgb888(y, line);
x = 0;
y++;
}
}
}
f_close(&fil);
}
// ===========================================================================================================
// HMI TASK (user interface, buttons manager, LCD)
// ===========================================================================================================
static qor_tcb_t HmiTcb;
static uint32_t HmiStack[4096];
static qor_mbox_t HmiMailBox;
typedef struct
{
uint8_t ev;
} ost_hmi_event_t;
static ost_hmi_event_t HmiEvent;
ost_hmi_event_t HmiQueue[10];
void HmiTask(void *args)
{
qor_mbox_init(&HmiMailBox, (void **)&HmiQueue, 10);
ost_hmi_event_t *e = NULL;
filesystem_read_index_file(&OstContext);
display_image("/ba869e4b-03d6-4249-9202-85b4cec767a7/images/bird.qoi");
while (1)
{
uint32_t res = qor_mbox_wait(&HmiMailBox, (void **)&e, 1000);
if (res == QOR_MBOX_OK)
{
}
else
{
debug_printf("H"); // pour le debug only
}
}
}
// ===========================================================================================================
// VIRTUAL MACHINE TASK
// ===========================================================================================================
static qor_tcb_t VmTcb;
static uint32_t VmStack[4096];
static qor_mbox_t VmMailBox;
typedef struct
{
uint8_t ev;
} ost_vm_event_t;
ost_vm_event_t VmQueue[10];
static ost_vm_event_t VmEvent;
static uint8_t m_rom_data[16 * 1024];
static uint8_t m_ram_data[16 * 1024];
static chip32_ctx_t m_chip32_ctx;
// Index file parameter, reference an index in the file
uint8_t vm_syscall(chip32_ctx_t *ctx, uint8_t signum)
{
}
void VmTask(void *args)
{
// VM Initialize
m_chip32_ctx.stack_size = 512;
m_chip32_ctx.rom.mem = m_rom_data;
m_chip32_ctx.rom.addr = 0;
m_chip32_ctx.rom.size = sizeof(m_rom_data);
m_chip32_ctx.ram.mem = m_ram_data;
m_chip32_ctx.ram.addr = sizeof(m_rom_data);
m_chip32_ctx.ram.size = sizeof(m_ram_data);
m_chip32_ctx.syscall = vm_syscall;
chip32_initialize(&m_chip32_ctx);
qor_mbox_init(&VmMailBox, (void **)&VmQueue, 10);
chip32_result_t run_result;
ost_vm_event_t *e = NULL;
while (1)
{
uint32_t res = qor_mbox_wait(&VmMailBox, (void **)&e, 300); // On devrait recevoir un message toutes les 3ms (durée d'envoi d'un buffer I2S)
if (res == QOR_MBOX_OK)
{
if (VmEvent.ev == 1)
{
do
{
run_result = chip32_step(&m_chip32_ctx);
} while (run_result != VM_OK);
}
}
}
}
// ===========================================================================================================
// AUDIO TASK
// ===========================================================================================================
static qor_tcb_t AudioTcb;
static uint32_t AudioStack[4096];
static qor_mbox_t AudioMailBox;
typedef struct
{
uint8_t ev;
} ost_audio_event_t;
static ost_audio_event_t wake_up;
ost_audio_event_t audio_queue[10];
static int dbg_state = 0;
// End of DMA transfer callback
static void audio_callback(void)
{
dbg_state = 1 - dbg_state;
qor_mbox_notify(&AudioMailBox, (void **)&wake_up, QOR_MBOX_OPTION_SEND_BACK);
}
void show_duration(uint32_t millisecondes)
{
uint32_t minutes, secondes, reste;
// Calcul des minutes, secondes et millisecondes
minutes = millisecondes / (60 * 1000);
reste = millisecondes % (60 * 1000);
secondes = reste / 1000;
reste = reste % 1000;
// Affichage du temps
debug_printf("Temps : %d minutes, %d secondes, %d millisecondes\r\n", minutes, secondes, reste);
}
void AudioTask(void *args)
{
// picture_show("example.bmp");
wake_up.ev = 34;
qor_mbox_init(&AudioMailBox, (void **)&audio_queue, 10);
ost_audio_register_callback(audio_callback);
static bool onetime = true;
while (1)
{
debug_printf("\r\n-------------------------------------------------------\r\nPlaying: out2.wav\r\n");
ost_system_stopwatch_start();
ost_audio_play("out2.wav");
ost_audio_event_t *e = NULL;
int isPlaying = 0;
int count = 0;
do
{
uint32_t res = qor_mbox_wait(&AudioMailBox, (void **)&e, 300); // On devrait recevoir un message toutes les 3ms (durée d'envoi d'un buffer I2S)
if (res == QOR_MBOX_OK)
{
isPlaying = ost_audio_process();
}
count++;
} while (isPlaying);
uint32_t executionTime = ost_system_stopwatch_stop();
ost_audio_stop();
debug_printf("\r\nPackets: %d\r\n", count);
show_duration(executionTime);
for (;;)
{
ost_hal_gpio_set(OST_GPIO_DEBUG_LED, 0);
qor_sleep(500);
ost_hal_gpio_set(OST_GPIO_DEBUG_LED, 1);
qor_sleep(500);
}
}
}
// ===========================================================================================================
// IDLE TASK
// ===========================================================================================================
@ -369,9 +54,9 @@ int main()
qor_init(125000000UL);
// 5. Initialize the tasks
qor_create_thread(&HmiTcb, HmiTask, HmiStack, sizeof(HmiStack) / sizeof(HmiStack[0]), 1, "HmiTask"); // less priority is the HMI (user inputs and LCD)
qor_create_thread(&VmTcb, VmTask, VmStack, sizeof(VmStack) / sizeof(VmStack[0]), 2, "VmTask");
qor_create_thread(&AudioTcb, AudioTask, AudioStack, sizeof(AudioStack) / sizeof(AudioStack[0]), 3, "AudioTask"); ///< High priority for audio
hmi_task_initialize();
vm_task_initialize();
fs_task_initialize();
// 6. Start the operating system!
qor_start(&IdleTcb, IdleTask, IdleStack, 1024);

9
software/system/system.h Normal file
View file

@ -0,0 +1,9 @@
#ifndef SYSTEM_H
#define SYSTEM_H
// On regroupe ici au les priorités des différents threads afin d'avoir une vision plus large
#define HMI_TASK_PRIORITY 1
#define VM_TASK_PRIORITY 2
#define FS_TASK_PRIORITY 3 ///< High priority for audio / file system access
#endif // SYSTEM_H

156
software/system/vm_task.c Normal file
View file

@ -0,0 +1,156 @@
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include "ost_hal.h"
#include "debug.h"
#include "qor.h"
#include "chip32_vm.h"
#include "system.h"
#include "vm_task.h"
#include "fs_task.h"
// ===========================================================================================================
// DEFINITIONS
// ===========================================================================================================
#define VM_EV_NO_EVENT 0
#define VM_EV_START_STORY_EVENT 0xA1
typedef struct
{
uint8_t ev;
const char *story_dir;
} ost_vm_event_t;
// ===========================================================================================================
// PRIVATE GLOBAL VARIABLES
// ===========================================================================================================
static qor_tcb_t VmTcb;
static uint32_t VmStack[4096];
static qor_mbox_t VmMailBox;
static ost_vm_event_t VmQueue[10];
static ost_vm_event_t VmStartEvent;
static uint8_t m_rom_data[16 * 1024];
static uint8_t m_ram_data[16 * 1024];
static chip32_ctx_t m_chip32_ctx;
static char CurrentStory[260]; // Current story path
static char ImageFile[260];
static char SoundFile[260];
// ===========================================================================================================
// VIRTUAL MACHINE TASK
// ===========================================================================================================
void get_file_from_memory(char *mem, uint32_t addr)
{
bool isRam = addr & 0x80000000;
addr &= 0xFFFF; // mask the RAM/ROM bit, ensure 16-bit addressing
if (isRam)
{
strcpy(&mem[0], (const char *)&m_chip32_ctx.ram.mem[addr]);
}
else
{
strcpy(&mem[0], (const char *)&m_chip32_ctx.rom.mem[addr]);
}
}
// Callback from the VM
// Called inside the thread context
uint8_t vm_syscall(chip32_ctx_t *ctx, uint8_t code)
{
uint8_t retCode = SYSCALL_RET_OK;
// Media
if (code == 1) // Execute media
{
if (m_chip32_ctx.registers[R0] != 0)
{
// image file name address is in R0
// QString imageFile = m_model.BuildFullImagePath(GetFileNameFromMemory(m_chip32_ctx.registers[R0]));
// m_ostHmiDock->SetImage(imageFile);
}
else
{
// m_ostHmiDock->ClearImage();
}
if (m_chip32_ctx.registers[R1] != 0)
{
// sound file name address is in R1
// QString soundFile = m_model.BuildFullSoundPath(GetFileNameFromMemory(m_chip32_ctx.registers[R1]));
// qDebug() << ", Sound: " << soundFile;
// m_model.PlaySoundFile(soundFile);
}
retCode = SYSCALL_RET_WAIT_EV; // set the VM in pause
}
// WAIT EVENT bits:
// 0: block
// 1: OK button
// 2: home button
// 3: pause button
// 4: rotary left
// 5: rotary right
else if (code == 2) // Wait for event
{
// Event mask is located in R0
// optional timeout is located in R1
// if timeout is set to zero, wait for infinite and beyond
retCode = SYSCALL_RET_WAIT_EV; // set the VM in pause
}
return retCode;
}
void VmTask(void *args)
{
// VM Initialize
m_chip32_ctx.stack_size = 512;
m_chip32_ctx.rom.mem = m_rom_data;
m_chip32_ctx.rom.addr = 0;
m_chip32_ctx.rom.size = sizeof(m_rom_data);
m_chip32_ctx.ram.mem = m_ram_data;
m_chip32_ctx.ram.addr = sizeof(m_rom_data);
m_chip32_ctx.ram.size = sizeof(m_ram_data);
m_chip32_ctx.syscall = vm_syscall;
chip32_result_t run_result;
ost_vm_event_t *e = NULL;
while (1)
{
uint32_t res = qor_mbox_wait(&VmMailBox, (void **)&e, 300); // On devrait recevoir un message toutes les 3ms (durée d'envoi d'un buffer I2S)
if (res == QOR_MBOX_OK)
{
if (VmStartEvent.ev == VM_EV_START_STORY_EVENT)
{
// Launch the execution of a story
chip32_initialize(&m_chip32_ctx);
do
{
run_result = chip32_step(&m_chip32_ctx);
} while (run_result != VM_OK);
}
}
}
}
void vm_task_start_story(const char *story_directory)
{
VmStartEvent.story_dir = story_directory;
qor_mbox_notify(&VmMailBox, (void **)&VmStartEvent, QOR_MBOX_OPTION_SEND_BACK);
}
void vm_task_initialize()
{
VmStartEvent.ev = VM_EV_START_STORY_EVENT;
qor_mbox_init(&VmMailBox, (void **)&VmQueue, 10);
qor_create_thread(&VmTcb, VmTask, VmStack, sizeof(VmStack) / sizeof(VmStack[0]), VM_TASK_PRIORITY, "VmTask");
}

View file

@ -0,0 +1,7 @@
#ifndef VM_TASK_H
#define VM_TASK_H
void vm_task_start_story(const char *story_directory);
void vm_task_initialize();
#endif // VM_TASK_H

View file

@ -202,6 +202,34 @@ MainWindow::MainWindow()
}
});
connect(m_resourcesDock, &ResourcesDock::sigChooseTitle, [&](bool isImage) {
m_chooseFileUi.tableView->setModel(&m_resourcesDock->getModel());
m_chooseFileDialog->exec();
// Get the file choosen
QModelIndexList selection = m_chooseFileUi.tableView->selectionModel()->selectedRows();
if (selection.count() > 0)
{
// Take first
QModelIndex index = selection.at(0);
Resource res;
if (m_project.GetResourceAt(index.row(), res))
{
if (isImage)
{
m_project.SetTitleImage(res.file);
m_resourcesDock->SetTitleImage(res.file.c_str());
}
else
{
m_project.SetTitleSound(res.file);
m_resourcesDock->SetTitleSound(res.file.c_str());
}
}
}
});
connect(m_toolbar, &ToolBar::sigNew, this, [&]() {
NewProject();
});
@ -366,7 +394,7 @@ void MainWindow::BuildAll()
// 2. Compile the assembly to machine binary
GenerateBinary();
// 3. Conert all media
// 3. Convert all media to desired type format
ConvertResources();
}
@ -422,6 +450,9 @@ void MainWindow::ExitProgram()
void MainWindow::RefreshProjectInformation()
{
setWindowTitle(QString("StoryTeller Editor - ") + m_project.GetProjectFilePath().c_str());
m_resourcesDock->SetTitleImage(m_project.GetTitleImage().c_str());
m_resourcesDock->SetTitleSound(m_project.GetTitleSound().c_str());
}
void MainWindow::MessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)

View file

@ -14,8 +14,8 @@
<string>Story Resources</string>
</property>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="addSoundButton">
@ -40,7 +40,57 @@
</item>
</layout>
</item>
<item row="1" column="0">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Title image</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="titleImage">
<property name="text">
<string>&lt;no file !&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="chooseTitleImageButton">
<property name="text">
<string>Choose file</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Title sound</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="titleSound">
<property name="text">
<string>&lt;no file !&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="chooseTitleSoundButton">
<property name="text">
<string>Choose file</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTableView" name="resourcesView">
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>

View file

@ -67,6 +67,24 @@ ResourcesDock::ResourcesDock(StoryProject &project, ResourceModel &model)
}
}
});
connect(m_uiOstResources.chooseTitleImageButton, &QPushButton::clicked, [&](bool enable) {
emit sigChooseTitle(true);
});
connect(m_uiOstResources.chooseTitleSoundButton, &QPushButton::clicked, [&](bool enable) {
emit sigChooseTitle(false);
});
}
void ResourcesDock::SetTitleImage(const QString &name)
{
m_uiOstResources.titleImage->setText(name);
}
void ResourcesDock::SetTitleSound(const QString &name)
{
m_uiOstResources.titleSound->setText(name);
}

View file

@ -19,6 +19,12 @@ public:
void SetFilterType(const QString &type) { m_proxyModel.setFilterType(type); }
void SetTitleImage(const QString &name);
void SetTitleSound(const QString &name);
signals:
void sigChooseTitle(bool isImage);
private:
StoryProject &m_project;
Ui::ostResources m_uiOstResources;

View file

@ -17,6 +17,13 @@ void StoryProject::SaveStory(const std::vector<uint8_t> &m_program)
std::ofstream o(m_working_dir + std::filesystem::path::preferred_separator + "story.c32", std::ios::out | std::ios::binary);
o.write(reinterpret_cast<const char*>(m_program.data()), m_program.size());
o.close();
// Generate title files
std::ofstream index(m_working_dir + std::filesystem::path::preferred_separator + "index.ost");
index << "/" << m_uuid << "/images/" << m_titleImage << "\n";
index << "/" << m_uuid << "/sounds/" << m_titleSound << "\n";
index.close();
}
void StoryProject::Initialize(const std::string &file_path)
@ -63,6 +70,8 @@ bool StoryProject::Load(const std::string &file_path, nlohmann::json &model)
m_name = projectData["name"].get<std::string>();
m_uuid = projectData["uuid"].get<std::string>();
m_titleImage = projectData.value("title_image", "");
m_titleSound = projectData.value("title_sound", "");
if (j.contains("resources"))
{
@ -159,7 +168,7 @@ bool StoryProject::Load(const std::string &file_path, nlohmann::json &model)
void StoryProject::Save(const nlohmann::json &model)
{
nlohmann::json j;
j["project"] = { {"name", m_name}, {"uuid", m_uuid} };
j["project"] = { {"name", m_name}, {"uuid", m_uuid}, { "title_image", m_titleImage }, { "title_sound", m_titleSound } };
{
nlohmann::json resourcesData;
@ -267,6 +276,16 @@ std::string StoryProject::FileToConstant(const std::string &FileName, const std:
return "$" + f + " DC8 \"" + f + extension + "\", 8\r\n";
}
void StoryProject::SetTitleImage(const std::string &titleImage)
{
m_titleImage = titleImage;
}
void StoryProject::SetTitleSound(const std::string &titleSound)
{
m_titleSound = titleSound;
}
void StoryProject::AppendResource(const Resource &res)
{
m_resources.push_back(res);

View file

@ -88,6 +88,12 @@ struct StoryProject
static void ReplaceCharacter(std::string &theString, const std::string &toFind, const std::string &toReplace);
static std::string FileToConstant(const std::string &FileName, const std::string &extension);
void SetTitleImage(const std::string &titleImage);
void SetTitleSound(const std::string &titleSound);
std::string GetTitleImage() const { return m_titleImage; }
std::string GetTitleSound() const { return m_titleSound; }
// ------------- Resources Management
void AppendResource(const Resource &res);
bool GetResourceAt(int index, Resource &resOut);
@ -117,6 +123,9 @@ private:
std::filesystem::path m_soundsPath;
bool m_initialized{false};
std::string m_titleImage;
std::string m_titleSound;
std::vector<Resource> m_resources;
std::string m_working_dir; /// Temporary folder based on the uuid, where the archive is unzipped
std::string m_project_file_path; /// JSON project file