Files
-
blinds / hardware_design / pcb / blinds_v60.brd
-
blinds / hardware_design / pcb / blinds_v60.sch
-
braids / hardware_design / pcb / braids_v50.brd
-
braids / hardware_design / pcb / braids_v50.sch
-
branches / hardware_design / pcb / branches_v40.brd
-
branches / hardware_design / pcb / branches_v40.sch
-
clouds / hardware_design / pcb / clouds_v30.brd
-
clouds / hardware_design / pcb / clouds_v30.sch
-
ears / hardware_design / panel / ears_panel_v30.brd
-
ears / hardware_design / panel / ears_panel_v30.sch
-
ears / hardware_design / pcb / ears_v40.brd
-
ears / hardware_design / pcb / ears_v40.sch
-
edges / hardware_design / pcb / edges_expander_v01.brd
-
edges / hardware_design / pcb / edges_expander_v01.sch
-
edges / hardware_design / pcb / edges_v20.brd
-
edges / hardware_design / pcb / edges_v20.sch
-
elements / hardware_design / pcb / elements_v02.brd
-
elements / hardware_design / pcb / elements_v02.sch
-
frames / hardware_design / pcb / frames_v03.brd
-
frames / hardware_design / pcb / frames_v03.sch
-
grids / hardware_design / pcb / grids_v02.brd
-
grids / hardware_design / pcb / grids_v02.sch
-
kinks / hardware_design / pcb / kinks_v41.brd
-
kinks / hardware_design / pcb / kinks_v41.sch
-
links / hardware_design / pcb / links_v40.brd
-
links / hardware_design / pcb / links_v40.sch
-
marbles / hardware_design / pcb / marbles_v70.brd
-
marbles / hardware_design / pcb / marbles_v70.sch
-
peaks / hardware_design / pcb / peaks_v30.brd
-
peaks / hardware_design / pcb / peaks_v30.sch
-
plaits / hardware_design / pcb / plaits_v50.brd
-
plaits / hardware_design / pcb / plaits_v50.sch
-
rings / hardware_design / pcb / rings_v30.brd
-
rings / hardware_design / pcb / rings_v30.sch
-
ripples / hardware_design / pcb / ripples_v40.brd
-
ripples / hardware_design / pcb / ripples_v40.sch
-
shades / hardware_design / pcb / shades_v30.brd
-
shades / hardware_design / pcb / shades_v30.sch
-
shelves / hardware_design / pcb / shelves_expander_v10.brd
-
shelves / hardware_design / pcb / shelves_expander_v10.sch
-
shelves / hardware_design / pcb / shelves_v05.brd
-
shelves / hardware_design / pcb / shelves_v05.sch
-
stages / hardware_design / pcb / stages_v70.brd
-
stages / hardware_design / pcb / stages_v70.sch
-
streams / hardware_design / pcb / streams_v02_bargraph.brd
-
streams / hardware_design / pcb / streams_v02_bargraph.sch
-
streams / hardware_design / pcb / streams_v05.brd
-
streams / hardware_design / pcb / streams_v05.sch
-
tides / hardware_design / pcb / tides_v40.brd
-
tides / hardware_design / pcb / tides_v40.sch
-
veils / hardware_design / pcb / veils_v40.brd
-
veils / hardware_design / pcb / veils_v40.sch
-
volts / hardware_design / pcb / volts_v01.brd
-
volts / hardware_design / pcb / volts_v01.sch
-
warps / hardware_design / pcb / warps_v30.brd
-
warps / hardware_design / pcb / warps_v30.sch
-
yarns / hardware_design / pcb / yarns_v03.brd
-
yarns / hardware_design / pcb / yarns_v03.sch
Last update 6 years 4 months
by
Olivier Gillet
granular_sample_player.h// Copyright 2014 Olivier Gillet. // // Author: Olivier Gillet (ol.gillet@gmail.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // // See http://creativecommons.org/licenses/MIT/ for more information. // // ----------------------------------------------------------------------------- // // Granular playback of audio stored in a buffer. #ifndef CLOUDS_DSP_GRANULAR_SAMPLE_PLAYER_H_ #define CLOUDS_DSP_GRANULAR_SAMPLE_PLAYER_H_ #include "stmlib/stmlib.h" #include <algorithm> #include "stmlib/dsp/atan.h" #include "stmlib/dsp/units.h" #include "stmlib/utils/random.h" #include "clouds/dsp/audio_buffer.h" #include "clouds/dsp/frame.h" #include "clouds/dsp/grain.h" #include "clouds/dsp/parameters.h" #include "clouds/resources.h" namespace clouds { const int32_t kMaxNumGrains = 64; using namespace stmlib; class GranularSamplePlayer { public: GranularSamplePlayer() { } ~GranularSamplePlayer() { } void Init(int32_t num_channels, int32_t max_num_grains) { max_num_grains_ = max_num_grains; num_midfi_grains_ = 3 * max_num_grains / 4; gain_normalization_ = 1.0f; for (int32_t i = 0; i < kMaxNumGrains; ++i) { grains_[i].Init(); } num_grains_ = 0.0f; num_channels_ = num_channels; grain_size_hint_ = 1024.0f; } template<Resolution resolution> void Play( const AudioBuffer<resolution>* buffer, const Parameters& parameters, float* out, size_t size) { float overlap = parameters.granular.overlap; overlap = overlap * overlap * overlap; float target_num_grains = max_num_grains_ * overlap; float p = target_num_grains / static_cast<float>(grain_size_hint_); float space_between_grains = grain_size_hint_ / target_num_grains; if (parameters.granular.use_deterministic_seed) { p = -1.0f; } else { grain_rate_phasor_ = -1000.0f; } // Build a list of available grains. int32_t num_available_grains = FillAvailableGrainsList(); // Try to schedule new grains. bool seed_trigger = parameters.trigger; for (size_t t = 0; t < size; ++t) { grain_rate_phasor_ += 1.0f; bool seed_probabilistic = Random::GetFloat() < p && target_num_grains > num_grains_; bool seed_deterministic = grain_rate_phasor_ >= space_between_grains; bool seed = seed_probabilistic || seed_deterministic || seed_trigger; if (num_available_grains && seed) { --num_available_grains; int32_t index = available_grains_[num_available_grains]; GrainQuality quality; if (num_available_grains < num_midfi_grains_) { quality = GRAIN_QUALITY_MEDIUM; } else { quality = GRAIN_QUALITY_HIGH; } Grain* g = &grains_[index]; ScheduleGrain( g, parameters, t, buffer->size(), buffer->head() - size + t, quality); grain_rate_phasor_ = 0.0f; seed_trigger = false; } } // Overlap grains. std::fill(&out[0], &out[size * 2], 0.0f); float* e = envelope_buffer_; for (int32_t i = 0; i < max_num_grains_; ++i) { Grain* g = &grains_[i]; if (g->recommended_quality() == GRAIN_QUALITY_HIGH) { if (num_channels_ == 1) { g->OverlapAdd<1, GRAIN_QUALITY_HIGH>(buffer, out, e, size); } else { g->OverlapAdd<2, GRAIN_QUALITY_HIGH>(buffer, out, e, size); } } else if (g->recommended_quality() == GRAIN_QUALITY_MEDIUM) { if (num_channels_ == 1) { g->OverlapAdd<1, GRAIN_QUALITY_MEDIUM>(buffer, out, e, size); } else { g->OverlapAdd<2, GRAIN_QUALITY_MEDIUM>(buffer, out, e, size); } } else { if (num_channels_ == 1) { g->OverlapAdd<1, GRAIN_QUALITY_LOW>(buffer, out, e, size); } else { g->OverlapAdd<2, GRAIN_QUALITY_LOW>(buffer, out, e, size); } } } // Compute normalization factor. int32_t active_grains = max_num_grains_ - num_available_grains; SLOPE(num_grains_, static_cast<float>(active_grains), 0.9f, 0.2f); float gain_normalization = num_grains_ > 2.0f ? fast_rsqrt_carmack(num_grains_ - 1.0f) : 1.0f; float window_gain = 1.0f + 2.0f * parameters.granular.window_shape; CONSTRAIN(window_gain, 1.0f, 2.0f); gain_normalization *= Crossfade( 1.0f, window_gain, parameters.granular.overlap); // Apply gain normalization. for (size_t t = 0; t < size; ++t) { ONE_POLE(gain_normalization_, gain_normalization, 0.01f) *out++ *= gain_normalization_; *out++ *= gain_normalization_; } } private: int32_t FillAvailableGrainsList() { int32_t num_available_grains = 0; for (int32_t i = 0; i < max_num_grains_; ++i) { if (!grains_[i].active()) { available_grains_[num_available_grains] = i; ++num_available_grains; } } return num_available_grains; } void ScheduleGrain( Grain* grain, const Parameters& parameters, int32_t pre_delay, int32_t buffer_size, int32_t buffer_head, GrainQuality quality) { float position = parameters.position; float pitch = parameters.pitch; float window_shape = parameters.granular.window_shape; float grain_size = Interpolate(lut_grain_size, parameters.size, 256.0f); float pitch_ratio = SemitonesToRatio(pitch); float inv_pitch_ratio = SemitonesToRatio(-pitch); float pan = 0.5f + parameters.stereo_spread * (Random::GetFloat() - 0.5f); float gain_l, gain_r; if (num_channels_ == 1) { gain_l = Interpolate(lut_sin, pan, 256.0f); gain_r = Interpolate(lut_sin + 256, pan, 256.0f); } else { if (pan < 0.5f) { gain_l = 1.0f; gain_r = 2.0f * pan; } else { gain_r = 1.0f; gain_l = 2.0f * (1.0f - pan); } } if (pitch_ratio > 1.0f) { // The grain's play-head moves faster than the buffer record-head. // we must make sure that the grain will not consume too much data. // In some situations, it might be necessary to reduce the size of the // grain. grain_size = std::min(grain_size, buffer_size * 0.25f * inv_pitch_ratio); } float eaten_by_play_head = grain_size * pitch_ratio; float eaten_by_recording_head = grain_size; float available = 0.0; available += static_cast<float>(buffer_size); available -= eaten_by_play_head; available -= eaten_by_recording_head; int32_t size = static_cast<int32_t>(grain_size) & ~1; int32_t start = buffer_head - static_cast<int32_t>( position * available + eaten_by_play_head); grain->Start( pre_delay, buffer_size, start, size, static_cast<uint32_t>(pitch_ratio * 65536.0f), window_shape, gain_l, gain_r, quality); ONE_POLE(grain_size_hint_, grain_size, 0.1f); } int32_t max_num_grains_; int32_t num_midfi_grains_; int32_t num_channels_; float num_grains_; float gain_normalization_; float grain_size_hint_; float grain_rate_phasor_; Grain grains_[kMaxNumGrains]; int32_t available_grains_[kMaxNumGrains]; float envelope_buffer_[kMaxBlockSize]; DISALLOW_COPY_AND_ASSIGN(GranularSamplePlayer); }; } // namespace clouds #endif // CLOUDS_DSP_GRANULAR_SAMPLE_PLAYER_H_