Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacques Lucke <jacques@blender.org>2021-11-14 16:29:29 +0300
committerJacques Lucke <jacques@blender.org>2021-11-14 16:29:29 +0300
commitb5cb2d50f4f0edf84693f9a2f316f05f5f6429eb (patch)
tree571e0971f70df6549398090b9920cfb2a3ce63d2
parent6ee633575fcc8e8dfad297d0423e420177db6624 (diff)
use multi-functiontemp-sample-sound-node
-rw-r--r--source/blender/nodes/CMakeLists.txt7
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_sample_sound.cc140
2 files changed, 125 insertions, 22 deletions
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;
}
}