summaryrefslogtreecommitdiff
path: root/src/libaudcore/plugin.h
blob: 66077ad24ebf5cbbac17a982e89faa0d92323294 (plain)
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
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
/*
 * plugin.h
 * Copyright 2005-2013 William Pitcock, Yoshiki Yazawa, Eugene Zagidullin, and
 *                     John Lindgren
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions, and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions, and the following disclaimer in the documentation
 *    provided with the distribution.
 *
 * This software is provided "as is" and without any warranty, express or
 * implied. In no event shall the authors be liable for any damages arising from
 * the use of this software.
 */

#ifndef LIBAUDCORE_PLUGIN_H
#define LIBAUDCORE_PLUGIN_H

#include <libaudcore/audio.h>
#include <libaudcore/plugins.h>
#include <libaudcore/tuple.h>
#include <libaudcore/visualizer.h>
#include <libaudcore/vfs.h>

enum class AudMenuID;
struct PluginPreferences;

/* "Magic" bytes identifying an Audacious plugin header. */
#define _AUD_PLUGIN_MAGIC ((int) 0x8EAC8DE2)

/* API version.  Plugins are marked with this number at compile time.
 *
 * _AUD_PLUGIN_VERSION is the current version; _AUD_PLUGIN_VERSION_MIN is
 * the oldest one we are backward compatible with.  Plugins marked older than
 * _AUD_PLUGIN_VERSION_MIN or newer than _AUD_PLUGIN_VERSION are not loaded.
 *
 * Before releases that add new pointers to the end of the API tables, increment
 * _AUD_PLUGIN_VERSION but leave _AUD_PLUGIN_VERSION_MIN the same.
 *
 * Before releases that break backward compatibility (e.g. remove pointers from
 * the API tables), increment _AUD_PLUGIN_VERSION *and* set
 * _AUD_PLUGIN_VERSION_MIN to the same value. */

#define _AUD_PLUGIN_VERSION_MIN 46 /* 3.6-devel */
#define _AUD_PLUGIN_VERSION     47 /* 3.7-devel */

/* compatibility flags ORed into the version field */
#define _AUD_PLUGIN_GLIB_ONLY 0x10000 /* plugin requires GLib mainloop */
#define _AUD_PLUGIN_QT_ONLY   0x20000 /* plugin requires Qt mainloop */

/* A NOTE ON THREADS
 *
 * How thread-safe a plugin must be depends on the type of plugin.  Note that
 * some parts of the Audacious API are *not* thread-safe and therefore cannot be
 * used in some parts of some plugins; for example, input plugins cannot use
 * GUI-related calls or access the playlist except in about() and configure().
 *
 * Thread-safe plugins: transport, playlist, input, effect, and output.  These
 * must be mostly thread-safe.  init() and cleanup() may be called from
 * secondary threads; however, no other functions provided by the plugin will be
 * called at the same time.  about() and configure() will be called only from
 * the main thread.  All other functions provided by the plugin may be called
 * from any thread and from multiple threads simultaneously.
 *
 * Exceptions:
 * - Because many existing input plugins are not coded to handle simultaneous
 *   calls to play(), play() will only be called from one thread at a time.  New
 *   plugins should not rely on this exception, though.
 * - Some combinations of calls, especially for output and effect plugins, make
 *   no sense; for example, flush() in an output plugin will only be called
 *   after open_audio() and before close_audio().
 *
 * Single-thread plugins: visualization, general, and interface.  Functions
 * provided by these plugins will only be called from the main thread. */

/* CROSS-PLUGIN MESSAGES
 *
 * Since 3.2, Audacious implements a basic messaging system between plugins.
 * Messages are sent using aud_plugin_send_message() and received through the
 * take_message() method specified in the header of the receiving plugin.
 * Plugins that do not need to receive messages can set take_message() to nullptr.
 *
 * Each message includes a code indicating the type of message, a pointer to
 * some data, and a value indicating the size of that data. What the message
 * data contains is entirely up to the two plugins involved. For this reason, it
 * is crucial that both plugins agree on the meaning of the message codes used.
 *
 * Once the message is sent, an integer error code is returned. If the receiving
 * plugin does not provide the take_message() method, -1 is returned. If
 * take_message() does not recognize the message code, it should ignore the
 * message and return -1. An error code of zero represents success. Other error
 * codes may be used with more specific meanings.
 *
 * For the time being, aud_plugin_send_message() should only be called from the
 * program's main thread. */

struct PluginInfo {
    const char * name;
    const char * domain; // for gettext
    const char * about;
    const PluginPreferences * prefs;
};

class Plugin
{
public:
    constexpr Plugin (PluginType type, PluginInfo info) :
        type (type),
        info (info) {}

