static void filter_complete (GMimeFilter *filter, char *inbuf, size_t inlen, size_t prespace, char **outbuf, size_t *outlen, size_t *outprespace) { if (inbuf && inlen) filter_filter (filter, inbuf, inlen, prespace, outbuf, outlen, outprespace); }
static const void * autoconvert_filter_filter(struct filter *_filter, const void *src, size_t src_size, size_t *dest_size_r, GError **error_r) { struct autoconvert_filter *filter = (struct autoconvert_filter *)_filter; if (filter->convert != NULL) { src = filter_filter(filter->convert, src, src_size, &src_size, error_r); if (src == NULL) return NULL; } return filter_filter(filter->filter, src, src_size, dest_size_r, error_r); }
static const void * chain_filter_filter(struct filter *_filter, const void *src, size_t src_size, size_t *dest_size_r, GError **error_r) { struct filter_chain *chain = (struct filter_chain *)_filter; for (GSList *i = chain->children; i != NULL; i = g_slist_next(i)) { struct filter *filter = i->data; /* feed the output of the previous filter as input into the current one */ src = filter_filter(filter, src, src_size, &src_size, error_r); if (src == NULL) return NULL; } /* return the output of the last filter */ *dest_size_r = src_size; return src; }
static const char * ao_chunk_data(struct audio_output *ao, const struct music_chunk *chunk, struct filter *replay_gain_filter, unsigned *replay_gain_serial_p, size_t *length_r) { assert(chunk != NULL); assert(!music_chunk_is_empty(chunk)); assert(music_chunk_check_format(chunk, &ao->in_audio_format)); const char *data = chunk->data; size_t length = chunk->length; (void)ao; assert(length % audio_format_frame_size(&ao->in_audio_format) == 0); if (length > 0 && replay_gain_filter != NULL) { if (chunk->replay_gain_serial != *replay_gain_serial_p) { replay_gain_filter_set_info(replay_gain_filter, chunk->replay_gain_serial != 0 ? &chunk->replay_gain_info : NULL); *replay_gain_serial_p = chunk->replay_gain_serial; } GError *error = NULL; data = filter_filter(replay_gain_filter, data, length, &length, &error); if (data == NULL) { g_warning("\"%s\" [%s] failed to filter: %s", ao->name, ao->plugin->name, error->message); g_error_free(error); return NULL; } } *length_r = length; return data; }
static const char * ao_filter_chunk(struct audio_output *ao, const struct music_chunk *chunk, size_t *length_r) { GError *error = NULL; size_t length; const char *data = ao_chunk_data(ao, chunk, ao->replay_gain_filter, &ao->replay_gain_serial, &length); if (data == NULL) return NULL; if (length == 0) { /* empty chunk, nothing to do */ *length_r = 0; return data; } /* cross-fade */ if (chunk->other != NULL) { size_t other_length; const char *other_data = ao_chunk_data(ao, chunk->other, ao->other_replay_gain_filter, &ao->other_replay_gain_serial, &other_length); if (other_data == NULL) return NULL; if (other_length == 0) { *length_r = 0; return data; } /* if the "other" chunk is longer, then that trailer is used as-is, without mixing; it is part of the "next" song being faded in, and if there's a rest, it means cross-fading ends here */ if (length > other_length) length = other_length; char *dest = pcm_buffer_get(&ao->cross_fade_buffer, other_length); memcpy(dest, other_data, other_length); pcm_mix(dest, data, length, &ao->in_audio_format, 1.0 - chunk->mix_ratio); data = dest; length = other_length; } /* apply filter chain */ data = filter_filter(ao->filter, data, length, &length, &error); if (data == NULL) { g_warning("\"%s\" [%s] failed to filter: %s", ao->name, ao->plugin->name, error->message); g_error_free(error); return NULL; } *length_r = length; return data; }
int main(int argc, char **argv) { struct audio_format audio_format; struct audio_format_string af_string; bool success; GError *error = NULL; struct filter *filter; const struct audio_format *out_audio_format; char buffer[4096]; size_t frame_size; if (argc < 3 || argc > 4) { g_printerr("Usage: run_filter CONFIG NAME [FORMAT] <IN\n"); return 1; } audio_format_init(&audio_format, 44100, SAMPLE_FORMAT_S16, 2); /* initialize GLib */ g_thread_init(NULL); g_log_set_default_handler(my_log_func, NULL); /* read configuration file (mpd.conf) */ config_global_init(); success = config_read_file(argv[1], &error); if (!success) { g_printerr("%s:", error->message); g_error_free(error); return 1; } /* parse the audio format */ if (argc > 3) { success = audio_format_parse(&audio_format, argv[3], false, &error); if (!success) { g_printerr("Failed to parse audio format: %s\n", error->message); g_error_free(error); return 1; } } /* initialize the filter */ filter = load_filter(argv[2]); if (filter == NULL) return 1; /* open the filter */ out_audio_format = filter_open(filter, &audio_format, &error); if (out_audio_format == NULL) { g_printerr("Failed to open filter: %s\n", error->message); g_error_free(error); filter_free(filter); return 1; } g_printerr("audio_format=%s\n", audio_format_to_string(out_audio_format, &af_string)); frame_size = audio_format_frame_size(&audio_format); /* play */ while (true) { ssize_t nbytes; size_t length; const void *dest; nbytes = read(0, buffer, sizeof(buffer)); if (nbytes <= 0) break; dest = filter_filter(filter, buffer, (size_t)nbytes, &length, &error); if (dest == NULL) { g_printerr("Filter failed: %s\n", error->message); filter_close(filter); filter_free(filter); return 1; } nbytes = write(1, dest, length); if (nbytes < 0) { g_printerr("Failed to write: %s\n", strerror(errno)); filter_close(filter); filter_free(filter); return 1; } } /* cleanup and exit */ filter_close(filter); filter_free(filter); config_global_finish(); return 0; }
static void filter_complete (CamelMimeFilter *filter, char *in, size_t len, size_t prespace, char **out, size_t *outlen, size_t *outprespace) { filter_filter (filter, in, len, prespace, out, outlen, outprespace); }
static bool ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk) { const char *data = chunk->data; size_t size = chunk->length; GError *error = NULL; assert(ao != NULL); assert(ao->filter != NULL); assert(!music_chunk_is_empty(chunk)); assert(music_chunk_check_format(chunk, &ao->in_audio_format)); assert(size % audio_format_frame_size(&ao->in_audio_format) == 0); if (chunk->tag != NULL) { g_mutex_unlock(ao->mutex); ao_plugin_send_tag(ao->plugin, ao->data, chunk->tag); g_mutex_lock(ao->mutex); } /* update replay gain */ if (ao->replay_gain_filter != NULL && chunk->replay_gain_serial != ao->replay_gain_serial) { replay_gain_filter_set_info(ao->replay_gain_filter, chunk->replay_gain_serial != 0 ? &chunk->replay_gain_info : NULL); ao->replay_gain_serial = chunk->replay_gain_serial; } if (size == 0) return true; data = filter_filter(ao->filter, data, size, &size, &error); if (data == NULL) { g_warning("\"%s\" [%s] failed to filter: %s", ao->name, ao->plugin->name, error->message); g_error_free(error); ao_close(ao, false); /* don't automatically reopen this device for 10 seconds */ ao->fail_timer = g_timer_new(); return false; } while (size > 0 && ao->command == AO_COMMAND_NONE) { size_t nbytes; g_mutex_unlock(ao->mutex); nbytes = ao_plugin_play(ao->plugin, ao->data, data, size, &error); g_mutex_lock(ao->mutex); if (nbytes == 0) { /* play()==0 means failure */ g_warning("\"%s\" [%s] failed to play: %s", ao->name, ao->plugin->name, error->message); g_error_free(error); ao_close(ao, false); /* don't automatically reopen this device for 10 seconds */ ao->fail_timer = g_timer_new(); return false; } assert(nbytes <= size); assert(nbytes % audio_format_frame_size(&ao->out_audio_format) == 0); data += nbytes; size -= nbytes; } return true; }