gtkglsink: fix crash when widget is resized after element destruction
[platform/upstream/gst-plugins-good.git] / gst / replaygain / rganalysis.c
index b20a08f..3040376 100644 (file)
@@ -109,14 +109,24 @@ struct _RgAnalysisCtx
 
   RgAnalysisAcc track;
   RgAnalysisAcc album;
+  void (*post_message) (gpointer analysis,
+      GstClockTime timestamp, GstClockTime duration, gdouble rglevel);
+  gpointer analysis;
+  /* The timestamp of the current incoming buffer. */
+  GstClockTime buffer_timestamp;
+  /* Number of samples processed in current buffer, during emit_signal,
+     this will always be on an RMS window boundary. */
+  guint buffer_n_samples_done;
 };
 
 /* Filter coefficients for the IIR filters that form the equal
  * loudness filter.  XFilter[ctx->sample_rate_index] gives the array
  * of the X coefficients (A or B) for the configured sample rate. */
 
-#ifdef G_OS_WIN32
+#ifdef _MSC_VER
 /* Disable double-to-float warning: */
+/* A better solution would be to append 'f' to each constant, but that
+ * makes the code ugly. */
 #pragma warning ( disable : 4305 )
 #endif
 
@@ -213,7 +223,7 @@ static const gfloat BButter[9][3] = {
   {0.94597685600279, -1.89195371200558, 0.94597685600279}
 };
 
-#ifdef G_OS_WIN32
+#ifdef _MSC_VER
 #pragma warning ( default : 4305 )
 #endif
 
@@ -244,7 +254,10 @@ static inline void
 yule_filter (const gfloat * input, gfloat * output,
     const gfloat * a, const gfloat * b)
 {
-  output[0] = input[0] * b[0]
+  /* 1e-10 is added below to avoid running into denormals when operating on
+   * near silence. */
+
+  output[0] = 1e-10 + input[0] * b[0]
       + input[-1] * b[1] - output[-1] * a[1]
       + input[-2] * b[2] - output[-2] * a[2]
       + input[-3] * b[3] - output[-3] * a[3]
@@ -401,6 +414,13 @@ rg_analysis_new (void)
   return ctx;
 }
 