    const int magic = _AUD_PLUGIN_MAGIC;
    const int version = _AUD_PLUGIN_VERSION
#ifdef AUD_PLUGIN_GLIB_ONLY
     | _AUD_PLUGIN_GLIB_ONLY
#endif
#ifdef AUD_PLUGIN_QT_ONLY
     | _AUD_PLUGIN_QT_ONLY
#endif
     ;

    const PluginType type;
    const PluginInfo info;

    virtual bool init () { return true; }
    virtual void cleanup () {}

    virtual int take_message (const char * code, const void * data, int size) { return -1; }
};

class TransportPlugin : public Plugin
{
public:
    constexpr TransportPlugin (const PluginInfo info,
     const ArrayRef<const char *> schemes) :
        Plugin (PluginType::Transport, info),
        schemes (schemes) {}

    /* supported URI schemes (without "://") */
    const ArrayRef<const char *> schemes;

    /* fopen() implementation */
    virtual VFSImpl * fopen (const char * filename, const char * mode, String & error) = 0;
};

class PlaylistPlugin : public Plugin
{
public:
    constexpr PlaylistPlugin (const PluginInfo info,
     const ArrayRef<const char *> extensions, bool can_save) :
        Plugin (PluginType::Playlist, info),
        extensions (extensions),
        can_save (can_save) {}

    /* supported file extensions (without periods) */
    const ArrayRef<const char *> extensions;

    /* true if the plugin can save playlists */
    const bool can_save;

    /* path: URI of playlist file (in)
     * file: VFS handle of playlist file (in, read-only file, not seekable)
     * title: title of playlist (out)
     * items: playlist entries (out) */
    virtual bool load (const char * path, VFSFile & file, String & title,
     Index<PlaylistAddItem> & items) = 0;

    /* path: URI of playlist file (in)
     * file: VFS handle of playlist file (in, write-only file, not seekable)
     * title: title of playlist (in)
     * items: playlist entries (in) */
    virtual bool save (const char * path, VFSFile & file, const char * title,
     const Index<PlaylistAddItem> & items) { return false; }
};

class OutputPlugin : public Plugin
{
public:
    constexpr OutputPlugin (const PluginInfo info, int priority, bool force_reopen = false) :
        Plugin (PluginType::Output, info),
        priority (priority),
        force_reopen (force_reopen) {}

    /* During probing, plugins with higher priority (10 to 0) are tried first. */
    const int priority;

    /* Whether close_audio() and open_audio() must always be called between
     * songs, even if the audio format is the same.  Note that this defeats
     * gapless playback. */
    const bool force_reopen;

    /* Returns current volume for left and right channels (0 to 100). */
    virtual StereoVolume get_volume () = 0;

    /* Changes volume for left and right channels (0 to 100). */
    virtual void set_volume (StereoVolume volume) = 0;

    /* Sets information about the song being played.  This function will be
     * called before open_audio(). */
    virtual void set_info (const char * filename, const Tuple & tuple) {}

    /* Begins playback of a PCM stream.  <format> is one of the FMT_*
     * enumeration values defined in libaudcore/audio.h.  Returns true on
     * success. */
    virtual bool open_audio (int format, int rate, int chans) = 0;

    /* Ends playback.  Any buffered audio data is discarded. */
    virtual void close_audio () = 0;

    /* Waits until write_audio() will return a size greater than zero.
     * get_delay(), pause(), and flush() may be called meanwhile; if flush()
     * is called, period_wait() should return immediately. */
    virtual void period_wait () = 0;

    /* Writes up to <size> bytes of data, in the format given to open_audio().
     * If there is not enough buffer space for all <size> bytes, writes only as
     * many bytes as can be written immediately without blocking.  Returns the
     * number of bytes actually written. */
    virtual int write_audio (const void * data, int size) = 0;

    /* Waits until all buffered data has been heard by the user. */
    virtual void drain () = 0;

    /* Returns an estimate of how many milliseconds will pass before all the
     * data passed to write_audio() has been heard by the user. */
    virtual int get_delay () = 0;

    /* Pauses the stream if <p> is nonzero; otherwise unpauses it.
     * write_audio() will not be called while the stream is paused. */
    virtual void pause (bool pause) = 0;

    /* Discards any buffered audio data. */
    virtual void flush () = 0;
};

class EffectPlugin : public Plugin
{
public:
    constexpr EffectPlugin (const PluginInfo info, int order, bool preserves_format) :
        Plugin (PluginType::Effect, info),
        order (order),
        preserves_format (preserves_format) {}

    /* Effects with lowest order (0 to 9) are applied first. */
    const int order;

    /* If the effect does not change the number of channels or the sampling
     * rate, it can be enabled and disabled more smoothly. */
    const bool preserves_format;

