[Bf-blender-cvs] [b5cb2d50f4f] temp-sample-sound-node: use multi-function

Jacques Lucke noreply at git.blender.org
Sun Nov 14 14:29:40 CET 2021


Commit: b5cb2d50f4f0edf84693f9a2f316f05f5f6429eb
Author: Jacques Lucke
Date:   Sun Nov 14 14:29:29 2021 +0100
Branches: temp-sample-sound-node
https://developer.blender.org/rBb5cb2d50f4f0edf84693f9a2f316f05f5f6429eb

use multi-function

===================================================================

M	source/blender/nodes/CMakeLists.txt
M	source/blender/nodes/geometry/nodes/node_geo_sample_sound.cc

===================================================================

diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index 2e7a4704c5a..6ac1d567da2 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -561,4 +561,11 @@ if(WITH_AUDASPACE)
   )
 endif()
 
+if(WITH_FFTW3)
+list(APPEND INC_SYS
+    ${FFTW3_INCLUDE_DIRS}
+  )
+  add_definitions(-DWITH_FFTW3)
+endif()
+
 blender_add_lib(bf_nodes "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/nodes/geometry/nodes/node_geo_sample_sound.cc b/source/blender/nodes/geometry/nodes/node_geo_sample_sound.cc
index 2de55e4769b..8c24336e325 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_sample_sound.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_sample_sound.cc
@@ -14,9 +14,13 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+#include <fftw3.h>
+
 #include "UI_interface.h"
 #include "UI_resources.h"
 
+#include "BLI_double2.hh"
+
 #include "DNA_sound_types.h"
 
 #include "AUD_Sound.h"
@@ -31,9 +35,10 @@ namespace blender::nodes {
 
 static void geo_node_sample_sound_declare(NodeDeclarationBuilder &b)
 {
-  b.add_input<decl::Float>(N_("Frame")).supports_field();
-  b.add_input<decl::Float>(N_("Min Frequency")).supports_field().default_value(0.0f);
-  b.add_input<decl::Float>(N_("Max Frequency")).supports_field().default_value(20000.0f);
+  b.add_input<decl::Float>(N_("Time")).supports_field();
+  b.add_input<decl::Float>(N_("Min Frequency")).supports_field().default_value(0.0f).min(0.0f);
+  b.add_input<decl::Float>(N_("Max Frequency")).supports_field().default_value(20000.0f).min(0.0f);
+  b.add_input<decl::Int>(N_("Channel")).min(0);
   b.add_output<decl::Float>(N_("Volume")).dependent_field();
 }
 
@@ -42,11 +47,106 @@ static void geo_node_sample_sound_layout(uiLayout *layout, bContext *UNUSED(C),
   uiItemR(layout, ptr, "sound", 0, "", ICON_NONE);
 }
 
+struct SoundData {
+  Span<float> samples;
+  float sample_rate;
+  int channels;
+};
+
+class SampleSoundFunction : public fn::MultiFunction {
+ private:
+  SoundData sound_data_;
+
+ public:
+  SampleSoundFunction(SoundData sound_data) : sound_data_(sound_data)
+  {
+    static fn::MFSignature signature = create_signature();
+    this->set_signature(&signature);
+  }
+
+  static fn::MFSignature create_signature()
+  {
+    fn::MFSignatureBuilder signature{"Sample Sound"};
+    signature.single_input<float>("Time");
+    signature.single_input<float>("Min Frequency");
+    signature.single_input<float>("Max Frequency");
+    signature.single_input<int>("Channel");
+    signature.single_output<float>("Volume");
+    return signature.build();
+  }
+
+  void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+  {
+    const VArray<float> &times = params.readonly_single_input<float>(0, "Time");
+    const VArray<float> &min_frequencies = params.readonly_single_input<float>(1,
+                                                                               "Min Frequencies");
+    const VArray<float> &max_frequencies = params.readonly_single_input<float>(2,
+                                                                               "Max Frequencies");
+    const VArray<int> &channels = params.readonly_single_input<int>(3, "Channel");
+    MutableSpan<float> r_volumes = params.uninitialized_single_output<float>(4, "Volume");
+
+    /* TODO: Remove this limitation. */
+    BLI_assert(times.is_single());
+    BLI_assert(channels.is_single());
+
+    if (mask.is_empty()) {
+      return;
+    }
+
+    const float time = times.get_internal_single();
+    const int channel = channels.get_internal_single();
+
+    const int desired_slice_size = 4000;
+    int end_sample = time * sound_data_.sample_rate;
+    int start_sample = end_sample - desired_slice_size;
+    CLAMP(start_sample, 0, sound_data_.samples.size());
+    CLAMP(end_sample, 0, sound_data_.samples.size());
+    const int slice_size = end_sample - start_sample;
+
+    if (slice_size == 0) {
+      r_volumes.fill_indices(mask, 0.0f);
+      return;
+    }
+
+    Array<double> raw_samples(slice_size);
+    Array<double2> frequencies(slice_size / 2 + 1);
+
+    fftw_plan plan = fftw_plan_dft_r2c_1d(
+        slice_size, raw_samples.data(), (fftw_complex *)frequencies.data(), 0);
+
+    for (const int i : IndexRange(slice_size)) {
+      raw_samples[i] = sound_data_.samples[(start_sample + i) * sound_data_.channels + channel];
+    }
+
+    fftw_execute(plan);
+    fftw_destroy_plan(plan);
+
+    const float band_per_index = sound_data_.sample_rate / slice_size;
+    auto frequency_to_index = [&](const float frequency) -> int {
+      return frequency / band_per_index;
+    };
+
+    for (const int i : mask) {
+      const float min_frequency = min_frequencies[i];
+      const float max_frequency = max_frequencies[i];
+      int min_index = frequency_to_index(min_frequency);
+      int max_index = frequency_to_index(max_frequency);
+      min_index = std::clamp<int>(min_index, 0, frequencies.size());
+      max_index = std::clamp<int>(max_index, min_index, frequencies.size());
+
+      float sum = 0.0f;
+      for (int frequency_index = min_index; frequency_index < max_index; frequency_index++) {
+        sum += fabsf(frequencies[frequency_index].x);
+      }
+      const float average = (max_index > min_index) ? sum / (max_index - min_index) : 0.0f;
+      r_volumes[i] = average;
+    }
+  }
+};
+
 static void geo_node_sample_sound_exec(GeoNodeExecParams params)
 {
   bSound *sound_id = (bSound *)params.node().id;
-  Scene *scene = DEG_get_input_scene(params.depsgraph());
-  const float fps = FPS;
   if (sound_id != nullptr) {
     AUD_Sound *sound = (AUD_Sound *)sound_id->handle;
     if (sound != NULL) {
@@ -55,23 +155,19 @@ static void geo_node_sample_sound_exec(GeoNodeExecParams params)
         sound_id->samples = AUD_Sound_data(sound, &sound_id->tot_samples, &specs);
       }
       AUD_Specs specs = AUD_Sound_getSpecs(sound);
-      const float *samples = sound_id->samples;
-      const int tot_samples = sound_id->tot_samples;
-      const float sample_rate = specs.rate;
-
-      const float frame = params.get_input<float>("Frame");
-      const float time = (frame - 1) / fps;
-      const int end_sample = std::max(
-          0, std::min<int>(time * sample_rate * specs.channels, tot_samples));
-      const int sample_duration = 1000;
-      const int start_sample = std::max(0,
-                                        std::min<int>(end_sample - sample_duration, tot_samples));
-      float sum = 0.0f;
-      for (int i = start_sample; i < end_sample; i++) {
-        sum += fabsf(samples[i]);
-      }
-      const float average = sum / (end_sample - start_sample);
-      params.set_output("Volume", average);
+      SoundData sound_data;
+      sound_data.channels = specs.channels;
+      sound_data.sample_rate = specs.rate;
+      sound_data.samples = Span<float>(sound_id->samples, sound_id->tot_samples);
+      auto sample_fn = std::make_shared<SampleSoundFunction>(sound_data);
+      auto sample_node = std::make_shared<FieldOperation>(
+          sample_fn,
+          Vector<GField>{params.extract_input<Field<float>>("Time"),
+                         params.extract_input<Field<float>>("Min Frequency"),
+                         params.extract_input<Field<float>>("Max Frequency"),
+                         params.extract_input<Field<int>>("Channel")});
+      Field<float> output_field{std::move(sample_node), 0};
+      params.set_output("Volume", std::move(output_field));
       return;
     }
   }



More information about the Bf-blender-cvs mailing list