+static gboolean
+volume_change_timeout(schanid_t chan)
+{
+ GTimeVal now;
+ g_get_current_time(&now);
+
+ if(now.tv_sec >= chan->target_time_sec && now.tv_usec >= chan->target_time_usec) {
+ /* We're done - make sure the volume is at the requested level */
+ g_object_set(chan->filter, "volume", chan->target_volume, NULL);
+
+ if(chan->volume_notify) {
+ /* Send a notification */
+ }
+ return FALSE;
+ }
+
+ /* Calculate the appropriate step every time - a busy system may delay or
+ * drop timer ticks */
+ double time_left_msec = (chan->target_time_sec - now.tv_sec) * 1000.0
+ + (chan->target_time_usec - now.tv_usec) / 1000.0;
+ double steps_left = time_left_msec / VOLUME_TIMER_RESOLUTION;
+ double current_volume;
+ g_object_get(chan->filter, "volume", ¤t_volume, NULL);
+ double volume_step = (chan->target_volume - current_volume) / steps_left;
+
+ g_printerr("Time left: %.2f ms\nVolume difference: %.2f\nVolume step: %.4f\n",
+ time_left_msec, chan->target_volume - current_volume, volume_step);
+
+ g_object_set(chan->filter, "volume", current_volume + volume_step, NULL);
+
+ return TRUE;
+}
+