A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2009, 2010 Wincent Balin
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "plugin.h"
23#include "pdbox.h"
24
25#include "lib/helper.h"
26
27#include "PDa/src/m_pd.h"
28#include "PDa/src/s_stuff.h"
29
30/* Welcome to the PDBox plugin */
31
32/* Name of the file to open. */
33char* filename;
34
35/* Running time. */
36uint64_t runningtime IBSS_ATTR = 0;
37
38/* Variables for Pure Data. */
39int sys_verbose;
40int sys_noloadbang;
41t_symbol *sys_libdir;
42t_namelist *sys_openlist;
43int sys_soundindevlist[MAXAUDIOINDEV];
44int sys_chinlist[MAXAUDIOINDEV];
45int sys_soundoutdevlist[MAXAUDIOOUTDEV];
46int sys_choutlist[MAXAUDIOOUTDEV];
47t_binbuf* inbinbuf;
48
49/* References for scheduler variables and functions. */
50extern t_time sys_time;
51extern t_time sys_time_per_dsp_tick;
52extern void sched_tick(t_time next_sys_time);
53
54/* LATER consider making this variable. It's now the LCM of all sample
55rates we expect to see: 32000, 44100, 48000, 88200, 96000. */
56#define TIMEUNITPERSEC (32.*441000.)
57
58
59/* Quit flag. */
60bool quit IBSS_ATTR = false;
61
62/* Stack sizes for threads. */
63#define CORESTACKSIZE (1 * 1024 * 1024)
64#define GUISTACKSIZE (512 * 1024)
65
66/* Thread stacks. */
67void* core_stack IBSS_ATTR;
68void* gui_stack IBSS_ATTR;
69
70/* Thread IDs. */
71unsigned int core_thread_id IBSS_ATTR;
72unsigned int gui_thread_id IBSS_ATTR;
73
74
75/* GUI thread */
76void gui_thread(void)
77{
78 struct pd_widget widget[128];
79 unsigned int widgets = 0;
80 struct datagram dg;
81 bool update;
82
83 /* Initialize GUI. */
84 pd_gui_init();
85
86 /* Load PD patch. */
87 widgets = pd_gui_load_patch(widget,
88 sizeof(widget) / sizeof(struct pd_widget));
89
90 /* Draw initial state of UI. */
91 pd_gui_draw(widget, widgets);
92
93 /* GUI loop */
94 while(!quit)
95 {
96 /* Reset update flag. */
97 update = false;
98
99 /* Apply timer to widgets. */
100 update |=
101 pd_gui_apply_timeouts(widget, widgets);
102
103 /* Process buttons. */
104 update |=
105 pd_gui_parse_buttons(widgets);
106
107 /* Receive and parse datagrams. */
108 while(RECEIVE_FROM_CORE(&dg))
109 {
110 update = true;
111 pd_gui_parse_message(&dg, widget, widgets);
112 }
113
114 /* If there is something to update in GUI, do so. */
115 if(update)
116 pd_gui_draw(widget, widgets);
117
118 rb->sleep(1);
119 }
120
121 rb->thread_exit();
122}
123
124/* Core thread */
125void core_thread(void)
126{
127 /* Add the directory the called .pd file resides in to lib directories. */
128 sys_findlibdir(filename);
129
130 /* Open the PD design file. */
131 sys_openlist = namelist_append(sys_openlist, filename);
132
133 /* Fake a GUI start. */
134 sys_startgui(NULL);
135
136 /* Core scheduler loop */
137 while(!quit)
138 {
139 /* Receive datagrams. */
140 struct datagram dg;
141
142 while(RECEIVE_TO_CORE(&dg))
143 rockbox_receive_callback(&dg);
144
145 /* Use sys_send_dacs() function as timer. */
146 while(sys_send_dacs() != SENDDACS_NO)
147 sched_tick(sys_time + sys_time_per_dsp_tick);
148
149 rb->sleep(1);
150 }
151
152 rb->thread_exit();
153}
154
155
156
157/* Plug-in entry point */
158enum plugin_status plugin_start(const void* parameter)
159{
160 /* Memory pool variables. */
161 size_t mem_size;
162 void* mem_pool;
163
164 /* Get the file name; check whether parameter contains no file name. */
165 filename = (char*) parameter;
166 if(strlen(filename) == 0)
167 {
168 rb->splash(HZ, "Play a .pd file!");
169 return PLUGIN_ERROR;
170 }
171
172 /* Initialize memory pool. */
173 mem_pool = rb->plugin_get_audio_buffer(&mem_size);
174 if(mem_size < MIN_MEM_SIZE)
175 {
176 rb->splash(HZ, "Not enough memory!");
177 return PLUGIN_ERROR;
178 }
179
180 init_memory_pool(mem_size, mem_pool);
181
182 /* Initialize net. */
183 net_init();
184
185 /* Initialize Pure Data, as does sys_main in s_main.c */
186 pd_init();
187
188 /* Set audio API. */
189 sys_set_audio_api(API_ROCKBOX);
190
191 /* Initialize audio subsystem. */
192 sys_open_audio(0, /* No sound input yet */
193 sys_soundindevlist,
194 0, /* No sound input yet */
195 sys_chinlist,
196 1, /* One sound output device */
197 sys_soundoutdevlist,
198 -1, /* Use the default amount (2) of channels */
199 sys_choutlist,
200 PD_SAMPLERATE, /* Sample rate */
201 DEFAULTADVANCE, /* Scheduler advance */
202 1 /* Enable */);
203
204 /* Initialize scheduler time variables. */
205 sys_time = 0;
206 sys_time_per_dsp_tick = (TIMEUNITPERSEC) *
207 ((double) sys_schedblocksize) / sys_dacsr;
208
209
210 /* Create stacks for threads. */
211 core_stack = getbytes(CORESTACKSIZE);
212 gui_stack = getbytes(GUISTACKSIZE);
213 if(core_stack == NULL || gui_stack == NULL)
214 {
215 rb->splash(HZ, "Not enough memory!");
216 return PLUGIN_ERROR;
217 }
218
219#ifdef HAVE_SCHEDULER_BOOSTCTRL
220 /* Boost CPU. */
221 rb->trigger_cpu_boost();
222#endif
223
224 /* Start threads. */
225 core_thread_id =
226 rb->create_thread(&core_thread,
227 core_stack,
228 CORESTACKSIZE,
229 0, /* FIXME Which flags? */
230 "PD core"
231 IF_PRIO(, PRIORITY_REALTIME)
232 IF_COP(, CPU));
233
234 gui_thread_id =
235 rb->create_thread(&gui_thread,
236 gui_stack,
237 GUISTACKSIZE,
238 0, /* FIXME Which flags? */
239 "PD GUI"
240 IF_PRIO(, PRIORITY_USER_INTERFACE)
241 IF_COP(, CPU));
242
243 /* If having an error creating threads, bail out. */
244 if(core_thread_id == 0 || gui_thread_id == 0)
245 return PLUGIN_ERROR;
246
247 /* Make backlight remain on -- making music requires attention. */
248 backlight_ignore_timeout();
249
250 /* Main loop. */
251 while(!quit)
252 {
253 /* Add time slice in milliseconds. */
254 runningtime += (1000 / HZ);
255
256 /* Sleep to the next time slice. */
257 rb->sleep(1);
258 }
259
260 /* Restore backlight. */
261 backlight_use_settings();
262
263 /* Wait for threads to complete. */
264 rb->thread_wait(gui_thread_id);
265 rb->thread_wait(core_thread_id);
266
267#ifdef HAVE_SCHEDULER_BOOSTCTRL
268 /* Unboost CPU. */
269 rb->cancel_cpu_boost();
270#endif
271
272 /* Close audio subsystem. */
273 sys_close_audio();
274
275 /* Destroy net. */
276 net_destroy();
277
278 /* Clear memory pool. */
279 destroy_memory_pool(mem_pool);
280
281 return PLUGIN_OK;
282}
283