+
+ do {
+ gint v = g_atomic_int_get (&_no_disk_output);
+ if (v > 0) {
+ if (g_atomic_int_compare_and_exchange (&_no_disk_output, v, v - 1)) {
+ break;
+ }
+ } else {
+ break;
+ }
+ } while (true);
+}
+
+DiskReader::DeclickAmp::DeclickAmp (samplecnt_t sample_rate)
+{
+ _a = 4550.f / (gain_t)sample_rate;
+ _l = -log1p (_a);
+ _g = 0;
+}
+
+void
+DiskReader::DeclickAmp::apply_gain (AudioBuffer& buf, samplecnt_t n_samples, const float target)
+{
+ if (n_samples == 0) {
+ return;
+ }
+ float g = _g;
+
+ if (g == target) {
+ Amp::apply_simple_gain (buf, n_samples, target, 0);
+ return;
+ }
+
+ const float a = _a;
+ Sample* const buffer = buf.data ();
+
+ const int max_nproc = 16;
+ uint32_t remain = n_samples;
+ uint32_t offset = 0;
+ while (remain > 0) {
+ uint32_t n_proc = remain > max_nproc ? max_nproc : remain;
+ for (uint32_t i = 0; i < n_proc; ++i) {
+ buffer[offset + i] *= g;
+ }
+#if 1
+ g += a * (target - g);
+#else /* accurate exponential fade */
+ if (n_proc == max_nproc) {
+ g += a * (target - g);
+ } else {
+ g = target - (target - g) * expf (_l * n_proc / max_nproc);
+ }
+#endif
+ remain -= n_proc;
+ offset += n_proc;
+ }
+
+ if (fabsf (g - target) < /* GAIN_COEFF_DELTA */ 1e-5) {
+ _g = target;
+ } else {
+ _g = g;
+ }