1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
|
/* SPDX-License-Identifier: GPL-2.0 */
/** \file afs.h The audio file selector API.
*
* The code of the audio file selector (afs) is distributed across several
* source files, which results in a rather large set of non-static functions
* and variables with intricate inter-dependencies.
*
* The central file is \ref afs.c, which contains the afs initialization
* and shutdown functions and the table-independent code. Other source files
* which include this header and whose object files become part of afs are
* \ref attribute.c, \ref aft.c, \ref score.c, \ref mood.c, \ref playlist.c
* and \ref blob.c.
*
* There is only this single header file for the afs API. All non-static
* symbols of the source files listed above are declared here.
*/
/** Audio file selector data stored in the audio file table. */
struct afs_info {
/** Seconds since the epoch. */
uint64_t last_played;
/** Bit field of set attributes. */
uint64_t attributes;
/** Counts how many times the file was selected. */
uint32_t num_played;
/** Image blob associated with this file (foreign key). */
uint32_t image_id;
/** Lyrics blob associated with this file (foreign key). */
uint32_t lyrics_id;
/** Mp3, ogg, ... */
uint8_t audio_format_id;
/** Amplification value. */
uint8_t amp;
};
/**
* Events caused by changes to an afs table.
*
* Whenever an afs table changes, an event is generated by calling \ref
* afs_event(), which calls the event handlers of all other tables. For
* example, if an audio file is added, the event handler of the mood table
* checks the new file for admissibility.
*/
enum afs_events {
/** An attribute was added. */
ATTRIBUTE_ADD,
/** An attribute was renamed. */
ATTRIBUTE_RENAME,
/** An attribute was removed. */
ATTRIBUTE_REMOVE,
/** The afs info struct of an audio file changed. */
AFSI_CHANGE,
/** The afh info struct of an audio file changed. */
AFHI_CHANGE,
/** The vss streams a new file, implies afsi and afhi change */
VSS_NEW_AUDIO_FILE,
/** An audio file was renamed. */
AUDIO_FILE_RENAME,
/** An audio file was added. */
AUDIO_FILE_ADD,
/** An audio file is about to be removed. */
AUDIO_FILE_REMOVE,
/** A new blob was added. */
BLOB_ADD,
/** A blob was renamed. */
BLOB_RENAME,
/** A blob is about to be removed. */
BLOB_REMOVE,
};
/** Methods for table startup/shutdown and event handling. */
struct afs_table_operations {
/** Gets called on startup and on SIGHUP. */
int (*open)(const char *base_dir);
/** Gets called on shutdown and on SIGHUP. */
void (*close)(void);
/** Called from the init command. */
int (*create)(const char *);
/** Handle events generated by other tables. See enum \ref afs_events. */
int (*event_handler)(enum afs_events event, void *data);
};
/**
* Codes used for communication between the server and the afs process.
*
* Before forking the afs child, para_server creates a bidirectional pipe
* through which both processes communicate. Usually para_server requests a
* new audio file in order to start streaming, for example when the end of
* the current audio file has been reached. The afs process responds to such
* a request by sending back an eight byte buffer. The first four bytes is
* the uint32_t representation of the code, usually NEXT_AUDIO_FILE if an
* admissible audio file was found, successfully opened and verified. The
* other four bytes encode the ID of the shared memory area that contains
* details about the audio file to be streamed. The open file descriptor of
* this file is also passed to the server process through the same pipe.
*/
enum afs_server_code {
/** An audio file was successfully opened. */
NEXT_AUDIO_FILE,
/** No admissible audio file was found. */
NO_ADMISSIBLE_FILES,
};
/** Flags passed to for_each_matching_row(). */
enum pattern_match_flags {
/** Loop in reverse order. */
PM_REVERSE_LOOP = 1,
/** If no pattern is given, loop over all rows. */
PM_NO_PATTERN_MATCHES_EVERYTHING = 2,
/** If the data in match_column is the empty string, skip this row. */
PM_SKIP_EMPTY_NAME = 4,
};
/** Structure passed to for_each_matching_row(). */
struct pattern_match_data {
/** Loop over all rows in this table. */
struct osl_table *table;
/** Determines the loop order. Must be an rbtree column. */
unsigned loop_col_num;
/** Data from this column is matched against the given patterns. */
unsigned match_col_num;
/** \see \ref pattern_match_flags. */
unsigned pm_flags;
/** This value is passed verbatim to fnmatch(). */
int fnmatch_flags;
/** Obtained by de-serializing the query buffer in the callback. */
struct lls_parse_result *lpr;
/** Do not try to match the first inputs of the lopsub parse result. */
unsigned input_skip;
/** Data pointer passed to the action function. */
void *data;
/** Gets increased by one for each match. */
unsigned num_matches;
/** For each matching row, this function will be called. */
int (*action)(struct osl_table *table, struct osl_row *row, const char *name, void *data);
};
/** Arguments passed to each afs callback. */
struct afs_callback_arg {
/** The local socket connecting afs and the command handler. */
int fd;
/** Callback-specific data. */
struct osl_object query;
/** Will be written on band SBD_OUTPUT, fully buffered. */
struct para_buffer pbout;
/**
* Convenience pointer for the de-serialized parse result.
*
* Most afs command handlers call \ref send_lls_callback_request()
* to serialize the parse result of the subcommand and pass it to
* the callback. In afs context a pointer to the de-serialized parse
* result is stored here.
*/
struct lls_parse_result *lpr;
};
/**
* Metadata for the result of a callback.
*
* A serialized version of an instance of this structure is combined with the
* result buffer produced by an afs callback. A reference to this combined
* buffer is then passed to the command handler via shared memory.
*
* \sa struct \ref afs_callback, \ref pass_buffer_as_shm().
*/
struct callback_result {
/** The number of bytes of the result. */
size_t result_size;
/** The band designator (loglevel for the result). */
uint8_t band;
};
/**
* The "top half" of an afs command.
*
* The afs command handler functions are called from sibling process of the
* afs process, so they can not change the address space of the afs process.
* Therefore, an afs subcommand typically consists of two functions: The
* command handler function and the corresponding callback function that
* runs in afs context.
*
* \sa \ref send_callback_request().
*/
typedef int afs_callback(struct afs_callback_arg *aca);
/**
* Dispatch the output of an afs callback.
*
* Most afs callbacks need to send data back to the command handler
* process. Pointers to this type of function are passed to \ref
* send_callback_request() and related functions to dispatch the data in the
* command handler process. Most callbacks pass \ref afs_cb_result_handler(),
* which sends the output of the callback to the connected client.
*/
typedef int callback_result_handler(struct osl_object *result, uint8_t band, void *private);
int afs_cb_result_handler(struct osl_object *result, uint8_t band, void *private);
int pass_buffer_as_shm(int fd, uint8_t band, const char *buf, size_t size);
/** Structure passed to the afs max_size handler. */
struct afs_max_size_handler_data {
/** Local socket connecting the command handler and the afs process. */
int fd;
/** The sideband designator for this data packet. */
uint8_t band;
};
__noreturn void afs_init(int socket_fd);
__must_check int afs_event(enum afs_events event, void *data);
int send_callback_request(afs_callback *f, int afs_fd, struct osl_object *query,
callback_result_handler *result_handler,
void *private_result_data);
struct command_context;
int send_lls_callback_request(afs_callback *f, int afs_fd,
const struct lls_command * const cmd,
struct lls_parse_result *lpr, struct command_context *cc);
__printf_2_3 void afs_error(const struct afs_callback_arg *aca,
const char *fmt,...);
int string_compare(const struct osl_object *obj1, const struct osl_object *obj2);
int for_each_matching_row(struct pattern_match_data *pmd);
/* score */
void score_open(struct osl_table **result);
void score_close(struct osl_table *t);
int score_loop(int (*cb)(struct osl_row *, long, void *),
struct osl_table *t, void *data);
int score_get_best(struct osl_row **aft_row, long *score, struct osl_table *t);
int score_add(const struct osl_row *aft_row, long score, struct osl_table *t);
int score_move_to_end(const struct osl_row *aft_row, struct osl_table *t);
int score_delete(const struct osl_row *aft_row, struct osl_table *t);
/* attribute */
extern const struct afs_table_operations attr_ops;
int attr_get_defined_mask(uint64_t *result);
int attr_name_to_bitnum(const char *att_name);
int attr_bitmap_to_text(uint64_t *atts, char **text);
int attr_get_max_bitnum(void);
/* aft */
extern const struct afs_table_operations aft_ops;
int aft_get_row_of_path(const char *path, struct osl_row **row);
int open_and_update_audio_file(int *fd);
int load_afd(int shmid, struct audio_file_data *afd);
int get_afsi_of_row(const struct osl_row *row, struct afs_info *afsi);
int get_afhi_of_row(const struct osl_row *row, struct afh_info *afhi);
int get_audio_file_path_of_row(const struct osl_row *row, char **path);
int audio_file_loop(void *private_data, osl_rbtree_loop_func *func);
int aft_check_callback(struct afs_callback_arg *aca);
void free_status_items(void);
/** How audio files are selected. */
enum selector_id {
/** Admissible files are determined by a mood definition. */
SEL_MOOD,
/** All listed files are admissible. */
SEL_PLAYLIST,
/** Only two so far. */
NUM_SELECTORS
};
/* Opaque, describes an active mood or playlist. */
struct selector_instance;
/**
* API implemented by the mood and playlist selectors.
*
* A selector implements a set of operations, including a method to map a
* name to a list of audio files called the admissible files.
*
* The mood and playlist selector determine the admissible files by readuing
* a blob of the corresponding blob table, then consulting the audio file
* table. Both selectors maintain a score table which contains one row for each
* admissible file. This implementation detail is hidden in the API, though.
*/
struct selector_operations {
/**
* A reference to the newly created instance is returned via the
* result pointer, which must not be NULL. The caller can pass this
* reference to various methods, for example to ->loop() to iterate
* the admissible files. It should call ->unload() to free the mood
* instance when it is no longer needed.
*
* If the message pointer is not NULL, a suitable message is returned
* there in all cases. The caller must free this string.
*/
int (*load)(const char *mood_name, struct para_buffer *pbout,
struct afs_callback_arg *aca, struct selector_instance **result);
/** Iterate over all admissible files. */
int (*loop)(int (*func)(struct osl_row *aft_row, long score, void *data),
struct selector_instance *si, void *data);
/** Free all memory allocated at load time. */
void (*unload)(struct selector_instance *si);
/** Perform integrity checks, if any. */
int (*check)(struct afs_callback_arg *aca);
/** Get a reference to the highest scoring audio file. */
int (*get_best)(struct osl_row **aft_row, long *score,
struct selector_instance *si);
/** Mark an audio file as non-admissible. */
int (*invalidate)(struct osl_row *aft_row, struct selector_instance *si);
};
/** Needed by com_select() and com_ls(). */
extern enum selector_id current_selector_id, previous_selector_id;
/** Used to turn a selector ID into an ops struct pointer. */
extern const struct selector_operations *selector_ops[NUM_SELECTORS];
/** The mood or playlist currently active. */
extern struct selector_instance *current_selector_instance;
/** Name of the current mood or playlist, including the {m,p}/ prefix. */
extern char *current_mop;
/** Name of the previous mood or playlist. */
extern char *previous_mop;
/**
* Get the selector operations of the current mood or playlist.
*
* \return Pointer to either the mood ops or the playlist ops.
*/
_static_inline_ const struct selector_operations *current_selector(void)
{
return selector_ops[current_selector_id];
};
/**
* Compare two numbers of arbitrary type.
*
* This evaluates to 1 if x < y, to -1 if x > y and to 0 if x == y. Beware: The macro
* expands both arguments twice.
*/
#define NUM_COMPARE(x, y) ((int)((x) < (y)) - (int)((x) > (y)))
/**
* Declare public functions and global variables of an osl blob table.
*
* This macro gets expanded four times to create symbols for the lyrics,
* images, moods and playlists tables.
*/
#define DECLARE_BLOB_SYMBOLS(table_name, cmd_prefix) \
int cmd_prefix ## _get_name_by_id(uint32_t id, char **name); \
int cmd_prefix ## _get_def_by_name(const char *name, struct osl_object *def); \
int cmd_prefix ## _get_name_and_def_by_row(const struct osl_row *row, \
char **name, struct osl_object *def); \
int table_name ##_event_handler(enum afs_events event, void *data); \
extern struct osl_table *table_name ## _table; \
extern const struct afs_table_operations table_name ## _ops;
/** \cond doxygen_ignore */
DECLARE_BLOB_SYMBOLS(lyrics, lyr)
DECLARE_BLOB_SYMBOLS(images, img)
DECLARE_BLOB_SYMBOLS(moods, mood)
DECLARE_BLOB_SYMBOLS(playlists, pl)
/** \endcond */
/** The columns of a blob table. */
enum blob_table_columns {
/** The identifier, a positive integer that never repeats. */
BLOBCOL_ID,
/** The unique name of the blob. */
BLOBCOL_NAME,
/** The actual blob contents. */
BLOBCOL_DEF,
/** Each blob table has this many columns. */
NUM_BLOB_COLUMNS
};
|