rockbox/apps/plugins/pdbox/pdbox.c
Thomas Martitz cae4ae2c71 Second try: Introduce plugin_crt0.c that every plugin links.
It handles exit() properly, calling the handler also when the plugin returns
normally (also make exit() more standard compliant while at it).
It also holds PLUGIN_HEADER, so that it doesn't need to be in each plugin anymore.

To work better together with callbacks passed to rb->default_event_handler_ex() introduce exit_on_usb() which will call the exit handler before showing the usb screen and exit() after it.
In most cases rb->default_event_handler_ex() was passed a callback which was manually called at all other return points. This can now be done via atexit().

In future plugin_crt0.c could also handle clearing bss, initializing iram and more.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@27873 a1c6a512-1295-4272-9138-f99709370657
2010-08-24 14:30:46 +00:00

287 lines
7.3 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2009, 2010 Wincent Balin
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "plugin.h"
#include "pdbox.h"
#include "lib/helper.h"
#include "PDa/src/m_pd.h"
#include "PDa/src/s_stuff.h"
/* Welcome to the PDBox plugin */
PLUGIN_IRAM_DECLARE
/* Name of the file to open. */
char* filename;
/* Running time. */
uint64_t runningtime IBSS_ATTR = 0;
/* Variables for Pure Data. */
int sys_verbose;
int sys_noloadbang;
t_symbol *sys_libdir;
t_namelist *sys_openlist;
int sys_soundindevlist[MAXAUDIOINDEV];
int sys_chinlist[MAXAUDIOINDEV];
int sys_soundoutdevlist[MAXAUDIOOUTDEV];
int sys_choutlist[MAXAUDIOOUTDEV];
t_binbuf* inbinbuf;
/* References for scheduler variables and functions. */
extern t_time sys_time;
extern t_time sys_time_per_dsp_tick;
extern void sched_tick(t_time next_sys_time);
/* LATER consider making this variable. It's now the LCM of all sample
rates we expect to see: 32000, 44100, 48000, 88200, 96000. */
#define TIMEUNITPERSEC (32.*441000.)
/* Quit flag. */
bool quit IBSS_ATTR = false;
/* Stack sizes for threads. */
#define CORESTACKSIZE (1 * 1024 * 1024)
#define GUISTACKSIZE (512 * 1024)
/* Thread stacks. */
void* core_stack IBSS_ATTR;
void* gui_stack IBSS_ATTR;
/* Thread IDs. */
unsigned int core_thread_id IBSS_ATTR;
unsigned int gui_thread_id IBSS_ATTR;
/* GUI thread */
void gui_thread(void)
{
struct pd_widget widget[128];
unsigned int widgets = 0;
struct datagram dg;
bool update;
/* Initialize GUI. */
pd_gui_init();
/* Load PD patch. */
widgets = pd_gui_load_patch(widget,
sizeof(widget) / sizeof(struct pd_widget));
/* Draw initial state of UI. */
pd_gui_draw(widget, widgets);
/* GUI loop */
while(!quit)
{
/* Reset update flag. */
update = false;
/* Apply timer to widgets. */
update |=
pd_gui_apply_timeouts(widget, widgets);
/* Process buttons. */
update |=
pd_gui_parse_buttons(widgets);
/* Receive and parse datagrams. */
while(RECEIVE_FROM_CORE(&dg))
{
update = true;
pd_gui_parse_message(&dg, widget, widgets);
}
/* If there is something to update in GUI, do so. */
if(update)
pd_gui_draw(widget, widgets);
rb->sleep(1);
}
rb->thread_exit();
}
/* Core thread */
void core_thread(void)
{
/* Add the directory the called .pd file resides in to lib directories. */
sys_findlibdir(filename);
/* Open the PD design file. */
sys_openlist = namelist_append(sys_openlist, filename);
/* Fake a GUI start. */
sys_startgui(NULL);
/* Core scheduler loop */
while(!quit)
{
/* Receive datagrams. */
struct datagram dg;
while(RECEIVE_TO_CORE(&dg))
rockbox_receive_callback(&dg);
/* Use sys_send_dacs() function as timer. */
while(sys_send_dacs() != SENDDACS_NO)
sched_tick(sys_time + sys_time_per_dsp_tick);
rb->sleep(1);
}
rb->thread_exit();
}
/* Plug-in entry point */
enum plugin_status plugin_start(const void* parameter)
{
PLUGIN_IRAM_INIT(rb)
/* Memory pool variables. */
size_t mem_size;
void* mem_pool;
/* Get the file name; check whether parameter contains no file name. */
filename = (char*) parameter;
if(strlen(filename) == 0)
{
rb->splash(HZ, "Play a .pd file!");
return PLUGIN_ERROR;
}
/* Initialize memory pool. */
mem_pool = rb->plugin_get_audio_buffer(&mem_size);
if(mem_size < MIN_MEM_SIZE)
{
rb->splash(HZ, "Not enough memory!");
return PLUGIN_ERROR;
}
init_memory_pool(mem_size, mem_pool);
/* Initialize net. */
net_init();
/* Initialize Pure Data, as does sys_main in s_main.c */
pd_init();
/* Set audio API. */
sys_set_audio_api(API_ROCKBOX);
/* Initialize audio subsystem. */
sys_open_audio(0, /* No sound input yet */
sys_soundindevlist,
0, /* No sound input yet */
sys_chinlist,
1, /* One sound output device */
sys_soundoutdevlist,
-1, /* Use the default amount (2) of channels */
sys_choutlist,
PD_SAMPLERATE, /* Sample rate */
DEFAULTADVANCE, /* Scheduler advance */
1 /* Enable */);
/* Initialize scheduler time variables. */
sys_time = 0;
sys_time_per_dsp_tick = (TIMEUNITPERSEC) *
((double) sys_schedblocksize) / sys_dacsr;
/* Create stacks for threads. */
core_stack = getbytes(CORESTACKSIZE);
gui_stack = getbytes(GUISTACKSIZE);
if(core_stack == NULL || gui_stack == NULL)
{
rb->splash(HZ, "Not enough memory!");
return PLUGIN_ERROR;
}
#ifdef HAVE_SCHEDULER_BOOSTCTRL
/* Boost CPU. */
rb->trigger_cpu_boost();
#endif
/* Start threads. */
core_thread_id =
rb->create_thread(&core_thread,
core_stack,
CORESTACKSIZE,
0, /* FIXME Which flags? */
"PD core"
IF_PRIO(, PRIORITY_REALTIME)
IF_COP(, COP));
gui_thread_id =
rb->create_thread(&gui_thread,
gui_stack,
GUISTACKSIZE,
0, /* FIXME Which flags? */
"PD GUI"
IF_PRIO(, PRIORITY_USER_INTERFACE)
IF_COP(, CPU));
/* If having an error creating threads, bail out. */
if(core_thread_id == 0 || gui_thread_id == 0)
return PLUGIN_ERROR;
/* Make backlight remain on -- making music requires attention. */
backlight_force_on();
/* Main loop. */
while(!quit)
{
/* Add time slice in milliseconds. */
runningtime += (1000 / HZ);
/* Sleep to the next time slice. */
rb->sleep(1);
}
/* Restore backlight. */
backlight_use_settings();
/* Wait for threads to complete. */
rb->thread_wait(gui_thread_id);
rb->thread_wait(core_thread_id);
#ifdef HAVE_SCHEDULER_BOOSTCTRL
/* Unboost CPU. */
rb->cancel_cpu_boost();
#endif
/* Close audio subsystem. */
sys_close_audio();
/* Destroy net. */
net_destroy();
/* Clear memory pool. */
destroy_memory_pool(mem_pool);
return PLUGIN_OK;
}