    /* All processing is done in floating point.  If the effect plugin wants to
     * change the channel count or sample rate, it can change the parameters
     * passed to start().  They cannot be changed in the middle of a song. */
    virtual void start (int & channels, int & rate) = 0;

    /* Performs effect processing.  process() may modify the audio samples in
     * place and return a reference to the same buffer, or it may return a
     * reference to an internal working buffer.  The number of output samples
     * need not be the same as the number of input samples. */
    virtual Index<float> & process (Index<float> & data) = 0;

    /* Optional.  A seek is taking place; any buffers should be discarded.
     * Unless the "force" flag is set, the plugin may choose to override the
     * normal flush behavior and handle the flush itself (for example, to
     * perform crossfading).  The flush() function should return false in this
     * case to prevent flush() from being called in downstream effect plugins. */
    virtual bool flush (bool force)
        { return true; }

    /* Exactly like process() except that any buffers should be drained (i.e.
     * the data processed and returned).  finish() will be called a second time
     * at the end of the last song in the playlist. */
    virtual Index<float> & finish (Index<float> & data, bool end_of_playlist)
        { return process (data); }

    /* Required only for plugins that change the time domain (e.g. a time
     * stretch) or use read-ahead buffering.  translate_delay() must do two
     * things: first, translate <delay> (which is in milliseconds) from the
     * output time domain back to the input time domain; second, increase
     * <delay> by the size of the read-ahead buffer.  It should return the
     * adjusted delay. */
    virtual int adjust_delay (int delay)
        { return delay; }
};

enum class InputKey {
    Ext,
    MIME,
    Scheme,
    count
};

class InputPlugin : public Plugin
{
public:
    enum {
        /* Indicates that the plugin can write file tags */
        FlagWritesTag = (1 << 0),

        /* Indicates that files handled by the plugin may contain more than one
         * song.  When reading the tuple for such a file, the plugin should set
         * the FIELD_SUBSONG_NUM field to the number of songs in the file.  For
         * all other files, the field should be left unset.
         *
         * Example:
         * 1. User adds a file named "somefile.xxx" to the playlist.  Having
         * determined that this plugin can handle the file, Audacious opens the
         * file and calls probe_for_tuple().  probe_for_tuple() sees that there
         * are 3 songs in the file and sets FIELD_SUBSONG_NUM to 3.
         * 2. For each song in the file, Audacious opens the file and calls
         * probe_for_tuple(); this time, however, a question mark and song
         * number are appended to the file name passed: "somefile.sid?2" refers
         * to the second song in the file "somefile.sid".
         * 3. When one of the songs is played, Audacious opens the file and
         * calls play() with a file name modified in this way. */
        FlagSubtunes = (1 << 1)
    };

    struct InputInfo
    {
        typedef const char * const * List;

        int flags, priority;
        aud::array<InputKey, List> keys;

        constexpr InputInfo (int flags = 0) :
            flags (flags), priority (0), keys {} {}

        /* Associates file extensions with the plugin. */
        constexpr InputInfo with_exts (List exts) const
            { return InputInfo (flags, priority,
              exts, keys[InputKey::MIME], keys[InputKey::Scheme]); }

        /* Associates MIME types with the plugin. */
        constexpr InputInfo with_mimes (List mimes) const
            { return InputInfo (flags, priority,
              keys[InputKey::Ext], mimes, keys[InputKey::Scheme]); }

        /* Associates custom URI schemes with the plugin.  Plugins using custom
         * URI schemes are expected to handle their own I/O.  Hence, any VFSFile
         * passed to play(), read_tuple(), etc. will be null. */
        constexpr InputInfo with_schemes (List schemes) const
            { return InputInfo (flags, priority,
              keys[InputKey::Ext], keys[InputKey::MIME], schemes); }

        /* Sets how quickly the plugin should be tried in searching for a plugin
         * to handle a file which could not be identified from its extension.
         * Plugins with priority 0 are tried first, 10 last. */
        constexpr InputInfo with_priority (int priority) const
            { return InputInfo (flags, priority,
              keys[InputKey::Ext], keys[InputKey::MIME], keys[InputKey::Scheme]); }

    private:
        constexpr InputInfo (int flags, int priority, List exts, List mimes, List schemes) :
            flags (flags), priority (priority), keys {exts, mimes, schemes} {}
    };

    constexpr InputPlugin (PluginInfo info, InputInfo input_info) :
        Plugin (PluginType::Input, info),
        input_info (input_info) {}

    const InputInfo input_info;

    /* Returns true if the plugin can handle the file. */
    virtual bool is_our_file (const char * filename, VFSFile & file) = 0;

