noise-gate-filter.c 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. #include <media-io/audio-math.h>
  2. #include <obs-module.h>
  3. #include <math.h>
  4. #define do_log(level, format, ...) \
  5. blog(level, "[noise gate: '%s'] " format, \
  6. obs_source_get_name(ng->context), ##__VA_ARGS__)
  7. #define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__)
  8. #define info(format, ...) do_log(LOG_INFO, format, ##__VA_ARGS__)
  9. #define S_OPEN_THRESHOLD "open_threshold"
  10. #define S_CLOSE_THRESHOLD "close_threshold"
  11. #define S_ATTACK_TIME "attack_time"
  12. #define S_HOLD_TIME "hold_time"
  13. #define S_RELEASE_TIME "release_time"
  14. #define MT_ obs_module_text
  15. #define TEXT_OPEN_THRESHOLD MT_("NoiseGate.OpenThreshold")
  16. #define TEXT_CLOSE_THRESHOLD MT_("NoiseGate.CloseThreshold")
  17. #define TEXT_ATTACK_TIME MT_("NoiseGate.AttackTime")
  18. #define TEXT_HOLD_TIME MT_("NoiseGate.HoldTime")
  19. #define TEXT_RELEASE_TIME MT_("NoiseGate.ReleaseTime")
  20. struct noise_gate_data {
  21. obs_source_t *context;
  22. float sample_rate_i;
  23. size_t channels;
  24. float open_threshold;
  25. float close_threshold;
  26. float decay_rate;
  27. float attack_rate;
  28. float release_rate;
  29. float hold_time;
  30. bool is_open;
  31. float attenuation;
  32. float level;
  33. float held_time;
  34. };
  35. #define VOL_MIN -96.0f
  36. #define VOL_MAX 0.0f
  37. static const char *noise_gate_name(void *unused)
  38. {
  39. UNUSED_PARAMETER(unused);
  40. return obs_module_text("NoiseGate");
  41. }
  42. static void noise_gate_destroy(void *data)
  43. {
  44. struct noise_gate_data *ng = data;
  45. bfree(ng);
  46. }
  47. static inline float ms_to_secf(int ms)
  48. {
  49. return (float)ms / 1000.0f;
  50. }
  51. static void noise_gate_update(void *data, obs_data_t *s)
  52. {
  53. struct noise_gate_data *ng = data;
  54. float open_threshold_db;
  55. float close_threshold_db;
  56. float sample_rate;
  57. int attack_time_ms;
  58. int hold_time_ms;
  59. int release_time_ms;
  60. open_threshold_db = (float)obs_data_get_double(s, S_OPEN_THRESHOLD);
  61. close_threshold_db = (float)obs_data_get_double(s, S_CLOSE_THRESHOLD);
  62. attack_time_ms = (int)obs_data_get_int(s, S_ATTACK_TIME);
  63. hold_time_ms = (int)obs_data_get_int(s, S_HOLD_TIME);
  64. release_time_ms = (int)obs_data_get_int(s, S_RELEASE_TIME);
  65. sample_rate = (float)audio_output_get_sample_rate(obs_get_audio());
  66. ng->sample_rate_i = 1.0f / sample_rate;
  67. ng->channels = audio_output_get_channels(obs_get_audio());
  68. ng->open_threshold = db_to_mul(open_threshold_db);
  69. ng->close_threshold = db_to_mul(close_threshold_db);
  70. ng->attack_rate = 1.0f / (ms_to_secf(attack_time_ms) * sample_rate);
  71. ng->release_rate = 1.0f / (ms_to_secf(release_time_ms) * sample_rate);
  72. const float threshold_diff = ng->open_threshold - ng->close_threshold;
  73. const float min_decay_period = (1.0f / 75.0f) * sample_rate;
  74. ng->decay_rate = threshold_diff / min_decay_period;
  75. ng->hold_time = ms_to_secf(hold_time_ms);
  76. ng->is_open = false;
  77. ng->attenuation = 0.0f;
  78. ng->level = 0.0f;
  79. ng->held_time = 0.0f;
  80. }
  81. static void *noise_gate_create(obs_data_t *settings, obs_source_t *filter)
  82. {
  83. struct noise_gate_data *ng = bzalloc(sizeof(*ng));
  84. ng->context = filter;
  85. noise_gate_update(ng, settings);
  86. return ng;
  87. }
  88. static struct obs_audio_data *noise_gate_filter_audio(void *data,
  89. struct obs_audio_data *audio)
  90. {
  91. struct noise_gate_data *ng = data;
  92. float *adata[2] = {(float*)audio->data[0], (float*)audio->data[1]};
  93. const float close_threshold = ng->close_threshold;
  94. const float open_threshold = ng->open_threshold;
  95. const float sample_rate_i = ng->sample_rate_i;
  96. const float release_rate = ng->release_rate;
  97. const float attack_rate = ng->attack_rate;
  98. const float decay_rate = ng->decay_rate;
  99. const float hold_time = ng->hold_time;
  100. const size_t channels = ng->channels;
  101. for (size_t i = 0; i < audio->frames; i++) {
  102. float cur_level = (channels == 2)
  103. ? fmaxf(fabsf(adata[0][i]), fabsf(adata[1][i]))
  104. : fabsf(adata[0][i]);
  105. if (cur_level > open_threshold && !ng->is_open) {
  106. ng->is_open = true;
  107. }
  108. if (ng->level < close_threshold && ng->is_open) {
  109. ng->held_time = 0.0f;
  110. ng->is_open = false;
  111. }
  112. ng->level = fmaxf(ng->level, cur_level) - decay_rate;
  113. if (ng->is_open) {
  114. ng->attenuation = fminf(1.0f,
  115. ng->attenuation + attack_rate);
  116. } else {
  117. ng->held_time += sample_rate_i;
  118. if (ng->held_time > hold_time) {
  119. ng->attenuation = fmaxf(0.0f,
  120. ng->attenuation - release_rate);
  121. }
  122. }
  123. for (size_t c = 0; c < channels; c++)
  124. adata[c][i] *= ng->attenuation;
  125. }
  126. return audio;
  127. }
  128. static void noise_gate_defaults(obs_data_t *s)
  129. {
  130. obs_data_set_default_double(s, S_OPEN_THRESHOLD, -26.0f);
  131. obs_data_set_default_double(s, S_CLOSE_THRESHOLD, -32.0f);
  132. obs_data_set_default_int (s, S_ATTACK_TIME, 25);
  133. obs_data_set_default_int (s, S_HOLD_TIME, 200);
  134. obs_data_set_default_int (s, S_RELEASE_TIME, 150);
  135. }
  136. static obs_properties_t *noise_gate_properties(void *data)
  137. {
  138. obs_properties_t *ppts = obs_properties_create();
  139. obs_properties_add_float_slider(ppts, S_CLOSE_THRESHOLD,
  140. TEXT_CLOSE_THRESHOLD, VOL_MIN, VOL_MAX, 1.0f);
  141. obs_properties_add_float_slider(ppts, S_OPEN_THRESHOLD,
  142. TEXT_OPEN_THRESHOLD, VOL_MIN, VOL_MAX, 1.0f);
  143. obs_properties_add_int(ppts, S_ATTACK_TIME, TEXT_ATTACK_TIME,
  144. 0, 10000, 1);
  145. obs_properties_add_int(ppts, S_HOLD_TIME, TEXT_HOLD_TIME,
  146. 0, 10000, 1);
  147. obs_properties_add_int(ppts, S_RELEASE_TIME, TEXT_RELEASE_TIME,
  148. 0, 10000, 1);
  149. UNUSED_PARAMETER(data);
  150. return ppts;
  151. }
  152. struct obs_source_info noise_gate_filter = {
  153. .id = "noise_gate_filter",
  154. .type = OBS_SOURCE_TYPE_FILTER,
  155. .output_flags = OBS_SOURCE_AUDIO,
  156. .get_name = noise_gate_name,
  157. .create = noise_gate_create,
  158. .destroy = noise_gate_destroy,
  159. .update = noise_gate_update,
  160. .filter_audio = noise_gate_filter_audio,
  161. .get_defaults = noise_gate_defaults,
  162. .get_properties = noise_gate_properties,
  163. };