[SCM] libgroove/upstream: support per-sink gain adjustment. closes #41

andrewrk-guest at users.alioth.debian.org andrewrk-guest at users.alioth.debian.org
Tue May 13 03:19:10 UTC 2014


The following commit has been merged in the upstream branch:
commit 4526985c50cee4e93f0876fd946d84a45424d090
Author: Andrew Kelley <superjoe30 at gmail.com>
Date:   Mon May 12 01:45:35 2014 -0700

    support per-sink gain adjustment. closes #41

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 637bfc7..4aeaa30 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@
  * GroovePlaylist: `volume` renamed to `gain`
    - `groove_playlist_set_gain` renamed to `groove_playlist_set_item_gain`
    - `groove_playlist_set_volume` renamed to `groove_playlist_set_gain`
+ * support per-sink gain adjustment. closes #41
 
 ### Version 3.1.1 (2014-04-21)
 
diff --git a/groove/encoder.c b/groove/encoder.c
index e77a71f..948279e 100644
--- a/groove/encoder.c
+++ b/groove/encoder.c
@@ -347,6 +347,7 @@ struct GrooveEncoder *groove_encoder_create(void) {
     encoder->target_audio_format.channel_layout = GROOVE_CH_LAYOUT_STEREO;
     encoder->sink_buffer_size = e->sink->buffer_size;
     encoder->encoded_buffer_size = 16 * 1024;
+    encoder->gain = e->sink->gain;
 
     return encoder;
 }
