├── clouds ├── Hardware │ ├── CLOUDY_EXP.dip │ ├── CLOUDY_LOGIC.dip │ ├── CLOUDY_POWER.dip │ └── CLOUDY_INTERFACE.dip ├── test │ ├── makefile │ └── clouds_test.cc ├── drivers │ ├── system.cc │ ├── system.h │ ├── debug_port.h │ ├── version.h │ ├── gate_input.cc │ ├── adc.h │ ├── gate_input.h │ ├── debug_pin.h │ ├── debug_port.cc │ ├── switches.h │ ├── switches.cc │ ├── codec.h │ ├── leds.h │ ├── leds.cc │ └── adc.cc ├── dsp │ ├── frame.h │ ├── parameters.h │ ├── pvoc │ │ ├── phase_vocoder.h │ │ ├── stft.h │ │ ├── frame_transformation.h │ │ ├── phase_vocoder.cc │ │ └── stft.cc │ ├── random_oscillator.h │ ├── mu_law.h │ ├── correlator.h │ ├── correlator.cc │ ├── sample_rate_converter.h │ ├── mu_law.cc │ ├── fx │ │ ├── diffuser.h │ │ ├── pitch_shifter.h │ │ ├── reverb.h │ │ └── oliverb.h │ ├── window.h │ ├── grain.h │ ├── granular_processor.h │ ├── looping_sample_player.h │ ├── granular_sample_player.h │ └── audio_buffer.h ├── bootloader │ ├── makefile │ └── bootloader.cc ├── makefile ├── meter.h ├── resources │ ├── src_filters.py │ ├── resources.py │ └── lookup_tables.py ├── resources.h ├── settings.cc ├── settings.h ├── ui.h ├── clouds.cc ├── cv_scaler.h └── cv_scaler.cc └── README.md /clouds/Hardware/CLOUDY_EXP.dip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomkatpedals/cloudy/HEAD/clouds/Hardware/CLOUDY_EXP.dip -------------------------------------------------------------------------------- /clouds/Hardware/CLOUDY_LOGIC.dip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomkatpedals/cloudy/HEAD/clouds/Hardware/CLOUDY_LOGIC.dip -------------------------------------------------------------------------------- /clouds/Hardware/CLOUDY_POWER.dip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomkatpedals/cloudy/HEAD/clouds/Hardware/CLOUDY_POWER.dip -------------------------------------------------------------------------------- /clouds/Hardware/CLOUDY_INTERFACE.dip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomkatpedals/cloudy/HEAD/clouds/Hardware/CLOUDY_INTERFACE.dip -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cloudy 2 | The files shared here work with the Mutable Dev Environment which is documented here - https://github.com/pichenettes/mutable-dev-environment. 3 | 4 | # license 5 | Code: MIT License. Hardware: CC-BY-SA-4.0 6 | -------------------------------------------------------------------------------- /clouds/test/makefile: -------------------------------------------------------------------------------- 1 | PACKAGES = clouds/dsp clouds/dsp/pvoc clouds/test stmlib/utils stmlib/dsp clouds 2 | 3 | VPATH = $(PACKAGES) 4 | 5 | TARGET = clouds_test 6 | BUILD_ROOT = build/ 7 | BUILD_DIR = $(BUILD_ROOT)$(TARGET)/ 8 | CC_FILES = atan.cc \ 9 | clouds_test.cc \ 10 | correlator.cc \ 11 | granular_processor.cc \ 12 | mu_law.cc \ 13 | random.cc \ 14 | resources.cc \ 15 | frame_transformation.cc \ 16 | phase_vocoder.cc \ 17 | stft.cc \ 18 | units.cc 19 | OBJ_FILES = $(CC_FILES:.cc=.o) 20 | OBJS = $(patsubst %,$(BUILD_DIR)%,$(OBJ_FILES)) $(STARTUP_OBJ) 21 | DEPS = $(OBJS:.o=.d) 22 | DEP_FILE = $(BUILD_DIR)depends.mk 23 | 24 | all: clouds_test 25 | 26 | $(BUILD_DIR): 27 | mkdir -p $(BUILD_DIR) 28 | 29 | $(BUILD_DIR)%.o: %.cc 30 | g++ -c -DTEST -g -Wall -Werror -I. $< -o $@ 31 | 32 | $(BUILD_DIR)%.d: %.cc 33 | g++ -MM -DTEST -I. $< -MF $@ -MT $(@:.d=.o) 34 | 35 | clouds_test: $(OBJS) 36 | g++ -o $(TARGET) $(OBJS) 37 | 38 | depends: $(DEPS) 39 | cat $(DEPS) > $(DEP_FILE) 40 | 41 | $(DEP_FILE): $(BUILD_DIR) $(DEPS) 42 | cat $(DEPS) > $(DEP_FILE) 43 | 44 | include $(DEP_FILE) 45 | -------------------------------------------------------------------------------- /clouds/drivers/system.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // System level initialization. 28 | 29 | #include "clouds/drivers/system.h" 30 | 31 | namespace clouds { 32 | 33 | void System::Init(bool application) { 34 | if (application) { 35 | NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x8000); 36 | } 37 | } 38 | 39 | void System::StartTimers() { 40 | SysTick_Config(F_CPU / 1000); 41 | } 42 | 43 | } // namespace clouds 44 | -------------------------------------------------------------------------------- /clouds/dsp/frame.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Audio processing frames. 28 | 29 | #ifndef CLOUDS_DSP_FRAME_H_ 30 | #define CLOUDS_DSP_FRAME_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | namespace clouds { 35 | 36 | const int32_t kMaxNumChannels = 2; 37 | const size_t kMaxBlockSize = 32; 38 | 39 | typedef struct { short l; short r; } ShortFrame; 40 | typedef struct { float l; float r; } FloatFrame; 41 | 42 | } // namespace clouds 43 | 44 | #endif // CLOUDS_DSP_FRAME_H_ 45 | -------------------------------------------------------------------------------- /clouds/bootloader/makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2014 Olivier Gillet. 2 | # 3 | # Author: Olivier Gillet (ol.gillet@gmail.com) 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | # THE SOFTWARE. 22 | # 23 | # See http://creativecommons.org/licenses/MIT/ for more information. 24 | 25 | # System specifications 26 | F_CRYSTAL = 8000000L 27 | F_CPU = 168000000L 28 | SYSCLOCK = SYSCLK_FREQ_168MHz 29 | FAMILY = f4xx 30 | # USB = enabled 31 | 32 | # Preferred upload command 33 | UPLOAD_COMMAND = upload_jtag_erase_first 34 | 35 | # Packages to build 36 | TARGET = clouds_bootloader 37 | PACKAGES = clouds/bootloader \ 38 | clouds/drivers \ 39 | stm_audio_bootloader/qpsk \ 40 | stmlib/dsp \ 41 | stmlib/utils \ 42 | stmlib/system 43 | RESOURCES = clouds/resources 44 | 45 | include stmlib/makefile.inc 46 | -------------------------------------------------------------------------------- /clouds/drivers/system.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // System-level initialization. 28 | 29 | #ifndef CLOUDS_DRIVERS_SYSTEM_H_ 30 | #define CLOUDS_DRIVERS_SYSTEM_H_ 31 | 32 | #include 33 | 34 | #include "stmlib/stmlib.h" 35 | 36 | namespace clouds { 37 | 38 | class System { 39 | public: 40 | System() { } 41 | ~System() { } 42 | 43 | void Init(bool application); 44 | void StartTimers(); 45 | 46 | private: 47 | DISALLOW_COPY_AND_ASSIGN(System); 48 | }; 49 | 50 | } // namespace clouds 51 | 52 | #endif // CLOUDS_DRIVERS_SYSTEM_H_ 53 | -------------------------------------------------------------------------------- /clouds/makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2014 Olivier Gillet. 2 | # 3 | # Author: Olivier Gillet (ol.gillet@gmail.com) 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | # THE SOFTWARE. 22 | # 23 | # See http://creativecommons.org/licenses/MIT/ for more information. 24 | 25 | # System specifications 26 | F_CRYSTAL = 8000000L 27 | F_CPU = 168000000L 28 | SYSCLOCK = SYSCLK_FREQ_168MHz 29 | FAMILY = f4xx 30 | # USB = enabled 31 | 32 | APPLICATION_LARGE = TRUE 33 | BOOTLOADER = clouds_bootloader 34 | 35 | # Preferred upload command 36 | UPLOAD_COMMAND = upload_combo_jtag_erase_first 37 | 38 | # Packages to build 39 | TARGET = clouds 40 | PACKAGES = clouds \ 41 | clouds/drivers \ 42 | clouds/dsp \ 43 | clouds/dsp/pvoc \ 44 | stmlib/dsp \ 45 | stmlib/utils \ 46 | stmlib/system \ 47 | stmlib/third_party/STM/CMSIS/DSP_Lib/CommonTables \ 48 | stmlib/third_party/STM/CMSIS/DSP_Lib/TransformFunctions 49 | RESOURCES = clouds/resources 50 | 51 | include stmlib/makefile.inc 52 | 53 | # Rule for building the firmware update file 54 | wav: $(TARGET_BIN) 55 | python stm_audio_bootloader/qpsk/encoder.py \ 56 | -t stm32f4 -s 48000 -b 12000 -c 6000 -p 256 \ 57 | $(TARGET_BIN) 58 | -------------------------------------------------------------------------------- /clouds/drivers/debug_port.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // UART driver for conversing with the factory testing program. 28 | 29 | #ifndef CLOUDS_DRIVERS_DEBUG_PORT_H_ 30 | #define CLOUDS_DRIVERS_DEBUG_PORT_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | #include 35 | 36 | namespace clouds { 37 | 38 | class DebugPort { 39 | public: 40 | 41 | DebugPort() { } 42 | ~DebugPort() { } 43 | 44 | void Init(); 45 | 46 | bool writable() { 47 | return USART1->SR & USART_FLAG_TXE; 48 | } 49 | 50 | bool readable() { 51 | return USART1->SR & USART_FLAG_RXNE; 52 | } 53 | 54 | void Write(uint8_t byte) { 55 | USART1->DR = byte; 56 | } 57 | 58 | uint8_t Read() { 59 | return USART1->DR; 60 | } 61 | 62 | private: 63 | DISALLOW_COPY_AND_ASSIGN(DebugPort); 64 | }; 65 | 66 | } // namespace clouds 67 | 68 | #endif // CLOUDS_DRIVERS_DEBUG_PORT_H_ 69 | -------------------------------------------------------------------------------- /clouds/dsp/parameters.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Parameters of the granular effect. 28 | 29 | #ifndef CLOUDS_DSP_PARAMETERS_H_ 30 | #define CLOUDS_DSP_PARAMETERS_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | namespace clouds { 35 | 36 | struct Parameters { 37 | float position; 38 | float size; 39 | float pitch; 40 | float density; 41 | float texture; 42 | float dry_wet; 43 | float stereo_spread; 44 | float feedback; 45 | float reverb; 46 | 47 | bool freeze; 48 | bool trigger; 49 | bool gate; 50 | 51 | struct Granular { 52 | float overlap; 53 | float window_shape; 54 | float stereo_spread; 55 | bool use_deterministic_seed; 56 | bool reverse; 57 | } granular; 58 | 59 | struct Spectral { 60 | float quantization; 61 | float refresh_rate; 62 | float phase_randomization; 63 | float warp; 64 | } spectral; 65 | }; 66 | 67 | } // namespace clouds 68 | 69 | #endif // CLOUDS_DSP_PARAMETERS_H_ 70 | -------------------------------------------------------------------------------- /clouds/drivers/version.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Driver for reading the hardware revision pin. 28 | 29 | #ifndef CLOUDS_DRIVERS_VERSION_H_ 30 | #define CLOUDS_DRIVERS_VERSION_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | #include 35 | 36 | namespace clouds { 37 | 38 | class Version { 39 | public: 40 | Version() { } 41 | ~Version() { } 42 | static void Init() { 43 | RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); 44 | 45 | GPIO_InitTypeDef gpio_init; 46 | gpio_init.GPIO_Pin = GPIO_Pin_0; 47 | gpio_init.GPIO_Mode = GPIO_Mode_IN; 48 | gpio_init.GPIO_OType = GPIO_OType_PP; 49 | gpio_init.GPIO_Speed = GPIO_Speed_25MHz; 50 | gpio_init.GPIO_PuPd = GPIO_PuPd_UP; 51 | GPIO_Init(GPIOB, &gpio_init); 52 | } 53 | static inline bool revised() { 54 | return !GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0); 55 | } 56 | private: 57 | DISALLOW_COPY_AND_ASSIGN(Version); 58 | }; 59 | 60 | } // namespace clouds 61 | 62 | #endif // CLOUDS_DRIVERS_VERSION_H_ 63 | -------------------------------------------------------------------------------- /clouds/drivers/gate_input.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Driver for the gate inputs. 28 | 29 | #include "clouds/drivers/gate_input.h" 30 | 31 | #include 32 | 33 | namespace clouds { 34 | 35 | using namespace std; 36 | 37 | void GateInput::Init() { 38 | RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); 39 | 40 | GPIO_InitTypeDef gpio_init; 41 | // gpio_init.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_7; 42 | gpio_init.GPIO_Pin = GPIO_Pin_7; 43 | gpio_init.GPIO_Mode = GPIO_Mode_IN; 44 | gpio_init.GPIO_OType = GPIO_OType_PP; 45 | gpio_init.GPIO_Speed = GPIO_Speed_25MHz; 46 | gpio_init.GPIO_PuPd = GPIO_PuPd_UP; 47 | GPIO_Init(GPIOB, &gpio_init); 48 | // freeze_ = false; 49 | trigger_ = false; 50 | // previous_freeze_ = false; 51 | previous_trigger_ = false; 52 | } 53 | 54 | void GateInput::Read() { 55 | // previous_freeze_ = freeze_; 56 | previous_trigger_ = trigger_; 57 | //freeze_ = !GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_7); 58 | trigger_ = !GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_7); 59 | 60 | } 61 | 62 | } // namespace clouds 63 | -------------------------------------------------------------------------------- /clouds/drivers/adc.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Driver for ADC. 28 | 29 | #ifndef CLOUDS_DRIVERS_ADC_H_ 30 | #define CLOUDS_DRIVERS_ADC_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | namespace clouds { 35 | 36 | enum AdcChannel { 37 | ADC_POSITION_POTENTIOMETER_CV, 38 | ADC_DENSITY_POTENTIOMETER_CV, 39 | ADC_SIZE_POTENTIOMETER, 40 | ADC_FEEDBACK_POTENTIOMETER, 41 | ADC_PITCH_POTENTIOMETER, 42 | ADC_V_OCT_CV, 43 | ADC_DRYWET_POTENTIOMETER, 44 | ADC_SPREAD_POTENTIOMETER, 45 | ADC_TEXTURE_POTENTIOMETER, 46 | ADC_REVERB_POTENTIOMETER, 47 | ADC_CHANNEL_LAST 48 | }; 49 | 50 | class Adc { 51 | public: 52 | Adc() { } 53 | ~Adc() { } 54 | 55 | void Init(); 56 | void DeInit(); 57 | void Convert(); 58 | inline uint16_t value(uint8_t channel) const { return values_[channel]; } 59 | inline float float_value(uint8_t channel) const { 60 | return static_cast(values_[channel]) / 65536.0f; 61 | } 62 | inline const uint16_t* values() { return &values_[0]; } 63 | 64 | private: 65 | uint16_t values_[ADC_CHANNEL_LAST]; 66 | 67 | DISALLOW_COPY_AND_ASSIGN(Adc); 68 | }; 69 | 70 | } // namespace clouds 71 | 72 | #endif // CLOUDS_DRIVERS_ADC_H_ 73 | -------------------------------------------------------------------------------- /clouds/drivers/gate_input.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Driver for the gate inputs. 28 | 29 | #ifndef CLOUDS_DRIVERS_GATE_INPUT_H_ 30 | #define CLOUDS_DRIVERS_GATE_INPUT_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | #include 35 | 36 | namespace clouds { 37 | 38 | class GateInput { 39 | public: 40 | GateInput() { } 41 | ~GateInput() { } 42 | 43 | void Init(); 44 | void Read(); 45 | // inline bool freeze() const { return freeze_; } 46 | inline bool trigger() const { return trigger_; } 47 | 48 | /* inline bool freeze_rising_edge() const { 49 | return freeze_ && !previous_freeze_; 50 | } 51 | inline bool freeze_falling_edge() const { 52 | return !freeze_ && previous_freeze_; 53 | } 54 | */ 55 | 56 | inline bool trigger_rising_edge() const { 57 | return trigger_ && !previous_trigger_; 58 | } 59 | inline bool gate() const { 60 | return trigger_; 61 | } 62 | 63 | private: 64 | // bool previous_freeze_; 65 | bool previous_trigger_; 66 | // bool freeze_; 67 | bool trigger_; 68 | 69 | DISALLOW_COPY_AND_ASSIGN(GateInput); 70 | }; 71 | 72 | } // namespace clouds 73 | 74 | #endif // CLOUDS_DRIVERS_GATE_INPUT_H_ 75 | -------------------------------------------------------------------------------- /clouds/dsp/pvoc/phase_vocoder.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Naive phase vocoder. 28 | 29 | #ifndef CLOUDS_DSP_PVOC_PHASE_VOCODER_H_ 30 | #define CLOUDS_DSP_PVOC_PHASE_VOCODER_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | #include "stmlib/fft/shy_fft.h" 35 | 36 | #include "clouds/dsp/frame.h" 37 | #include "clouds/dsp/pvoc/stft.h" 38 | #include "clouds/dsp/pvoc/frame_transformation.h" 39 | 40 | namespace clouds { 41 | 42 | struct Parameters; 43 | 44 | class PhaseVocoder { 45 | public: 46 | PhaseVocoder() { } 47 | ~PhaseVocoder() { } 48 | 49 | void Init( 50 | void** buffer, size_t* buffer_size, 51 | const float* large_window_lut, size_t largest_fft_size, 52 | int32_t num_channels, 53 | int32_t resolution, 54 | float sample_rate); 55 | 56 | void Process( 57 | const Parameters& parameters, 58 | const FloatFrame* input, 59 | FloatFrame* output, 60 | size_t size); 61 | void Buffer(); 62 | 63 | private: 64 | FFT fft_; 65 | 66 | STFT stft_[2]; 67 | FrameTransformation frame_transformation_[2]; 68 | 69 | int32_t num_channels_; 70 | 71 | DISALLOW_COPY_AND_ASSIGN(PhaseVocoder); 72 | }; 73 | 74 | } // namespace clouds 75 | 76 | #endif // CLOUDS_DSP_PVOC_PHASE_VOCODER_H_ 77 | -------------------------------------------------------------------------------- /clouds/drivers/debug_pin.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Driver for the debug (timing) pin. 28 | 29 | #ifndef CLOUDS_DRIVERS_DEBUG_PIN_H_ 30 | #define CLOUDS_DRIVERS_DEBUG_PIN_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | #ifndef TEST 35 | #include 36 | #endif 37 | 38 | namespace clouds { 39 | 40 | class DebugPin { 41 | public: 42 | DebugPin() { } 43 | ~DebugPin() { } 44 | #ifdef TEST 45 | static void Init() { } 46 | static void High() { } 47 | static void Low() { } 48 | #else 49 | static void Init() { 50 | RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); 51 | 52 | GPIO_InitTypeDef gpio_init; 53 | gpio_init.GPIO_Pin = GPIO_Pin_9; 54 | gpio_init.GPIO_Mode = GPIO_Mode_OUT; 55 | gpio_init.GPIO_OType = GPIO_OType_PP; 56 | gpio_init.GPIO_Speed = GPIO_Speed_25MHz; 57 | gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL; 58 | GPIO_Init(GPIOA, &gpio_init); 59 | } 60 | static inline void High() { 61 | GPIOA->BSRRL = GPIO_Pin_9; 62 | } 63 | static inline void Low() { 64 | GPIOA->BSRRH = GPIO_Pin_9; 65 | } 66 | #endif 67 | private: 68 | DISALLOW_COPY_AND_ASSIGN(DebugPin); 69 | }; 70 | 71 | #define TIC DebugPin::High(); 72 | #define TOC DebugPin::Low(); 73 | 74 | } // namespace clouds 75 | 76 | #endif // CLOUDS_DRIVERS_DEBUG_PIN_H_ 77 | -------------------------------------------------------------------------------- /clouds/drivers/debug_port.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // UART driver for conversing with the factory testing program. 28 | 29 | #include "clouds/drivers/debug_port.h" 30 | 31 | #include 32 | 33 | namespace clouds { 34 | 35 | void DebugPort::Init() { 36 | RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); 37 | RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); 38 | 39 | // Initialize TX and RX pins. 40 | GPIO_InitTypeDef gpio_init; 41 | gpio_init.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; 42 | gpio_init.GPIO_Speed = GPIO_Speed_50MHz; 43 | gpio_init.GPIO_Mode = GPIO_Mode_AF; 44 | gpio_init.GPIO_OType = GPIO_OType_PP; 45 | gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL; 46 | GPIO_Init(GPIOA, &gpio_init); 47 | 48 | GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1); 49 | GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1); 50 | 51 | // Initialize USART. 52 | USART_InitTypeDef usart_init; 53 | usart_init.USART_BaudRate = 9600; 54 | usart_init.USART_WordLength = USART_WordLength_8b; 55 | usart_init.USART_StopBits = USART_StopBits_1; 56 | usart_init.USART_Parity = USART_Parity_No; 57 | usart_init.USART_HardwareFlowControl = USART_HardwareFlowControl_None; 58 | usart_init.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; 59 | USART_Init(USART1, &usart_init); 60 | 61 | USART_Cmd(USART1, ENABLE); 62 | } 63 | 64 | } // namespace clouds 65 | -------------------------------------------------------------------------------- /clouds/drivers/switches.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Driver for the front panel switches. 28 | 29 | #ifndef CLOUDS_DRIVERS_SWITCHES_H_ 30 | #define CLOUDS_DRIVERS_SWITCHES_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | #include 35 | 36 | namespace clouds { 37 | 38 | const uint8_t kNumSwitches = 4; 39 | 40 | class Switches { 41 | public: 42 | Switches() { } 43 | ~Switches() { } 44 | 45 | void Init(); 46 | void Debounce(); 47 | 48 | inline bool released(uint8_t index) const { 49 | return switch_state_[index] == 0x7f; 50 | } 51 | 52 | inline bool just_pressed(uint8_t index) const { 53 | return switch_state_[index] == 0x80; 54 | } 55 | 56 | inline bool pressed(uint8_t index) const { 57 | return switch_state_[index] == 0x00; 58 | } 59 | 60 | inline bool pressed_immediate(uint8_t index) const { 61 | if (index == 2) { 62 | return !GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_8); 63 | } else if (index == 3) { 64 | return !GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_6); 65 | } else { 66 | const uint16_t pins[] = { GPIO_Pin_11, GPIO_Pin_10 }; 67 | return !GPIO_ReadInputDataBit(GPIOC, pins[index]); 68 | } 69 | } 70 | 71 | private: 72 | uint8_t switch_state_[kNumSwitches]; 73 | 74 | DISALLOW_COPY_AND_ASSIGN(Switches); 75 | }; 76 | 77 | } // namespace clouds 78 | 79 | #endif // CLOUDS_DRIVERS_SWITCHES_H_ 80 | -------------------------------------------------------------------------------- /clouds/meter.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Vu-meter 28 | 29 | #ifndef CLOUDS_METER_H_ 30 | #define CLOUDS_METER_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | #include "clouds/drivers/codec.h" 35 | 36 | namespace clouds { 37 | 38 | class Meter { 39 | public: 40 | Meter() { } 41 | ~Meter() { } 42 | 43 | void Init(int32_t sample_rate) { 44 | attack_ = 32768 * 100 / sample_rate; 45 | release_ = 32768 * 4 / sample_rate; 46 | peak_l_ = 0; 47 | peak_r_ = 0; 48 | } 49 | 50 | void Process(Codec::Frame* frames, size_t size) { 51 | while (size--) { 52 | int32_t sample; 53 | int32_t error; 54 | int32_t coefficient; 55 | 56 | sample = frames->l; 57 | if (sample < 0) sample = -sample; 58 | error = sample - peak_l_; 59 | coefficient = error > 0 ? attack_ : release_; 60 | peak_l_ += error * coefficient >> 15; 61 | 62 | sample = frames->r; 63 | if (sample < 0) sample = -sample; 64 | error = sample - peak_r_; 65 | coefficient = error > 0 ? attack_ : release_; 66 | peak_r_ += error * coefficient >> 15; 67 | ++frames; 68 | } 69 | } 70 | 71 | int32_t peak() { return peak_l_ > peak_r_ ? peak_l_ : peak_r_; } 72 | 73 | private: 74 | int32_t peak_l_; 75 | int32_t peak_r_; 76 | int32_t attack_; 77 | int32_t release_; 78 | 79 | DISALLOW_COPY_AND_ASSIGN(Meter); 80 | }; 81 | 82 | } // namespace clouds 83 | 84 | #endif // CLOUDS_METER_H_ 85 | -------------------------------------------------------------------------------- /clouds/drivers/switches.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Driver for the front panel switches. 28 | 29 | #include "clouds/drivers/switches.h" 30 | 31 | #include 32 | 33 | namespace clouds { 34 | 35 | using namespace std; 36 | 37 | void Switches::Init() { 38 | RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); 39 | RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); 40 | 41 | GPIO_InitTypeDef gpio_init; 42 | gpio_init.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_8; 43 | gpio_init.GPIO_Mode = GPIO_Mode_IN; 44 | gpio_init.GPIO_OType = GPIO_OType_PP; 45 | gpio_init.GPIO_Speed = GPIO_Speed_25MHz; 46 | gpio_init.GPIO_PuPd = GPIO_PuPd_UP; 47 | GPIO_Init(GPIOB, &gpio_init); 48 | 49 | gpio_init.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11; 50 | gpio_init.GPIO_Mode = GPIO_Mode_IN; 51 | gpio_init.GPIO_OType = GPIO_OType_PP; 52 | gpio_init.GPIO_Speed = GPIO_Speed_25MHz; 53 | gpio_init.GPIO_PuPd = GPIO_PuPd_UP; 54 | GPIO_Init(GPIOC, &gpio_init); 55 | 56 | fill(&switch_state_[0], &switch_state_[kNumSwitches], 0xff); 57 | } 58 | 59 | void Switches::Debounce() { 60 | const uint16_t pins[] = { GPIO_Pin_11, GPIO_Pin_10 }; 61 | for (uint8_t i = 0; i < 2; ++i) { 62 | switch_state_[i] = (switch_state_[i] << 1) | \ 63 | GPIO_ReadInputDataBit(GPIOC, pins[i]); 64 | } 65 | switch_state_[2] = (switch_state_[2] << 1) | GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_8); 66 | switch_state_[3] = (switch_state_[3] << 1) | GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_6); 67 | } 68 | 69 | } // namespace clouds 70 | -------------------------------------------------------------------------------- /clouds/dsp/random_oscillator.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Matthias Puech. 2 | // 3 | // Author: Matthias Puech (matthias.puech@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Smoothed random oscillator 28 | 29 | #include "../resources.h" 30 | #include "stmlib/utils/random.h" 31 | #include "stmlib/dsp/dsp.h" 32 | 33 | #ifndef CLOUDS_RANDOM_OSCILLATOR_H_ 34 | #define CLOUDS_RANDOM_OSCILLATOR_H_ 35 | 36 | using namespace stmlib; 37 | 38 | namespace clouds 39 | { 40 | const float kOscillationMinimumGap = 0.3f; 41 | 42 | class RandomOscillator 43 | { 44 | public: 45 | 46 | void Init() { 47 | value_ = 0.0f; 48 | next_value_ = Random::GetFloat() * 2.0f - 1.0f; 49 | } 50 | 51 | inline void set_slope(float slope) { 52 | phase_increment_ = 1.0f / fabs(next_value_ - value_) * slope; 53 | if (phase_increment_ > 1.0f) 54 | phase_increment_ = 1.0f; 55 | } 56 | 57 | float Next() { 58 | phase_ += phase_increment_; 59 | if (phase_ > 1.0f) { 60 | phase_--; 61 | value_ = next_value_; 62 | direction_ = !direction_; 63 | float rnd = (1.0f - kOscillationMinimumGap) * Random::GetFloat() + kOscillationMinimumGap; 64 | next_value_ = direction_ ? 65 | value_ + (1.0f - value_) * rnd : 66 | value_ - (1.0f + value_) * rnd; 67 | } 68 | 69 | float sin = Interpolate(lut_raised_cos, phase_, LUT_RAISED_COS_SIZE-1); 70 | return value_ + (next_value_ - value_) * sin; 71 | } 72 | 73 | private: 74 | float phase_; 75 | float phase_increment_; 76 | float value_; 77 | float next_value_; 78 | bool direction_; 79 | }; 80 | } 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /clouds/dsp/mu_law.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Mu-law encoding. 28 | 29 | #ifndef CLOUDS_DSP_MU_LAW_H_ 30 | #define CLOUDS_DSP_MU_LAW_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | namespace clouds { 35 | 36 | // inline short MuLaw2Lin(uint8_t u_val) { 37 | // int16_t t; 38 | // u_val = ~u_val; 39 | // t = ((u_val & 0xf) << 3) + 0x84; 40 | // t <<= ((unsigned)u_val & 0x70) >> 4; 41 | // return ((u_val & 0x80) ? (0x84 - t) : (t - 0x84)); 42 | // } 43 | 44 | extern int16_t lut_ulaw[256]; 45 | 46 | inline short MuLaw2Lin(uint8_t u_val) { 47 | return lut_ulaw[u_val]; 48 | } 49 | 50 | inline unsigned char Lin2MuLaw(int16_t pcm_val) { 51 | int16_t mask; 52 | int16_t seg; 53 | uint8_t uval; 54 | pcm_val = pcm_val >> 2; 55 | if (pcm_val < 0) { 56 | pcm_val = -pcm_val; 57 | mask = 0x7f; 58 | } else { 59 | mask = 0xff; 60 | } 61 | if (pcm_val > 8159) pcm_val = 8159; 62 | pcm_val += (0x84 >> 2); 63 | 64 | if (pcm_val <= 0x3f) seg = 0; 65 | else if (pcm_val <= 0x7f) seg = 1; 66 | else if (pcm_val <= 0xff) seg = 2; 67 | else if (pcm_val <= 0x1ff) seg = 3; 68 | else if (pcm_val <= 0x3ff) seg = 4; 69 | else if (pcm_val <= 0x7ff) seg = 5; 70 | else if (pcm_val <= 0xfff) seg = 6; 71 | else if (pcm_val <= 0x1fff) seg = 7; 72 | else seg = 8; 73 | if (seg >= 8) 74 | return static_cast(0x7f ^ mask); 75 | else { 76 | uval = static_cast((seg << 4) | ((pcm_val >> (seg + 1)) & 0x0f)); 77 | return (uval ^ mask); 78 | } 79 | } 80 | 81 | } // namespace clouds 82 | 83 | #endif // CLOUDS_DSP_MU_LAW_H_ 84 | -------------------------------------------------------------------------------- /clouds/dsp/correlator.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Search for stretch/shift splicing points by maximizing correlation. 28 | // Correlation is computed by XOR-ing the bit sign of samples - this allows 29 | // 32 samples to be matched in one single XOR operation. 30 | 31 | #ifndef CLOUDS_DSP_CORRELATOR_H_ 32 | #define CLOUDS_DSP_CORRELATOR_H_ 33 | 34 | #include "stmlib/stmlib.h" 35 | 36 | namespace clouds { 37 | 38 | class Correlator { 39 | public: 40 | Correlator() { } 41 | ~Correlator() { } 42 | 43 | void Init(uint32_t* source, uint32_t* destination); 44 | 45 | void StartSearch(int32_t size, int32_t offset, int32_t increment); 46 | 47 | inline int32_t best_match() const { 48 | return offset_ + (best_match_ * (increment_ >> 4) >> 12); 49 | } 50 | 51 | inline void EvaluateSomeCandidates() { 52 | size_t num_candidates = (size_ >> 2) + 16; 53 | while (num_candidates) { 54 | EvaluateNextCandidate(); 55 | --num_candidates; 56 | } 57 | } 58 | 59 | void EvaluateNextCandidate(); 60 | 61 | inline uint32_t* source() { return source_; } 62 | inline uint32_t* destination() { return destination_; } 63 | inline int32_t candidate() { return candidate_; } 64 | 65 | inline bool done() { return done_; } 66 | 67 | private: 68 | uint32_t* source_; 69 | uint32_t* destination_; 70 | 71 | int32_t offset_; 72 | int32_t increment_; 73 | int32_t size_; 74 | int32_t candidate_; 75 | 76 | uint32_t best_score_; 77 | int32_t best_match_; 78 | 79 | int32_t trace_; 80 | 81 | bool done_; 82 | 83 | DISALLOW_COPY_AND_ASSIGN(Correlator); 84 | }; 85 | 86 | } // namespace clouds 87 | 88 | #endif // CLOUDS_DSP_CORRELATOR_H_ 89 | -------------------------------------------------------------------------------- /clouds/dsp/correlator.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Search for stretch/shift splicing points by maximizing correlation. 28 | 29 | #include "clouds/dsp/correlator.h" 30 | 31 | #include 32 | 33 | namespace clouds { 34 | 35 | using namespace std; 36 | 37 | void Correlator::Init(uint32_t* source, uint32_t* destination) { 38 | source_ = source; 39 | destination_ = destination; 40 | offset_ = 0; 41 | best_match_ = 0; 42 | done_ = true; 43 | } 44 | 45 | void Correlator::EvaluateNextCandidate() { 46 | if (done_) { 47 | return; 48 | } 49 | uint32_t num_words = size_ >> 5; 50 | uint32_t offset_words = candidate_ >> 5; 51 | uint32_t offset_bits = candidate_ & 0x1f; 52 | uint32_t* source = &source_[0]; 53 | uint32_t* destination = &destination_[offset_words]; 54 | 55 | uint32_t xcorr = 0; 56 | for (uint32_t i = 0; i < num_words; ++i) { 57 | uint32_t source_bits = source[i]; 58 | uint32_t destination_bits = 0; 59 | destination_bits |= destination[i] << offset_bits; 60 | destination_bits |= destination[i + 1] >> (32 - offset_bits); 61 | uint32_t count = ~(source_bits ^ destination_bits); 62 | count = count - ((count >> 1) & 0x55555555); 63 | count = (count & 0x33333333) + ((count >> 2) & 0x33333333); 64 | count = (((count + (count >> 4)) & 0xf0f0f0f) * 0x1010101) >> 24; 65 | xcorr += count; 66 | } 67 | if (xcorr > best_score_) { 68 | best_match_ = candidate_; 69 | best_score_ = xcorr; 70 | } 71 | ++candidate_; 72 | done_ = candidate_ >= size_; 73 | } 74 | 75 | void Correlator::StartSearch( 76 | int32_t size, 77 | int32_t offset, 78 | int32_t increment) { 79 | offset_ = offset; 80 | increment_ = increment; 81 | best_score_ = 0; 82 | best_match_ = 0; 83 | candidate_ = 0; 84 | size_ = size; 85 | done_ = false; 86 | } 87 | 88 | } // namespace clouds 89 | -------------------------------------------------------------------------------- /clouds/resources/src_filters.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2.5 2 | # 3 | # Copyright 2014 Olivier Gillet. 4 | # 5 | # Author: Olivier Gillet (ol.gillet@gmail.com) 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | # THE SOFTWARE. 24 | # 25 | # See http://creativecommons.org/licenses/MIT/ for more information. 26 | # 27 | # ----------------------------------------------------------------------------- 28 | # 29 | # Lookup table definitions. 30 | 31 | import numpy 32 | import pylab 33 | import scipy.signal 34 | 35 | SAMPLE_RATE = 32000 36 | CRITICAL_FREQUENCY = 12000.0 37 | IR_LENGTH = 8192 38 | 39 | filters = [] 40 | 41 | ratios = [2] 42 | lengths = [31, 45, 63, 91] 43 | 44 | configurations = [] 45 | for oversampled in [False]: 46 | for ratio in ratios: 47 | for length in lengths: 48 | configurations += [(oversampled, ratio, length)] 49 | 50 | generate_figures = False 51 | for oversampled, ratio, length in configurations: 52 | transition = 0.15 * 0.25 ** (length / 100.0) if oversampled else 0.4 53 | ir = scipy.signal.remez( 54 | length, 55 | [0, transition / ratio, 0.499 / ratio, 0.5], [1, 0]) 56 | ir /= sum(ir) 57 | gain = 20 * numpy.log10(numpy.abs(numpy.fft.rfft(ir, IR_LENGTH))) 58 | f = numpy.arange(IR_LENGTH / 2 + 1) / float(IR_LENGTH) * SAMPLE_RATE * ratio 59 | bin_index = CRITICAL_FREQUENCY / ratio / SAMPLE_RATE * IR_LENGTH 60 | critical_gain = gain[int(round(bin_index))] 61 | 62 | name = 'filter_%s_%d_%d' % ('2x' if oversampled else '1x', ratio, length) 63 | if generate_figures: 64 | pylab.figure(figsize=(8, 12)) 65 | pylab.subplot(211) 66 | pylab.plot(f, gain) 67 | pylab.xlim([0.0, SAMPLE_RATE]) 68 | pylab.ylim([-75.0, 3.0]) 69 | pylab.xlabel('Frequency (Hz)') 70 | pylab.ylabel('Gain (dB)') 71 | caption = 'Resampling filter, N=%d, Ratio=%d. Gain at %d Hz = %.2fdB' 72 | pylab.title(caption % (length, ratio, CRITICAL_FREQUENCY, critical_gain)) 73 | 74 | pylab.subplot(212) 75 | pylab.plot(f, gain) 76 | pylab.xlim([0.0, 20000.0]) 77 | pylab.ylim([-3.0, 1.0]) 78 | pylab.xlabel('Frequency (Hz)') 79 | pylab.ylabel('Gain (dB)') 80 | 81 | pylab.savefig(name + '.pdf') 82 | pylab.close() 83 | filters += [(name, ir)] 84 | -------------------------------------------------------------------------------- /clouds/drivers/codec.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // WM8371 Codec support. 28 | 29 | #ifndef CLOUDS_DRIVERS_CODEC_H_ 30 | #define CLOUDS_DRIVERS_CODEC_H_ 31 | 32 | #include 33 | 34 | #include "stmlib/stmlib.h" 35 | 36 | namespace clouds { 37 | 38 | const size_t kMaxCodecBlockSize = 32; 39 | 40 | class Codec { 41 | public: 42 | Codec() { } 43 | ~Codec() { } 44 | 45 | typedef struct { 46 | short l; 47 | short r; 48 | } Frame; 49 | 50 | typedef void (*FillBufferCallback)(Frame* rx, Frame* tx, size_t size); 51 | 52 | bool Init( 53 | bool mcu_is_master, 54 | int32_t sample_rate); 55 | 56 | bool Start(size_t block_size) { 57 | // No callback - the caller is supposed to poll with available() 58 | return Start(block_size, NULL); 59 | } 60 | 61 | bool Start(size_t block_size, FillBufferCallback callback); 62 | void Stop(); 63 | 64 | void Fill(size_t offset); 65 | 66 | bool set_line_input_gain(int32_t channel, int32_t gain); 67 | bool set_line_input_gain(int32_t gain); 68 | 69 | static Codec* GetInstance() { return instance_; } 70 | 71 | private: 72 | bool InitializeGPIO(); 73 | bool InitializeControlInterface(); 74 | bool InitializeAudioInterface(bool, int32_t); 75 | bool InitializeCodec(bool, int32_t); 76 | 77 | bool WriteControlRegister(uint8_t address, uint16_t data); 78 | 79 | bool InitializeDMA(); 80 | 81 | static Codec* instance_; 82 | 83 | bool mcu_is_master_; 84 | int32_t sample_rate_; 85 | size_t block_size_; 86 | size_t stride_; 87 | 88 | FillBufferCallback callback_; 89 | 90 | DMA_InitTypeDef dma_init_tx_; 91 | DMA_InitTypeDef dma_init_rx_; 92 | 93 | short tx_dma_buffer_[kMaxCodecBlockSize * 6 * 2]; 94 | short rx_dma_buffer_[kMaxCodecBlockSize * 6 * 2]; 95 | 96 | DISALLOW_COPY_AND_ASSIGN(Codec); 97 | }; 98 | 99 | } // namespace clouds 100 | 101 | #endif // CLOUDS_DRIVERS_CODEC_H_ 102 | -------------------------------------------------------------------------------- /clouds/resources.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Resources definitions. 28 | // 29 | // Automatically generated with: 30 | // make resources 31 | 32 | 33 | #ifndef CLOUDS_RESOURCES_H_ 34 | #define CLOUDS_RESOURCES_H_ 35 | 36 | 37 | #include "stmlib/stmlib.h" 38 | 39 | 40 | 41 | namespace clouds { 42 | 43 | typedef uint8_t ResourceId; 44 | 45 | extern const float* src_filter_table[]; 46 | 47 | extern const int16_t* lookup_table_int16_table[]; 48 | 49 | extern const float* lookup_table_table[]; 50 | 51 | extern const float src_filter_1x_2_31[]; 52 | extern const float src_filter_1x_2_45[]; 53 | extern const float src_filter_1x_2_63[]; 54 | extern const float src_filter_1x_2_91[]; 55 | extern const int16_t lut_db[]; 56 | extern const float lut_sin[]; 57 | extern const float lut_raised_cos[]; 58 | extern const float lut_xfade_in[]; 59 | extern const float lut_xfade_out[]; 60 | extern const float lut_window[]; 61 | extern const float lut_sine_window_4096[]; 62 | extern const float lut_cutoff[]; 63 | extern const float lut_grain_size[]; 64 | extern const float lut_quantized_pitch[]; 65 | #define SRC_FILTER_1X_2_31 0 66 | #define SRC_FILTER_1X_2_31_SIZE 31 67 | #define SRC_FILTER_1X_2_45 1 68 | #define SRC_FILTER_1X_2_45_SIZE 45 69 | #define SRC_FILTER_1X_2_63 2 70 | #define SRC_FILTER_1X_2_63_SIZE 63 71 | #define SRC_FILTER_1X_2_91 3 72 | #define SRC_FILTER_1X_2_91_SIZE 91 73 | #define LUT_DB 0 74 | #define LUT_DB_SIZE 257 75 | #define LUT_SIN 0 76 | #define LUT_SIN_SIZE 1281 77 | #define LUT_RAISED_COS 1 78 | #define LUT_RAISED_COS_SIZE 257 79 | #define LUT_XFADE_IN 2 80 | #define LUT_XFADE_IN_SIZE 17 81 | #define LUT_XFADE_OUT 3 82 | #define LUT_XFADE_OUT_SIZE 17 83 | #define LUT_WINDOW 4 84 | #define LUT_WINDOW_SIZE 4097 85 | #define LUT_SINE_WINDOW_4096 5 86 | #define LUT_SINE_WINDOW_4096_SIZE 4096 87 | #define LUT_CUTOFF 6 88 | #define LUT_CUTOFF_SIZE 257 89 | #define LUT_GRAIN_SIZE 7 90 | #define LUT_GRAIN_SIZE_SIZE 257 91 | #define LUT_QUANTIZED_PITCH 8 92 | #define LUT_QUANTIZED_PITCH_SIZE 1025 93 | 94 | } // namespace clouds 95 | 96 | #endif // CLOUDS_RESOURCES_H_ 97 | -------------------------------------------------------------------------------- /clouds/settings.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Settings storage. 28 | 29 | #include "clouds/settings.h" 30 | 31 | #include "stmlib/system/storage.h" 32 | 33 | #include "clouds/dsp/granular_processor.h" 34 | 35 | namespace clouds { 36 | 37 | stmlib::Storage<1> storage; 38 | 39 | void Settings::Init() { 40 | freshly_baked_ = false; 41 | if (!storage.ParsimoniousLoad(&data_, &version_token_)) { 42 | data_.calibration_data.pitch_offset = 66.67f; 43 | data_.calibration_data.pitch_scale = -84.26f; 44 | for (size_t i = 0; i < ADC_CHANNEL_LAST; ++i) { 45 | data_.calibration_data.offset[i] = 0.505f; 46 | } 47 | data_.state.quality = 0; 48 | data_.state.blend_parameter = 0; 49 | data_.state.playback_mode = PLAYBACK_MODE_GRANULAR; 50 | data_.state.blend_value[0] = 255; 51 | data_.state.blend_value[1] = 128; 52 | data_.state.blend_value[2] = 0; 53 | data_.state.blend_value[3] = 0; 54 | freshly_baked_ = true; 55 | Save(); 56 | } 57 | } 58 | 59 | void Settings::SaveSampleMemory( 60 | uint32_t index, 61 | PersistentBlock* blocks, 62 | size_t num_blocks) { 63 | uint32_t* data = mutable_sample_flash_data(index); 64 | 65 | // Unprotect flash and erase sector. 66 | FLASH_Unlock(); 67 | FLASH_ClearFlag( 68 | FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | 69 | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR| FLASH_FLAG_PGSERR); 70 | FLASH_EraseSector(sample_flash_sector(index) * 8, VoltageRange_3); 71 | 72 | // Write all data blocks. 73 | for (size_t block = 0; block < num_blocks; ++block) { 74 | FLASH_ProgramWord((uint32_t)(data++), blocks[block].tag); 75 | FLASH_ProgramWord((uint32_t)(data++), blocks[block].size); 76 | size_t size = blocks[block].size; 77 | const uint32_t* words = (const uint32_t*)(blocks[block].data); 78 | while (size >= 4) { 79 | FLASH_ProgramWord((uint32_t)(data++), *words++); 80 | size -= 4; 81 | } 82 | } 83 | } 84 | 85 | void Settings::Save() { 86 | storage.ParsimoniousSave(data_, &version_token_); 87 | } 88 | 89 | } // namespace clouds 90 | -------------------------------------------------------------------------------- /clouds/dsp/pvoc/stft.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // STFT with overlap-add. 28 | 29 | #ifndef CLOUDS_DSP_PVOC_STFT_H_ 30 | #define CLOUDS_DSP_PVOC_STFT_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | // #define USE_ARM_FFT 35 | 36 | #ifdef USE_ARM_FFT 37 | #include 38 | #else 39 | #include "stmlib/fft/shy_fft.h" 40 | #endif // USE_ARM_FFT 41 | 42 | namespace clouds { 43 | 44 | struct Parameters; 45 | 46 | const size_t kMaxFftSize = 4096; 47 | #ifdef USE_ARM_FFT 48 | typedef arm_rfft_fast_instance_f32 FFT; 49 | #else 50 | typedef stmlib::ShyFFT FFT; 51 | #endif // USE_ARM_FFT 52 | 53 | typedef class FrameTransformation Modifier; 54 | 55 | class STFT { 56 | public: 57 | STFT() { } 58 | ~STFT() { } 59 | 60 | struct Frame { short l; short r; }; 61 | 62 | void Init( 63 | FFT* fft, 64 | size_t fft_size, 65 | size_t hop_size, 66 | float* fft_buffer, 67 | float* ifft_buffer, 68 | const float* window_lut, 69 | short* stft_frame_processor_buffer, 70 | Modifier* modifier); 71 | 72 | void Reset(); 73 | 74 | void Process( 75 | const Parameters& parameters, 76 | const float* input, 77 | float* output, 78 | size_t size, 79 | size_t stride); 80 | 81 | void Buffer(); 82 | 83 | private: 84 | FFT* fft_; 85 | size_t fft_size_; 86 | size_t fft_num_passes_; 87 | size_t hop_size_; 88 | size_t buffer_size_; 89 | float* fft_in_; 90 | float* fft_out_; 91 | float* ifft_out_; 92 | float* ifft_in_; 93 | 94 | const float* window_; 95 | size_t window_stride_; 96 | 97 | short* analysis_; 98 | short* synthesis_; 99 | 100 | size_t buffer_ptr_; 101 | size_t process_ptr_; 102 | size_t block_size_; 103 | 104 | size_t ready_; 105 | size_t done_; 106 | 107 | const Parameters* parameters_; 108 | 109 | Modifier* modifier_; 110 | 111 | DISALLOW_COPY_AND_ASSIGN(STFT); 112 | }; 113 | 114 | } // namespace clouds 115 | 116 | #endif // CLOUDS_DSP_PVOC_STFT_H_ 117 | -------------------------------------------------------------------------------- /clouds/resources/resources.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2.5 2 | # 3 | # Copyright 2014 Olivier Gillet. 4 | # 5 | # Author: Olivier Gillet (ol.gillet@gmail.com) 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | # THE SOFTWARE. 24 | # 25 | # See http://creativecommons.org/licenses/MIT/ for more information. 26 | # 27 | # ----------------------------------------------------------------------------- 28 | # 29 | # Master resources file. 30 | 31 | header = """// Copyright 2014 Olivier Gillet. 32 | // 33 | // Author: Olivier Gillet (ol.gillet@gmail.com) 34 | // 35 | // Permission is hereby granted, free of charge, to any person obtaining a copy 36 | // of this software and associated documentation files (the "Software"), to deal 37 | // in the Software without restriction, including without limitation the rights 38 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 39 | // copies of the Software, and to permit persons to whom the Software is 40 | // furnished to do so, subject to the following conditions: 41 | // 42 | // The above copyright notice and this permission notice shall be included in 43 | // all copies or substantial portions of the Software. 44 | // 45 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 46 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 47 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 48 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 49 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 50 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 51 | // THE SOFTWARE. 52 | // 53 | // See http://creativecommons.org/licenses/MIT/ for more information. 54 | // 55 | // ----------------------------------------------------------------------------- 56 | // 57 | // Resources definitions. 58 | // 59 | // Automatically generated with: 60 | // make resources 61 | """ 62 | 63 | namespace = 'clouds' 64 | target = 'clouds' 65 | types = ['uint8_t'] 66 | includes = """ 67 | #include "stmlib/stmlib.h" 68 | """ 69 | 70 | import lookup_tables 71 | import src_filters 72 | 73 | create_specialized_manager = True 74 | 75 | resources = [ 76 | (src_filters.filters, 77 | 'src_filter', 'SRC', 'float', float, False), 78 | (lookup_tables.int16_lookup_tables, 79 | 'lookup_table_int16', 'LUT', 'int16_t', int, False), 80 | (lookup_tables.lookup_tables, 81 | 'lookup_table', 'LUT', 'float', float, False), 82 | ] 83 | -------------------------------------------------------------------------------- /clouds/dsp/sample_rate_converter.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Sample rate converter 28 | 29 | #ifndef CLOUDS_DSP_SAMPLE_RATE_CONVERTER_H_ 30 | #define CLOUDS_DSP_SAMPLE_RATE_CONVERTER_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | #include "clouds/dsp/frame.h" 35 | 36 | namespace clouds { 37 | 38 | template 39 | class SampleRateConverter { 40 | public: 41 | SampleRateConverter() { } 42 | ~SampleRateConverter() { } 43 | 44 | void Init() { 45 | for (int32_t i = 0; i < filter_size * 2; ++i) { 46 | history_[i].l = history_[i].r = 0.0f; 47 | } 48 | std::copy(&coefficients[0], &coefficients[filter_size], &coefficients_[0]); 49 | history_ptr_ = filter_size - 1; 50 | }; 51 | 52 | void Process(const FloatFrame* in, FloatFrame* out, size_t input_size) { 53 | int32_t history_ptr = history_ptr_; 54 | FloatFrame* history = history_; 55 | const float scale = ratio < 0 ? 1.0f : float(ratio); 56 | while (input_size) { 57 | int32_t consumed = ratio < 0 ? -ratio : 1; 58 | for (int32_t i = 0; i < consumed; ++i) { 59 | history[history_ptr + filter_size] = history[history_ptr] = *in++; 60 | --input_size; 61 | --history_ptr; 62 | if (history_ptr < 0) { 63 | history_ptr += filter_size; 64 | } 65 | } 66 | 67 | int32_t produced = ratio > 0 ? ratio : 1; 68 | for (int32_t i = 0; i < produced; ++i) { 69 | float y_l = 0.0f; 70 | float y_r = 0.0f; 71 | const FloatFrame* x = &history[history_ptr + 1]; 72 | for (int32_t j = i; j < filter_size; j += produced) { 73 | const float h = coefficients_[j]; 74 | y_l += x->l * h; 75 | y_r += x->r * h; 76 | ++x; 77 | } 78 | out->l = y_l * scale; 79 | out->r = y_r * scale; 80 | ++out; 81 | } 82 | } 83 | history_ptr_ = history_ptr; 84 | } 85 | 86 | private: 87 | float coefficients_[filter_size]; 88 | FloatFrame history_[filter_size * 2]; 89 | int32_t history_ptr_; 90 | 91 | DISALLOW_COPY_AND_ASSIGN(SampleRateConverter); 92 | }; 93 | 94 | } // namespace clouds 95 | 96 | #endif // CLOUDS_DSP_SAMPLE_RATE_CONVERTER_H_ 97 | -------------------------------------------------------------------------------- /clouds/settings.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Settings storage. 28 | 29 | #ifndef CLOUDS_SETTINGS_H_ 30 | #define CLOUDS_SETTINGS_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | #include "clouds/drivers/adc.h" 35 | 36 | namespace clouds { 37 | 38 | struct CalibrationData { 39 | float pitch_offset; 40 | float pitch_scale; 41 | float offset[ADC_CHANNEL_LAST]; 42 | }; 43 | 44 | struct State { 45 | uint8_t quality; 46 | uint8_t blend_parameter; 47 | uint8_t playback_mode; 48 | uint8_t blend_value[4]; 49 | uint8_t padding; 50 | }; 51 | 52 | struct SettingsData { 53 | CalibrationData calibration_data; // 48 bytes 54 | State state; // 8 bytes 55 | uint8_t padding[8]; 56 | }; 57 | 58 | class PersistentBlock; 59 | 60 | class Settings { 61 | public: 62 | Settings() { } 63 | ~Settings() { } 64 | 65 | void Init(); 66 | void Save(); 67 | 68 | inline const uint32_t* sample_flash_data(uint32_t index) const { 69 | return (const uint32_t*)(mutable_sample_flash_data(index)); 70 | } 71 | 72 | inline uint32_t* mutable_sample_flash_data(uint32_t index) const { 73 | return (uint32_t*)(0x08080000 + index * 0x0020000); 74 | } 75 | 76 | inline uint32_t sample_flash_sector(uint32_t index) { 77 | return index + 8; 78 | } 79 | 80 | void SaveSampleMemory( 81 | uint32_t index, 82 | PersistentBlock* blocks, 83 | size_t num_blocks); 84 | 85 | inline CalibrationData* mutable_calibration_data() { 86 | return &data_.calibration_data; 87 | } 88 | 89 | inline State* mutable_state() { 90 | return &data_.state; 91 | } 92 | 93 | inline const State& state() const { 94 | return data_.state; 95 | } 96 | 97 | // True when no calibration data has been found on flash sector 1, that is 98 | // to say when the module has just been flashed. 99 | inline bool freshly_baked() const { 100 | return freshly_baked_; 101 | } 102 | 103 | private: 104 | bool freshly_baked_; 105 | SettingsData data_; 106 | uint16_t version_token_; 107 | 108 | DISALLOW_COPY_AND_ASSIGN(Settings); 109 | }; 110 | 111 | } // namespace clouds 112 | 113 | #endif // CLOUDS_SETTINGS_H_ 114 | -------------------------------------------------------------------------------- /clouds/drivers/leds.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Driver for the 4 bicolor LEDs controlled by a 595. 28 | 29 | #ifndef CLOUDS_DRIVERS_LEDS_H_ 30 | #define CLOUDS_DRIVERS_LEDS_H_ 31 | 32 | #include 33 | 34 | #include "stmlib/stmlib.h" 35 | 36 | namespace clouds { 37 | 38 | class Leds { 39 | public: 40 | Leds() { } 41 | ~Leds() { } 42 | 43 | void Init(); 44 | 45 | void set_status(uint8_t channel, uint8_t red, uint8_t green) { 46 | red_[channel] = red; 47 | green_[channel] = green; 48 | } 49 | 50 | void set_intensity(uint8_t channel, uint8_t value) { 51 | uint8_t red = 0; 52 | uint8_t green = 0; 53 | if (value < 128) { 54 | green = value << 1; 55 | } else if (value < 192) { 56 | green = 255; 57 | red = (value - 128) << 2; 58 | } else { 59 | green = 255 - ((value - 192) << 2); 60 | red = 255; 61 | } 62 | set_status(channel, red, green); 63 | } 64 | 65 | void PaintBar(int32_t db) { 66 | if (db < 0) { 67 | return; 68 | } 69 | if (db > 32767) { 70 | db = 32767; 71 | } 72 | db <<= 1; 73 | if (db >= 49152) { 74 | set_status(3, (db - 49152) >> 6, 0); 75 | set_status(2, 255, 255); 76 | set_status(1, 0, 255); 77 | set_status(0, 0, 255); 78 | } else if (db >= 32768) { 79 | set_status(2, (db - 32768) >> 6, (db - 32768) >> 6); 80 | set_status(1, 0, 255); 81 | set_status(0, 0, 255); 82 | } else if (db >= 16384) { 83 | set_status(1, 0, (db - 16384) >> 6); 84 | set_status(0, 0, 255); 85 | } else { 86 | set_status(0, 0, db >> 6); 87 | } 88 | } 89 | 90 | void Clear(); 91 | 92 | void set_freeze(bool status) { 93 | freeze_led_ = status; 94 | } 95 | 96 | void set_enabled(bool status) { 97 | enabled_led_ = status; 98 | } 99 | 100 | void Write(); 101 | 102 | private: 103 | bool enabled_led_; 104 | bool freeze_led_; 105 | uint8_t pwm_counter_; 106 | uint8_t red_[4]; 107 | uint8_t green_[4]; 108 | 109 | DISALLOW_COPY_AND_ASSIGN(Leds); 110 | }; 111 | 112 | } // namespace clouds 113 | 114 | #endif // CLOUDS_DRIVERS_LEDS_H_ 115 | -------------------------------------------------------------------------------- /clouds/dsp/pvoc/frame_transformation.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Transformations applied to a single STFT slice. 28 | 29 | #ifndef CLOUDS_DSP_PVOC_FRAME_TRANSFORMATION_H_ 30 | #define CLOUDS_DSP_PVOC_FRAME_TRANSFORMATION_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | #include "clouds/dsp/pvoc/stft.h" 35 | 36 | #include "clouds/resources.h" 37 | 38 | namespace clouds { 39 | 40 | const int32_t kMaxNumTextures = 7; 41 | const int32_t kHighFrequencyTruncation = 16; 42 | 43 | struct Parameters; 44 | 45 | class FrameTransformation { 46 | public: 47 | FrameTransformation() { } 48 | ~FrameTransformation() { } 49 | 50 | void Init(float* buffer, int32_t fft_size, int32_t num_textures); 51 | void Reset(); 52 | 53 | void Process( 54 | const Parameters& parameters, 55 | float* fft_out, 56 | float* ifft_in); 57 | 58 | private: 59 | void RectangularToPolar(float* fft_data); 60 | void PolarToRectangular(float* fft_data); 61 | void AddGlitch(float* xf_polar); 62 | void ShiftMagnitudes( 63 | float* source, 64 | float* xf_polar, 65 | float pitch_ratio); 66 | void WarpMagnitudes( 67 | float* source, 68 | float* xf_polar, 69 | float amount); 70 | void QuantizeMagnitudes(float* xf_polar, float amount); 71 | void StoreMagnitudes(float* xf_polar, float position, float feedback); 72 | void SetPhases(float* destination, float diffusion, float pitch_ratio); 73 | void ReplayMagnitudes(float* xf_polar, float position); 74 | void DiffuseMagnitudes(float* xf_polar, float diffusion); 75 | 76 | inline void fast_p2r(float magnitude, uint16_t angle, float* re, float* im) { 77 | angle >>= 6; 78 | *re = magnitude * lut_sin[angle + 256]; 79 | *im = magnitude * lut_sin[angle]; 80 | } 81 | 82 | int32_t fft_size_; 83 | int32_t num_textures_; 84 | int32_t size_; 85 | 86 | // Magnitude buffers. 87 | float* textures_[kMaxNumTextures]; 88 | 89 | // Original phase and phase unrolling buffers. 90 | uint16_t* phases_; 91 | uint16_t* phases_delta_; 92 | 93 | int8_t glitch_algorithm_; 94 | 95 | DISALLOW_COPY_AND_ASSIGN(FrameTransformation); 96 | }; 97 | 98 | } // namespace clouds 99 | 100 | #endif // CLOUDS_DSP_PVOC_FRAME_TRANSFORMATION_H_ 101 | -------------------------------------------------------------------------------- /clouds/dsp/mu_law.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Mu-law encoding. 28 | 29 | #include "clouds/dsp/mu_law.h" 30 | 31 | namespace clouds { 32 | 33 | /* extern */ 34 | int16_t lut_ulaw[256] = { 35 | -32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956, 36 | -23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764, 37 | -15996, -15484, -14972, -14460, -13948, -13436, -12924, -12412, 38 | -11900, -11388, -10876, -10364, -9852, -9340, -8828, -8316, 39 | -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, 40 | -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, 41 | -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, 42 | -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, 43 | -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, 44 | -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, 45 | -876, -844, -812, -780, -748, -716, -684, -652, 46 | -620, -588, -556, -524, -492, -460, -428, -396, 47 | -372, -356, -340, -324, -308, -292, -276, -260, 48 | -244, -228, -212, -196, -180, -164, -148, -132, 49 | -120, -112, -104, -96, -88, -80, -72, -64, 50 | -56, -48, -40, -32, -24, -16, -8, 0, 51 | 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, 52 | 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, 53 | 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412, 54 | 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, 55 | 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, 56 | 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, 57 | 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, 58 | 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, 59 | 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, 60 | 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, 61 | 876, 844, 812, 780, 748, 716, 684, 652, 62 | 620, 588, 556, 524, 492, 460, 428, 396, 63 | 372, 356, 340, 324, 308, 292, 276, 260, 64 | 244, 228, 212, 196, 180, 164, 148, 132, 65 | 120, 112, 104, 96, 88, 80, 72, 64, 66 | 56, 48, 40, 32, 24, 16, 8, 0 67 | }; 68 | 69 | } // namespace clouds 70 | -------------------------------------------------------------------------------- /clouds/dsp/fx/diffuser.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // AP diffusion network. 28 | 29 | #ifndef CLOUDS_DSP_FX_DIFFUSER_H_ 30 | #define CLOUDS_DSP_FX_DIFFUSER_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | #include "clouds/dsp/fx/fx_engine.h" 35 | 36 | namespace clouds { 37 | 38 | class Diffuser { 39 | public: 40 | Diffuser() { } 41 | ~Diffuser() { } 42 | 43 | void Init(float* buffer) { 44 | engine_.Init(buffer); 45 | } 46 | 47 | void Process(FloatFrame* in_out, size_t size) { 48 | typedef E::Reserve<126, 49 | E::Reserve<180, 50 | E::Reserve<269, 51 | E::Reserve<444, 52 | E::Reserve<151, 53 | E::Reserve<205, 54 | E::Reserve<245, 55 | E::Reserve<405> > > > > > > > Memory; 56 | E::DelayLine apl1; 57 | E::DelayLine apl2; 58 | E::DelayLine apl3; 59 | E::DelayLine apl4; 60 | E::DelayLine apr1; 61 | E::DelayLine apr2; 62 | E::DelayLine apr3; 63 | E::DelayLine apr4; 64 | E::Context c; 65 | const float kap = 0.625f; 66 | while (size--) { 67 | engine_.Start(&c); 68 | 69 | float wet = 0.0f; 70 | c.Read(in_out->l); 71 | c.Read(apl1 TAIL, kap); 72 | c.WriteAllPass(apl1, -kap); 73 | c.Read(apl2 TAIL, kap); 74 | c.WriteAllPass(apl2, -kap); 75 | c.Read(apl3 TAIL, kap); 76 | c.WriteAllPass(apl3, -kap); 77 | c.Read(apl4 TAIL, kap); 78 | c.WriteAllPass(apl4, -kap); 79 | c.Write(wet, 0.0f); 80 | in_out->l += amount_ * (wet - in_out->l); 81 | 82 | c.Read(in_out->r); 83 | c.Read(apr1 TAIL, kap); 84 | c.WriteAllPass(apr1, -kap); 85 | c.Read(apr2 TAIL, kap); 86 | c.WriteAllPass(apr2, -kap); 87 | c.Read(apr3 TAIL, kap); 88 | c.WriteAllPass(apr3, -kap); 89 | c.Read(apr4 TAIL, kap); 90 | c.WriteAllPass(apr4, -kap); 91 | c.Write(wet, 0.0f); 92 | in_out->r += amount_ * (wet - in_out->r); 93 | 94 | ++in_out; 95 | } 96 | } 97 | 98 | void set_amount(float amount) { 99 | amount_ = amount; 100 | } 101 | 102 | private: 103 | typedef FxEngine<2048, FORMAT_32_BIT> E; 104 | E engine_; 105 | 106 | float amount_; 107 | DISALLOW_COPY_AND_ASSIGN(Diffuser); 108 | }; 109 | 110 | } // namespace clouds 111 | 112 | #endif // CLOUDS_DSP_FX_DIFFUSER_H_ 113 | -------------------------------------------------------------------------------- /clouds/ui.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // User interface 28 | 29 | #ifndef CLOUDS_UI_H_ 30 | #define CLOUDS_UI_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | #include "stmlib/ui/event_queue.h" 35 | 36 | #include "clouds/drivers/leds.h" 37 | #include "clouds/drivers/switches.h" 38 | 39 | namespace clouds { 40 | 41 | enum UiMode { 42 | UI_MODE_VU_METER, 43 | UI_MODE_BLEND_METER, 44 | UI_MODE_QUALITY, 45 | UI_MODE_BLENDING, 46 | UI_MODE_PLAYBACK_MODE, 47 | UI_MODE_LOAD, 48 | UI_MODE_SAVE, 49 | UI_MODE_PANIC, 50 | UI_MODE_CALIBRATION_1, 51 | UI_MODE_CALIBRATION_2, 52 | UI_MODE_SAVING, 53 | UI_MODE_SPLASH, 54 | UI_MODE_LAST 55 | }; 56 | 57 | enum SwitchIndex { 58 | SWITCH_MODE, 59 | SWITCH_WRITE, 60 | SWITCH_FREEZE, 61 | SWITCH_BYPASS, 62 | }; 63 | 64 | enum FactoryTestingCommand { 65 | FACTORY_TESTING_READ_POT, 66 | FACTORY_TESTING_READ_CV, 67 | FACTORY_TESTING_READ_GATE, 68 | FACTORY_TESTING_SET_BYPASS, 69 | FACTORY_TESTING_CALIBRATE 70 | }; 71 | 72 | class GranularProcessor; 73 | class CvScaler; 74 | class Meter; 75 | class Settings; 76 | 77 | class Ui { 78 | public: 79 | Ui() { } 80 | ~Ui() { } 81 | 82 | void Init( 83 | Settings* settings, 84 | CvScaler* cv_scaler, 85 | GranularProcessor* processor, 86 | Meter* meter); 87 | void Poll(); 88 | void DoEvents(); 89 | void FlushEvents(); 90 | void Panic() { 91 | mode_ = UI_MODE_PANIC; 92 | } 93 | 94 | 95 | uint8_t HandleFactoryTestingRequest(uint8_t command); 96 | 97 | private: 98 | void OnSwitchPressed(const stmlib::Event& e); 99 | void OnSwitchReleased(const stmlib::Event& e); 100 | void OnSecretHandshake(); 101 | 102 | void CalibrateC1(); 103 | void CalibrateC3(); 104 | 105 | void SaveState(); 106 | void PaintLeds(); 107 | void LoadSampleMemory(); 108 | void SaveSampleMemory(); 109 | 110 | stmlib::EventQueue<16> queue_; 111 | 112 | Settings* settings_; 113 | CvScaler* cv_scaler_; 114 | 115 | Leds leds_; 116 | Switches switches_; 117 | uint32_t press_time_[kNumSwitches]; 118 | uint32_t long_press_time_[kNumSwitches]; 119 | UiMode mode_; 120 | 121 | GranularProcessor* processor_; 122 | Meter* meter_; 123 | 124 | uint8_t load_save_location_; 125 | uint16_t ignore_releases_; 126 | 127 | DISALLOW_COPY_AND_ASSIGN(Ui); 128 | }; 129 | 130 | } // namespace clouds 131 | 132 | #endif // CLOUDS_UI_H_ 133 | -------------------------------------------------------------------------------- /clouds/drivers/leds.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Driver for the 4 bicolor LEDs controlled by a 595. 28 | 29 | #include "clouds/drivers/leds.h" 30 | 31 | #include 32 | 33 | namespace clouds { 34 | 35 | using namespace std; 36 | 37 | const uint16_t kPinClk = GPIO_Pin_5; 38 | const uint16_t kPinEnable = GPIO_Pin_4; 39 | const uint16_t kPinData = GPIO_Pin_5; 40 | const uint16_t kPinFreezeLed = GPIO_Pin_9; 41 | 42 | void Leds::Init() { 43 | RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); 44 | RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); 45 | 46 | GPIO_InitTypeDef gpio_init; 47 | gpio_init.GPIO_Pin = kPinData | kPinFreezeLed; 48 | gpio_init.GPIO_Mode = GPIO_Mode_OUT; 49 | gpio_init.GPIO_OType = GPIO_OType_PP; 50 | gpio_init.GPIO_Speed = GPIO_Speed_25MHz; 51 | gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL; 52 | GPIO_Init(GPIOB, &gpio_init); 53 | 54 | gpio_init.GPIO_Pin = kPinClk | kPinEnable; 55 | gpio_init.GPIO_Mode = GPIO_Mode_OUT; 56 | gpio_init.GPIO_OType = GPIO_OType_PP; 57 | gpio_init.GPIO_Speed = GPIO_Speed_25MHz; 58 | gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL; 59 | GPIO_Init(GPIOC, &gpio_init); 60 | 61 | // "Enabled" LED 62 | gpio_init.GPIO_Pin = GPIO_Pin_9; 63 | GPIO_Init(GPIOC, &gpio_init); 64 | 65 | Clear(); 66 | pwm_counter_ = 0; 67 | } 68 | 69 | void Leds::Write() { 70 | // Pack data. 71 | pwm_counter_ += 32; 72 | uint8_t data = 0; 73 | for (uint16_t i = 0; i < 4; ++i) { 74 | data <<= 2; 75 | if (red_[i] && red_[i] >= pwm_counter_) { 76 | data |= 0x1; 77 | } 78 | if (green_[i] && green_[i] >= pwm_counter_) { 79 | data |= 0x2; 80 | } 81 | } 82 | // Shift out. 83 | GPIO_WriteBit(GPIOC, kPinEnable, Bit_RESET); 84 | for (uint16_t i = 0; i < 8; ++i) { 85 | GPIO_WriteBit(GPIOC, kPinClk, Bit_RESET); 86 | if (data & 0x80) { 87 | GPIO_WriteBit(GPIOB, kPinData, Bit_SET); 88 | } else { 89 | GPIO_WriteBit(GPIOB, kPinData, Bit_RESET); 90 | } 91 | data <<= 1; 92 | GPIO_WriteBit(GPIOC, kPinClk, Bit_SET); 93 | } 94 | GPIO_WriteBit(GPIOC, kPinEnable, Bit_SET); 95 | GPIO_WriteBit(GPIOB, kPinFreezeLed, static_cast(freeze_led_)); 96 | GPIO_WriteBit(GPIOC, GPIO_Pin_9, static_cast(enabled_led_)); 97 | } 98 | 99 | void Leds::Clear() { 100 | fill(&red_[0], &red_[4], 0); 101 | fill(&green_[0], &green_[4], 0); 102 | freeze_led_ = false; 103 | enabled_led_ = false; 104 | } 105 | 106 | } // namespace clouds 107 | -------------------------------------------------------------------------------- /clouds/dsp/pvoc/phase_vocoder.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Naive phase vocoder. 28 | 29 | #include "clouds/dsp/pvoc/phase_vocoder.h" 30 | 31 | #include 32 | 33 | #include "stmlib/utils/buffer_allocator.h" 34 | 35 | namespace clouds { 36 | 37 | using namespace std; 38 | using namespace stmlib; 39 | 40 | void PhaseVocoder::Init( 41 | void** buffer, 42 | size_t* buffer_size, 43 | const float* large_window_lut, 44 | size_t largest_fft_size, 45 | int32_t num_channels, 46 | int32_t resolution, 47 | float sample_rate) { 48 | num_channels_ = num_channels; 49 | 50 | size_t fft_size = largest_fft_size; 51 | size_t hop_ratio = 4; 52 | 53 | BufferAllocator allocator_0(buffer[0], buffer_size[0]); 54 | BufferAllocator allocator_1(buffer[1], buffer_size[1]); 55 | BufferAllocator* allocator[2] = { &allocator_0, &allocator_1 }; 56 | float* fft_buffer = allocator[0]->Allocate(fft_size); 57 | float* ifft_buffer = allocator[num_channels_ - 1]->Allocate(fft_size); 58 | 59 | size_t num_textures = kMaxNumTextures; 60 | size_t texture_size = (fft_size >> 1) - kHighFrequencyTruncation; 61 | for (int32_t i = 0; i < num_channels_; ++i) { 62 | short* ana_syn_buffer = allocator[i]->Allocate( 63 | (fft_size + (fft_size >> 1)) * 2); 64 | 65 | num_textures = min( 66 | allocator[i]->free() / (sizeof(float) * texture_size), 67 | num_textures); 68 | stft_[i].Init( 69 | &fft_, 70 | fft_size, 71 | fft_size / hop_ratio, 72 | fft_buffer, 73 | ifft_buffer, 74 | large_window_lut, 75 | ana_syn_buffer, 76 | &frame_transformation_[i]); 77 | } 78 | for (int32_t i = 0; i < num_channels_; ++i) { 79 | float* texture_buffer = allocator[i]->Allocate( 80 | num_textures * texture_size); 81 | frame_transformation_[i].Init(texture_buffer, fft_size, num_textures); 82 | } 83 | } 84 | 85 | void PhaseVocoder::Process( 86 | const Parameters& parameters, 87 | const FloatFrame* input, 88 | FloatFrame* output, size_t size) { 89 | const float* input_samples = &input[0].l; 90 | float* output_samples = &output[0].l; 91 | for (int32_t i = 0; i < num_channels_; ++i) { 92 | stft_[i].Process( 93 | parameters, 94 | input_samples + i, 95 | output_samples + i, 96 | size, 97 | 2); 98 | } 99 | } 100 | 101 | void PhaseVocoder::Buffer() { 102 | for (int32_t i = 0; i < num_channels_; ++i) { 103 | stft_[i].Buffer(); 104 | } 105 | } 106 | 107 | } // namespace clouds 108 | -------------------------------------------------------------------------------- /clouds/dsp/window.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Variant of the grain class used for WSOLA. 28 | 29 | #ifndef CLOUDS_DSP_WINDOW_H_ 30 | #define CLOUDS_DSP_WINDOW_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | #include "stmlib/dsp/dsp.h" 35 | 36 | #include "clouds/dsp/audio_buffer.h" 37 | 38 | #include "clouds/resources.h" 39 | 40 | namespace clouds { 41 | 42 | enum WindowFlags { 43 | WINDOW_FLAGS_HALF_DONE = 1, 44 | WINDOW_FLAGS_REGENERATED = 2, 45 | WINDOW_FLAGS_DONE = 4 46 | }; 47 | 48 | class Window { 49 | public: 50 | Window() { } 51 | ~Window() { } 52 | 53 | void Init() { 54 | done_ = true; 55 | regenerated_ = false; 56 | half_ = false; 57 | } 58 | 59 | void Start( 60 | int32_t buffer_size, 61 | int32_t start, 62 | int32_t width, 63 | int32_t phase_increment) { 64 | first_sample_ = (start + buffer_size) % buffer_size; 65 | phase_increment_ = phase_increment; 66 | phase_ = 0; 67 | done_ = false; 68 | regenerated_ = false; 69 | done_ = false; 70 | envelope_phase_increment_ = 2.0f / static_cast(width); 71 | } 72 | 73 | template 74 | inline void OverlapAdd( 75 | const AudioBuffer* buffer, 76 | float* samples, 77 | int32_t channels, 78 | float swap_channels) { 79 | if (done_) { 80 | return; 81 | } 82 | int32_t phase_integral = phase_ >> 16; 83 | int32_t phase_fractional = phase_ & 0xffff; 84 | int32_t sample_index = first_sample_ + phase_integral; 85 | 86 | float envelope_phase = phase_integral * envelope_phase_increment_; 87 | done_ = envelope_phase >= 2.0f; 88 | half_ = envelope_phase >= 1.0f; 89 | float gain = envelope_phase >= 1.0f 90 | ? 2.0f - envelope_phase 91 | : envelope_phase; 92 | 93 | float l = buffer[0].ReadHermite(sample_index, phase_fractional) * gain; 94 | if (channels == 1) { 95 | *samples++ += l; 96 | *samples++ += l; 97 | } else if (channels == 2) { 98 | float r = buffer[1].ReadHermite(sample_index, phase_fractional) * gain; 99 | *samples++ += l + (r - l) * swap_channels; 100 | *samples++ += r + (l - r) * swap_channels; 101 | } 102 | phase_ += phase_increment_; 103 | } 104 | 105 | inline bool done() { return done_; } 106 | inline bool needs_regeneration() { return half_ && !regenerated_; } 107 | inline void MarkAsRegenerated() { regenerated_ = true; } 108 | 109 | private: 110 | Window* next_; 111 | int32_t first_sample_; 112 | int32_t phase_; 113 | int32_t phase_increment_; 114 | float envelope_phase_increment_; 115 | 116 | bool done_; 117 | bool half_; 118 | bool regenerated_; 119 | 120 | DISALLOW_COPY_AND_ASSIGN(Window); 121 | }; 122 | 123 | } // namespace clouds 124 | 125 | #endif // CLOUDS_DSP_WINDOW_H_ 126 | -------------------------------------------------------------------------------- /clouds/dsp/fx/pitch_shifter.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Pitch shifter. 28 | 29 | #ifndef CLOUDS_DSP_FX_PITCH_SHIFTER_H_ 30 | #define CLOUDS_DSP_FX_PITCH_SHIFTER_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | #include "stmlib/dsp/dsp.h" 34 | 35 | #include "clouds/resources.h" 36 | #include "clouds/dsp/frame.h" 37 | #include "clouds/dsp/fx/fx_engine.h" 38 | 39 | namespace clouds { 40 | 41 | class PitchShifter { 42 | public: 43 | PitchShifter() { } 44 | ~PitchShifter() { } 45 | 46 | void Init(uint16_t* buffer) { 47 | engine_.Init(buffer); 48 | phase_ = 0; 49 | size_ = 2047.0f; 50 | dry_wet_ = 0.0f; 51 | } 52 | 53 | void Clear() { 54 | engine_.Clear(); 55 | } 56 | 57 | inline void Process(FloatFrame* input_output, size_t size) { 58 | while (size--) { 59 | Process(input_output); 60 | ++input_output; 61 | } 62 | } 63 | 64 | void Process(FloatFrame* input_output) { 65 | typedef E::Reserve<2047, E::Reserve<2047> > Memory; 66 | E::DelayLine left; 67 | E::DelayLine right; 68 | E::Context c; 69 | engine_.Start(&c); 70 | 71 | phase_ += (1.0f - ratio_) / size_; 72 | if (phase_ >= 1.0f) { 73 | phase_ -= 1.0f; 74 | } 75 | if (phase_ <= 0.0f) { 76 | phase_ += 1.0f; 77 | } 78 | float tri = 2.0f * (phase_ >= 0.5f ? 1.0f - phase_ : phase_); 79 | tri = stmlib::Interpolate(lut_window, tri, LUT_WINDOW_SIZE-1); 80 | float phase = phase_ * size_; 81 | float half = phase + size_ * 0.5f; 82 | if (half >= size_) { 83 | half -= size_; 84 | } 85 | 86 | float wet = 0.0f; 87 | 88 | c.Read(input_output->l, 1.0f); 89 | c.Write(left, 0.0f); 90 | c.InterpolateHermite(left, phase, tri); 91 | c.InterpolateHermite(left, half, 1.0f - tri); 92 | c.Write(wet, 0.0f); 93 | 94 | input_output->l += (wet - input_output->l) * dry_wet_; 95 | 96 | c.Read(input_output->r, 1.0f); 97 | c.Write(right, 0.0f); 98 | c.InterpolateHermite(right, phase, tri); 99 | c.InterpolateHermite(right, half, 1.0f - tri); 100 | c.Write(wet, 0.0f); 101 | 102 | input_output->r += (wet - input_output->r) * dry_wet_; 103 | 104 | } 105 | 106 | inline void set_ratio(float ratio) { 107 | ratio_ = ratio; 108 | } 109 | 110 | inline void set_dry_wet(float dry_wet) { 111 | dry_wet_ = dry_wet; 112 | } 113 | 114 | inline void set_size(float size) { 115 | float target_size = 128.0f + (2047.0f - 128.0f) * size * size * size; 116 | ONE_POLE(size_, target_size, 0.05f) 117 | } 118 | 119 | private: 120 | typedef FxEngine<4096, FORMAT_16_BIT> E; 121 | E engine_; 122 | float phase_; 123 | float ratio_; 124 | float size_; 125 | float dry_wet_; 126 | 127 | DISALLOW_COPY_AND_ASSIGN(PitchShifter); 128 | }; 129 | 130 | } // namespace clouds 131 | 132 | #endif // CLOUDS_DSP_FX_MINI_CHORUS_H_ 133 | -------------------------------------------------------------------------------- /clouds/clouds.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | 25 | #include "clouds/cv_scaler.h" 26 | #include "clouds/drivers/codec.h" 27 | #include "clouds/drivers/debug_pin.h" 28 | #include "clouds/drivers/debug_port.h" 29 | #include "clouds/drivers/system.h" 30 | #include "clouds/drivers/version.h" 31 | #include "clouds/dsp/granular_processor.h" 32 | #include "clouds/meter.h" 33 | #include "clouds/resources.h" 34 | #include "clouds/settings.h" 35 | #include "clouds/ui.h" 36 | 37 | // #define PROFILE_INTERRUPT 1 38 | 39 | using namespace clouds; 40 | using namespace stmlib; 41 | 42 | GranularProcessor processor; 43 | Codec codec; 44 | DebugPort debug_port; 45 | CvScaler cv_scaler; 46 | Meter meter; 47 | Settings settings; 48 | Ui ui; 49 | 50 | // Pre-allocate big blocks in main memory and CCM. No malloc here. 51 | uint8_t block_mem[118784]; 52 | uint8_t block_ccm[65536 - 128] __attribute__ ((section (".ccmdata"))); 53 | 54 | int __errno; 55 | 56 | // Default interrupt handlers. 57 | extern "C" { 58 | 59 | void NMI_Handler() { } 60 | void HardFault_Handler() { while (1); } 61 | void MemManage_Handler() { while (1); } 62 | void BusFault_Handler() { while (1); } 63 | void UsageFault_Handler() { while (1); } 64 | void SVC_Handler() { } 65 | void DebugMon_Handler() { } 66 | void PendSV_Handler() { } 67 | 68 | } 69 | 70 | extern "C" { 71 | 72 | void SysTick_Handler() { 73 | ui.Poll(); 74 | if (settings.freshly_baked()) { 75 | if (debug_port.readable()) { 76 | uint8_t command = debug_port.Read(); 77 | uint8_t response = ui.HandleFactoryTestingRequest(command); 78 | debug_port.Write(response); 79 | } 80 | } 81 | } 82 | 83 | } 84 | 85 | void FillBuffer(Codec::Frame* input, Codec::Frame* output, size_t n) { 86 | #ifdef PROFILE_INTERRUPT 87 | TIC 88 | #endif // PROFILE_INTERRUPT 89 | cv_scaler.Read(processor.mutable_parameters()); 90 | processor.Process((ShortFrame*)input, (ShortFrame*)output, n); 91 | meter.Process(processor.parameters().freeze ? output : input, n); 92 | #ifdef PROFILE_INTERRUPT 93 | TOC 94 | #endif // PROFILE_INTERRUPT 95 | } 96 | 97 | void Init() { 98 | System sys; 99 | Version version; 100 | 101 | sys.Init(true); 102 | version.Init(); 103 | 104 | // Init granular processor. 105 | processor.Init( 106 | block_mem, sizeof(block_mem), 107 | block_ccm, sizeof(block_ccm)); 108 | 109 | settings.Init(); 110 | cv_scaler.Init(settings.mutable_calibration_data()); 111 | meter.Init(32000); 112 | ui.Init(&settings, &cv_scaler, &processor, &meter); 113 | 114 | bool master = !version.revised(); 115 | if (!codec.Init(master, 32000)) { 116 | ui.Panic(); 117 | } 118 | if (!codec.Start(32, &FillBuffer)) { 119 | ui.Panic(); 120 | } 121 | if (settings.freshly_baked()) { 122 | #ifdef PROFILE_INTERRUPT 123 | DebugPin::Init(); 124 | #else 125 | debug_port.Init(); 126 | #endif // PROFILE_INTERRUPT 127 | } 128 | sys.StartTimers(); 129 | } 130 | 131 | int main(void) { 132 | Init(); 133 | while (1) { 134 | ui.DoEvents(); 135 | processor.Prepare(); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /clouds/cv_scaler.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Calibration settings. 28 | 29 | #ifndef CLOUDS_CV_SCALER_H_ 30 | #define CLOUDS_CV_SCALER_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | #include "clouds/settings.h" 35 | #include "clouds/drivers/adc.h" 36 | #include "clouds/drivers/gate_input.h" 37 | #include "clouds/dsp/parameters.h" 38 | 39 | namespace clouds { 40 | 41 | enum BlendParameter { 42 | BLEND_PARAMETER_DRY_WET, 43 | BLEND_PARAMETER_STEREO_SPREAD, 44 | BLEND_PARAMETER_FEEDBACK, 45 | BLEND_PARAMETER_REVERB, 46 | BLEND_PARAMETER_LAST 47 | }; 48 | 49 | struct CvTransformation { 50 | bool flip; 51 | bool remove_offset; 52 | float filter_coefficient; 53 | }; 54 | 55 | class CvScaler { 56 | public: 57 | CvScaler() { } 58 | ~CvScaler() { } 59 | 60 | void Init(CalibrationData* calibration_data); 61 | void Read(Parameters* parameters); 62 | 63 | void CalibrateC1() { 64 | cv_c1_ = adc_.float_value(ADC_V_OCT_CV); 65 | } 66 | 67 | void CalibrateOffsets() { 68 | for (size_t i = 0; i < ADC_CHANNEL_LAST; ++i) { 69 | calibration_data_->offset[i] = adc_.float_value(i); 70 | } 71 | } 72 | 73 | bool CalibrateC3() { 74 | float c3 = adc_.float_value(ADC_V_OCT_CV); // 0.4848 v0.1 ; 0.3640 v0.2 75 | float c1 = cv_c1_; // 0.6666 v0.1 ; 0.6488 v0.2 76 | float delta = c3 - c1; 77 | if (delta > -0.5f && delta < -0.0f) { 78 | calibration_data_->pitch_scale = 24.0f / (c3 - c1); 79 | calibration_data_->pitch_offset = 12.0f - \ 80 | calibration_data_->pitch_scale * c1; 81 | return true; 82 | } else { 83 | return false; 84 | } 85 | } 86 | 87 | inline uint8_t adc_value(size_t index) const { 88 | return adc_.value(index) >> 8; 89 | } 90 | 91 | inline bool gate(size_t index) const { 92 | return gate_input_.trigger(); 93 | } 94 | 95 | inline void set_blend_parameter(BlendParameter parameter) { 96 | blend_parameter_ = parameter; 97 | blend_knob_origin_ = previous_blend_knob_value_; 98 | } 99 | 100 | inline BlendParameter blend_parameter() const { 101 | return blend_parameter_; 102 | } 103 | 104 | inline float blend_value(BlendParameter parameter) const { 105 | return blend_[parameter]; 106 | } 107 | 108 | inline void set_blend_value(BlendParameter parameter, float value) { 109 | blend_[parameter] = value; 110 | } 111 | 112 | inline bool blend_knob_touched() { 113 | if (blend_knob_touched_) { 114 | blend_knob_touched_ = false; 115 | return true; 116 | } else return false; 117 | } 118 | 119 | void UnlockBlendKnob() { 120 | previous_blend_knob_value_ = -1.0f; 121 | } 122 | 123 | private: 124 | void UpdateBlendParameters(float knob, float cv); 125 | static const int kAdcLatency = 5; 126 | 127 | Adc adc_; 128 | GateInput gate_input_; 129 | CalibrationData* calibration_data_; 130 | 131 | float smoothed_adc_value_[ADC_CHANNEL_LAST]; 132 | static CvTransformation transformations_[ADC_CHANNEL_LAST]; 133 | 134 | float note_; 135 | 136 | BlendParameter blend_parameter_; 137 | float blend_[BLEND_PARAMETER_LAST]; 138 | float blend_mod_[BLEND_PARAMETER_LAST]; 139 | float previous_blend_knob_value_; 140 | 141 | float blend_knob_origin_; 142 | float blend_knob_quantized_; 143 | bool blend_knob_touched_; 144 | 145 | float cv_c1_; 146 | 147 | bool previous_trigger_[kAdcLatency]; 148 | bool previous_gate_[kAdcLatency]; 149 | 150 | DISALLOW_COPY_AND_ASSIGN(CvScaler); 151 | }; 152 | 153 | } // namespace clouds 154 | 155 | #endif // CLOUDS_CV_SCALER_H_ 156 | -------------------------------------------------------------------------------- /clouds/drivers/adc.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Driver for ADC. 28 | 29 | #include "clouds/drivers/adc.h" 30 | 31 | #include 32 | 33 | namespace clouds { 34 | 35 | void Adc::Init() { 36 | RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); 37 | RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOC, ENABLE); 38 | RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); 39 | 40 | DMA_InitTypeDef dma_init; 41 | ADC_CommonInitTypeDef adc_common_init; 42 | ADC_InitTypeDef adc_init; 43 | GPIO_InitTypeDef gpio_init; 44 | 45 | gpio_init.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3; 46 | gpio_init.GPIO_Pin |= GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7; 47 | gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL; 48 | gpio_init.GPIO_Mode = GPIO_Mode_AN; 49 | GPIO_Init(GPIOA, &gpio_init); 50 | 51 | gpio_init.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1; 52 | gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL; 53 | gpio_init.GPIO_Mode = GPIO_Mode_AN; 54 | GPIO_Init(GPIOC, &gpio_init); 55 | 56 | // Use DMA to automatically copy ADC data register to values_ buffer. 57 | dma_init.DMA_Channel = DMA_Channel_0; 58 | dma_init.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; 59 | dma_init.DMA_Memory0BaseAddr = (uint32_t)&values_[0]; 60 | dma_init.DMA_DIR = DMA_DIR_PeripheralToMemory; 61 | dma_init.DMA_BufferSize = ADC_CHANNEL_LAST; 62 | dma_init.DMA_PeripheralInc = DMA_PeripheralInc_Disable; 63 | dma_init.DMA_MemoryInc = DMA_MemoryInc_Enable; 64 | dma_init.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; 65 | dma_init.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; 66 | dma_init.DMA_Mode = DMA_Mode_Circular; 67 | dma_init.DMA_Priority = DMA_Priority_High; 68 | dma_init.DMA_FIFOMode = DMA_FIFOMode_Disable; 69 | dma_init.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; 70 | dma_init.DMA_MemoryBurst = DMA_MemoryBurst_Single; 71 | dma_init.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; 72 | DMA_Init(DMA2_Stream0, &dma_init); 73 | DMA_Cmd(DMA2_Stream0, ENABLE); 74 | 75 | adc_common_init.ADC_Mode = ADC_Mode_Independent; 76 | adc_common_init.ADC_Prescaler = ADC_Prescaler_Div8; 77 | adc_common_init.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; 78 | adc_common_init.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles; 79 | ADC_CommonInit(&adc_common_init); 80 | 81 | adc_init.ADC_Resolution = ADC_Resolution_12b; 82 | adc_init.ADC_ScanConvMode = ENABLE; 83 | adc_init.ADC_ContinuousConvMode = DISABLE; 84 | adc_init.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1; 85 | adc_init.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; 86 | adc_init.ADC_DataAlign = ADC_DataAlign_Left; 87 | adc_init.ADC_NbrOfConversion = ADC_CHANNEL_LAST; 88 | ADC_Init(ADC1, &adc_init); 89 | 90 | // 168M / 2 / 8 / (10 x (480 + 20)) = 2.1kHz. 91 | ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 1, ADC_SampleTime_480Cycles); 92 | ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 2, ADC_SampleTime_480Cycles); 93 | ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 3, ADC_SampleTime_480Cycles); 94 | ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 4, ADC_SampleTime_480Cycles); 95 | ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 5, ADC_SampleTime_480Cycles); 96 | ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 6, ADC_SampleTime_480Cycles); 97 | ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 7, ADC_SampleTime_480Cycles); 98 | ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 8, ADC_SampleTime_480Cycles); 99 | ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 9, ADC_SampleTime_480Cycles); 100 | ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 10, ADC_SampleTime_480Cycles); 101 | 102 | ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE); 103 | ADC_Cmd(ADC1, ENABLE); 104 | ADC_DMACmd(ADC1, ENABLE); 105 | Convert(); 106 | } 107 | 108 | void Adc::Convert() { 109 | ADC_SoftwareStartConv(ADC1); 110 | } 111 | 112 | void Adc::DeInit() { 113 | ADC_DeInit(); 114 | } 115 | 116 | } // namespace clouds 117 | -------------------------------------------------------------------------------- /clouds/test/clouds_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include "clouds/dsp/granular_processor.h" 36 | #include "clouds/resources.h" 37 | 38 | using namespace clouds; 39 | using namespace std; 40 | using namespace stmlib; 41 | 42 | const size_t kSampleRate = 32000; 43 | const size_t kBlockSize = 32; 44 | 45 | void write_wav_header(FILE* fp, int num_samples, int num_channels) { 46 | uint32_t l; 47 | uint16_t s; 48 | 49 | fwrite("RIFF", 4, 1, fp); 50 | l = 36 + num_samples * 2 * num_channels; 51 | fwrite(&l, 4, 1, fp); 52 | fwrite("WAVE", 4, 1, fp); 53 | 54 | fwrite("fmt ", 4, 1, fp); 55 | l = 16; 56 | fwrite(&l, 4, 1, fp); 57 | s = 1; 58 | fwrite(&s, 2, 1, fp); 59 | s = num_channels; 60 | fwrite(&s, 2, 1, fp); 61 | l = kSampleRate; 62 | fwrite(&l, 4, 1, fp); 63 | l = static_cast(kSampleRate) * 2 * num_channels; 64 | fwrite(&l, 4, 1, fp); 65 | s = 2 * num_channels; 66 | fwrite(&s, 2, 1, fp); 67 | s = 16; 68 | fwrite(&s, 2, 1, fp); 69 | 70 | fwrite("data", 4, 1, fp); 71 | l = num_samples * 2 * num_channels; 72 | fwrite(&l, 4, 1, fp); 73 | } 74 | 75 | void TestDSP() { 76 | size_t duration = 15; 77 | 78 | FILE* fp_in = fopen("audio_samples/sine.wav", "rb"); 79 | FILE* fp_out = fopen("clouds.wav", "wb"); 80 | 81 | size_t remaining_samples = kSampleRate * duration; 82 | write_wav_header(fp_out, remaining_samples, 2); 83 | fseek(fp_in, 48, SEEK_SET); 84 | 85 | uint8_t large_buffer[118784]; 86 | uint8_t small_buffer[65536 - 128]; 87 | 88 | GranularProcessor processor; 89 | processor.Init( 90 | &large_buffer[0], sizeof(large_buffer), 91 | &small_buffer[0],sizeof(small_buffer)); 92 | 93 | processor.set_num_channels(2); 94 | processor.set_low_fidelity(false); 95 | processor.set_playback_mode(PLAYBACK_MODE_GRANULAR); 96 | 97 | Parameters* p = processor.mutable_parameters(); 98 | 99 | size_t block_counter = 0; 100 | float phase_ = 0.0f; 101 | bool synthetic = false; 102 | processor.Prepare(); 103 | float pot_noise = 0.0f; 104 | while (remaining_samples) { 105 | // uint16_t tri = (remaining_samples * 0.4); 106 | // tri = tri > 32767 ? 65535 - tri : tri; 107 | // float triangle = tri / 32768.0f; 108 | 109 | p->gate = false; 110 | p->trigger = false;// || (block_counter & 2047) > 1024; 111 | p->freeze = false; // || (block_counter & 2047) > 1024; 112 | p->granular.reverse = true; 113 | pot_noise += 0.05f * ((Random::GetSample() / 32768.0f) * 0.05f - pot_noise); 114 | p->position = Random::GetFloat();//triangle * 0.0f + 0.5f; 115 | p->size = Random::GetFloat();//0.99f; 116 | p->pitch = Random::GetFloat() * 24.0f; //0.0f + (triangle > 0.5f ? 1.0f : 0.0f) * 0.0f; 117 | p->density = 0.99f; 118 | p->texture = 0.7f; 119 | p->dry_wet = 1.0f; 120 | p->stereo_spread = 0.0f; 121 | p->feedback = 0.0f; 122 | p->reverb = 0.0f; 123 | 124 | ++block_counter; 125 | ShortFrame input[kBlockSize]; 126 | ShortFrame output[kBlockSize]; 127 | 128 | if (synthetic) { 129 | for (size_t i = 0; i < kBlockSize; ++i) { 130 | phase_ += 400.0f / kSampleRate; // (block_counter & 512 ? 110.0f : 220.0f) / kSampleRate; 131 | while (phase_ >= 1.0) { 132 | phase_ -= 1.0; 133 | } 134 | input[i].l = 16384.0f * sinf(phase_ * M_PI * 2); 135 | input[i].r = 32768.0f * (phase_ - 0.5); 136 | // input[i].r = input[i].l = 0; 137 | } 138 | remaining_samples -= kBlockSize; 139 | } else { 140 | if (fread( 141 | input, 142 | sizeof(ShortFrame), 143 | kBlockSize, 144 | fp_in) != kBlockSize) { 145 | break; 146 | } 147 | remaining_samples -= kBlockSize; 148 | } 149 | processor.Process(input, output, kBlockSize); 150 | processor.Prepare(); 151 | fwrite(output, sizeof(ShortFrame), kBlockSize, fp_out); 152 | } 153 | fclose(fp_out); 154 | fclose(fp_in); 155 | } 156 | 157 | int main(void) { 158 | _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); 159 | TestDSP(); 160 | // TestGrainSize(); 161 | } 162 | -------------------------------------------------------------------------------- /clouds/dsp/fx/reverb.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Reverb. 28 | 29 | #ifndef CLOUDS_DSP_FX_REVERB_H_ 30 | #define CLOUDS_DSP_FX_REVERB_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | #include "clouds/dsp/fx/fx_engine.h" 35 | 36 | namespace clouds { 37 | 38 | class Reverb { 39 | public: 40 | Reverb() { } 41 | ~Reverb() { } 42 | 43 | void Init(uint16_t* buffer) { 44 | engine_.Init(buffer); 45 | engine_.SetLFOFrequency(LFO_1, 0.5f / 32000.0f); 46 | engine_.SetLFOFrequency(LFO_2, 0.3f / 32000.0f); 47 | lp_ = 0.7f; 48 | diffusion_ = 0.625f; 49 | } 50 | 51 | void Process(FloatFrame* in_out, size_t size) { 52 | // This is the Griesinger topology described in the Dattorro paper 53 | // (4 AP diffusers on the input, then a loop of 2x 2AP+1Delay). 54 | // Modulation is applied in the loop of the first diffuser AP for additional 55 | // smearing; and to the two long delays for a slow shimmer/chorus effect. 56 | typedef E::Reserve<113, 57 | E::Reserve<162, 58 | E::Reserve<241, 59 | E::Reserve<399, 60 | E::Reserve<1653, 61 | E::Reserve<2038, 62 | E::Reserve<3411, 63 | E::Reserve<1913, 64 | E::Reserve<1663, 65 | E::Reserve<4782> > > > > > > > > > Memory; 66 | E::DelayLine ap1; 67 | E::DelayLine ap2; 68 | E::DelayLine ap3; 69 | E::DelayLine ap4; 70 | E::DelayLine dap1a; 71 | E::DelayLine dap1b; 72 | E::DelayLine del1; 73 | E::DelayLine dap2a; 74 | E::DelayLine dap2b; 75 | E::DelayLine del2; 76 | E::Context c; 77 | 78 | const float kap = diffusion_; 79 | const float klp = lp_; 80 | const float krt = reverb_time_; 81 | const float amount = amount_; 82 | const float gain = input_gain_; 83 | 84 | float lp_1 = lp_decay_1_; 85 | float lp_2 = lp_decay_2_; 86 | 87 | while (size--) { 88 | float wet; 89 | float apout = 0.0f; 90 | engine_.Start(&c); 91 | 92 | // Smear AP1 inside the loop. 93 | c.Interpolate(ap1, 10.0f, LFO_1, 60.0f, 1.0f); 94 | c.Write(ap1, 100, 0.0f); 95 | 96 | c.Read(in_out->l + in_out->r, gain); 97 | 98 | // Diffuse through 4 allpasses. 99 | c.Read(ap1 TAIL, kap); 100 | c.WriteAllPass(ap1, -kap); 101 | c.Read(ap2 TAIL, kap); 102 | c.WriteAllPass(ap2, -kap); 103 | c.Read(ap3 TAIL, kap); 104 | c.WriteAllPass(ap3, -kap); 105 | c.Read(ap4 TAIL, kap); 106 | c.WriteAllPass(ap4, -kap); 107 | c.Write(apout); 108 | 109 | // Main reverb loop. 110 | c.Load(apout); 111 | c.Interpolate(del2, 4680.0f, LFO_2, 100.0f, krt); 112 | c.Lp(lp_1, klp); 113 | c.Read(dap1a TAIL, -kap); 114 | c.WriteAllPass(dap1a, kap); 115 | c.Read(dap1b TAIL, kap); 116 | c.WriteAllPass(dap1b, -kap); 117 | c.Write(del1, 2.0f); 118 | c.Write(wet, 0.0f); 119 | 120 | in_out->l += (wet - in_out->l) * amount; 121 | 122 | c.Load(apout); 123 | // c.Interpolate(del1, 4450.0f, LFO_1, 50.0f, krt); 124 | c.Read(del1 TAIL, krt); 125 | c.Lp(lp_2, klp); 126 | c.Read(dap2a TAIL, kap); 127 | c.WriteAllPass(dap2a, -kap); 128 | c.Read(dap2b TAIL, -kap); 129 | c.WriteAllPass(dap2b, kap); 130 | c.Write(del2, 2.0f); 131 | c.Write(wet, 0.0f); 132 | 133 | in_out->r += (wet - in_out->r) * amount; 134 | 135 | ++in_out; 136 | } 137 | 138 | lp_decay_1_ = lp_1; 139 | lp_decay_2_ = lp_2; 140 | } 141 | 142 | inline void set_amount(float amount) { 143 | amount_ = amount; 144 | } 145 | 146 | inline void set_input_gain(float input_gain) { 147 | input_gain_ = input_gain; 148 | } 149 | 150 | inline void set_time(float reverb_time) { 151 | reverb_time_ = reverb_time; 152 | } 153 | 154 | inline void set_diffusion(float diffusion) { 155 | diffusion_ = diffusion; 156 | } 157 | 158 | inline void set_lp(float lp) { 159 | lp_ = lp; 160 | } 161 | 162 | private: 163 | typedef FxEngine<16384, FORMAT_12_BIT> E; 164 | E engine_; 165 | 166 | float amount_; 167 | float input_gain_; 168 | float reverb_time_; 169 | float diffusion_; 170 | float lp_; 171 | 172 | float lp_decay_1_; 173 | float lp_decay_2_; 174 | 175 | DISALLOW_COPY_AND_ASSIGN(Reverb); 176 | }; 177 | 178 | } // namespace clouds 179 | 180 | #endif // CLOUDS_DSP_FX_REVERB_H_ 181 | -------------------------------------------------------------------------------- /clouds/resources/lookup_tables.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2.5 2 | # 3 | # Copyright 2014 Olivier Gillet. 4 | # 5 | # Author: Olivier Gillet (ol.gillet@gmail.com) 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | # THE SOFTWARE. 24 | # 25 | # See http://creativecommons.org/licenses/MIT/ for more information. 26 | # 27 | # ----------------------------------------------------------------------------- 28 | # 29 | # Lookup table definitions. 30 | 31 | import scipy.signal 32 | import numpy 33 | import pylab 34 | 35 | lookup_tables = [] 36 | int16_lookup_tables = [] 37 | 38 | """---------------------------------------------------------------------------- 39 | Cosine table. 40 | ----------------------------------------------------------------------------""" 41 | 42 | size = 1024 43 | t = numpy.arange(0, size + size / 4 + 1) / float(size) * numpy.pi * 2 44 | lookup_tables.append(('sin', numpy.sin(t))) 45 | 46 | """---------------------------------------------------------------------------- 47 | Raised cosine. 48 | ----------------------------------------------------------------------------""" 49 | 50 | size = 256 51 | t = numpy.arange(0, size + 1) / float(size) 52 | lookup_tables.append(('raised_cos', 1.0 - (numpy.cos(t * numpy.pi) + 1) / 2)) 53 | 54 | """---------------------------------------------------------------------------- 55 | XFade table 56 | ----------------------------------------------------------------------------""" 57 | 58 | size = 17 59 | t = numpy.arange(0, size) / float(size-1) 60 | t = 1.04 * t - 0.02 61 | t[t < 0] = 0 62 | t[t >= 1] = 1 63 | t *= numpy.pi / 2 64 | lookup_tables.append(('xfade_in', numpy.sin(t) * (2 ** -0.5))) 65 | lookup_tables.append(('xfade_out', numpy.cos(t) * (2 ** -0.5))) 66 | 67 | """---------------------------------------------------------------------------- 68 | Grain window. 69 | ----------------------------------------------------------------------------""" 70 | 71 | size = 4096 72 | t = numpy.arange(0, size + 1) / float(size) 73 | lookup_tables.append(('window', 1.0 - (numpy.cos(t * numpy.pi) + 1) / 2)) 74 | 75 | 76 | """---------------------------------------------------------------------------- 77 | Sine window. 78 | ----------------------------------------------------------------------------""" 79 | 80 | def sum_window(window, steps): 81 | n = window.shape[0] 82 | start = 0 83 | stride = n / steps 84 | s = 0 85 | for i in xrange(steps): 86 | s = s + window[start:start+stride] ** 2 87 | start += stride 88 | return s 89 | 90 | 91 | window_size = 4096 92 | t = numpy.arange(0.0, window_size) / window_size 93 | 94 | # Perfect reconstruction for overlap of 2 95 | sine = numpy.sin(numpy.pi * t) 96 | 97 | # Perfect reconstruction for overlap of 4 98 | raised = (0.5 * numpy.cos(numpy.pi * t * 2) + 0.5) * numpy.sqrt(4.0 / 3.0) 99 | 100 | # Needs tweaks to provide good reconstruction 101 | power = (1.0 - (2 * t - 1.0) ** 2.0) ** 1.25 102 | compensation = sum_window(power, 2) ** 0.5 103 | compensation = numpy.array(list(compensation) * 2) 104 | power /= compensation 105 | 106 | lookup_tables.append(('sine_window_4096', power)) 107 | 108 | 109 | 110 | """---------------------------------------------------------------------------- 111 | Linear to dB, for display 112 | ----------------------------------------------------------------------------""" 113 | 114 | db = numpy.arange(0, 257) 115 | db[0] = 1 116 | db[db > 255] = 255 117 | db = numpy.log2(db / 16.0) * 32768 / 4 118 | int16_lookup_tables += [('db', db)] 119 | 120 | 121 | 122 | """---------------------------------------------------------------------------- 123 | LPG cutoff 124 | ----------------------------------------------------------------------------""" 125 | 126 | TABLE_SIZE = 256 127 | 128 | cutoff = numpy.arange(0.0, TABLE_SIZE + 1) / TABLE_SIZE 129 | lookup_tables.append(('cutoff', 0.49 * 2 ** (-6 * (1 - cutoff)))) 130 | 131 | 132 | 133 | """---------------------------------------------------------------------------- 134 | Grain size table 135 | ----------------------------------------------------------------------------""" 136 | 137 | size = numpy.arange(0.0, TABLE_SIZE + 1) / TABLE_SIZE * 5 138 | lookup_tables.append(('grain_size', numpy.floor(512 * (2 ** size)))) 139 | 140 | 141 | 142 | """---------------------------------------------------------------------------- 143 | Quantizer for pitch. 144 | ----------------------------------------------------------------------------""" 145 | 146 | PITCH_TABLE_SIZE = 1025 147 | pitch = numpy.zeros((PITCH_TABLE_SIZE, )) 148 | notches = [-24, -12, -7, -4, -3, -1, -0.1, 0, 149 | 0.1, 1, 3, 4, 7, 12, 12, 24] 150 | n = len(notches) - 1 151 | for i in xrange(n): 152 | start_index = int(float(i) / n * PITCH_TABLE_SIZE) 153 | end_index = int(float(i + 1) / n * PITCH_TABLE_SIZE) 154 | length = end_index - start_index 155 | x = numpy.arange(0.0, length) / (length - 1) 156 | raised_cosine = 0.5 - 0.5 * numpy.cos(x * numpy.pi) 157 | xfade = 0.8 * raised_cosine + 0.2 * x 158 | pitch[start_index:end_index] = notches[i] + (notches[i + 1] - notches[i]) * xfade 159 | 160 | lookup_tables.append(('quantized_pitch', pitch)) 161 | -------------------------------------------------------------------------------- /clouds/dsp/pvoc/stft.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // STFT with overlap-add. 28 | 29 | #include "clouds/dsp/pvoc/stft.h" 30 | 31 | #include 32 | 33 | #include "clouds/dsp/pvoc/frame_transformation.h" 34 | #include "stmlib/dsp/dsp.h" 35 | 36 | namespace clouds { 37 | 38 | using namespace std; 39 | using namespace stmlib; 40 | 41 | void STFT::Init( 42 | FFT* fft, 43 | size_t fft_size, 44 | size_t hop_size, 45 | float* fft_buffer, 46 | float* ifft_buffer, 47 | const float* window_lut, 48 | short* analysis_synthesis_buffer, 49 | Modifier* modifier) { 50 | fft_size_ = fft_size; 51 | hop_size_ = hop_size; 52 | fft_num_passes_ = 0; 53 | for (size_t t = fft_size; t > 1; t >>= 1) { 54 | ++fft_num_passes_; 55 | } 56 | buffer_size_ = fft_size_ + hop_size_; 57 | 58 | fft_ = fft; 59 | #ifdef USE_ARM_FFT 60 | arm_rfft_fast_init_f32(fft_, fft_size); 61 | #else 62 | fft_->Init(); 63 | #endif // USE_ARM_FFT 64 | 65 | analysis_ = &analysis_synthesis_buffer[0]; 66 | synthesis_ = &analysis_synthesis_buffer[buffer_size_]; 67 | 68 | ifft_in_ = fft_in_ = fft_buffer; 69 | ifft_out_ = fft_out_ = ifft_buffer; 70 | 71 | window_ = window_lut; 72 | window_stride_ = LUT_SINE_WINDOW_4096_SIZE / fft_size; 73 | modifier_ = modifier; 74 | 75 | parameters_ = NULL; 76 | 77 | Reset(); 78 | } 79 | 80 | void STFT::Reset() { 81 | buffer_ptr_ = 0; 82 | process_ptr_ = (2 * hop_size_) % buffer_size_; 83 | block_size_ = 0; 84 | fill(&analysis_[0], &analysis_[buffer_size_], 0); 85 | fill(&synthesis_[0], &synthesis_[buffer_size_], 0); 86 | ready_ = 0; 87 | done_ = 0; 88 | } 89 | 90 | void STFT::Process( 91 | const Parameters& parameters, 92 | const float* input, 93 | float* output, 94 | size_t size, 95 | size_t stride) { 96 | parameters_ = ¶meters; 97 | while (size) { 98 | size_t processed = min(size, hop_size_ - block_size_); 99 | for (size_t i = 0; i < processed; ++i) { 100 | int32_t sample = *input * 32768.0f; 101 | analysis_[buffer_ptr_ + i] = Clip16(sample); 102 | *output = static_cast(synthesis_[buffer_ptr_ + i]) / 16384.0f; 103 | input += stride; 104 | output += stride; 105 | } 106 | 107 | block_size_ += processed; 108 | size -= processed; 109 | buffer_ptr_ += processed; 110 | if (buffer_ptr_ >= buffer_size_) { 111 | buffer_ptr_ -= buffer_size_; 112 | } 113 | if (block_size_ >= hop_size_) { 114 | block_size_ -= hop_size_; 115 | ++ready_; 116 | } 117 | } 118 | } 119 | 120 | void STFT::Buffer() { 121 | if (ready_ == done_) { 122 | return; 123 | } 124 | 125 | // Copy block to FFT buffer and apply window. 126 | size_t source_ptr = process_ptr_; 127 | const float* w = window_; 128 | for (size_t i = 0; i < fft_size_; ++i) { 129 | fft_in_[i] = w[0] * analysis_[source_ptr]; 130 | ++source_ptr; 131 | if (source_ptr >= buffer_size_) { 132 | source_ptr -= buffer_size_; 133 | } 134 | w += window_stride_; 135 | } 136 | 137 | // Compute FFT. fft_in is lost. 138 | #ifdef USE_ARM_FFT 139 | arm_rfft_fast_f32(fft_, fft_in_, fft_out_, 0); 140 | copy(&fft_out_[0], &fft_out_[fft_size_], &fft_in_[0]); 141 | // Re-arrange data. 142 | for (size_t i = 0; i < fft_size_ / 2; ++i) { 143 | fft_out_[i] = fft_in_[2 * i]; 144 | fft_out_[i + fft_size_ / 2] = fft_in_[2 * i + 1]; 145 | } 146 | #else 147 | if (fft_size_ != FFT::max_size) { 148 | fft_->Direct(fft_in_, fft_out_, fft_num_passes_); 149 | } else { 150 | fft_->Direct(fft_in_, fft_out_); 151 | } 152 | #endif // USE_ARM_FFT 153 | // Process in the frequency domain. 154 | if (modifier_ != NULL && parameters_ != NULL) { 155 | modifier_->Process(*parameters_, &fft_out_[0], &ifft_in_[0]); 156 | } else { 157 | copy(&fft_out_[0], &fft_out_[fft_size_], &ifft_in_[0]); 158 | } 159 | 160 | // Compute IFFT. ifft_in is lost. 161 | #ifdef USE_ARM_FFT 162 | // Re-arrange data. 163 | copy(&ifft_in_[0], &ifft_in_[fft_size_], &ifft_out_[0]); 164 | for (size_t i = 0; i < fft_size_ / 2; ++i) { 165 | ifft_in_[2 * i] = ifft_out_[i]; 166 | ifft_in_[2 * i + 1] = ifft_out_[i + fft_size_ / 2]; 167 | } 168 | arm_rfft_fast_f32(fft_, ifft_in_, ifft_out_, 1); 169 | #else 170 | if (fft_size_ != FFT::max_size) { 171 | fft_->Inverse(ifft_in_, ifft_out_, fft_num_passes_); 172 | } else { 173 | fft_->Inverse(ifft_in_, ifft_out_); 174 | } 175 | #endif // USE_ARM_FFT 176 | 177 | size_t destination_ptr = process_ptr_; 178 | #ifdef USE_ARM_FFT 179 | float inverse_window_size = 1.0f / \ 180 | float(fft_size_ / hop_size_ >> 1); 181 | #else 182 | float inverse_window_size = 1.0f / \ 183 | float(fft_size_ * fft_size_ / hop_size_ >> 1); 184 | #endif // USE_ARM_FFT 185 | 186 | w = window_; 187 | for (size_t i = 0; i < fft_size_; ++i) { 188 | float s = ifft_out_[i] * w[0] * inverse_window_size; 189 | 190 | int32_t x = static_cast(s); 191 | if (i < fft_size_ - hop_size_) { 192 | // Overlap-add. 193 | x += synthesis_[destination_ptr]; 194 | } 195 | synthesis_[destination_ptr] = Clip16(x); 196 | ++destination_ptr; 197 | if (destination_ptr >= buffer_size_) { 198 | destination_ptr -= buffer_size_; 199 | } 200 | w += window_stride_; 201 | } 202 | 203 | ++done_; 204 | process_ptr_ += hop_size_; 205 | if (process_ptr_ >= buffer_size_) { 206 | process_ptr_ -= buffer_size_; 207 | } 208 | } 209 | 210 | } // namespace clouds 211 | -------------------------------------------------------------------------------- /clouds/dsp/grain.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Single grain synthesis. 28 | 29 | #ifndef CLOUDS_DSP_GRAIN_H_ 30 | #define CLOUDS_DSP_GRAIN_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | #include "stmlib/dsp/dsp.h" 35 | 36 | #include "clouds/dsp/audio_buffer.h" 37 | 38 | #include "clouds/resources.h" 39 | 40 | namespace clouds { 41 | 42 | const float slope_response[4] = { 1.3f, 1.0f, 1.0f, 1.0f }; 43 | const float bias_response[4] = { 1.0f, 2.0f - 1.0f/500.0f, 1.0f/500.0f, 1.0f }; 44 | 45 | enum GrainQuality { 46 | GRAIN_QUALITY_LOW, 47 | GRAIN_QUALITY_MEDIUM, 48 | GRAIN_QUALITY_HIGH 49 | }; 50 | 51 | class Grain { 52 | public: 53 | Grain() { } 54 | ~Grain() { } 55 | 56 | inline float InterpolatePlateau(const float* table, float index, float size) { 57 | index *= size; 58 | MAKE_INTEGRAL_FRACTIONAL(index) 59 | float a = table[index_integral]; 60 | float b = table[index_integral + 1]; 61 | if (index_fractional < 1.0f/1.1f) 62 | return a + (b - a) * index_fractional * 1.1f; 63 | else 64 | return b; 65 | } 66 | 67 | void Init() { 68 | active_ = false; 69 | envelope_phase_ = 2.0f; 70 | } 71 | 72 | void Start( 73 | int32_t pre_delay, 74 | int32_t buffer_size, 75 | int32_t start, 76 | int32_t width, 77 | bool reverse, 78 | int32_t phase_increment, 79 | float window_shape, 80 | float gain_l, 81 | float gain_r, 82 | GrainQuality recommended_quality) { 83 | pre_delay_ = pre_delay; 84 | reverse_ = reverse; 85 | 86 | first_sample_ = (start + buffer_size) % buffer_size; 87 | if (reverse) { 88 | phase_increment_ = -phase_increment; 89 | phase_ = width * phase_increment; 90 | } else { 91 | phase_increment_ = phase_increment; 92 | phase_ = 0; 93 | } 94 | envelope_phase_ = 0.0f; 95 | envelope_phase_increment_ = 2.0f / static_cast(width); 96 | 97 | envelope_slope_ = InterpolatePlateau(slope_response, window_shape, 3); 98 | envelope_slope_ *= envelope_slope_ * envelope_slope_; 99 | envelope_slope_ *= envelope_slope_ * envelope_slope_; 100 | envelope_slope_ *= envelope_slope_ * envelope_slope_; 101 | envelope_bias_ = InterpolatePlateau(bias_response, window_shape, 3); 102 | 103 | active_ = true; 104 | gain_l_ = gain_l; 105 | gain_r_ = gain_r; 106 | recommended_quality_ = recommended_quality; 107 | } 108 | 109 | inline void RenderEnvelope(float* destination, size_t size) { 110 | const float increment = envelope_phase_increment_; 111 | const float slope = envelope_slope_; 112 | const float bias = envelope_bias_; 113 | 114 | float phase = envelope_phase_; 115 | while (size--) { 116 | float gain = phase <= bias ? 117 | phase * slope / bias : 118 | (2.0f - phase) * slope / (2.0f - bias); 119 | if (gain > 1.0f) gain = 1.0f; 120 | phase += increment; 121 | if (phase >= 2.0f) { 122 | *destination = -1.0f; 123 | break; 124 | } 125 | *destination++ = gain; 126 | } 127 | envelope_phase_ = phase; 128 | } 129 | 130 | template 131 | inline void OverlapAdd( 132 | const AudioBuffer* buffer, 133 | float* destination, 134 | float* envelope, 135 | size_t size) { 136 | if (!active_) { 137 | return; 138 | } 139 | // Rendering is done on 32-sample long blocks. The pre-delay allows grains 140 | // to start at arbitrary samples within a block, rather than at block 141 | // boundaries. 142 | while (pre_delay_ && size) { 143 | destination += 2; 144 | --size; 145 | --pre_delay_; 146 | } 147 | 148 | // Pre-render the envelope in one pass. 149 | RenderEnvelope(envelope, size); 150 | 151 | const int32_t phase_increment = phase_increment_; 152 | const int32_t first_sample = first_sample_; 153 | const float gain_l = gain_l_; 154 | const float gain_r = gain_r_; 155 | int32_t phase = phase_; 156 | while (size--) { 157 | int32_t sample_index = first_sample + (phase >> 16); 158 | 159 | float gain = *envelope++; 160 | if (gain == -1.0f) { 161 | active_ = false; 162 | break; 163 | } 164 | 165 | float l = buffer[0].template Read( 166 | sample_index, phase & 65535) * gain; 167 | if (num_channels == 1) { 168 | *destination++ += l * gain_l; 169 | *destination++ += l * gain_r; 170 | } else if (num_channels == 2) { 171 | float r = buffer[1].template Read( 172 | sample_index, phase & 65535) * gain; 173 | *destination++ += l * gain_l + r * (1.0f - gain_r); 174 | *destination++ += r * gain_r + l * (1.0f - gain_l); 175 | } 176 | phase += phase_increment; 177 | } 178 | phase_ = phase; 179 | } 180 | 181 | inline bool active() { return active_; } 182 | 183 | inline GrainQuality recommended_quality() const { 184 | return recommended_quality_; 185 | } 186 | 187 | private: 188 | int32_t first_sample_; 189 | int32_t phase_; 190 | int32_t phase_increment_; 191 | int32_t pre_delay_; 192 | 193 | float envelope_slope_; 194 | float envelope_bias_; /* asymetry of envelope: -1..1 */ 195 | float envelope_phase_; 196 | float envelope_phase_increment_; 197 | 198 | float gain_l_; 199 | float gain_r_; 200 | 201 | bool active_; 202 | bool reverse_; 203 | 204 | GrainQuality recommended_quality_; 205 | 206 | DISALLOW_COPY_AND_ASSIGN(Grain); 207 | }; 208 | 209 | } // namespace clouds 210 | 211 | #endif // CLOUDS_DSP_GRAIN_H_ 212 | -------------------------------------------------------------------------------- /clouds/dsp/granular_processor.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Main processing class. 28 | 29 | #ifndef CLOUDS_DSP_GRANULAR_PROCESSOR_H_ 30 | #define CLOUDS_DSP_GRANULAR_PROCESSOR_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | #include "stmlib/dsp/filter.h" 34 | 35 | #include "clouds/dsp/correlator.h" 36 | #include "clouds/dsp/frame.h" 37 | #include "clouds/dsp/fx/diffuser.h" 38 | #include "clouds/dsp/fx/pitch_shifter.h" 39 | #include "clouds/dsp/fx/reverb.h" 40 | #include "clouds/dsp/resonestor.h" 41 | #include "clouds/dsp/fx/oliverb.h" 42 | #include "clouds/dsp/granular_processor.h" 43 | #include "clouds/dsp/granular_sample_player.h" 44 | #include "clouds/dsp/looping_sample_player.h" 45 | #include "clouds/dsp/pvoc/phase_vocoder.h" 46 | #include "clouds/dsp/sample_rate_converter.h" 47 | #include "clouds/dsp/wsola_sample_player.h" 48 | 49 | namespace clouds { 50 | 51 | const int32_t kDownsamplingFactor = 2; 52 | 53 | enum PlaybackMode { 54 | PLAYBACK_MODE_GRANULAR, 55 | PLAYBACK_MODE_STRETCH, 56 | PLAYBACK_MODE_LOOPING_DELAY, 57 | PLAYBACK_MODE_SPECTRAL, 58 | PLAYBACK_MODE_OLIVERB, 59 | PLAYBACK_MODE_RESONESTOR, 60 | PLAYBACK_MODE_LAST 61 | }; 62 | 63 | // State of the recording buffer as saved in one of the 4 sample memories. 64 | struct PersistentState { 65 | int32_t write_head[2]; 66 | uint8_t quality; 67 | uint8_t spectral; 68 | }; 69 | 70 | // Data block as saved in one of the 4 sample memories. 71 | struct PersistentBlock { 72 | uint32_t tag; 73 | uint32_t size; 74 | void* data; 75 | }; 76 | 77 | class GranularProcessor { 78 | public: 79 | GranularProcessor() { } 80 | ~GranularProcessor() { } 81 | 82 | void Init( 83 | void* large_buffer, 84 | size_t large_buffer_size, 85 | void* small_buffer, 86 | size_t small_buffer_size); 87 | 88 | void Process(ShortFrame* input, ShortFrame* output, size_t size); 89 | void Prepare(); 90 | 91 | inline Parameters* mutable_parameters() { 92 | return ¶meters_; 93 | } 94 | 95 | inline const Parameters& parameters() const { 96 | return parameters_; 97 | } 98 | 99 | inline void ToggleFreeze() { 100 | parameters_.freeze = !parameters_.freeze; 101 | } 102 | 103 | inline void ToggleReverse() { 104 | parameters_.granular.reverse = !parameters_.granular.reverse; 105 | } 106 | 107 | inline void ToggleBypass() { 108 | bypass_ = !bypass_; 109 | } 110 | 111 | inline void set_inf_reverb(bool state) { 112 | inf_reverb_ = state; 113 | } 114 | 115 | inline bool inf_reverb() { 116 | return inf_reverb_; 117 | } 118 | 119 | inline bool frozen() const { 120 | return parameters_.freeze; 121 | } 122 | 123 | inline bool reversed() const { 124 | return parameters_.granular.reverse; 125 | } 126 | 127 | inline void set_silence(bool silence) { 128 | silence_ = silence; 129 | } 130 | 131 | inline void set_bypass(bool bypass) { 132 | bypass_ = bypass; 133 | } 134 | 135 | inline bool bypass() { return bypass_; } 136 | 137 | inline void set_playback_mode(PlaybackMode playback_mode) { 138 | playback_mode_ = playback_mode; 139 | } 140 | 141 | inline PlaybackMode playback_mode() const { return playback_mode_; } 142 | 143 | inline void set_quality(int32_t quality) { 144 | set_num_channels(quality & 1 ? 1 : 2); 145 | set_low_fidelity(quality >> 1 ? true : false); 146 | } 147 | 148 | inline void set_num_channels(int32_t num_channels) { 149 | reset_buffers_ = reset_buffers_ || num_channels_ != num_channels; 150 | num_channels_ = num_channels; 151 | } 152 | 153 | inline void set_low_fidelity(bool low_fidelity) { 154 | reset_buffers_ = reset_buffers_ || low_fidelity != low_fidelity_; 155 | low_fidelity_ = low_fidelity; 156 | } 157 | 158 | inline int32_t quality() const { 159 | int32_t quality = 0; 160 | if (num_channels_ == 1) quality |= 1; 161 | if (low_fidelity_) quality |= 2; 162 | return quality; 163 | } 164 | 165 | void GetPersistentData(PersistentBlock* block, size_t *num_blocks); 166 | bool LoadPersistentData(const uint32_t* data); 167 | void PreparePersistentData(); 168 | 169 | private: 170 | inline int32_t resolution() const { 171 | return low_fidelity_ ? 8 : 16; 172 | } 173 | 174 | inline float sample_rate() const { 175 | return 32000.0f / \ 176 | (low_fidelity_ ? kDownsamplingFactor : 1); 177 | } 178 | 179 | void ResetFilters(); 180 | void ProcessGranular(FloatFrame* input, FloatFrame* output, size_t size); 181 | 182 | PlaybackMode playback_mode_; 183 | PlaybackMode previous_playback_mode_; 184 | int32_t num_channels_; 185 | bool low_fidelity_; 186 | 187 | bool silence_; 188 | bool bypass_; 189 | bool inf_reverb_; 190 | bool reset_buffers_; 191 | float freeze_lp_; 192 | float dry_wet_, dry_wet_lp_; 193 | 194 | void* buffer_[2]; 195 | size_t buffer_size_[2]; 196 | 197 | Correlator correlator_; 198 | 199 | GranularSamplePlayer player_; 200 | WSOLASamplePlayer ws_player_; 201 | LoopingSamplePlayer looper_; 202 | PhaseVocoder phase_vocoder_; 203 | 204 | Diffuser diffuser_; 205 | Reverb reverb_; 206 | Oliverb oliverb_; 207 | Resonestor resonestor_; 208 | PitchShifter pitch_shifter_; 209 | stmlib::Svf fb_filter_[2]; 210 | stmlib::Svf hp_filter_[2]; 211 | stmlib::Svf lp_filter_[2]; 212 | 213 | AudioBuffer buffer_8_[2]; 214 | AudioBuffer buffer_16_[2]; 215 | 216 | FloatFrame in_[kMaxBlockSize]; 217 | FloatFrame in_downsampled_[kMaxBlockSize / kDownsamplingFactor]; 218 | FloatFrame out_downsampled_[kMaxBlockSize / kDownsamplingFactor]; 219 | FloatFrame out_[kMaxBlockSize]; 220 | FloatFrame fb_[kMaxBlockSize]; 221 | 222 | int16_t tail_buffer_[2][256]; 223 | 224 | Parameters parameters_; 225 | 226 | SampleRateConverter<-kDownsamplingFactor, 45, src_filter_1x_2_45> src_down_; 227 | SampleRateConverter<+kDownsamplingFactor, 45, src_filter_1x_2_45> src_up_; 228 | 229 | PersistentState persistent_state_; 230 | 231 | DISALLOW_COPY_AND_ASSIGN(GranularProcessor); 232 | }; 233 | 234 | } // namespace clouds 235 | 236 | #endif // CLOUDS_DSP_GRANULAR_PROCESSOR_H_ 237 | -------------------------------------------------------------------------------- /clouds/dsp/looping_sample_player.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Naive playback of audio stored in a buffer. 28 | 29 | #ifndef CLOUDS_DSP_LOOPING_SAMPLE_PLAYER_H_ 30 | #define CLOUDS_DSP_LOOPING_SAMPLE_PLAYER_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | #include 35 | 36 | #include "stmlib/dsp/units.h" 37 | 38 | #include "clouds/dsp/audio_buffer.h" 39 | #include "clouds/dsp/frame.h" 40 | #include "clouds/dsp/parameters.h" 41 | 42 | #include "clouds/resources.h" 43 | 44 | namespace clouds { 45 | 46 | const float kCrossfadeDuration = 64.0f; 47 | const int kMultDivSteps = 16; 48 | const float kMultDivs[kMultDivSteps] = { 49 | 1.0f/16.0f, 3.0f/32.0f, 1.0f/8.0f, 3.0f/16.0f, 50 | 1.0f/4.0f, 3.0f/8.0f, 1.0f/2.0f, 3.0f/4.0f, 51 | 1.0f, 52 | 3.0f/2.0f, 2.0f/1.0f, 3.0f/1.0f, 4.0f/1.0f, 53 | 6.0f/1.0f, 8.0f/1.0f, 12.0f/1.0f 54 | }; 55 | 56 | using namespace stmlib; 57 | 58 | class LoopingSamplePlayer { 59 | public: 60 | LoopingSamplePlayer() { } 61 | ~LoopingSamplePlayer() { } 62 | 63 | void Init(int32_t num_channels) { 64 | num_channels_ = num_channels; 65 | phase_ = 0.0f; 66 | current_delay_ = 0.0f; 67 | loop_point_ = 0.0f; 68 | loop_duration_ = 0.0f; 69 | tap_delay_ = 0; 70 | tap_delay_counter_ = 0; 71 | synchronized_ = false; 72 | tail_duration_ = 1.0f; 73 | } 74 | 75 | inline bool synchronized() const { return synchronized_; } 76 | 77 | template 78 | void Play( 79 | const AudioBuffer* buffer, 80 | const Parameters& parameters, 81 | float* out, size_t size) { 82 | 83 | int32_t max_delay = buffer->size() - kCrossfadeDuration; 84 | tap_delay_counter_ += size; 85 | if (tap_delay_counter_ > max_delay) { 86 | tap_delay_ = 0; 87 | tap_delay_counter_ = 0; 88 | synchronized_ = false; 89 | } 90 | if (parameters.trigger) { 91 | if(tap_delay_counter_ > 128) { 92 | synchronized_ = true; 93 | tap_delay_ = tap_delay_counter_; 94 | loop_reset_ = phase_; 95 | phase_ = 0.0f; 96 | } 97 | tap_delay_counter_ = 0; 98 | } 99 | 100 | if (synchronized_) 101 | smoothed_tap_delay_ += 0.01f * (tap_delay_ - smoothed_tap_delay_); 102 | 103 | float target_delay = parameters.position * parameters.position * max_delay; 104 | if (synchronized_) { 105 | int index = roundf(parameters.position * 106 | static_cast(kMultDivSteps)); 107 | CONSTRAIN(index, 0, kMultDivSteps-1); 108 | do target_delay = kMultDivs[index--] * static_cast(smoothed_tap_delay_); 109 | while (target_delay > max_delay && index >= 0); 110 | } 111 | 112 | const float swap_channels = parameters.stereo_spread; 113 | 114 | if (!parameters.freeze) { 115 | while (size--) { 116 | float error = (target_delay - current_delay_); 117 | float delay = current_delay_ + 0.0005f * error; 118 | current_delay_ = delay; 119 | int32_t delay_int = (buffer->head() - 4 - size + buffer->size()) << 12; 120 | delay_int -= static_cast(delay * 4096.0f); 121 | 122 | float l = buffer[0].ReadHermite((delay_int >> 12), delay_int << 4); 123 | if (num_channels_ == 1) { 124 | *out++ = l; 125 | *out++ = l; 126 | } else if (num_channels_ == 2) { 127 | float r = buffer[1].ReadHermite((delay_int >> 12), delay_int << 4); 128 | *out++ = l + (r - l) * swap_channels; 129 | *out++ = r + (l - r) * swap_channels; 130 | } 131 | } 132 | phase_ = 0.0f; 133 | } else { 134 | float loop_point = parameters.position * max_delay * 15.0f / 16.0f; 135 | loop_point += kCrossfadeDuration; 136 | float d = parameters.size; 137 | float loop_duration = (0.01f + 0.99f * d * d) * max_delay; 138 | if (synchronized_) { 139 | int index = roundf(d * static_cast(kMultDivSteps)); 140 | CONSTRAIN(index, 0, kMultDivSteps-1); 141 | do loop_duration = kMultDivs[index--] * static_cast(smoothed_tap_delay_); 142 | while (loop_duration > max_delay && index >= 0); 143 | } 144 | if (loop_point + loop_duration >= max_delay) { 145 | loop_point = max_delay - loop_duration; 146 | } 147 | float phase_increment = synchronized_ 148 | ? 1.0f 149 | : SemitonesToRatio(parameters.pitch); 150 | 151 | while (size--) { 152 | ONE_POLE(smoothed_tap_delay_, tap_delay_, 0.00001f); 153 | 154 | if (phase_ >= loop_duration_ || phase_ == 0.0f) { 155 | if (phase_ >= loop_duration_) { 156 | loop_reset_ = loop_duration_; 157 | } 158 | if (loop_reset_ >= loop_duration_) { 159 | loop_reset_ = loop_duration_; 160 | } 161 | tail_start_ = loop_duration_ - loop_reset_ + loop_point_; 162 | phase_ = 0.0f; 163 | tail_duration_ = std::min( 164 | kCrossfadeDuration, 165 | kCrossfadeDuration * phase_increment); 166 | loop_point_ = loop_point; 167 | loop_duration_ = loop_duration; 168 | } 169 | phase_ += phase_increment; 170 | 171 | float gain = 1.0f; 172 | if (tail_duration_ != 0.0f) { 173 | gain = phase_ / tail_duration_; 174 | CONSTRAIN(gain, 0.0f, 1.0f); 175 | } 176 | int32_t delay_int = (buffer->head() - 4 + buffer->size()) << 12; 177 | 178 | float ph = parameters.granular.reverse ? 179 | loop_duration_ - phase_ : 180 | phase_; 181 | 182 | int32_t position = delay_int - static_cast( 183 | (loop_duration_ - ph + loop_point_) * 4096.0f); 184 | float l = buffer[0].ReadHermite((position >> 12), position << 4); 185 | if (num_channels_ == 1) { 186 | out[0] = l * gain; 187 | out[1] = l * gain; 188 | } else if (num_channels_ == 2) { 189 | float r = buffer[1].ReadHermite((position >> 12), position << 4); 190 | out[0] = (l + (r - l) * swap_channels) * gain; 191 | out[1] = (r + (l - r) * swap_channels) * gain; 192 | } 193 | 194 | if (gain != 1.0f) { 195 | gain = 1.0f - gain; 196 | int32_t position = delay_int - static_cast( 197 | (-phase_ + tail_start_) * 4096.0f); 198 | 199 | float l = buffer[0].ReadHermite((position >> 12), position << 4); 200 | if (num_channels_ == 1) { 201 | out[0] += l * gain; 202 | out[1] += l * gain; 203 | } else if (num_channels_ == 2) { 204 | float r = buffer[1].ReadHermite((position >> 12), position << 4); 205 | out[0] += (l + (r - l) * swap_channels) * gain; 206 | out[1] += (r + (l - r) * swap_channels) * gain; 207 | } 208 | } 209 | out += 2; 210 | } 211 | } 212 | } 213 | 214 | private: 215 | float phase_; 216 | float current_delay_; 217 | 218 | float loop_point_; 219 | float loop_duration_; 220 | float tail_start_; 221 | float tail_duration_; 222 | float loop_reset_; 223 | 224 | bool synchronized_; 225 | 226 | int32_t num_channels_; 227 | int32_t elapsed_; 228 | int32_t tap_delay_; 229 | int32_t smoothed_tap_delay_; 230 | int32_t tap_delay_counter_; 231 | 232 | DISALLOW_COPY_AND_ASSIGN(LoopingSamplePlayer); 233 | }; 234 | 235 | } // namespace clouds 236 | 237 | #endif // CLOUDS_DSP_LOOPING_SAMPLE_PLAYER_H_ 238 | -------------------------------------------------------------------------------- /clouds/cv_scaler.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Calibration settings. 28 | 29 | #include "clouds/cv_scaler.h" 30 | 31 | #include 32 | #include 33 | 34 | #include "stmlib/dsp/dsp.h" 35 | 36 | #include "clouds/resources.h" 37 | 38 | namespace clouds { 39 | 40 | using namespace std; 41 | 42 | /* static */ 43 | CvTransformation CvScaler::transformations_[ADC_CHANNEL_LAST] = { 44 | // ADC_POSITION_POTENTIOMETER_CV, 45 | { true, false, 0.05f }, 46 | // ADC_DENSITY_POTENTIOMETER_CV, 47 | { true, false, 0.01f }, 48 | // ADC_SIZE_POTENTIOMETER, 49 | { false, false, 0.01f }, 50 | // ADC_FEEDBACK_POTENTIOMETER, 51 | { false, false, 0.05f }, 52 | // ADC_PITCH_POTENTIOMETER, 53 | { false, false, 0.01f }, 54 | // ADC_V_OCT_CV, 55 | { false, false, 1.00f }, 56 | // ADC_DRYWET_POTENTIOMETER, 57 | { false, false, 0.05f }, 58 | // ADC_SPREAD_POTENTIOMETER, 59 | { false, false, 0.05f }, 60 | // ADC_TEXTURE_POTENTIOMETER, 61 | { false, false, 0.01f }, 62 | // ADC_REVERB_POTENTIOMETER, 63 | { false, false, 0.05f } 64 | }; 65 | 66 | void CvScaler::Init(CalibrationData* calibration_data) { 67 | adc_.Init(); 68 | gate_input_.Init(); 69 | calibration_data_ = calibration_data; 70 | fill(&smoothed_adc_value_[0], &smoothed_adc_value_[ADC_CHANNEL_LAST], 0.0f); 71 | note_ = 0.0f; 72 | 73 | fill(&blend_[0], &blend_[BLEND_PARAMETER_LAST], 0.0f); 74 | fill(&blend_mod_[0], &blend_mod_[BLEND_PARAMETER_LAST], 0.0f); 75 | previous_blend_knob_value_ = 0.0f; 76 | blend_parameter_ = BLEND_PARAMETER_DRY_WET; 77 | blend_knob_quantized_ = -1.0f; 78 | blend_knob_touched_ = false; 79 | 80 | fill(&previous_trigger_[0], &previous_trigger_[kAdcLatency], false); 81 | fill(&previous_gate_[0], &previous_gate_[kAdcLatency], false); 82 | } 83 | 84 | void CvScaler::UpdateBlendParameters(float knob_value, float cv) { 85 | // Update the blending settings (base value and modulation) from the 86 | // Blend knob and CV. 87 | for (int32_t i = 0; i < BLEND_PARAMETER_LAST; ++i) { 88 | float target = i == blend_parameter_ ? cv : 0.0f; 89 | float coefficient = i == blend_parameter_ ? 0.1f : 0.002f; 90 | blend_mod_[i] += coefficient * (target - blend_mod_[i]); 91 | } 92 | 93 | // Determines if the blend knob has been touched. 94 | if (blend_knob_quantized_ == -1.0f) { 95 | blend_knob_quantized_ = knob_value; 96 | } 97 | blend_knob_touched_ = fabs(knob_value - blend_knob_quantized_) > 0.02f; 98 | if (blend_knob_touched_) { 99 | blend_knob_quantized_ = knob_value; 100 | } 101 | 102 | if (previous_blend_knob_value_ == -1.0f) { 103 | blend_[blend_parameter_] = knob_value; 104 | previous_blend_knob_value_ = knob_value; 105 | blend_knob_origin_ = knob_value; 106 | } 107 | 108 | float parameter_value = blend_[blend_parameter_]; 109 | float delta = knob_value - previous_blend_knob_value_; 110 | float skew_ratio = delta > 0.0f 111 | ? (1.001f - parameter_value) / (1.001f - previous_blend_knob_value_) 112 | : (0.001f + parameter_value) / (0.001f + previous_blend_knob_value_); 113 | CONSTRAIN(skew_ratio, 0.1f, 10.0f); 114 | if (fabs(knob_value - blend_knob_origin_) < 0.02f) { 115 | delta = 0.0f; 116 | } else { 117 | blend_knob_origin_ = -1.0f; 118 | } 119 | parameter_value += skew_ratio * delta; 120 | CONSTRAIN(parameter_value, 0.0f, 1.0f); 121 | blend_[blend_parameter_] = parameter_value; 122 | previous_blend_knob_value_ = knob_value; 123 | } 124 | 125 | void CvScaler::Read(Parameters* parameters) { 126 | for (size_t i = 0; i < ADC_CHANNEL_LAST; ++i) { 127 | const CvTransformation& transformation = transformations_[i]; 128 | 129 | float value = adc_.float_value(i); 130 | if (transformation.flip) { 131 | value = 1.0f - value; 132 | } 133 | if (transformation.remove_offset) { 134 | value -= calibration_data_->offset[i]; 135 | } 136 | smoothed_adc_value_[i] += transformation.filter_coefficient * \ 137 | (value - smoothed_adc_value_[i]); 138 | } 139 | 140 | parameters->position = smoothed_adc_value_[ADC_POSITION_POTENTIOMETER_CV]; 141 | 142 | float texture = smoothed_adc_value_[ADC_TEXTURE_POTENTIOMETER]; 143 | CONSTRAIN(texture, 0.0f, 1.0f); 144 | parameters->texture = texture; 145 | 146 | float density = smoothed_adc_value_[ADC_DENSITY_POTENTIOMETER_CV]; 147 | CONSTRAIN(density, 0.0f, 1.0f); 148 | parameters->density = density; 149 | 150 | parameters->size = smoothed_adc_value_[ADC_SIZE_POTENTIOMETER]; 151 | CONSTRAIN(parameters->size, 0.0f, 1.0f); 152 | 153 | float dry_wet = smoothed_adc_value_[ADC_DRYWET_POTENTIOMETER]; 154 | dry_wet = dry_wet * 1.05f - 0.025f; 155 | CONSTRAIN(dry_wet, 0.0f, 1.0f); 156 | parameters->dry_wet = dry_wet; 157 | // compare with last reading and set blend_knob_touched_ for display 158 | if (fabs(dry_wet - blend_[BLEND_PARAMETER_DRY_WET]) > 0.02f) { 159 | blend_knob_touched_ = true; 160 | blend_[BLEND_PARAMETER_DRY_WET] = dry_wet; 161 | } 162 | 163 | float reverb_amount = smoothed_adc_value_[ADC_REVERB_POTENTIOMETER]; 164 | reverb_amount = reverb_amount * 1.05f - 0.025f; 165 | CONSTRAIN(reverb_amount, 0.0f, 1.0f); 166 | parameters->reverb = reverb_amount; 167 | // compare with last reading and set blend_knob_touched_ for display 168 | if (fabs(reverb_amount - blend_[BLEND_PARAMETER_REVERB]) > 0.02f) { 169 | blend_knob_touched_ = true; 170 | blend_[BLEND_PARAMETER_REVERB] = reverb_amount; 171 | } 172 | 173 | float feedback = smoothed_adc_value_[ADC_FEEDBACK_POTENTIOMETER]; 174 | feedback = feedback * 1.05f - 0.025f; 175 | CONSTRAIN(feedback, 0.0f, 1.0f); 176 | parameters->feedback = feedback; 177 | // compare with last reading and set blend_knob_touched_ for display 178 | if (fabs(feedback - blend_[BLEND_PARAMETER_FEEDBACK]) > 0.02f) { 179 | blend_knob_touched_ = true; 180 | blend_[BLEND_PARAMETER_FEEDBACK] = feedback; 181 | } 182 | 183 | float stereo_spread = smoothed_adc_value_[ADC_SPREAD_POTENTIOMETER]; 184 | stereo_spread = stereo_spread * 1.05f - 0.025f; 185 | CONSTRAIN(stereo_spread, 0.0f, 1.0f); 186 | parameters->stereo_spread = stereo_spread; 187 | // compare with last reading and set blend_knob_touched_ for display 188 | if (fabs(stereo_spread - blend_[BLEND_PARAMETER_STEREO_SPREAD]) > 0.02f) { 189 | blend_knob_touched_ = true; 190 | blend_[BLEND_PARAMETER_STEREO_SPREAD] = stereo_spread; 191 | } 192 | 193 | parameters->pitch = stmlib::Interpolate( 194 | lut_quantized_pitch, 195 | smoothed_adc_value_[ADC_PITCH_POTENTIOMETER], 196 | 1024.0f); 197 | 198 | //added below to allow pitch cv 199 | /* 200 | float note = calibration_data_->pitch_offset; 201 | note += smoothed_adc_value_[ADC_V_OCT_CV] * calibration_data_->pitch_scale; 202 | if (fabs(note - note_) > 0.5f) { 203 | note_ = note; 204 | } else { 205 | ONE_POLE(note_, note, 0.0f) 206 | } 207 | 208 | parameters->pitch += note_; 209 | */ 210 | //to here - didn't work right 211 | 212 | CONSTRAIN(parameters->pitch, -48.0f, 48.0f); 213 | 214 | gate_input_.Read(); 215 | /* 216 | if (gate_input_.freeze_rising_edge()) { 217 | parameters->freeze = true; 218 | } else if (gate_input_.freeze_falling_edge()) { 219 | parameters->freeze = false; 220 | } 221 | */ 222 | 223 | //parameters->trigger = false; 224 | parameters->trigger = previous_trigger_[0]; //added from here 225 | parameters->gate = previous_gate_[0]; 226 | for (int i = 0; i < kAdcLatency - 1; ++i) { 227 | previous_trigger_[i] = previous_trigger_[i + 1]; 228 | previous_gate_[i] = previous_gate_[i + 1]; 229 | } 230 | previous_trigger_[kAdcLatency - 1] = gate_input_.trigger_rising_edge(); 231 | previous_gate_[kAdcLatency - 1] = gate_input_.gate(); //to here 232 | 233 | 234 | adc_.Convert(); 235 | } 236 | 237 | } // namespace clouds 238 | -------------------------------------------------------------------------------- /clouds/dsp/fx/oliverb.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Reverb (used in the dedicated "Oliverb" playback mode). 28 | 29 | #ifndef CLOUDS_DSP_FX_OLIVERB_H_ 30 | #define CLOUDS_DSP_FX_OLIVERB_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | #include "clouds/dsp/fx/fx_engine.h" 35 | #include "clouds/dsp/random_oscillator.h" 36 | 37 | namespace clouds { 38 | 39 | class Oliverb { 40 | public: 41 | Oliverb() { } 42 | ~Oliverb() { } 43 | 44 | void Init(uint16_t* buffer) { 45 | engine_.Init(buffer); 46 | diffusion_ = 0.625f; 47 | size_ = 1.0f; 48 | mod_amount_ = 0.0f; 49 | mod_rate_ = 0.0f; 50 | size_ = 0.5f; 51 | input_gain_ = 1.0f; 52 | decay_ = 0.5f; 53 | lp_ = 1.0f; 54 | hp_= 0.0f; 55 | phase_ = 0.0f; 56 | ratio_ = 0.0f; 57 | pitch_shift_amount_ = 1.0f; 58 | level_ = 0.0f; 59 | for (int i=0; i<9; i++) 60 | lfo_[i].Init(); 61 | } 62 | 63 | void Process(FloatFrame* in_out, size_t size) { 64 | // This is the Griesinger topology described in the Dattorro paper 65 | // (4 AP diffusers on the input, then a loop of 2x 2AP+1Delay). 66 | // Modulation is applied in the loop of the first diffuser AP for additional 67 | // smearing; and to the two long delays for a slow shimmer/chorus effect. 68 | typedef E::Reserve<113, /* ap1 */ 69 | E::Reserve<162, /* ap2 */ 70 | E::Reserve<241, /* ap3 */ 71 | E::Reserve<399, /* ap4 */ 72 | E::Reserve<1253, /* dap1a */ 73 | E::Reserve<1738, /* dap1b */ 74 | E::Reserve<3411, /* del1 */ 75 | E::Reserve<1513, /* dap2a */ 76 | E::Reserve<1363, /* dap2b */ 77 | E::Reserve<4782> > > > > > > > > > Memory; /* del2 */ 78 | E::DelayLine ap1; 79 | E::DelayLine ap2; 80 | E::DelayLine ap3; 81 | E::DelayLine ap4; 82 | E::DelayLine dap1a; 83 | E::DelayLine dap1b; 84 | E::DelayLine del1; 85 | E::DelayLine dap2a; 86 | E::DelayLine dap2b; 87 | E::DelayLine del2; 88 | E::Context c; 89 | 90 | const float kap = diffusion_; 91 | 92 | float lp_1 = lp_decay_1_; 93 | float lp_2 = lp_decay_2_; 94 | float hp_1 = hp_decay_1_; 95 | float hp_2 = hp_decay_2_; 96 | 97 | /* Set frequency of LFOs */ 98 | float slope = mod_rate_ * mod_rate_; 99 | slope *= slope * slope; 100 | slope /= 200.0f; 101 | for (int i=0; i<9; i++) 102 | lfo_[i].set_slope(slope); 103 | 104 | while (size--) { 105 | engine_.Start(&c); 106 | 107 | // Smooth parameters to avoid delay glitches 108 | ONE_POLE(smooth_size_, size_, 0.01f); 109 | 110 | // compute windowing info for the pitch shifter 111 | float ps_size = 128.0f + (3410.0f - 128.0f) * smooth_size_; 112 | phase_ += (1.0f - ratio_) / ps_size; 113 | if (phase_ >= 1.0f) phase_ -= 1.0f; 114 | if (phase_ <= 0.0f) phase_ += 1.0f; 115 | float tri = 2.0f * (phase_ >= 0.5f ? 1.0f - phase_ : phase_); 116 | tri = Interpolate(lut_window, tri, LUT_WINDOW_SIZE-1); 117 | float phase = phase_ * ps_size; 118 | float half = phase + ps_size * 0.5f; 119 | if (half >= ps_size) half -= ps_size; 120 | 121 | #define INTERPOLATE_LFO(del, lfo, gain) \ 122 | { \ 123 | float offset = (del.length - 1) * smooth_size_; \ 124 | offset += lfo.Next() * mod_amount_; \ 125 | CONSTRAIN(offset, 1.0f, del.length - 1); \ 126 | c.InterpolateHermite(del, offset, gain); \ 127 | } 128 | 129 | #define INTERPOLATE(del, gain) \ 130 | { \ 131 | float offset = (del.length - 1) * smooth_size_; \ 132 | CONSTRAIN(offset, 1.0f, del.length - 1); \ 133 | c.InterpolateHermite(del, offset, gain); \ 134 | } 135 | 136 | // Smear AP1 inside the loop. 137 | c.Interpolate(ap1, 10.0f, LFO_1, 60.0f, 1.0f); 138 | c.Write(ap1, 100, 0.0f); 139 | 140 | c.Read(in_out->l + in_out->r, input_gain_); 141 | // Diffuse through 4 allpasses. 142 | INTERPOLATE_LFO(ap1, lfo_[1], kap); 143 | c.WriteAllPass(ap1, -kap); 144 | INTERPOLATE_LFO(ap2, lfo_[2], kap); 145 | c.WriteAllPass(ap2, -kap); 146 | INTERPOLATE_LFO(ap3, lfo_[3], kap); 147 | c.WriteAllPass(ap3, -kap); 148 | INTERPOLATE_LFO(ap4, lfo_[4], kap); 149 | c.WriteAllPass(ap4, -kap); 150 | 151 | float apout; 152 | c.Write(apout); 153 | 154 | INTERPOLATE_LFO(del2, lfo_[5], decay_ * (1.0f - pitch_shift_amount_)); 155 | /* blend in the pitch shifted feedback */ 156 | c.InterpolateHermite(del2, phase, tri * decay_ * pitch_shift_amount_); 157 | c.InterpolateHermite(del2, half, (1.0f - tri) * decay_ * pitch_shift_amount_); 158 | 159 | c.Lp(lp_1, lp_); 160 | c.Hp(hp_1, hp_); 161 | c.SoftLimit(); 162 | INTERPOLATE_LFO(dap1a, lfo_[6], -kap); 163 | c.WriteAllPass(dap1a, kap); 164 | INTERPOLATE(dap1b, kap); 165 | c.WriteAllPass(dap1b, -kap); 166 | c.Write(del1, 2.0f); 167 | c.Write(in_out->l, 0.0f); 168 | 169 | c.Load(apout); 170 | 171 | INTERPOLATE_LFO(del1, lfo_[7], decay_ * (1.0f - pitch_shift_amount_)); 172 | /* blend in the pitch shifted feedback */ 173 | c.InterpolateHermite(del1, phase, tri * decay_ * pitch_shift_amount_); 174 | c.InterpolateHermite(del1, half, (1.0f - tri) * decay_ * pitch_shift_amount_); 175 | c.Lp(lp_2, lp_); 176 | c.Hp(hp_2, hp_); 177 | c.SoftLimit(); 178 | INTERPOLATE_LFO(dap2a, lfo_[8], kap); 179 | c.WriteAllPass(dap2a, -kap); 180 | INTERPOLATE(dap2b, -kap); 181 | c.WriteAllPass(dap2b, kap); 182 | c.Write(del2, 2.0f); 183 | c.Write(in_out->r, 0.0f); 184 | 185 | ++in_out; 186 | } 187 | 188 | lp_decay_1_ = lp_1; 189 | lp_decay_2_ = lp_2; 190 | hp_decay_1_ = hp_1; 191 | hp_decay_2_ = hp_2; 192 | } 193 | 194 | inline void set_input_gain(float input_gain) { 195 | input_gain_ = input_gain; 196 | } 197 | 198 | inline void set_decay(float decay) { 199 | decay_ = decay; 200 | } 201 | 202 | inline void set_diffusion(float diffusion) { 203 | diffusion_ = diffusion; 204 | } 205 | 206 | inline void set_lp(float lp) { 207 | lp_ = lp; 208 | } 209 | 210 | inline void set_hp(float hp) { 211 | hp_ = hp; 212 | } 213 | 214 | inline void set_size(float size) { 215 | size_ = size; 216 | } 217 | 218 | inline void set_mod_amount(float mod_amount) { 219 | mod_amount_ = mod_amount; 220 | } 221 | 222 | inline void set_mod_rate(float mod_rate) { 223 | mod_rate_ = mod_rate; 224 | } 225 | 226 | inline void set_ratio(float ratio) { 227 | ratio_ = ratio; 228 | } 229 | 230 | inline void set_pitch_shift_amount(float pitch_shift) { 231 | pitch_shift_amount_ = pitch_shift; 232 | } 233 | 234 | private: 235 | typedef FxEngine<16384, FORMAT_16_BIT> E; 236 | E engine_; 237 | 238 | float input_gain_; 239 | float decay_; 240 | float diffusion_; 241 | float lp_; 242 | float hp_; 243 | float size_, smooth_size_; 244 | float mod_amount_; 245 | float mod_rate_; 246 | float pitch_shift_amount_; 247 | 248 | float lp_decay_1_; 249 | float lp_decay_2_; 250 | float hp_decay_1_; 251 | float hp_decay_2_; 252 | 253 | float phase_; 254 | float ratio_; 255 | float level_; 256 | 257 | RandomOscillator lfo_[9]; 258 | 259 | DISALLOW_COPY_AND_ASSIGN(Oliverb); 260 | }; 261 | 262 | } // namespace clouds 263 | 264 | #endif // CLOUDS_DSP_FX_OLIVERB_H_ 265 | -------------------------------------------------------------------------------- /clouds/dsp/granular_sample_player.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Granular playback of audio stored in a buffer. 28 | 29 | #ifndef CLOUDS_DSP_GRANULAR_SAMPLE_PLAYER_H_ 30 | #define CLOUDS_DSP_GRANULAR_SAMPLE_PLAYER_H_ 31 | 32 | #include "stmlib/stmlib.h" 33 | 34 | #include 35 | 36 | #include "stmlib/dsp/atan.h" 37 | #include "stmlib/dsp/units.h" 38 | #include "stmlib/utils/random.h" 39 | 40 | #include "clouds/dsp/audio_buffer.h" 41 | #include "clouds/dsp/frame.h" 42 | #include "clouds/dsp/grain.h" 43 | #include "clouds/dsp/parameters.h" 44 | 45 | #include "clouds/resources.h" 46 | 47 | namespace clouds { 48 | 49 | const int32_t kMaxNumGrains = 40; 50 | 51 | using namespace stmlib; 52 | 53 | class GranularSamplePlayer { 54 | public: 55 | GranularSamplePlayer() { } 56 | ~GranularSamplePlayer() { } 57 | 58 | void Init(int32_t num_channels, int32_t max_num_grains) { 59 | max_num_grains_ = max_num_grains; 60 | num_midfi_grains_ = 3 * max_num_grains / 4; 61 | gain_normalization_ = 1.0f; 62 | for (int32_t i = 0; i < kMaxNumGrains; ++i) { 63 | grains_[i].Init(); 64 | } 65 | num_grains_ = 0.0f; 66 | num_channels_ = num_channels; 67 | grain_size_hint_ = 1024.0f; 68 | } 69 | 70 | template 71 | void Play( 72 | const AudioBuffer* buffer, 73 | const Parameters& parameters, 74 | float* out, size_t size) { 75 | float overlap = parameters.granular.overlap; 76 | overlap = (overlap * overlap) * (overlap * overlap); 77 | float target_num_grains = max_num_grains_ * overlap; 78 | float p = target_num_grains / static_cast(grain_size_hint_); 79 | float space_between_grains = grain_size_hint_ / target_num_grains; 80 | if (parameters.granular.use_deterministic_seed) { 81 | p = -1.0f; 82 | } else { 83 | grain_rate_phasor_ = -1000.0f; 84 | } 85 | 86 | // Build a list of available grains. 87 | int32_t num_available_grains = FillAvailableGrainsList(); 88 | 89 | // Try to schedule new grains. 90 | bool seed_trigger = parameters.trigger; 91 | for (size_t t = 0; t < size; ++t) { 92 | grain_rate_phasor_ += 1.0f; 93 | bool seed_probabilistic = Random::GetFloat() < p 94 | && target_num_grains > num_grains_; 95 | bool seed_deterministic = grain_rate_phasor_ >= space_between_grains; 96 | bool seed = seed_probabilistic || seed_deterministic || seed_trigger; 97 | if (num_available_grains && seed) { 98 | --num_available_grains; 99 | int32_t index = available_grains_[num_available_grains]; 100 | GrainQuality quality; 101 | if (num_available_grains < num_midfi_grains_) { 102 | quality = GRAIN_QUALITY_MEDIUM; 103 | } else { 104 | quality = GRAIN_QUALITY_HIGH; 105 | } 106 | 107 | Grain* g = &grains_[index]; 108 | ScheduleGrain( 109 | g, 110 | parameters, 111 | t, 112 | buffer->size(), 113 | buffer->head() - size + t, 114 | quality); 115 | grain_rate_phasor_ = 0.0f; 116 | seed_trigger = false; 117 | } 118 | } 119 | 120 | // Overlap grains. 121 | std::fill(&out[0], &out[size * 2], 0.0f); 122 | float* e = envelope_buffer_; 123 | for (int32_t i = 0; i < max_num_grains_; ++i) { 124 | Grain* g = &grains_[i]; 125 | if (g->recommended_quality() == GRAIN_QUALITY_HIGH) { 126 | if (num_channels_ == 1) { 127 | g->OverlapAdd<1, GRAIN_QUALITY_HIGH>(buffer, out, e, size); 128 | } else { 129 | g->OverlapAdd<2, GRAIN_QUALITY_HIGH>(buffer, out, e, size); 130 | } 131 | } else if (g->recommended_quality() == GRAIN_QUALITY_MEDIUM) { 132 | if (num_channels_ == 1) { 133 | g->OverlapAdd<1, GRAIN_QUALITY_MEDIUM>(buffer, out, e, size); 134 | } else { 135 | g->OverlapAdd<2, GRAIN_QUALITY_MEDIUM>(buffer, out, e, size); 136 | } 137 | } else { 138 | if (num_channels_ == 1) { 139 | g->OverlapAdd<1, GRAIN_QUALITY_LOW>(buffer, out, e, size); 140 | } else { 141 | g->OverlapAdd<2, GRAIN_QUALITY_LOW>(buffer, out, e, size); 142 | } 143 | } 144 | } 145 | 146 | // Compute normalization factor. 147 | int32_t active_grains = max_num_grains_ - num_available_grains; 148 | SLOPE(num_grains_, static_cast(active_grains), 0.9f, 0.2f); 149 | 150 | float gain_normalization = num_grains_ > 2.0f 151 | ? fast_rsqrt_carmack(num_grains_ - 1.0f) 152 | : 1.0f; 153 | float window_gain = 1.0f + 2.0f * parameters.granular.window_shape; 154 | CONSTRAIN(window_gain, 1.0f, 2.0f); 155 | gain_normalization *= Crossfade( 156 | 1.0f, window_gain, parameters.granular.overlap); 157 | 158 | // Apply gain normalization. 159 | for (size_t t = 0; t < size; ++t) { 160 | ONE_POLE(gain_normalization_, gain_normalization, 0.01f) 161 | *out++ *= gain_normalization_; 162 | *out++ *= gain_normalization_; 163 | } 164 | } 165 | 166 | private: 167 | int32_t FillAvailableGrainsList() { 168 | int32_t num_available_grains = 0; 169 | for (int32_t i = 0; i < max_num_grains_; ++i) { 170 | if (!grains_[i].active()) { 171 | available_grains_[num_available_grains] = i; 172 | ++num_available_grains; 173 | } 174 | } 175 | return num_available_grains; 176 | } 177 | 178 | void ScheduleGrain( 179 | Grain* grain, 180 | const Parameters& parameters, 181 | int32_t pre_delay, 182 | int32_t buffer_size, 183 | int32_t buffer_head, 184 | GrainQuality quality) { 185 | float position = parameters.position; 186 | float pitch = parameters.pitch; 187 | float window_shape = parameters.granular.window_shape; 188 | float grain_size = Interpolate(lut_grain_size, parameters.size, 256.0f); 189 | float pitch_ratio = SemitonesToRatio(pitch); 190 | float inv_pitch_ratio = SemitonesToRatio(-pitch); 191 | float pan = 0.5f + parameters.stereo_spread * (Random::GetFloat() - 0.5f); 192 | float gain_l, gain_r; 193 | if (num_channels_ == 1) { 194 | gain_l = Interpolate(lut_sin, pan, 256.0f); 195 | gain_r = Interpolate(lut_sin + 256, pan, 256.0f); 196 | } else { 197 | if (pan < 0.5f) { 198 | gain_l = 1.0f; 199 | gain_r = 2.0f * pan; 200 | } else { 201 | gain_r = 1.0f; 202 | gain_l = 2.0f * (1.0f - pan); 203 | } 204 | } 205 | 206 | if (pitch_ratio > 1.0f) { 207 | // The grain's play-head moves faster than the buffer record-head. 208 | // we must make sure that the grain will not consume too much data. 209 | // In some situations, it might be necessary to reduce the size of the 210 | // grain. 211 | grain_size = std::min(grain_size, buffer_size * 0.25f * inv_pitch_ratio); 212 | } 213 | 214 | float eaten_by_play_head = grain_size * pitch_ratio; 215 | float eaten_by_recording_head = grain_size; 216 | 217 | float available = 0.0; 218 | available += static_cast(buffer_size); 219 | available -= eaten_by_play_head; 220 | available -= eaten_by_recording_head; 221 | 222 | bool reverse = parameters.granular.reverse; 223 | int32_t size = static_cast(grain_size) & ~1; 224 | int32_t start = buffer_head - static_cast( 225 | position * available + eaten_by_play_head); 226 | grain->Start( 227 | pre_delay, 228 | buffer_size, 229 | start, 230 | size, 231 | reverse, 232 | static_cast(pitch_ratio * 65536.0f), 233 | window_shape, 234 | gain_l, 235 | gain_r, 236 | quality); 237 | grain_size_hint_ = grain_size; 238 | } 239 | 240 | int32_t max_num_grains_; 241 | int32_t num_midfi_grains_; 242 | int32_t num_channels_; 243 | 244 | float num_grains_; 245 | float gain_normalization_; 246 | float grain_size_hint_; 247 | float grain_rate_phasor_; 248 | 249 | Grain grains_[kMaxNumGrains]; 250 | int32_t available_grains_[kMaxNumGrains]; 251 | float envelope_buffer_[kMaxBlockSize]; 252 | 253 | DISALLOW_COPY_AND_ASSIGN(GranularSamplePlayer); 254 | }; 255 | 256 | } // namespace clouds 257 | 258 | #endif // CLOUDS_DSP_GRANULAR_SAMPLE_PLAYER_H_ 259 | -------------------------------------------------------------------------------- /clouds/dsp/audio_buffer.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | // 25 | // ----------------------------------------------------------------------------- 26 | // 27 | // Circular buffer storing audio samples. 28 | 29 | #ifndef CLOUDS_DSP_AUDIO_BUFFER_H_ 30 | #define CLOUDS_DSP_AUDIO_BUFFER_H_ 31 | 32 | #include 33 | 34 | #include "stmlib/stmlib.h" 35 | 36 | #include "stmlib/dsp/dsp.h" 37 | #include "stmlib/utils/dsp.h" 38 | 39 | #include "clouds/dsp/mu_law.h" 40 | 41 | const int32_t kCrossFadeSize = 256; 42 | const int32_t kInterpolationTail = 8; 43 | 44 | namespace clouds { 45 | 46 | enum Resolution { 47 | RESOLUTION_16_BIT, 48 | RESOLUTION_8_BIT, 49 | RESOLUTION_8_BIT_DITHERED, 50 | RESOLUTION_8_BIT_MU_LAW, 51 | }; 52 | 53 | enum InterpolationMethod { 54 | INTERPOLATION_ZOH, 55 | INTERPOLATION_LINEAR, 56 | INTERPOLATION_HERMITE 57 | }; 58 | 59 | template 60 | class AudioBuffer { 61 | public: 62 | AudioBuffer() { } 63 | ~AudioBuffer() { } 64 | 65 | void Init( 66 | void* buffer, 67 | int32_t size, 68 | int16_t* tail_buffer) { 69 | s16_ = static_cast(buffer); 70 | s8_ = static_cast(buffer); 71 | size_ = size - kInterpolationTail; 72 | write_head_ = 0; 73 | quantization_error_ = 0.0f; 74 | crossfade_counter_ = 0; 75 | if (resolution == RESOLUTION_16_BIT) { 76 | std::fill(&s16_[0], &s16_[size], 0); 77 | } else { 78 | std::fill( 79 | &s8_[0], 80 | &s8_[size], 81 | resolution == RESOLUTION_8_BIT_MU_LAW ? 127 : 0); 82 | } 83 | tail_ = tail_buffer; 84 | } 85 | 86 | inline void Resync(int32_t head) { 87 | write_head_ = head; 88 | crossfade_counter_ = 0; 89 | } 90 | 91 | inline void Write(float in) { 92 | if (resolution == RESOLUTION_16_BIT) { 93 | s16_[write_head_] = stmlib::Clip16( 94 | static_cast(in * 32768.0f)); 95 | } else if (resolution == RESOLUTION_8_BIT_DITHERED) { 96 | float sample = in * 127.0f; 97 | sample += quantization_error_; 98 | int32_t quantized = static_cast(sample); 99 | if (quantized < -127) quantized = -127; 100 | else if (quantized > 127) quantized = 127; 101 | quantization_error_ = sample - static_cast(in); 102 | s8_[write_head_] = quantized; 103 | } else if (resolution == RESOLUTION_8_BIT_MU_LAW) { 104 | int16_t sample = stmlib::Clip16(static_cast(in * 32768.0f)); 105 | s8_[write_head_] = Lin2MuLaw(sample); 106 | } else { 107 | s8_[write_head_] = static_cast( 108 | stmlib::Clip16(in * 32768.0f) >> 8); 109 | } 110 | 111 | if (resolution == RESOLUTION_16_BIT) { 112 | if (write_head_ < kInterpolationTail) { 113 | s16_[write_head_ + size_] = s16_[write_head_]; 114 | } 115 | } else { 116 | if (write_head_ < kInterpolationTail) { 117 | s8_[write_head_ + size_] = s8_[write_head_]; 118 | } 119 | } 120 | ++write_head_; 121 | if (write_head_ >= size_) { 122 | write_head_ = 0; 123 | } 124 | } 125 | 126 | inline void WriteFade( 127 | const float* in, 128 | int32_t size, 129 | int32_t stride, 130 | bool write) { 131 | if (!write) { 132 | // Continue recording samples to have something to crossfade with 133 | // when recording resumes. 134 | if (crossfade_counter_ < kCrossFadeSize) { 135 | while (size--) { 136 | if (crossfade_counter_ < kCrossFadeSize) { 137 | tail_[crossfade_counter_++] = stmlib::Clip16( 138 | static_cast(*in * 32767.0f)); 139 | in += stride; 140 | } 141 | } 142 | } 143 | } else if (write && !crossfade_counter_ && 144 | resolution == RESOLUTION_16_BIT && 145 | write_head_ >= kInterpolationTail && write_head_ < (size_ - size)) { 146 | // Fast write routine for the most common case. 147 | while (size--) { 148 | s16_[write_head_] = stmlib::Clip16( 149 | static_cast(*in * 32767.0f)); 150 | ++write_head_; 151 | in += stride; 152 | } 153 | } else { 154 | while (size--) { 155 | float sample = *in; 156 | if (crossfade_counter_) { 157 | --crossfade_counter_; 158 | float tail_sample = tail_[kCrossFadeSize - crossfade_counter_]; 159 | float gain = crossfade_counter_ * (1.0f / float(kCrossFadeSize)); 160 | sample += (tail_sample / 32768.0f - sample) * gain; 161 | } 162 | Write(sample); 163 | in += stride; 164 | } 165 | } 166 | } 167 | 168 | inline void Write(const float* in, int32_t size, int32_t stride) { 169 | if (resolution == RESOLUTION_16_BIT 170 | && write_head_ >= kInterpolationTail && write_head_ < (size_ - size)) { 171 | // Fast write routine for the most common case. 172 | while (size--) { 173 | s16_[write_head_] = stmlib::Clip16( 174 | static_cast(*in * 32768.0f)); 175 | ++write_head_; 176 | in += stride; 177 | } 178 | } else { 179 | while (size--) { 180 | Write(*in); 181 | in += stride; 182 | } 183 | } 184 | } 185 | 186 | template 187 | inline float Read(int32_t integral, uint16_t fractional) const { 188 | if (method == INTERPOLATION_ZOH) { 189 | return ReadZOH(integral, fractional); 190 | } else if (method == INTERPOLATION_LINEAR) { 191 | return ReadLinear(integral, fractional); 192 | } else if (method == INTERPOLATION_HERMITE) { 193 | return ReadHermite(integral, fractional); 194 | } 195 | } 196 | 197 | inline float ReadZOH(int32_t integral, uint16_t fractional) const { 198 | if (integral >= size_) { 199 | integral -= size_; 200 | } 201 | 202 | float x0, scale; 203 | if (resolution == RESOLUTION_16_BIT) { 204 | x0 = s16_[integral]; 205 | scale = 1.0f / 32768.0f; 206 | } else if (resolution == RESOLUTION_8_BIT_MU_LAW) { 207 | x0 = MuLaw2Lin(s8_[integral]); 208 | scale = 1.0f / 32768.0f; 209 | } else { 210 | x0 = s8_[integral]; 211 | scale = 1.0f / 128.0f; 212 | } 213 | return x0 * scale; 214 | } 215 | 216 | inline float ReadLinear(int32_t integral, uint16_t fractional) const { 217 | if (integral >= size_) { 218 | integral -= size_; 219 | } 220 | 221 | // assert(integral >= 0 && integral < size_); 222 | 223 | float x0, x1, scale; 224 | float t = static_cast(fractional) / 65536.0f; 225 | if (resolution == RESOLUTION_16_BIT) { 226 | x0 = s16_[integral]; 227 | x1 = s16_[integral + 1]; 228 | scale = 1.0f / 32768.0f; 229 | } else if (resolution == RESOLUTION_8_BIT_MU_LAW) { 230 | x0 = MuLaw2Lin(s8_[integral]); 231 | x1 = MuLaw2Lin(s8_[integral + 1]); 232 | scale = 1.0f / 32768.0f; 233 | } else { 234 | x0 = s8_[integral]; 235 | x1 = s8_[integral + 1]; 236 | scale = 1.0f / 128.0f; 237 | } 238 | return (x0 + (x1 - x0) * t) * scale; 239 | } 240 | 241 | inline float ReadHermite(int32_t integral, uint16_t fractional) const { 242 | if (integral >= size_) { 243 | integral -= size_; 244 | } 245 | 246 | // assert(integral >= 0 && integral < size_); 247 | 248 | float xm1, x0, x1, x2, scale; 249 | float t = static_cast(fractional) / 65536.0f; 250 | 251 | if (resolution == RESOLUTION_16_BIT) { 252 | xm1 = s16_[integral]; 253 | x0 = s16_[integral + 1]; 254 | x1 = s16_[integral + 2]; 255 | x2 = s16_[integral + 3]; 256 | scale = 1.0f / 32768.0f; 257 | } else if (resolution == RESOLUTION_8_BIT_MU_LAW) { 258 | xm1 = MuLaw2Lin(s8_[integral]); 259 | x0 = MuLaw2Lin(s8_[integral + 1]); 260 | x1 = MuLaw2Lin(s8_[integral + 2]); 261 | x2 = MuLaw2Lin(s8_[integral + 3]); 262 | scale = 1.0f / 32768.0f; 263 | } else { 264 | xm1 = s8_[integral]; 265 | x0 = s8_[integral + 1]; 266 | x1 = s8_[integral + 2]; 267 | x2 = s8_[integral + 3]; 268 | scale = 1.0f / 128.0f; 269 | } 270 | 271 | // Laurent de Soras's Hermite interpolator. 272 | const float c = (x1 - xm1) * 0.5f; 273 | const float v = x0 - x1; 274 | const float w = c + v; 275 | const float a = w + v + (x2 - x0) * 0.5f; 276 | const float b_neg = w + a; 277 | return ((((a * t) - b_neg) * t + c) * t + x0) * scale; 278 | } 279 | 280 | inline int32_t size() const { return size_; } 281 | inline int32_t head() const { return write_head_; } 282 | 283 | private: 284 | int16_t* s16_; 285 | int8_t* s8_; 286 | 287 | float quantization_error_; 288 | 289 | int16_t tail_ptr_; 290 | 291 | int32_t size_; 292 | int32_t write_head_; 293 | 294 | int16_t* tail_; 295 | int32_t crossfade_counter_; 296 | 297 | DISALLOW_COPY_AND_ASSIGN(AudioBuffer); 298 | }; 299 | 300 | } // namespace clouds 301 | 302 | #endif // CLOUDS_DSP_AUDIO_BUFFER_H_ 303 | -------------------------------------------------------------------------------- /clouds/bootloader/bootloader.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Olivier Gillet. 2 | // 3 | // Author: Olivier Gillet (ol.gillet@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | // See http://creativecommons.org/licenses/MIT/ for more information. 24 | 25 | #include "stmlib/system/bootloader_utils.h" 26 | #include "stmlib/system/system_clock.h" 27 | 28 | #include "clouds/drivers/codec.h" 29 | #include "clouds/drivers/leds.h" 30 | #include "clouds/drivers/switches.h" 31 | #include "clouds/drivers/system.h" 32 | #include "clouds/drivers/version.h" 33 | #include "clouds/meter.h" 34 | 35 | #include "stm_audio_bootloader/qpsk/packet_decoder.h" 36 | #include "stm_audio_bootloader/qpsk/demodulator.h" 37 | 38 | #include 39 | 40 | using namespace clouds; 41 | using namespace stmlib; 42 | using namespace stm_audio_bootloader; 43 | 44 | const double kSampleRate = 48000.0; 45 | const double kModulationRate = 6000.0; 46 | const double kBitRate = 12000.0; 47 | const uint32_t kStartAddress = 0x08008000; 48 | 49 | Codec codec; 50 | Meter meter; 51 | Leds leds; 52 | Switches switches; 53 | PacketDecoder decoder; 54 | Demodulator demodulator; 55 | 56 | int __errno; 57 | 58 | // Default interrupt handlers. 59 | extern "C" { 60 | 61 | void NMI_Handler() { } 62 | void HardFault_Handler() { while (1); } 63 | void MemManage_Handler() { while (1); } 64 | void BusFault_Handler() { while (1); } 65 | void UsageFault_Handler() { while (1); } 66 | void SVC_Handler() { } 67 | void DebugMon_Handler() { } 68 | void PendSV_Handler() { } 69 | 70 | } 71 | 72 | extern "C" { 73 | 74 | const int16_t lut_db[] = { 75 | -32768, -32768, -24576, -19783, -16384, -13746, -11591, -9770, 76 | -8192, -6799, -5554, -4428, -3399, -2453, -1578, -762, 77 | 0, 716, 1392, 2031, 2637, 3213, 3763, 4289, 78 | 4792, 5274, 5738, 6184, 6613, 7028, 7429, 7816, 79 | 8192, 8555, 8908, 9251, 9584, 9907, 10223, 10530, 80 | 10829, 11121, 11405, 11683, 11955, 12221, 12481, 12735, 81 | 12984, 13227, 13466, 13700, 13930, 14155, 14376, 14592, 82 | 14805, 15015, 15220, 15422, 15621, 15816, 16008, 16197, 83 | 16384, 16567, 16747, 16925, 17100, 17273, 17443, 17610, 84 | 17776, 17939, 18099, 18258, 18415, 18569, 18722, 18872, 85 | 19021, 19168, 19313, 19456, 19597, 19737, 19875, 20012, 86 | 20147, 20281, 20413, 20543, 20673, 20800, 20927, 21052, 87 | 21176, 21298, 21419, 21539, 21658, 21776, 21892, 22007, 88 | 22122, 22235, 22347, 22458, 22568, 22676, 22784, 22891, 89 | 22997, 23102, 23207, 23310, 23412, 23514, 23614, 23714, 90 | 23813, 23911, 24008, 24105, 24200, 24295, 24389, 24483, 91 | 24576, 24667, 24759, 24849, 24939, 25028, 25117, 25205, 92 | 25292, 25379, 25465, 25550, 25635, 25719, 25802, 25885, 93 | 25968, 26049, 26131, 26211, 26291, 26371, 26450, 26529, 94 | 26607, 26684, 26761, 26838, 26914, 26989, 27064, 27139, 95 | 27213, 27286, 27360, 27432, 27505, 27576, 27648, 27719, 96 | 27789, 27860, 27929, 27999, 28067, 28136, 28204, 28272, 97 | 28339, 28406, 28473, 28539, 28605, 28670, 28735, 28800, 98 | 28865, 28929, 28992, 29056, 29119, 29181, 29244, 29306, 99 | 29368, 29429, 29490, 29551, 29611, 29671, 29731, 29791, 100 | 29850, 29909, 29968, 30026, 30084, 30142, 30199, 30257, 101 | 30314, 30370, 30427, 30483, 30539, 30594, 30650, 30705, 102 | 30760, 30814, 30868, 30923, 30976, 31030, 31083, 31136, 103 | 31189, 31242, 31294, 31347, 31399, 31450, 31502, 31553, 104 | 31604, 31655, 31706, 31756, 31806, 31856, 31906, 31955, 105 | 32005, 32054, 32103, 32152, 32200, 32248, 32297, 32345, 106 | 32392, 32440, 32487, 32534, 32581, 32628, 32675, 32721, 107 | 32721, 108 | }; 109 | 110 | enum UiState { 111 | UI_STATE_WAITING, 112 | UI_STATE_RECEIVING, 113 | UI_STATE_ERROR, 114 | UI_STATE_WRITING 115 | }; 116 | 117 | volatile bool switch_released = false; 118 | volatile UiState ui_state; 119 | 120 | void UpdateLeds() { 121 | leds.Clear(); 122 | leds.set_freeze(true); 123 | leds.set_enabled(true); 124 | switch (ui_state) { 125 | case UI_STATE_WAITING: 126 | leds.set_freeze(system_clock.milliseconds() & 128); 127 | break; 128 | 129 | case UI_STATE_RECEIVING: 130 | leds.set_freeze(system_clock.milliseconds() & 32); 131 | leds.PaintBar(lut_db[meter.peak() >> 7]); 132 | break; 133 | 134 | case UI_STATE_ERROR: 135 | { 136 | bool on = system_clock.milliseconds() & 256; 137 | for (uint8_t i = 0; i < 4; ++i) { 138 | leds.set_status(i, on ? 255 : 0, 0); 139 | } 140 | } 141 | break; 142 | 143 | case UI_STATE_WRITING: 144 | { 145 | for (uint8_t i = 0; i < 4; ++i) { 146 | leds.set_status(i, 0, 255); 147 | } 148 | } 149 | break; 150 | } 151 | leds.Write(); 152 | } 153 | 154 | void SysTick_Handler() { 155 | system_clock.Tick(); 156 | switches.Debounce(); 157 | if (switches.released(2)) { 158 | switch_released = true; 159 | } 160 | UpdateLeds(); 161 | } 162 | 163 | } 164 | 165 | size_t discard_samples = 8000; 166 | void FillBuffer(Codec::Frame* input, Codec::Frame* output, size_t n) { 167 | meter.Process(input, n); 168 | while (n--) { 169 | int32_t sample = (input->l >> 4) + 2048; 170 | if (!discard_samples) { 171 | demodulator.PushSample(sample); 172 | } else { 173 | --discard_samples; 174 | } 175 | *output = *input; 176 | ++output; 177 | ++input; 178 | } 179 | } 180 | 181 | static size_t current_address; 182 | static uint16_t packet_index; 183 | static uint32_t kSectorBaseAddress[] = { 184 | 0x08000000, 185 | 0x08004000, 186 | 0x08008000, 187 | 0x0800C000, 188 | 0x08010000, 189 | 0x08020000, 190 | 0x08040000, 191 | 0x08060000, 192 | 0x08080000, 193 | 0x080A0000, 194 | 0x080C0000, 195 | 0x080E0000 196 | }; 197 | const uint32_t kBlockSize = 16384; 198 | const uint16_t kPacketsPerBlock = kBlockSize / kPacketSize; 199 | uint8_t rx_buffer[kBlockSize]; 200 | 201 | void ProgramPage(const uint8_t* data, size_t size) { 202 | FLASH_Unlock(); 203 | FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | 204 | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR|FLASH_FLAG_PGSERR); 205 | for (int32_t i = 0; i < 12; ++i) { 206 | if (current_address == kSectorBaseAddress[i]) { 207 | FLASH_EraseSector(i * 8, VoltageRange_3); 208 | } 209 | } 210 | const uint32_t* words = static_cast( 211 | static_cast(data)); 212 | for (size_t written = 0; written < size; written += 4) { 213 | FLASH_ProgramWord(current_address, *words++); 214 | current_address += 4; 215 | } 216 | } 217 | 218 | void Init() { 219 | System sys; 220 | Version version; 221 | 222 | sys.Init(false); 223 | leds.Init(); 224 | meter.Init(48000); 225 | switches.Init(); 226 | version.Init(); 227 | if (!codec.Init(!version.revised(), 48000)) { } 228 | if (!codec.Start(32, &FillBuffer)) { } 229 | sys.StartTimers(); 230 | } 231 | 232 | void InitializeReception() { 233 | decoder.Init(20000); 234 | demodulator.Init( 235 | kModulationRate / kSampleRate * 4294967296.0, 236 | kSampleRate / kModulationRate, 237 | 2.0 * kSampleRate / kBitRate); 238 | demodulator.SyncCarrier(true); 239 | decoder.Reset(); 240 | current_address = kStartAddress; 241 | packet_index = 0; 242 | ui_state = UI_STATE_WAITING; 243 | } 244 | 245 | int main(void) { 246 | InitializeReception(); 247 | Init(); 248 | 249 | bool exit_updater = !switches.pressed_immediate(2); 250 | 251 | while (!exit_updater) { 252 | bool error = false; 253 | 254 | if (demodulator.state() == DEMODULATOR_STATE_OVERFLOW) { 255 | error = true; 256 | } else { 257 | demodulator.ProcessAtLeast(32); 258 | } 259 | 260 | while (demodulator.available() && !error && !exit_updater) { 261 | uint8_t symbol = demodulator.NextSymbol(); 262 | PacketDecoderState state = decoder.ProcessSymbol(symbol); 263 | switch (state) { 264 | case PACKET_DECODER_STATE_OK: 265 | { 266 | ui_state = UI_STATE_RECEIVING; 267 | memcpy( 268 | rx_buffer + (packet_index % kPacketsPerBlock) * kPacketSize, 269 | decoder.packet_data(), 270 | kPacketSize); 271 | ++packet_index; 272 | if ((packet_index % kPacketsPerBlock) == 0) { 273 | ui_state = UI_STATE_WRITING; 274 | ProgramPage(rx_buffer, kBlockSize); 275 | decoder.Reset(); 276 | demodulator.SyncCarrier(false); 277 | } else { 278 | decoder.Reset(); 279 | demodulator.SyncDecision(); 280 | } 281 | } 282 | break; 283 | case PACKET_DECODER_STATE_ERROR_SYNC: 284 | case PACKET_DECODER_STATE_ERROR_CRC: 285 | error = true; 286 | break; 287 | case PACKET_DECODER_STATE_END_OF_TRANSMISSION: 288 | exit_updater = true; 289 | break; 290 | default: 291 | break; 292 | } 293 | } 294 | if (error) { 295 | ui_state = UI_STATE_ERROR; 296 | switch_released = false; 297 | while (!switch_released); // Polled in ISR 298 | InitializeReception(); 299 | } 300 | } 301 | codec.Stop(); 302 | Uninitialize(); 303 | JumpTo(kStartAddress); 304 | while (1) { } 305 | } 306 | --------------------------------------------------------------------------------