    /* Reads metadata from the file.  Optional if the plugin implements read_tag(). */
    virtual Tuple read_tuple (const char * filename, VFSFile & file)
        { return Tuple(); }

    /* Plays the file.  Returns false on error.  Also see input-api.h. */
    virtual bool play (const char * filename, VFSFile & file) = 0;

    /* Optional.  Writes metadata to the file, returning false on error. */
    virtual bool write_tuple (const char * filename, VFSFile & file, const Tuple & tuple)
        { return false; }

    /* Optional.  Reads an album art image (JPEG or PNG data) from the file.
     * Returns an empty buffer on error. */
    virtual Index<char> read_image (const char * filename, VFSFile & file)
        { return Index<char> (); }

    /* Optional.  Displays a window showing info about the file.  In general,
     * this function should be avoided since Audacious already provides a file
     * info window. */
    virtual bool file_info_box (const char * filename, VFSFile & file)
        { return false; }

    /* Optional.  Reads metadata and/or an album art from the file.
     * Providing this function is encouraged over providing a separate
     * read_tuple() and read_image().  The filename fields of the tuple
     * (if not null) are already set before the function is called. */
    virtual bool read_tag (const char * filename, VFSFile & file, Tuple * tuple,
     Index<char> * image)
        { return default_read_tag (filename, file, tuple, image); }

    /* compatibility (non-virtual) implementation of read_tag(); do not use. */
    bool default_read_tag (const char * filename, VFSFile & file, Tuple * tuple,
     Index<char> * image);

protected:
    /* Prepares the output system for playback in the specified format.  Also
     * triggers the "playback ready" hook.  Hence, if you call set_replay_gain,
     * set_playback_tuple, or set_stream_bitrate, consider doing so before
     * calling open_audio.  There is no return value.  If the requested audio
     * format is not supported, write_audio() will do nothing and check_stop()
     * will immediately return true. */
    static void open_audio (int format, int rate, int channels);

    /* Informs the output system of replay gain values for the current song so
     * that volume levels can be adjusted accordingly, if the user so desires.
     * This may be called at any time during playback should the values change. */
    static void set_replay_gain (const ReplayGainInfo & gain);

    /* Passes audio data to the output system for playback.  The data must be in
     * the format passed to open_audio(), and the length (in bytes) must be an
     * integral number of frames.  This function blocks until all the data has
     * been written (though it may not yet be heard by the user). */
    static void write_audio (const void * data, int length);

    /* Returns the current tuple for the stream. */
    static Tuple get_playback_tuple ();

    /* Updates the tuple for the stream. */
    static void set_playback_tuple (Tuple && tuple);

    /* Updates the displayed bitrate, in bits per second. */
    static void set_stream_bitrate (int bitrate);

    /* Checks whether playback is to be stopped.  The play() function should
     * poll check_stop() periodically and return as soon as check_stop() returns
     * true. */
    static bool check_stop ();

    /* Checks whether a seek has been requested.  If so, returns the position to
     * seek to, in milliseconds.  Otherwise, returns -1. */
    static int check_seek ();
};

class DockablePlugin : public Plugin
{
public:
    constexpr DockablePlugin (PluginType type, PluginInfo info) :
        Plugin (type, info) {}

    /* GtkWidget * get_gtk_widget () */
    virtual void * get_gtk_widget () { return nullptr; }

    /* QWidget * get_qt_widget () */
    virtual void * get_qt_widget () { return nullptr; }
};

class GeneralPlugin : public DockablePlugin
{
public:
    constexpr GeneralPlugin (PluginInfo info, bool enabled_by_default) :
        DockablePlugin (PluginType::General, info),
        enabled_by_default (enabled_by_default) {}

    const bool enabled_by_default;
};

class VisPlugin : public DockablePlugin, public Visualizer
{
public:
    constexpr VisPlugin (PluginInfo info, int type_mask) :
        DockablePlugin (PluginType::Vis, info),
        Visualizer (type_mask) {}
};

class IfacePlugin : public Plugin
{
public:
    constexpr IfacePlugin (PluginInfo info) :
        Plugin (PluginType::Iface, info) {}

    virtual void show (bool show) = 0;
    virtual void run () = 0;
    virtual void quit () = 0;

    virtual void show_about_window () = 0;
    virtual void hide_about_window () = 0;
    virtual void show_filebrowser (bool open) = 0;
    virtual void hide_filebrowser () = 0;
    virtual void show_jump_to_song () = 0;
    virtual void hide_jump_to_song () = 0;
    virtual void show_prefs_window () = 0;
    virtual void hide_prefs_window () = 0;
    virtual void plugin_menu_add (AudMenuID id, void func (), const char * name, const char * icon) = 0;
    virtual void plugin_menu_remove (AudMenuID id, void func ()) = 0;
};

#endif