+static void
+reset_silence_detection (RgAnalysisCtx * ctx)
+{
+  ctx->buffer_timestamp = GST_CLOCK_TIME_NONE;
+  ctx->buffer_n_samples_done = 0;
+}
+
 /* Adapt to given sample rate.  Does nothing if already the current
  * rate (returns TRUE then).  Returns FALSE only if given sample rate
  * is not supported.  If the configured rate changes, the last
@@ -453,11 +473,29 @@ rg_analysis_set_sample_rate (RgAnalysisCtx * ctx, gint sample_rate)
       / 1000);
 
   reset_filters (ctx);
+  reset_silence_detection (ctx);
 
   return TRUE;
 }
 
 void
+rg_analysis_init_silence_detection (RgAnalysisCtx * ctx,
+    void (*post_message) (gpointer analysis, GstClockTime timestamp,
+        GstClockTime duration, gdouble rglevel), gpointer analysis)
+{
+  ctx->post_message = post_message;
+  ctx->analysis = analysis;
+  reset_silence_detection (ctx);
+}
+
+void
+rg_analysis_start_buffer (RgAnalysisCtx * ctx, GstClockTime buffer_timestamp)
+{
+  ctx->buffer_timestamp = buffer_timestamp;
+  ctx->buffer_n_samples_done = 0;
+}
+
+void
 rg_analysis_destroy (RgAnalysisCtx * ctx)
 {
   g_free (ctx);
@@ -537,7 +575,7 @@ rg_analysis_analyze_mono_int16 (RgAnalysisCtx * ctx, gconstpointer data,
   gint32 peak_sample = 0;
   const gint16 *samples = (gint16 *) data;
   guint n_samples = size / sizeof (gint16);
-  gint shift = sizeof (gint16) * 8 - depth;
+  gint shift = 1 << (sizeof (gint16) * 8 - depth);
   gint i;
 
   g_return_if_fail (depth <= (sizeof (gint16) * 8));
@@ -548,7 +586,7 @@ rg_analysis_analyze_mono_int16 (RgAnalysisCtx * ctx, gconstpointer data,
 
     n_samples -= n;
     for (i = 0; i < n; i++) {
-      gint16 old_sample = samples[i] << shift;
+      gint16 old_sample = samples[i] * shift;
 
       peak_sample = MAX (peak_sample, ABS ((gint32) old_sample));
       conv_samples[i] = (gfloat) old_sample;
@@ -569,7 +607,7 @@ rg_analysis_analyze_stereo_int16 (RgAnalysisCtx * ctx, gconstpointer data,
   gint32 peak_sample = 0;
   const gint16 *samples = (gint16 *) data;
   guint n_frames = size / (sizeof (gint16) * 2);
-  gint shift = sizeof (gint16) * 8 - depth;
+  gint shift = 1 << (sizeof (gint16) * 8 - depth);
   gint i;
 
   g_return_if_fail (depth <= (sizeof (gint16) * 8));
@@ -582,11 +620,11 @@ rg_analysis_analyze_stereo_int16 (RgAnalysisCtx * ctx, gconstpointer data,
     for (i = 0; i < n; i++) {
       gint16 old_sample;
 
-      old_sample = samples[2 * i] << shift;
+      old_sample = samples[2 * i] * shift;
       peak_sample = MAX (peak_sample, ABS ((gint32) old_sample));
       conv_samples_l[i] = (gfloat) old_sample;
 
-      old_sample = samples[2 * i + 1] << shift;
+      old_sample = samples[2 * i + 1] * shift;
       peak_sample = MAX (peak_sample, ABS ((gint32) old_sample));
       conv_samples_r[i] = (gfloat) old_sample;
     }
@@ -660,6 +698,7 @@ rg_analysis_analyze (RgAnalysisCtx * ctx, const gfloat * samples_l,
           * ctx->out_r[ctx->window_n_samples_done + i];
 
     ctx->window_n_samples_done += n_samples_current;
+    ctx->buffer_n_samples_done += n_samples_current;
 
     g_return_if_fail (ctx->window_n_samples_done <= ctx->window_n_samples);
 
@@ -669,6 +708,17 @@ rg_analysis_analyze (RgAnalysisCtx * ctx, const gfloat * samples_l,
           ctx->window_n_samples * 0.5 + 1.e-37);
       gint ival = CLAMP ((gint) val, 0,
           (gint) G_N_ELEMENTS (ctx->track.histogram) - 1);
+      /* Compute the per-window gain */
+      const gdouble gain = PINK_REF - (gdouble) ival / STEPS_PER_DB;
+      const GstClockTime timestamp = ctx->buffer_timestamp
+          + gst_util_uint64_scale_int_ceil (GST_SECOND,
+          ctx->buffer_n_samples_done,
+          ctx->sample_rate)
+          - RMS_WINDOW_MSECS * GST_MSECOND;
+
+      ctx->post_message (ctx->analysis, timestamp,
+          RMS_WINDOW_MSECS * GST_MSECOND, -gain);
+
 
       ctx->track.histogram[ival]++;
       ctx->window_square_sum = 0.;
@@ -731,6 +781,7 @@ rg_analysis_track_result (RgAnalysisCtx * ctx, gdouble * gain, gdouble * peak)
   accumulator_clear (&ctx->track);
 
   reset_filters (ctx);
+  reset_silence_detection (ctx);
 
   return result;
 }
@@ -769,4 +820,5 @@ rg_analysis_reset (RgAnalysisCtx * ctx)
   reset_filters (ctx);
   accumulator_clear (&ctx->track);
   accumulator_clear (&ctx->album);
+  reset_silence_detection (ctx);
 }