/* * Copyright (C) 2003-2013 The Music Player Daemon Project * http://www.musicpd.org * * 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 program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #include "VolumeFilterPlugin.hxx" #include "FilterPlugin.hxx" #include "FilterInternal.hxx" #include "FilterRegistry.hxx" #include "conf.h" #include "pcm_buffer.h" #include "PcmVolume.hxx" #include "audio_format.h" #include #include struct volume_filter { struct filter filter; /** * The current volume, from 0 to #PCM_VOLUME_1. */ unsigned volume; struct audio_format audio_format; struct pcm_buffer buffer; }; static inline GQuark volume_quark(void) { return g_quark_from_static_string("pcm_volume"); } static struct filter * volume_filter_init(G_GNUC_UNUSED const struct config_param *param, G_GNUC_UNUSED GError **error_r) { struct volume_filter *filter = g_new(struct volume_filter, 1); filter_init(&filter->filter, &volume_filter_plugin); filter->volume = PCM_VOLUME_1; return &filter->filter; } static void volume_filter_finish(struct filter *filter) { g_free(filter); } static const struct audio_format * volume_filter_open(struct filter *_filter, struct audio_format *audio_format, G_GNUC_UNUSED GError **error_r) { struct volume_filter *filter = (struct volume_filter *)_filter; filter->audio_format = *audio_format; pcm_buffer_init(&filter->buffer); return &filter->audio_format; } static void volume_filter_close(struct filter *_filter) { struct volume_filter *filter = (struct volume_filter *)_filter; pcm_buffer_deinit(&filter->buffer); } static const void * volume_filter_filter(struct filter *_filter, const void *src, size_t src_size, size_t *dest_size_r, GError **error_r) { struct volume_filter *filter = (struct volume_filter *)_filter; bool success; void *dest; *dest_size_r = src_size; if (filter->volume >= PCM_VOLUME_1) /* optimized special case: 100% volume = no-op */ return src; dest = pcm_buffer_get(&filter->buffer, src_size); if (filter->volume <= 0) { /* optimized special case: 0% volume = memset(0) */ /* XXX is this valid for all sample formats? What about floating point? */ memset(dest, 0, src_size); return dest; } memcpy(dest, src, src_size); success = pcm_volume(dest, src_size, sample_format(filter->audio_format.format), filter->volume); if (!success) { g_set_error(error_r, volume_quark(), 0, "pcm_volume() has failed"); return NULL; } return dest; } const struct filter_plugin volume_filter_plugin = { "volume", volume_filter_init, volume_filter_finish, volume_filter_open, volume_filter_close, volume_filter_filter, }; unsigned volume_filter_get(const struct filter *_filter) { const struct volume_filter *filter = (const struct volume_filter *)_filter; assert(filter->filter.plugin == &volume_filter_plugin); assert(filter->volume <= PCM_VOLUME_1); return filter->volume; } void volume_filter_set(struct filter *_filter, unsigned volume) { struct volume_filter *filter = (struct volume_filter *)_filter; assert(filter->filter.plugin == &volume_filter_plugin); assert(volume <= PCM_VOLUME_1); filter->volume = volume; }