@@ -581,6 +582,7 @@ int groove_encoder_attach(struct GrooveEncoder *encoder, struct GroovePlaylist *
     e->sink->buffer_size = encoder->sink_buffer_size;
     e->sink->buffer_sample_count = (codec->capabilities & CODEC_CAP_VARIABLE_FRAME_SIZE) ?
         0 : codec_ctx->frame_size;
+    e->sink->gain = encoder->gain;
 
     if (groove_sink_attach(e->sink, playlist) < 0) {
         groove_encoder_detach(encoder);
@@ -680,3 +682,9 @@ void groove_encoder_position(struct GrooveEncoder *encoder,
 
     pthread_mutex_unlock(&e->encode_head_mutex);
 }
+
+int groove_encoder_set_gain(struct GrooveEncoder *encoder, double gain) {
+    struct GrooveEncoderPrivate *e = (struct GrooveEncoderPrivate *) encoder;
+    encoder->gain = gain;
+    return groove_sink_set_gain(e->sink, gain);
+}
diff --git a/groove/encoder.h b/groove/encoder.h
index bb82e3d..dc721b4 100644
--- a/groove/encoder.h
+++ b/groove/encoder.h
@@ -65,6 +65,15 @@ struct GrooveEncoder {
      */
     int encoded_buffer_size;
 
+    /* This volume adjustment to make to this player.
+     * It is recommended that you leave this at 1.0 and instead adjust the
+     * gain of the underlying playlist.
+     * If you want to change this value after you have already attached the
+     * sink to the playlist, you must use groove_encoder_set_gain.
+     * float format. Defaults to 1.0
+     */
+    double gain;
+
     /* read-only. set when attached and cleared when detached */
     struct GroovePlaylist *playlist;
 
@@ -116,6 +125,11 @@ int groove_encoder_metadata_set(struct GrooveEncoder *encoder, const char *key,
 void groove_encoder_position(struct GrooveEncoder *encoder,
         struct GroovePlaylistItem **item, double *seconds);
 
+/* See the gain property of GrooveSink. It is recommended that you leave this
+ * at 1.0 and instead adjust the gain of the playlist.
+ * returns 0 on success, < 0 on error
+ */
+int groove_encoder_set_gain(struct GrooveEncoder *encoder, double gain);
 
 #ifdef __cplusplus
 }
diff --git a/groove/groove.h b/groove/groove.h
index 15efc2a..835deb8 100644
--- a/groove/groove.h
+++ b/groove/groove.h
@@ -307,6 +307,15 @@ struct GrooveSink {
      */
     int buffer_size;
 
+    /* This volume adjustment only applies to this sink.
+     * It is recommended that you leave this at 1.0 and instead adjust the
+     * gain of the playlist.
+     * If you want to change this value after you have already attached the
+     * sink to the playlist, you must use groove_sink_set_gain.
+     * float format. Defaults to 1.0
+     */
+    double gain;
+
     /* set to whatever you want */
     void *userdata;
     /* called when the audio queue is flushed. For example, if you seek to a
@@ -352,6 +361,12 @@ int groove_sink_buffer_get(struct GrooveSink *sink,
  */
 int groove_sink_buffer_peek(struct GrooveSink *sink, int block);
 
+/* See the gain property of GrooveSink. It is recommended that you leave this
+ * at 1.0 and instead adjust the gain of the playlist.
+ * returns 0 on success, < 0 on error
+ */
+int groove_sink_set_gain(struct GrooveSink *sink, double gain);
+
 
 #ifdef __cplusplus
 }
diff --git a/groove/playlist.c b/groove/playlist.c
index a34c071..22bc7b7 100644
--- a/groove/playlist.c
+++ b/groove/playlist.c
@@ -55,9 +55,13 @@ struct GroovePlaylistPrivate {
     char strbuf[512];
     AVFilterGraph *filter_graph;
     AVFilterContext *abuffer_ctx;
-    AVFilterContext *volume_ctx;
-    AVFilterContext *compand_ctx;
-    AVFilterContext *asplit_ctx;
+
+    AVFilter *volume_filter;
+    AVFilter *compand_filter;
+    AVFilter *abuffer_filter;
+    AVFilter *asplit_filter;
+    AVFilter *aformat_filter;
+    AVFilter *abuffersink_filter;
 
     // this mutex applies to the variables in this block
     pthread_mutex_t decode_head_mutex;
@@ -260,6 +264,58 @@ static double gain_to_dB(double gain) {
     return log(gain) / dB_scale;
 }
 
+static int create_volume_filter(struct GroovePlaylistPrivate *p, AVFilterContext **audio_src_ctx,
+        double vol, double amp_vol)
+{
+    int err;
+
+    if (vol < 0.0) vol = 0.0;
+    if (amp_vol < 1.0) {
+        snprintf(p->strbuf, sizeof(p->strbuf), "volume=%f", vol);
+        av_log(NULL, AV_LOG_INFO, "volume: %s\n", p->strbuf);
+        AVFilterContext *volume_ctx;
+        err = avfilter_graph_create_filter(&volume_ctx, p->volume_filter, NULL,
+                p->strbuf, NULL, p->filter_graph);
+        if (err < 0) {
+            av_log(NULL, AV_LOG_ERROR, "error initializing volume filter\n");
+            return err;
+        }
+        err = avfilter_link(*audio_src_ctx, 0, volume_ctx, 0);
+        if (err < 0) {
+            av_strerror(err, p->strbuf, sizeof(p->strbuf));
+            av_log(NULL, AV_LOG_ERROR, "unable to link volume filter: %s\n", p->strbuf);
+            return err;
+        }
+        *audio_src_ctx = volume_ctx;
+    } else if (amp_vol > 1.0) {
+        double attack = 0.1;
+        double decay = 0.2;
+        const char *points = "-2/-2";
+        double soft_knee = 0.02;
+        double gain = gain_to_dB(vol);
+        double volume_param = 0.0;
+        double delay = 0.2;
+        snprintf(p->strbuf, sizeof(p->strbuf), "%f:%f:%s:%f:%f:%f:%f",
+                attack, decay, points, soft_knee, gain, volume_param, delay);
+        av_log(NULL, AV_LOG_INFO, "compand: %s\n", p->strbuf);
+        AVFilterContext *compand_ctx;
+        err = avfilter_graph_create_filter(&compand_ctx, p->compand_filter, NULL,
+                p->strbuf, NULL, p->filter_graph);
+        if (err < 0) {
+            av_log(NULL, AV_LOG_ERROR, "error initializing compand filter\n");
+            return err;
+        }
+        err = avfilter_link(*audio_src_ctx, 0, compand_ctx, 0);
+        if (err < 0) {
+            av_strerror(err, p->strbuf, sizeof(p->strbuf));
+            av_log(NULL, AV_LOG_ERROR, "unable to link compand filter: %s\n", p->strbuf);
+            return err;
+        }
+        *audio_src_ctx = compand_ctx;
+    }
+    return 0;
+}
+
 // abuffer -> volume -> asplit for each audio format
 //                     -> aformat -> abuffersink
 // if the volume gain is > 1.0, we use a compand filter instead
@@ -278,13 +334,6 @@ static int init_filter_graph(struct GroovePlaylist *playlist, struct GrooveFile
         return -1;
     }
 
-    AVFilter *abuffer = avfilter_get_by_name("abuffer");
-    AVFilter *volume = avfilter_get_by_name("volume");
-    AVFilter *compand = avfilter_get_by_name("compand");
-    AVFilter *asplit = avfilter_get_by_name("asplit");
-    AVFilter *aformat = avfilter_get_by_name("aformat");
-    AVFilter *abuffersink = avfilter_get_by_name("abuffersink");
-
     int err;
     // create abuffer filter
     AVCodecContext *avctx = f->audio_st->codec;
@@ -301,7 +350,7 @@ static int init_filter_graph(struct GroovePlaylist *playlist, struct GrooveFile
     p->in_channel_layout = avctx->channel_layout;
     p->in_sample_fmt = avctx->sample_fmt;
     p->in_time_base = time_base;
-    err = avfilter_graph_create_filter(&p->abuffer_ctx, abuffer,
+    err = avfilter_graph_create_filter(&p->abuffer_ctx, p->abuffer_filter,
             NULL, p->strbuf, NULL, p->filter_graph);
     if (err < 0) {
         av_log(NULL, AV_LOG_ERROR, "error initializing abuffer filter\n");
@@ -323,67 +372,27 @@ static int init_filter_graph(struct GroovePlaylist *playlist, struct GrooveFile
     // comes out to 0.96 so we know that we can safely amplify by 1.2 even
     // though it's greater than 1.0.
     double amp_vol = vol * (p->peak > 1.0 ? 1.0 : p->peak);
-    if (vol < 0.0) vol = 0.0;
-    if (amp_vol < 1.0) {
-        snprintf(p->strbuf, sizeof(p->strbuf), "volume=%f", vol);
-        av_log(NULL, AV_LOG_INFO, "volume: %s\n", p->strbuf);
-        err = avfilter_graph_create_filter(&p->volume_ctx, volume, NULL,
-                p->strbuf, NULL, p->filter_graph);
-        if (err < 0) {
-            av_log(NULL, AV_LOG_ERROR, "error initializing volume filter\n");
-            return err;
-        }
-        err = avfilter_link(audio_src_ctx, 0, p->volume_ctx, 0);
-        if (err < 0) {
-            av_log(NULL, AV_LOG_ERROR, "unable to link filters\n");
-            return err;
-        }
-        audio_src_ctx = p->volume_ctx;
-    } else if (amp_vol > 1.0) {
-        double attack = 0.1;
-        double decay = 0.2;
-        const char *points = "-2/-2";
-        double soft_knee = 0.02;
-        double gain = gain_to_dB(vol);
-        double volume_param = 0.0;
-        double delay = 0.2;
-        snprintf(p->strbuf, sizeof(p->strbuf), "%f:%f:%s:%f:%f:%f:%f",
-                attack, decay, points, soft_knee, gain, volume_param, delay);
-        av_log(NULL, AV_LOG_INFO, "compand: %s\n", p->strbuf);
-        err = avfilter_graph_create_filter(&p->compand_ctx, compand, NULL,
-                p->strbuf, NULL, p->filter_graph);
-        if (err < 0) {
-            av_log(NULL, AV_LOG_ERROR, "error initializing compand filter\n");
-            return err;
-        }
-        err = avfilter_link(audio_src_ctx, 0, p->compand_ctx, 0);
-        if (err < 0) {
-            av_log(NULL, AV_LOG_ERROR, "unable to link filters\n");
-            return err;
-        }
-        audio_src_ctx = p->compand_ctx;
-    } else {
-        p->volume_ctx = NULL;
-    }
+    err = create_volume_filter(p, &audio_src_ctx, vol, amp_vol);
+    if (err < 0)
+        return err;
 
     // if only one sink, no need for asplit
-    if (p->sink_map_count < 2) {
-        p->asplit_ctx = NULL;
-    } else {
+    if (p->sink_map_count >= 2) {
+        AVFilterContext *asplit_ctx;
         snprintf(p->strbuf, sizeof(p->strbuf), "%d", p->sink_map_count);
         av_log(NULL, AV_LOG_INFO, "asplit: %s\n", p->strbuf);
-        err = avfilter_graph_create_filter(&p->asplit_ctx, asplit,
+        err = avfilter_graph_create_filter(&asplit_ctx, p->asplit_filter,
                 NULL, p->strbuf, NULL, p->filter_graph);
         if (err < 0) {
             av_log(NULL, AV_LOG_ERROR, "unable to create asplit filter\n");
             return err;
         }
-        err = avfilter_link(audio_src_ctx, 0, p->asplit_ctx, 0);
+        err = avfilter_link(audio_src_ctx, 0, asplit_ctx, 0);
         if (err < 0) {
             av_log(NULL, AV_LOG_ERROR, "unable to link to asplit\n");
             return err;
         }
-        audio_src_ctx = p->asplit_ctx;
+        audio_src_ctx = asplit_ctx;
     }
 
     // for each audio format, create aformat and abuffersink filters
@@ -394,6 +403,12 @@ static int init_filter_graph(struct GroovePlaylist *playlist, struct GrooveFile
         struct GrooveAudioFormat *audio_format = &example_sink->audio_format;
 
         AVFilterContext *inner_audio_src_ctx = audio_src_ctx;
+
+        // create volume filter
+        err = create_volume_filter(p, &inner_audio_src_ctx, example_sink->gain, example_sink->gain);
+        if (err < 0)
+            return err;
+
         if (example_sink->disable_resample) {
             map_item->aformat_ctx = NULL;
         } else {
@@ -403,7 +418,7 @@ static int init_filter_graph(struct GroovePlaylist *playlist, struct GrooveFile
                     av_get_sample_fmt_name((enum AVSampleFormat)audio_format->sample_fmt),
                     audio_format->sample_rate, audio_format->channel_layout);
             av_log(NULL, AV_LOG_INFO, "aformat: %s\n", p->strbuf);
-            err = avfilter_graph_create_filter(&map_item->aformat_ctx, aformat,
+            err = avfilter_graph_create_filter(&map_item->aformat_ctx, p->aformat_filter,
                     NULL, p->strbuf, NULL, p->filter_graph);
             if (err < 0) {
                 av_strerror(err, p->strbuf, sizeof(p->strbuf));
@@ -411,16 +426,17 @@ static int init_filter_graph(struct GroovePlaylist *playlist, struct GrooveFile
                         p->strbuf);
                 return err;
             }
-            err = avfilter_link(audio_src_ctx, pad_index, map_item->aformat_ctx, 0);
+            err = avfilter_link(inner_audio_src_ctx, pad_index, map_item->aformat_ctx, 0);
             if (err < 0) {
-                av_log(NULL, AV_LOG_ERROR, "unable to link filters\n");
+                av_strerror(err, p->strbuf, sizeof(p->strbuf));
+                av_log(NULL, AV_LOG_ERROR, "unable to link aformat filter: %s\n", p->strbuf);
                 return err;
             }
             inner_audio_src_ctx = map_item->aformat_ctx;
         }
 
         // create abuffersink filter
-        err = avfilter_graph_create_filter(&map_item->abuffersink_ctx, abuffersink,
+        err = avfilter_graph_create_filter(&map_item->abuffersink_ctx, p->abuffersink_filter,
                 NULL, NULL, NULL, p->filter_graph);
         if (err < 0) {
             av_log(NULL, AV_LOG_ERROR, "unable to create abuffersink filter\n");
@@ -428,7 +444,8 @@ static int init_filter_graph(struct GroovePlaylist *playlist, struct GrooveFile
         }
         err = avfilter_link(inner_audio_src_ctx, 0, map_item->abuffersink_ctx, 0);
         if (err < 0) {
-            av_log(NULL, AV_LOG_ERROR, "unable to link filters\n");
+            av_strerror(err, p->strbuf, sizeof(p->strbuf));
+            av_log(NULL, AV_LOG_ERROR, "unable to link abuffersink filter: %s\n", p->strbuf);
             return err;
         }
 
@@ -697,6 +714,8 @@ static void *decode_thread(void *arg) {
 static int sink_formats_equal(const struct GrooveSink *a, const struct GrooveSink *b) {
     if (a->buffer_sample_count != b->buffer_sample_count)
         return 0;
+    if (a->gain != b->gain)
+        return 0;
     if (a->disable_resample) {
         return b->disable_resample;
     } else {
@@ -919,6 +938,48 @@ struct GroovePlaylist * groove_playlist_create(void) {
         return NULL;
     }
 
+    p->volume_filter = avfilter_get_by_name("volume");
+    if (!p->volume_filter) {
+        groove_playlist_destroy(playlist);
+        av_log(NULL, AV_LOG_ERROR, "unable to get volume filter\n");
+        return NULL;
+    }
+
+    p->compand_filter = avfilter_get_by_name("compand");
+    if (!p->compand_filter) {
+        groove_playlist_destroy(playlist);
+        av_log(NULL, AV_LOG_ERROR, "unable to get compand filter\n");
+        return NULL;
+    }
+
+    p->abuffer_filter = avfilter_get_by_name("abuffer");
+    if (!p->abuffer_filter) {
+        groove_playlist_destroy(playlist);
+        av_log(NULL, AV_LOG_ERROR, "unable to get abuffer filter\n");
+        return NULL;
+    }
+
+    p->asplit_filter = avfilter_get_by_name("asplit");
+    if (!p->asplit_filter) {
+        groove_playlist_destroy(playlist);
+        av_log(NULL, AV_LOG_ERROR, "unable to get asplit filter\n");
+        return NULL;
+    }
+
+    p->aformat_filter = avfilter_get_by_name("aformat");
+    if (!p->aformat_filter) {
+        groove_playlist_destroy(playlist);
+        av_log(NULL, AV_LOG_ERROR, "unable to get aformat filter\n");
+        return NULL;
+    }
+
+    p->abuffersink_filter = avfilter_get_by_name("abuffersink");
+    if (!p->abuffersink_filter) {
+        groove_playlist_destroy(playlist);
+        av_log(NULL, AV_LOG_ERROR, "unable to get abuffersink filter\n");
+        return NULL;
+    }
+
     return playlist;
 }
 
@@ -1171,6 +1232,7 @@ struct GrooveSink * groove_sink_create(void) {
     struct GrooveSink *sink = &s->externals;
 
     sink->buffer_size = 8192;
+    sink->gain = 1.0;
 
     s->audioq = groove_queue_create();
 
@@ -1200,3 +1262,28 @@ void groove_sink_destroy(struct GrooveSink *sink) {
 
     av_free(s);
 }
+
+int groove_sink_set_gain(struct GrooveSink *sink, double gain) {
+    // we must re-create the sink mapping and the filter graph
+    // if the gain changes
+
+    struct GroovePlaylist *playlist = sink->playlist;
+    struct GroovePlaylistPrivate *p = (struct GroovePlaylistPrivate *) playlist;
+
+
+    pthread_mutex_lock(&p->decode_head_mutex);
+    sink->gain = gain;
+    int err = remove_sink_from_map(sink);
+    if (err) {
+        pthread_mutex_unlock(&p->decode_head_mutex);
+        return err;
+    }
+    err = add_sink_to_map(playlist, sink);
+    if (err) {
+        pthread_mutex_unlock(&p->decode_head_mutex);
+        return err;
+    }
+    p->rebuild_filter_graph_flag = 1;
+    pthread_mutex_unlock(&p->decode_head_mutex);
+    return 0;
+}
diff --git a/grooveplayer/player.c b/grooveplayer/player.c
index c3aedc8..d1f4f5b 100644
--- a/grooveplayer/player.c
+++ b/grooveplayer/player.c
@@ -212,6 +212,7 @@ struct GroovePlayer *groove_player_create(void) {
     // small because there is no way to clear the buffer.
     player->device_buffer_size = 1024;
     player->sink_buffer_size = 8192;
+    player->gain = p->sink->gain;
 
     return player;
 }
@@ -262,6 +263,7 @@ int groove_player_attach(struct GroovePlayer *player, struct GroovePlaylist *pla
     // based on spec that we got, attach a sink with those properties
     p->sink->buffer_size = player->sink_buffer_size;
     p->sink->audio_format = player->actual_audio_format;
+    p->sink->gain = player->gain;
 
     if (p->sink->audio_format.sample_fmt == GROOVE_SAMPLE_FMT_NONE) {
         groove_player_detach(player);
@@ -347,3 +349,9 @@ int groove_player_event_peek(struct GroovePlayer *player, int block) {
     struct GroovePlayerPrivate *p = (struct GroovePlayerPrivate *) player;
     return groove_queue_peek(p->eventq, block);
 }
+
+int groove_player_set_gain(struct GroovePlayer *player, double gain) {
+    struct GroovePlayerPrivate *p = (struct GroovePlayerPrivate *) player;
+    player->gain = gain;
+    return groove_sink_set_gain(p->sink, gain);
+}
diff --git a/grooveplayer/player.h b/grooveplayer/player.h
index 652ec90..fda3057 100644
--- a/grooveplayer/player.h
+++ b/grooveplayer/player.h
@@ -55,6 +55,15 @@ struct GroovePlayer {
      */
     int sink_buffer_size;
 
+    /* This volume adjustment to make to this player.
+     * It is recommended that you leave this at 1.0 and instead adjust the
+     * gain of the underlying playlist.
+     * If you want to change this value after you have already attached the
+     * sink to the playlist, you must use groove_player_set_gain.
+     * float format. Defaults to 1.0
+     */
+    double gain;
+
     /* read-only. set when you call groove_player_attach and cleared when
      * you call groove_player_detach
      */
@@ -115,6 +124,11 @@ int groove_player_event_get(struct GroovePlayer *player,
  */
 int groove_player_event_peek(struct GroovePlayer *player, int block);
 
+/* See the gain property of GrooveSink. It is recommended that you leave this
+ * at 1.0 and instead adjust the gain of the playlist.
+ * returns 0 on success, < 0 on error
+ */
+int groove_player_set_gain(struct GroovePlayer *player, double gain);
 
 #ifdef __cplusplus
 }

-- 
libgroove packaging



More information about the pkg-multimedia-commits mailing list