├── .github └── workflows │ └── release.yml ├── .gitignore ├── .gitmodules ├── Makefile ├── README.md ├── common ├── assets │ ├── DialMap.lua │ ├── UnitShared.lua │ └── ViewControl │ │ ├── Branch.lua │ │ ├── Split │ │ ├── Paged.lua │ │ └── init.lua │ │ ├── SubControl │ │ ├── Button.lua │ │ ├── Chain.lua │ │ ├── Readout.lua │ │ ├── ScaleList.lua │ │ ├── Toggle.lua │ │ └── init.lua │ │ └── SubView │ │ ├── GainBias.lua │ │ ├── Gate.lua │ │ ├── Parameter.lua │ │ ├── Pitch.lua │ │ ├── PitchTrack.lua │ │ ├── Scale.lua │ │ └── init.lua ├── common.cpp.swig ├── dsp │ ├── compressor.h │ ├── env.h │ ├── filter.h │ ├── frame.h │ ├── latch.h │ ├── mix.h │ ├── osc.h │ ├── pitch.h │ ├── quantizer.h │ ├── slew.h │ ├── tempo.h │ └── timer.h ├── graphics │ ├── PagedGraphic.h │ ├── composites │ │ ├── BarChart.h │ │ ├── CircleChart.h │ │ ├── Fader.h │ │ ├── FollowableText.h │ │ ├── FollowableValue.h │ │ ├── GateList.h │ │ ├── Grid.h │ │ ├── Keyboard.h │ │ ├── ListWindow.h │ │ ├── ReadoutList.h │ │ ├── ScaleList.h │ │ └── Text.h │ ├── controls │ │ ├── MainControl.h │ │ ├── ParameterControl.h │ │ ├── ReadoutView.cpp │ │ ├── ReadoutView.h │ │ ├── ScaleListView.cpp │ │ └── ScaleListView.h │ ├── interfaces │ │ ├── all.cpp │ │ └── all.h │ └── primitives │ │ ├── Box.h │ │ ├── Circle.h │ │ ├── Line.h │ │ ├── Point.h │ │ ├── Range.h │ │ ├── all.h │ │ └── constants.h ├── sense.h ├── ui │ ├── DialMap.cpp │ ├── DialMap.h │ ├── LinearDialMap.cpp │ ├── LinearDialMap.h │ └── dial │ │ ├── dial.h │ │ └── state.h └── util │ ├── Units.h │ ├── math.h │ └── v2d.h ├── docker └── er-301-am335x-build-env │ └── Dockerfile ├── mods ├── lojik │ ├── And.cpp │ ├── And.h │ ├── Chance.cpp │ ├── Chance.h │ ├── DLatch.cpp │ ├── DLatch.h │ ├── Euclid.cpp │ ├── Euclid.h │ ├── EuclidCircle.h │ ├── Latch.cpp │ ├── Latch.h │ ├── Not.cpp │ ├── Not.h │ ├── OneTime.h │ ├── Or.cpp │ ├── Or.h │ ├── Pick.cpp │ ├── Pick.h │ ├── Pulse.cpp │ ├── Pulse.h │ ├── Register.cpp │ ├── Register.h │ ├── Register2.cpp │ ├── Register2.h │ ├── RegisterMainView.cpp │ ├── RegisterMainView.h │ ├── RegisterState.h │ ├── TLatch.cpp │ ├── TLatch.h │ ├── Trig.cpp │ ├── Trig.h │ ├── Wait.cpp │ ├── Wait.h │ ├── assets │ │ ├── Divide │ │ │ ├── Chance.lua │ │ │ ├── Euclid.lua │ │ │ └── Wait.lua │ │ ├── Latch │ │ │ ├── DLatch.lua │ │ │ ├── Latch.lua │ │ │ ├── Pick.lua │ │ │ └── TLatch.lua │ │ ├── Logic │ │ │ ├── And.lua │ │ │ ├── Not.lua │ │ │ ├── Or.lua │ │ │ └── Trig.lua │ │ ├── Pulse.lua │ │ ├── Sequencer │ │ │ ├── Register.lua │ │ │ ├── Register2.lua │ │ │ ├── RegisterShared.lua │ │ │ ├── Seq.lua │ │ │ └── Turing.lua │ │ ├── ViewControl │ │ │ ├── EuclidCircle.lua │ │ │ ├── Quantizer.lua │ │ │ └── RegisterView.lua │ │ └── toc.lua │ ├── controls │ │ └── main │ │ │ ├── QuantizerView.cpp │ │ │ └── QuantizerView.h │ ├── docs │ │ └── units.md │ ├── lojik.cpp.swig │ └── mod.mk ├── polygon │ ├── Duodecet.cpp │ ├── Observable.cpp │ ├── Observable.h │ ├── Octet.cpp │ ├── Polygon.cpp.swig │ ├── Polygon.h │ ├── Quartet.cpp │ ├── RoundRobinGateView.cpp │ ├── RoundRobinGateView.h │ ├── RoundRobinPitchView.cpp │ ├── RoundRobinPitchView.h │ ├── assets │ │ ├── Duodecet.lua │ │ ├── Octet.lua │ │ ├── Polygon.lua │ │ ├── Quartet.lua │ │ ├── ViewControl │ │ │ ├── OutputMeter.lua │ │ │ ├── RoundRobinGate.lua │ │ │ ├── RoundRobinPitch.lua │ │ │ └── TrackablePitch.lua │ │ └── toc.lua │ ├── docs │ │ ├── README.md │ │ └── images │ │ │ ├── reverse-sync-fast.png │ │ │ ├── reverse-sync.gif │ │ │ ├── screen-detune.png │ │ │ ├── screen-fall.png │ │ │ ├── screen-ff0.png │ │ │ ├── screen-gate.png │ │ │ ├── screen-main.png │ │ │ ├── screen-output.png │ │ │ ├── screen-shape.png │ │ │ ├── screen-vpo.png │ │ │ ├── voice-diagram.png │ │ │ ├── voice-switch.gif │ │ │ ├── voice-track.gif │ │ │ └── voice.drawio │ ├── mod.mk │ └── voice.h ├── scratch │ ├── Curl.cpp │ ├── Curl.h │ └── assets │ │ └── Curl.lua ├── sloop │ ├── ClockMarks.h │ ├── Slew.h │ ├── Sloop.cpp │ ├── Sloop.cpp.swig │ ├── Sloop.h │ ├── Sloop2.h │ ├── SloopHeadMainDisplay.h │ ├── SloopHeadSubDisplay.h │ ├── SyncLatch.h │ ├── assets │ │ ├── Sloop-10s.unit │ │ ├── Sloop-15s.unit │ │ ├── Sloop-5s.unit │ │ ├── Sloop-Delay-2s.unit │ │ ├── Sloop.lua │ │ ├── SloopFlagControl.lua │ │ ├── SloopHeader.lua │ │ ├── SloopOptionControl.lua │ │ ├── SloopView.lua │ │ ├── TaskListControl.lua │ │ └── toc.lua │ ├── docs │ │ └── README.md │ └── mod.mk └── strike │ ├── Arc.cpp │ ├── Arc.h │ ├── Bique.cpp │ ├── Bique.h │ ├── CPR.cpp │ ├── CPR.h │ ├── CompressorScope.cpp │ ├── CompressorScope.h │ ├── Fin.cpp │ ├── Fin.h │ ├── Formant.cpp │ ├── Formant.h │ ├── Lift.cpp │ ├── Lift.h │ ├── MultiOptionView.h │ ├── Sieve.cpp │ ├── Sieve.h │ ├── SimpleScope.h │ ├── Softy.cpp │ ├── Softy.h │ ├── Strike.cpp │ ├── Strike.h │ ├── Tanh.cpp │ ├── Tanh.h │ ├── assets │ ├── Arc.lua │ ├── Bique.lua │ ├── CPR.lua │ ├── CompressorScope.lua │ ├── DualOptionControl.lua │ ├── Fin.lua │ ├── Formant.lua │ ├── Lift.lua │ ├── OutputMeter.lua │ ├── SidechainMeter.lua │ ├── Sieve.lua │ ├── Softy.lua │ ├── Strike.lua │ ├── Tanh.lua │ └── toc.lua │ ├── mod.mk │ └── strike.cpp.swig ├── scripts ├── docker-shell.sh ├── env.mk ├── mod-builder.mk ├── rules.mk └── utils.mk └── src ├── common └── assets │ └── ViewControl │ └── Sub │ ├── Control │ ├── Button.lua │ ├── Chain.lua │ ├── Readout.lua │ └── Toggle.lua │ └── View │ ├── Gate.lua │ ├── Parameter.lua │ ├── Pitch.lua │ └── PitchTrack.lua └── mods └── polygon └── assets └── ViewControl ├── RoundRobinGate.lua ├── RoundRobinPitch.lua └── TrackablePitch.lua /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | container: tomjfiset/er-301-am335x-build-env:1.1.2 13 | 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v2 17 | with: 18 | submodules: true 19 | 20 | - name: Build Release 21 | run: make -j all ARCH=am335x PROFILE=release 22 | 23 | - name: Upload Artifacts 24 | uses: actions/upload-artifact@v2 25 | with: 26 | path: release/am335x/*.pkg 27 | name: packages 28 | if-no-files-found: error 29 | retention-days: 5 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | testing/ 3 | debug/ 4 | release/ 5 | *.s 6 | 7 | .DS_Store 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "er-301"] 2 | path = er-301 3 | url = https://github.com/odevices/er-301.git 4 | branch = master 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # top-level makefile 2 | 3 | PROJECTS = lojik sloop strike polygon 4 | 5 | docker_image = tomjfiset/er-301-am335x-build-env:1.1.2 6 | 7 | all: $(PROJECTS) 8 | 9 | asm: $(addsuffix -asm,$(PROJECTS)) 10 | 11 | $(PROJECTS): 12 | +$(MAKE) -j -f mods/$@/mod.mk PKGNAME=$@ 13 | 14 | $(addsuffix -install,$(PROJECTS)): $(@:-install=) 15 | $(eval PROJECT := $(@:-install=)) 16 | +$(MAKE) -f mods/$(PROJECT)/mod.mk install PKGNAME=$(PROJECT) 17 | 18 | $(addsuffix -install-sd,$(PROJECTS)): 19 | $(eval PROJECT := $(@:-install-sd=)) 20 | +$(MAKE) -f mods/$(PROJECT)/mod.mk install-sd PKGNAME=$(PROJECT) ARCH=am335x PROFILE=release 21 | 22 | $(addsuffix -install-sd-testing,$(PROJECTS)): 23 | $(eval PROJECT := $(@:-install-sd-testing=)) 24 | +$(MAKE) -f mods/$(PROJECT)/mod.mk install-sd PKGNAME=$(PROJECT) ARCH=am335x PROFILE=testing 25 | 26 | $(addsuffix -missing,$(PROJECTS)): 27 | $(eval PROJECT := $(@:-missing=)) 28 | +$(MAKE) -f mods/$(PROJECT)/mod.mk missing PKGNAME=$(PROJECT) 29 | 30 | $(addsuffix -asm,$(PROJECTS)): 31 | $(eval PROJECT := $(@:-asm=)) 32 | +$(MAKE) -f mods/$(PROJECT)/mod.mk asm PKGNAME=$(PROJECT) 33 | 34 | $(addsuffix -list,$(PROJECTS)): 35 | $(eval PROJECT := $(@:-list=)) 36 | +$(MAKE) -f mods/$(PROJECT)/mod.mk list PKGNAME=$(PROJECT) 37 | 38 | emu: 39 | @cd er-301; make -j emu && testing/darwin/emu/emu.elf 40 | 41 | am335x-docker: 42 | docker build docker/er-301-am335x-build-env/ -t er-301-am335x-build-env --platform=linux/amd64 43 | 44 | release: 45 | docker run -it -v `pwd`:/er-301-custom-units -w /er-301-custom-units $(docker_image) \ 46 | make -j all ARCH=am335x PROFILE=release 47 | 48 | testing: 49 | docker run -it -v `pwd`:/er-301-custom-units -w /er-301-custom-units --platform=linux/amd64 $(docker_image) \ 50 | make -j 4 all ARCH=am335x PROFILE=testing 51 | 52 | release-asm: 53 | docker run -it -v `pwd`:/er-301-custom-units -w /er-301-custom-units --platform=linux/amd64 $(docker_image) \ 54 | make -j asm ARCH=am335x PROFILE=release 55 | 56 | er-301-docker: 57 | docker run --privileged -it -v `pwd`:/er-301-custom-units -w /er-301-custom-units/er-301 --platform=linux/amd64 $(docker_image) \ 58 | make -j 4 ARCH=am335x PROFILE=release 59 | 60 | er-301-docker-testing: 61 | docker run --privileged -it -v `pwd`:/er-301-custom-units -w /er-301-custom-units/er-301 --platform=linux/amd64 $(docker_image) \ 62 | make -j 4 ARCH=am335x PROFILE=testing 63 | 64 | release-missing: 65 | docker run -it -v `pwd`:/er-301-custom-units -w /er-301-custom-units --platform=linux/amd64 $(docker_image) \ 66 | make -j 4 strike-missing ARCH=am335x PROFILE=release 67 | 68 | clean: 69 | rm -rf testing debug release 70 | 71 | .PHONY: all clean $(PROJECTS) $(addsuffix -install,$(PROJECTS)) $(addsuffix -install-sd,$(PROJECTS)) $(addsuffix -install-sd-testing,$(PROJECTS)) $(addsuffix -missing,$(PROJECTS)) emu am335x-docker release testing er-301-docker release-missing clean 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # er-301-custom-units 2 | [![Release](https://github.com/tmfset/er-301-custom-units/actions/workflows/release.yml/badge.svg)](https://github.com/tmfset/er-301-custom-units/actions/workflows/release.yml) 3 | 4 | A collection of bespoke units for the ER-301. 5 | 6 | Go to the [hub](https://er301-hub.netlify.app/) for module documentation. 7 | 8 | ## Building 9 | 10 | We need [odevices/er-301](https://github.com/odevices/er-301) as a submodule to access it's header files during the build: 11 | ``` 12 | git submodule update --init 13 | ``` 14 | 15 | ### Make Commands 16 | 17 | 18 | | Command | Description | 19 | |----|----| 20 | | `make -j all` | Build everything | 21 | | `make -j ` | Build a single module | 22 | | `make -j -install` | Build and copy to the emulator dir at `~/.od/front/ER-301/packages` | 23 | | `make emu` | Build and start the emulator | 24 | | `make release` | Build and package everything for release | 25 | | `make -install-sd` | Copy a release package to the SD card | 26 | -------------------------------------------------------------------------------- /common/assets/DialMap.lua: -------------------------------------------------------------------------------- 1 | local common = require "common.lib" 2 | 3 | local function linearMap(min, max, zero, rounding, wrap) 4 | return function (sc, c, f, sf) 5 | return common.LinearDialMap(min, max, zero, rounding, wrap or false, sc, c, f, sf) 6 | end 7 | end 8 | 9 | local with = { 10 | unit = linearMap(-1, 1, 0, 0.001), 11 | punit = linearMap( 0, 1, 0, 0.001), 12 | count = { 13 | zeroTo = function (max, zero, wrap) 14 | return linearMap(0, max, zero or 0, 1, wrap) 15 | end, 16 | oneTo = function (max, zero, wrap) 17 | return linearMap(1, max, zero or 1, 1, wrap) 18 | end, 19 | span = function (max, zero, wrap) 20 | return linearMap(-max, max, zero or 0, 1, wrap) 21 | end 22 | }, 23 | cents = { 24 | threeOctaves = linearMap(-3600, 3600, 0, 1) 25 | } 26 | } 27 | 28 | return { 29 | unit = { 30 | with = with.unit, 31 | default = with.unit(0.1, 0.01, 0.005, 0.001) 32 | }, 33 | punit = { 34 | with = with.punit, 35 | default = with.punit(0.1, 0.01, 0.005, 0.001) 36 | }, 37 | count = { 38 | zeroTo = with.count.zeroTo, 39 | oneTo = with.count.oneTo, 40 | span = with.count.span 41 | }, 42 | cents = { 43 | threeOctaves = with.cents.threeOctaves(1200, 100, 10, 1) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /common/assets/ViewControl/Branch.lua: -------------------------------------------------------------------------------- 1 | local Class = require "Base.Class" 2 | local ViewControl = require "Unit.ViewControl" 3 | 4 | local Branch = Class {} 5 | Branch:include(ViewControl) 6 | 7 | function Branch:init(args) 8 | local name = args.name or app.logError("%s.init: name is missing.", self) 9 | local branch = args.branch or app.logError("%s.init: branch is missing.", self) 10 | ViewControl.init(self, name) 11 | self.branch = branch 12 | end 13 | 14 | return Branch -------------------------------------------------------------------------------- /common/assets/ViewControl/Split/Paged.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local common = require "common.lib" 3 | local Class = require "Base.Class" 4 | local Base = require "common.assets.ViewControl.Split" 5 | 6 | local Paged = Class {} 7 | Paged:include(Base) 8 | 9 | function Paged:init(args) 10 | Base.init(self, args) 11 | 12 | self.subViews = {} 13 | self.pages = {} 14 | self.pageIndexByKey = {} 15 | 16 | self.subGraphic = common.PagedGraphic(0, 0, 128, 64); 17 | end 18 | 19 | function Paged:attachFollower(follower) 20 | self.follower = follower 21 | end 22 | 23 | function Paged:updateFollower(pageIndex) 24 | if self.follower then self.follower:updatePageIndex(pageIndex, false) end 25 | end 26 | 27 | function Paged:onRemove() 28 | for _, subView in ipairs(self.subViews) do 29 | subView:onRemove() 30 | end 31 | end 32 | 33 | function Paged:addSubView(subView) 34 | local nextIndex = #self.subViews + 1 35 | 36 | self.subViews[nextIndex] = subView 37 | self.pages[nextIndex] = subView.name 38 | self.pageIndexByKey[subView.name] = nextIndex 39 | 40 | subView:setViewControl(self) 41 | self.subGraphic:addChild(subView.graphic) 42 | end 43 | 44 | function Paged:subView() 45 | local currentPage = self.subGraphic:currentPage() 46 | return self.subViews[currentPage + 1] 47 | end 48 | 49 | function Paged:getFloatingMenuItems() 50 | return self.pages 51 | end 52 | 53 | function Paged:getPageName(i) 54 | return self.pages[i] or "" 55 | end 56 | 57 | function Paged:getPageIndex(choice) 58 | return self.pageIndexByKey[choice] or 1 59 | end 60 | 61 | function Paged:getFloatingMenuDefaultChoice() 62 | return self:getPageName(self.subGraphic:currentPage() + 1) 63 | end 64 | 65 | function Paged:onFloatingMenuEnter() 66 | self:unfocusSubView() 67 | end 68 | 69 | function Paged:onFloatingMenuChange(choice) 70 | self:unfocusSubView() 71 | self:updatePageIndex(self:getPageIndex(choice), true) 72 | end 73 | 74 | function Paged:onFloatingMenuSelection(choice) 75 | self:updatePageIndex(self:getPageIndex(choice), true) 76 | self:focus() 77 | end 78 | 79 | function Paged:updatePageIndex(pageIndex, propogate) 80 | self.subGraphic:setPage(pageIndex - 1) 81 | if propogate then self:updateFollower(pageIndex) end 82 | end 83 | 84 | return Paged 85 | -------------------------------------------------------------------------------- /common/assets/ViewControl/Split/init.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local Class = require "Base.Class" 3 | local Base = require "Unit.ViewControl.EncoderControl" 4 | 5 | local Split = Class {} 6 | Split:include(Base) 7 | 8 | function Split:init(args) 9 | Base.init(self, args.name) 10 | end 11 | 12 | function Split:addSubView(subView) 13 | if self._subview then 14 | app.logError("%s.addSubView: subView already set.", self) 15 | return 16 | end 17 | 18 | self._subView = subView 19 | self._subView:setViewControl(self) 20 | self.subGraphic = subView.graphic 21 | end 22 | 23 | function Split:subView() 24 | if not self._subView then 25 | app.logError("%s.subView: missing sub view", self) 26 | end 27 | 28 | return self._subView 29 | end 30 | 31 | function Split:onFocused() 32 | self:subView():onFocused() 33 | Base.onFocused(self) 34 | end 35 | 36 | function Split:onCursorEnter() 37 | self:subView():onCursorEnter() 38 | Base.onCursorEnter(self) 39 | end 40 | 41 | function Split:zeroPressed() 42 | return self:subView():zeroPressed() 43 | end 44 | 45 | function Split:cancelReleased() 46 | return self:subView():cancelReleased() 47 | end 48 | 49 | function Split:subReleased(i, shifted) 50 | if shifted then return false end 51 | return self:subView():subReleased(i, shifted) 52 | end 53 | 54 | function Split:subPressed(i, shifted) 55 | if shifted then return false end 56 | return self:subView():subPressed(i) 57 | end 58 | 59 | function Split:dialPressed(shifted) 60 | return self:subView():dialPressed(shifted) 61 | end 62 | 63 | function Split:encoder(change, shifted) 64 | return self:subView():encoder(change, shifted) 65 | end 66 | 67 | function Split:unfocusSubView() 68 | self:subView():clearFocusedPosition() 69 | self:unfocus() 70 | end 71 | 72 | return Split 73 | -------------------------------------------------------------------------------- /common/assets/ViewControl/SubControl/Button.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local Class = require "Base.Class" 3 | local Base = require "common.assets.ViewControl.SubControl" 4 | 5 | local Button = Class {} 6 | Button:include(Base) 7 | 8 | function Button:init(args) 9 | Base.init(self, args) 10 | self.onPress = args.onPress or self.onPress 11 | self.onRelease = args.onRelease or self.onRelease 12 | end 13 | 14 | return Button 15 | -------------------------------------------------------------------------------- /common/assets/ViewControl/SubControl/Chain.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local Class = require "Base.Class" 3 | local Base = require "common.assets.ViewControl.SubControl" 4 | 5 | local Chain = Class {} 6 | Chain:include(Base) 7 | 8 | function Chain:init(args) 9 | args.name = args.name or "empty" 10 | Base.init(self, args) 11 | 12 | self.branch = args.branch or app.logError("%s.addScope: missing branch.", self) 13 | 14 | local column = args.column or app.BUTTON1_CENTER 15 | local row = args.row or app.GRID5_CENTER1 16 | 17 | self.scope = app.MiniScope(column, row, 40, 45) 18 | self.scope:setBorder(1) 19 | self.scope:setCornerRadius(3, 3, 3, 3) 20 | self:addGraphic(self.scope) 21 | 22 | self.branch:subscribe("contentChanged", self) 23 | end 24 | 25 | function Chain:contentChanged(chain) 26 | if not chain == self.branch then return end 27 | local outlet = chain:getMonitoringOutput(1) 28 | self.scope:watchOutlet(outlet) 29 | self.button:setText(chain:mnemonic()) 30 | end 31 | 32 | function Chain:onRemove() 33 | self.branch:unsubscribe("contentChanged", self) 34 | end 35 | 36 | function Chain:onRelease() 37 | self:unfocusParent() 38 | self.branch:show() 39 | end 40 | 41 | return Chain 42 | -------------------------------------------------------------------------------- /common/assets/ViewControl/SubControl/Readout.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local ply = app.SECTION_PLY 3 | 4 | local common = require "common.lib" 5 | local DialMap = require "common.assets.DialMap" 6 | local Class = require "Base.Class" 7 | local Encoder = require "Encoder" 8 | local Base = require "common.assets.ViewControl.SubControl" 9 | 10 | local Readout = Class {} 11 | Readout:include(Base) 12 | 13 | function Readout:init(args) 14 | Base.init(self, args) 15 | 16 | local param = args.parameter or app.logError("%s: missing parameter.", self) 17 | param:enableSerialization() 18 | 19 | local units = args.units or common.unitNone 20 | local dialMap = args.dialMap or DialMap.punit.default 21 | local precision = args.precision or 2 22 | local column = args.column or app.BUTTON1_CENTER 23 | local row = args.row or app.GRID5_CENTER1 24 | 25 | self.readout = common.ReadoutView(param) 26 | self.readout:setAttributes(units, dialMap) 27 | self.readout:setPrecision(precision) 28 | self.readout:setCenter(column, row) 29 | self:addGraphic(self.readout) 30 | 31 | self.editMessage = args.editMessage or "" 32 | self.commitMessage = args.commitMessage or "" 33 | self.encoderState = args.encoderState or Encoder.Coarse 34 | end 35 | 36 | function Readout:doKeyboardSet() 37 | local Decimal = require "Keyboard.Decimal" 38 | 39 | local keyboard = Decimal { 40 | message = self.editMessage, 41 | commitMessage = self.commitMessage, 42 | initialValue = self.readout:getValueInUnits() 43 | } 44 | 45 | local task = function(value) 46 | if value then 47 | self.readout:save() 48 | self.readout:setValueInUnits(value) 49 | end 50 | end 51 | 52 | keyboard:subscribe("done", task) 53 | keyboard:subscribe("commit", task) 54 | keyboard:show() 55 | end 56 | 57 | function Readout:onFocus() 58 | if not self:hasParentFocus("encoder") then self:focusParent() end 59 | Encoder.set(self.encoderState) 60 | self.readout:save() 61 | end 62 | 63 | function Readout:getCursorController() 64 | return self.readout 65 | end 66 | 67 | function Readout:onZero() 68 | self.readout:zero() 69 | end 70 | 71 | function Readout:onCancel(focused) 72 | self.readout:restore() 73 | end 74 | 75 | function Readout:onRelease(focused) 76 | if focused then self:doKeyboardSet() end 77 | end 78 | 79 | function Readout:onDialPress() 80 | if self.encoderState == Encoder.Coarse then 81 | self.encoderState = Encoder.Fine 82 | else 83 | self.encoderState = Encoder.Coarse 84 | end 85 | Encoder.set(self.encoderState) 86 | end 87 | 88 | function Readout:onEncoder(change, shifted) 89 | self.readout:encoder(change, shifted, self.encoderState == Encoder.Fine) 90 | end 91 | 92 | return Readout 93 | -------------------------------------------------------------------------------- /common/assets/ViewControl/SubControl/ScaleList.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local ply = app.SECTION_PLY 3 | 4 | local common = require "common.lib" 5 | local DialMap = require "common.assets.DialMap" 6 | local Class = require "Base.Class" 7 | local Encoder = require "Encoder" 8 | local Base = require "common.assets.ViewControl.SubControl" 9 | 10 | local ScaleList = Class {} 11 | ScaleList:include(Base) 12 | 13 | function ScaleList:init(args) 14 | args.label = false 15 | Base.init(self, args) 16 | 17 | local param = args.parameter or app.logError("%s: missing parameter.", self) 18 | local source = args.source or app.logError("%s: missing source.", self) 19 | 20 | local column = args.column or app.BUTTON1_CENTER 21 | local row = args.row or app.GRID5_CENTER1 22 | 23 | self.list = common.ScaleListView(source, param) 24 | self.list:setAttributes( 25 | common.unitNone, 26 | common.LinearDialMap(0, source:getScaleBookSize(), 0, 0.001, false, 1, 0.25, 0.1, 0.1) 27 | ); 28 | self.list:setCenter(column, row) 29 | self:addGraphic(self.list) 30 | 31 | self.encoderState = args.encoderState or Encoder.Coarse 32 | end 33 | 34 | function ScaleList:onFocus() 35 | if not self:hasParentFocus("encoder") then self:focusParent() end 36 | Encoder.set(self.encoderState) 37 | end 38 | 39 | function ScaleList:onDialPress() 40 | if self.encoderState == Encoder.Coarse then 41 | self.encoderState = Encoder.Fine 42 | else 43 | self.encoderState = Encoder.Coarse 44 | end 45 | Encoder.set(self.encoderState) 46 | end 47 | 48 | function ScaleList:getCursorController() 49 | return self.list 50 | end 51 | 52 | function ScaleList:onEncoder(change, shifted) 53 | self.list:encoder(change, shifted, self.encoderState == Encoder.Fine) 54 | end 55 | 56 | return ScaleList -------------------------------------------------------------------------------- /common/assets/ViewControl/SubControl/Toggle.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local Class = require "Base.Class" 3 | local Base = require "common.assets.ViewControl.SubControl" 4 | 5 | local Toggle = Class {} 6 | Toggle:include(Base) 7 | 8 | function Toggle:init(args) 9 | Base.init(self, args) 10 | self.option = args.option 11 | self.option:enableSerialization() 12 | 13 | self.valueOn = args.on 14 | self.valueOff = args.off 15 | 16 | self.onPress = function () 17 | self:toggle() 18 | self:updateViewState() 19 | end 20 | 21 | local column = args.column or app.BUTTON2_CENTER 22 | local row = args.row or app.GRID5_CENTER3 23 | 24 | self.indicator = app.BinaryIndicator(0, 0, 32, 32) 25 | self.indicator:setCenter(column, row) 26 | self:addGraphic(self.indicator) 27 | end 28 | 29 | function Toggle:onCursorEnter() 30 | self:updateViewState() 31 | return Base.onCursorEnter(self) 32 | end 33 | 34 | function Toggle:isOn() 35 | return self.option:value() == self.valueOn 36 | end 37 | 38 | function Toggle:toggle() 39 | if self:isOn() then 40 | self.option:set(self.valueOff) 41 | else 42 | self.option:set(self.valueOn) 43 | end 44 | end 45 | 46 | function Toggle:updateViewState() 47 | if self:isOn() then 48 | self.indicator:on() 49 | else 50 | self.indicator:off() 51 | end 52 | end 53 | 54 | return Toggle 55 | -------------------------------------------------------------------------------- /common/assets/ViewControl/SubControl/init.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local Class = require "Base.Class" 3 | local Observable = require "Base.Observable" 4 | 5 | local Control = Class {} 6 | Control:include(Observable) 7 | 8 | function Control:init(args) 9 | self.parent = args.parent or app.logError("%s.init: missing parent.", self) 10 | self.name = args.name or app.logError("%s.init: missing name.", self) 11 | self.position = args.position or app.logError("%s.init: missing position.", self) 12 | 13 | if (args.label == nil) or args.label then 14 | self.button = app.SubButton(self.name, self.position) 15 | self:addGraphic(self.button) 16 | end 17 | 18 | self.parent:addControl(self.position, self) 19 | end 20 | 21 | function Control:addGraphic(graphic) 22 | return self.parent:addGraphic(graphic) 23 | end 24 | 25 | function Control:hasParentFocus(str) 26 | return self.parent:hasFocus(str) 27 | end 28 | 29 | function Control:focusParent() 30 | return self.parent:focus() 31 | end 32 | 33 | function Control:unfocusParent() 34 | return self.parent:unfocus() 35 | end 36 | 37 | function Control:onCursorEnter() end 38 | 39 | function Control:getCursorController() end 40 | 41 | function Control:onRemove() end 42 | function Control:onFocus() end 43 | 44 | function Control:onZero() end 45 | function Control:onCancel() end 46 | function Control:onPress(focused) end 47 | function Control:onRelease(focused) end 48 | function Control:onDialPress() end 49 | function Control:onEncoder(change, shifted) end 50 | 51 | return Control 52 | -------------------------------------------------------------------------------- /common/assets/ViewControl/SubView/Parameter.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local Class = require "Base.Class" 3 | 4 | local Base = require "common.assets.ViewControl.SubView" 5 | local Readout = require "common.assets.ViewControl.SubControl.Readout" 6 | 7 | local Parameter = Class {} 8 | Parameter:include(Base) 9 | 10 | function Parameter:init(args) 11 | Base.init(self, args) 12 | 13 | Readout { 14 | parent = self, 15 | position = 1, 16 | name = args.param1.name, 17 | parameter = args.param1.parameter, 18 | editMessage = args.param1.editMessage, 19 | commitMessage = args.param1.commitMessage, 20 | column = app.BUTTON1_CENTER, 21 | row = app.GRID5_CENTER4 22 | } 23 | 24 | Readout { 25 | parent = self, 26 | position = 2, 27 | name = args.param2.name, 28 | parameter = args.param2.parameter, 29 | editMessage = args.param2.editMessage, 30 | commitMessage = args.param2.commitMessage, 31 | column = app.BUTTON2_CENTER, 32 | row = app.GRID5_CENTER4 33 | } 34 | 35 | Readout { 36 | parent = self, 37 | position = 3, 38 | name = args.param3.name, 39 | parameter = args.param3.parameter, 40 | editMessage = args.param3.editMessage, 41 | commitMessage = args.param3.commitMessage, 42 | column = app.BUTTON3_CENTER, 43 | row = app.GRID5_CENTER4 44 | } 45 | end 46 | 47 | return Parameter 48 | -------------------------------------------------------------------------------- /common/assets/ViewControl/SubView/Pitch.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local Class = require "Base.Class" 3 | local Encoder = require "Encoder" 4 | 5 | local common = require "common.lib" 6 | local DialMap = require "common.assets.DialMap" 7 | local Base = require "common.assets.ViewControl.SubView" 8 | local Readout = require "common.assets.ViewControl.SubControl.Readout" 9 | local Chain = require "common.assets.ViewControl.SubControl.Chain" 10 | 11 | local col2 = app.BUTTON2_CENTER 12 | local col3 = app.BUTTON3_CENTER 13 | local col2h = col2 + (col3 - col2) / 2 14 | 15 | local Pitch = Class {} 16 | Pitch:include(Base) 17 | 18 | local overlay = (function () 19 | local instructions = app.DrawingInstructions() 20 | 21 | local line1 = app.GRID5_LINE1 22 | local center3 = app.GRID5_CENTER3 23 | local col1 = app.BUTTON1_CENTER 24 | 25 | -- sum 26 | instructions:circle(col2, center3, 8) 27 | instructions:hline(col2 - 5, col2 + 5, center3) 28 | instructions:vline(col2, center3 - 5, center3 + 5) 29 | 30 | -- 2^V 31 | instructions:box(col2h - 8, center3 - 8, 16, 16) 32 | 33 | -- arrow: branch to bias 34 | instructions:hline(col1 + 20, col2 - 9, center3) 35 | instructions:triangle(col2 - 12, center3, 0, 3) 36 | 37 | -- arrow: bias to 2^V 38 | instructions:hline(col2 + 9, col2h - 8, center3) 39 | --instructions:triangle(col2h - 11, center3, 0, 3) 40 | 41 | -- arrow: 2^V to title 42 | instructions:vline(col2h, center3 + 8, line1 - 2) 43 | instructions:triangle(col2h, line1 - 2, 90, 3) 44 | 45 | return instructions 46 | end)() 47 | 48 | function Pitch:addDrawing() 49 | local drawing = app.Drawing(0, 0, 128, 64) 50 | drawing:add(overlay) 51 | self.graphic:addChild(drawing) 52 | 53 | local two = app.Label("2", 12) 54 | two:fitToText(0) 55 | two:setCenter(col2h - 2, app.GRID5_CENTER3) 56 | self.graphic:addChild(two) 57 | 58 | local v = app.Label("v", 10) 59 | v:fitToText(0) 60 | v:setCenter(col2h + 3, app.GRID5_CENTER3 + 5) 61 | self.graphic:addChild(v) 62 | end 63 | 64 | function Pitch:init(args) 65 | Base.init(self, args) 66 | 67 | self:addDrawing() 68 | 69 | Chain { 70 | parent = self, 71 | position = 1, 72 | name = "empty", 73 | branch = args.branch, 74 | column = app.BUTTON1_CENTER - 20, 75 | row = app.GRID5_LINE4 76 | } 77 | 78 | Readout { 79 | parent = self, 80 | position = 2, 81 | name = "tune", 82 | parameter = args.tune, 83 | dialMap = DialMap.cents.threeOctaves, 84 | units = common.unitCents, 85 | precision = 0, 86 | editMessage = "Pitch offset.", 87 | commitMessage = "Updated pitch offset.", 88 | column = app.BUTTON2_CENTER, 89 | row = app.GRID5_CENTER4 90 | } 91 | end 92 | 93 | function Pitch:onFocused() 94 | self:setFocusedPosition(2) 95 | Base.onFocused(self) 96 | end 97 | 98 | return Pitch 99 | -------------------------------------------------------------------------------- /common/assets/ViewControl/SubView/PitchTrack.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local Class = require "Base.Class" 3 | 4 | local Base = require "common.assets.ViewControl.SubView.Pitch" 5 | local Toggle = require "common.assets.ViewControl.SubControl.Toggle" 6 | 7 | local PitchTrack = Class {} 8 | PitchTrack:include(Base) 9 | 10 | function PitchTrack:init(args) 11 | Base.init(self, args) 12 | 13 | Toggle { 14 | parent = self, 15 | position = 3, 16 | name = "track", 17 | option = args.option, 18 | on = 1, 19 | off = 2, 20 | column = app.BUTTON3_CENTER, 21 | row = app.GRID5_CENTER3 22 | } 23 | end 24 | 25 | return PitchTrack 26 | -------------------------------------------------------------------------------- /common/assets/ViewControl/SubView/Scale.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local Class = require "Base.Class" 3 | 4 | local Base = require "common.assets.ViewControl.SubView" 5 | local Chain = require "common.assets.ViewControl.SubControl.Chain" 6 | local Readout = require "common.assets.ViewControl.SubControl.Readout" 7 | local ScaleList = require "common.assets.ViewControl.SubControl.ScaleList" 8 | 9 | local Scale = Class {} 10 | Scale:include(Base) 11 | 12 | local overlay = (function () 13 | local instructions = app.DrawingInstructions() 14 | 15 | local line1 = app.GRID5_LINE1 16 | local line4 = app.GRID5_LINE4 17 | 18 | local center3 = app.GRID5_CENTER3 19 | 20 | local col1 = app.BUTTON1_CENTER 21 | local col2 = app.BUTTON2_CENTER 22 | local col3 = app.BUTTON3_CENTER 23 | 24 | -- multiply 25 | instructions:circle(col2, center3, 8) 26 | instructions:line(col2 - 3, center3 - 3, col2 + 3, center3 + 3) 27 | instructions:line(col2 - 3, center3 + 3, col2 + 3, center3 - 3) 28 | 29 | -- sum 30 | -- instructions:circle(col3, center3, 8) 31 | -- instructions:hline(col3 - 5, col3 + 5, center3) 32 | -- instructions:vline(col3, center3 - 5, center3 + 5) 33 | 34 | -- arrow: branch to gain 35 | instructions:hline(col1 + 20, col2 - 9, center3) 36 | instructions:triangle(col2 - 12, center3, 0, 3) 37 | 38 | -- arrow: gain to bias 39 | instructions:hline(col2 + 9, col3 - 8, center3) 40 | instructions:triangle(col3 - 11, center3, 0, 3) 41 | 42 | -- arrow: bias to title 43 | -- instructions:vline(col3, center3 + 8, line1 - 2) 44 | -- instructions:triangle(col3, line1 - 2, 90, 3) 45 | 46 | return instructions 47 | end)() 48 | 49 | function Scale:addDrawing() 50 | local drawing = app.Drawing(0, 0, 128, 64) 51 | drawing:add(overlay) 52 | self.graphic:addChild(drawing) 53 | end 54 | 55 | function Scale:init(args) 56 | Base.init(self, args) 57 | 58 | self:addDrawing() 59 | 60 | Chain { 61 | parent = self, 62 | position = 1, 63 | name = "empty", 64 | branch = args.branch, 65 | column = app.BUTTON1_CENTER - 20, 66 | row = app.GRID5_LINE4 67 | } 68 | 69 | Readout { 70 | parent = self, 71 | position = 2, 72 | name = "gain", 73 | parameter = args.gainBias:getParameter("Gain"), 74 | dialMap = args.gainDialMap, 75 | units = args.units, 76 | precision = args.precision, 77 | editMessage = string.format("'%s' modulation gain.", self.name), 78 | commitMessage = string.format("'%s' gain updated.", self.name), 79 | column = app.BUTTON2_CENTER, 80 | row = app.GRID5_CENTER4 - 1 81 | } 82 | 83 | ScaleList { 84 | parent = self, 85 | position = 3, 86 | name = "scale", 87 | parameter = args.gainBias:getParameter("Bias"), 88 | source = args.scaleSource, 89 | column = app.BUTTON3_CENTER, 90 | row = app.GRID5_CENTER4 91 | } 92 | end 93 | 94 | function Scale:onFocused() 95 | self:setFocusedPosition(3) 96 | Base.onFocused(self) 97 | end 98 | 99 | return Scale -------------------------------------------------------------------------------- /common/common.cpp.swig: -------------------------------------------------------------------------------- 1 | 2 | %{ 3 | #undef SWIGLUA 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #define SWIGLUA 18 | %} 19 | 20 | %include 21 | %include 22 | %include 23 | %include 24 | %include 25 | %include 26 | %include 27 | %include 28 | -------------------------------------------------------------------------------- /common/dsp/compressor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace compressor { 9 | struct Slew { 10 | inline void setRiseFall(float rise, float fall) { 11 | auto sp = globalConfig.samplePeriod; 12 | rise = util::fmax(rise, sp); 13 | fall = util::fmax(fall, sp); 14 | 15 | mRiseCoeff = exp(-sp / rise); 16 | mFallCoeff = exp(-sp / fall); 17 | } 18 | 19 | inline float32x4_t process(float32x4_t to) { 20 | float _last = mValue, 21 | rc = mRiseCoeff, 22 | fc = mFallCoeff; 23 | 24 | float _target[4]; 25 | vst1q_f32(_target, to); 26 | 27 | for (int i = 0; i < 4; i++) { 28 | auto target = _target[i]; 29 | 30 | auto c = target > _last ? rc : fc; 31 | _last = c * _last + (1.0f - c) * target; 32 | 33 | _target[i] = _last; 34 | } 35 | 36 | mValue = _last; 37 | return vld1q_f32(_target); 38 | } 39 | 40 | float mValue = 0; 41 | float mRiseCoeff = 0; 42 | float mFallCoeff = 0; 43 | }; 44 | 45 | // For reference, 46 | // https://www.eecs.qmul.ac.uk/~josh/documents/2012/GiannoulisMassbergReiss-dynamicrangecompression-JAES2012.pdf 47 | struct Compressor { 48 | 49 | inline void setRiseFall(float rise, float fall) { 50 | mSlew.setRiseFall(rise, rise + fall); 51 | } 52 | 53 | inline void setThresholdRatio(float threshold, float ratio, bool makeupEnabled) { 54 | auto thresholdDb = util::toDecibels(threshold); 55 | auto ratioI = 1 / util::fmax(ratio, 1); 56 | auto over = -thresholdDb; 57 | auto makeupDb = util::fmax(over - over * ratioI, 0); 58 | auto makeup = util::fromDecibels(makeupDb); 59 | 60 | mMakeup = makeupEnabled ? makeup : 1.0f; 61 | mMakeupDb = makeupDb; 62 | mThresholdDb = thresholdDb; 63 | mRatioI = ratioI; 64 | } 65 | 66 | inline void excite(float32x4_t excite) { 67 | auto exciteDb = util::simd::toDecibels(excite); 68 | auto thresholdDb = vdupq_n_f32(mThresholdDb); 69 | auto ratioI = vdupq_n_f32(mRatioI); 70 | auto over = vmaxq_f32(exciteDb - thresholdDb, vdupq_n_f32(0)); 71 | auto slew = mSlew.process(over - over * ratioI); 72 | 73 | mActive = vcgtq_f32(slew, vdupq_n_f32(0.001)); 74 | mReduction = util::simd::fromDecibels(vnegq_f32(slew)); 75 | } 76 | 77 | inline float32x4_t makeup(float32x4_t signal) { 78 | return signal * mMakeup; 79 | } 80 | 81 | inline float32x4_t compress(float32x4_t signal) { 82 | return signal * mReduction; 83 | } 84 | 85 | uint32x4_t mActive; 86 | float32x4_t mReduction; 87 | 88 | float mThresholdDb; 89 | float mRatioI; 90 | float mMakeup; 91 | float mMakeupDb; 92 | 93 | Slew mSlew; 94 | }; 95 | } -------------------------------------------------------------------------------- /common/dsp/frame.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace dsp { 7 | namespace frame { 8 | inline uint32_t anyOverThreshold(const float *frame, float threshold) { 9 | auto _out = vdupq_n_u32(0); 10 | auto _threshold = vdupq_n_f32(threshold); 11 | 12 | for (int i = 0; i < FRAMELENGTH; i += 4) { 13 | auto over = vcgtq_f32(vld1q_f32(frame + i), _threshold); 14 | _out = vorrq_u32(_out, over); 15 | } 16 | 17 | auto t = vpmax_u32(vget_low_u32(_out), vget_high_u32(_out)); 18 | return vget_lane_u32(vpmax_u32(t, t), 0); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /common/dsp/tempo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace dsp { 7 | struct TapTempoClock { 8 | uint32_t process(uint32_t signal) { 9 | mPhase++; 10 | mCounter++; 11 | 12 | if (signal > mThreshold) { 13 | if (util::abs(mCounter - mPeriod) > mHysteresis) { 14 | mPeriod = mCounter; 15 | } 16 | mCounter = 0; 17 | } 18 | 19 | if (mPhase >= mPeriod) { 20 | mPhase = 0; 21 | return 1.0f; 22 | } 23 | 24 | return 0.0f; 25 | } 26 | 27 | float mThreshold = 0; 28 | uint32_t mHysteresis = 2; 29 | 30 | uint32_t mCounter = 0; 31 | uint32_t mPhase = 0; 32 | uint32_t mPeriod = 44000; 33 | }; 34 | } -------------------------------------------------------------------------------- /common/graphics/PagedGraphic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | namespace common { 9 | class PagedGraphic : public od::Graphic { 10 | public: 11 | PagedGraphic(int left, int bottom, int width, int height) : od::Graphic(left, bottom, width, height) { } 12 | virtual ~PagedGraphic() {} 13 | 14 | void switchPage() { 15 | mPage = (mPage + 1) % mChildren.size(); 16 | } 17 | 18 | void setPage(int page) { 19 | mPage = page % mChildren.size(); 20 | } 21 | 22 | int currentPage() { 23 | return mPage; 24 | } 25 | 26 | private: 27 | void draw(od::FrameBuffer &fb) { 28 | advanceFrame(); 29 | auto slew = mPageSlew.process(mPageSlewRate, mPage); 30 | 31 | const int pageCount = mChildren.size(); 32 | const int isMultiPage = pageCount > 1; 33 | for (int i = 0; i < pageCount; i++) { 34 | if (isMultiPage) drawPageIndicator(fb, WHITE, i); 35 | mChildren[i]->setPosition(0, (slew - i) * (mHeight + 1)); 36 | } 37 | 38 | od::Graphic::draw(fb); 39 | } 40 | 41 | inline void drawPageIndicator(od::FrameBuffer &fb, int color, int page) const { 42 | const auto pad = 3.0f; 43 | const auto size = 3.0f; 44 | const auto center = mHeight / 2.0f; 45 | 46 | const auto offset = mPage - page; 47 | const auto isCurrent = offset == 0; 48 | 49 | if (isCurrent && isFillFrame()) { 50 | fb.fillCircle(color, mWidth, center, size); 51 | } else { 52 | fb.circle(color, mWidth, center + offset * (size * 2.0f + pad), size); 53 | } 54 | } 55 | 56 | inline void advanceFrame() { 57 | mFrame = (mFrame + 1) % (int)GRAPHICS_REFRESH_RATE; 58 | } 59 | 60 | inline bool isFillFrame() const { 61 | //return mFrame > 1; 62 | return true; 63 | } 64 | 65 | int mPage = 0; 66 | int mFrame = 0; 67 | slew::SlewRate mPageSlewRate = slew::SlewRate::fromRate(0.1, GRAPHICS_REFRESH_PERIOD); 68 | slew::Slew mPageSlew; 69 | }; 70 | } -------------------------------------------------------------------------------- /common/graphics/composites/CircleChart.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace graphics { 8 | class CircleChart { 9 | public: 10 | inline CircleChart(HasChart &data, float size) : 11 | mChartData(data), 12 | mSize(size) { 13 | mChartData.attach(); 14 | } 15 | 16 | inline ~CircleChart() { 17 | mChartData.release(); 18 | } 19 | 20 | inline void draw(od::FrameBuffer &fb, const Box& world) const { 21 | auto length = mChartData.getChartSize(); 22 | auto current = mChartData.getChartCurrentIndex(); 23 | 24 | auto outer = world.minCircle(); 25 | auto inner = outer.scale(mSize); 26 | auto span = (outer.radius - inner.radius) / 2.0f; 27 | auto center = inner.grow(span); 28 | 29 | inner.trace(fb, GRAY1); 30 | center.trace(fb, GRAY5); 31 | outer.trace(fb, GRAY1); 32 | 33 | if (length < 1) return; 34 | 35 | float delta = M_PI * 2.0f / (float)length; 36 | 37 | v2d first, prev, curr; 38 | for (int i = 0; i < length; i++) { 39 | auto next = getCirclePoint(center, span, delta, i); 40 | 41 | if (i == 0) first = next; 42 | if (i == current) curr = next; 43 | 44 | if (i > 0) Line::of(prev, next).trace(fb, GRAY10); 45 | 46 | prev = next; 47 | } 48 | 49 | Line::of(prev, first).trace(fb, GRAY10); 50 | 51 | Point::of(curr).diamond(fb, WHITE); 52 | } 53 | 54 | private: 55 | v2d getCirclePoint(const Circle ¢er, float span, float delta, int i) const { 56 | float amount = mChartData.getChartValue(i); 57 | return center.grow(span * amount).pointAtTheta(delta * (float)i); 58 | } 59 | 60 | HasChart &mChartData; 61 | float mSize; 62 | }; 63 | } -------------------------------------------------------------------------------- /common/graphics/composites/FollowableText.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | namespace graphics { 9 | class FollowableText { 10 | public: 11 | inline FollowableText(FollowableValue &value) : 12 | mValue(value) { } 13 | 14 | inline FollowableText(FollowableValue &value, int size) : 15 | mValue(value) { 16 | setSize(size); 17 | } 18 | 19 | void setPrecision(int precision) { 20 | mForceRefresh = true; 21 | mPrecision = precision; 22 | } 23 | 24 | void setClear(bool clear) { 25 | mText.setClear(clear); 26 | } 27 | 28 | void setOutline(bool outline) { 29 | mText.setOutline(outline); 30 | } 31 | 32 | void setSize(int size) { 33 | mText.setSize(size); 34 | } 35 | 36 | inline Box draw( 37 | od::FrameBuffer &fb, 38 | od::Color color, 39 | const Box &world, 40 | JustifyAlign justifyAlign 41 | ) { 42 | prepareToSuppressZeros(); 43 | refresh(); 44 | return mText.draw(fb, color, world, justifyAlign); 45 | } 46 | 47 | private: 48 | inline void prepareToSuppressZeros() { 49 | if (mTimeToSuppressZeros > 0) { 50 | mTimeToSuppressZeros--; 51 | mForceRefresh = suppressZeros(); 52 | } 53 | } 54 | 55 | inline bool suppressZeros() { 56 | return mTimeToSuppressZeros == 0; 57 | } 58 | 59 | inline void refresh() { 60 | bool isRefreshed = mValue.refresh(); 61 | if (isRefreshed) mTimeToSuppressZeros = GRAPHICS_REFRESH_RATE; 62 | 63 | bool shouldRefresh = mForceRefresh || isRefreshed; 64 | if (!shouldRefresh) return; 65 | 66 | mForceRefresh = false; 67 | mText.setText(mValue.toString(mPrecision, suppressZeros())); 68 | } 69 | 70 | FollowableValue &mValue; 71 | Text mText { "" }; 72 | 73 | bool mForceRefresh = true; 74 | int mTimeToSuppressZeros = 0; 75 | int mPrecision = 0; 76 | }; 77 | } -------------------------------------------------------------------------------- /common/graphics/composites/FollowableValue.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace graphics { 8 | class FollowableValue { 9 | public: 10 | inline FollowableValue(od::Followable &followable) : 11 | mFollowable(followable) { 12 | mFollowable.attach(); 13 | } 14 | 15 | inline ~FollowableValue() { 16 | mFollowable.release(); 17 | } 18 | 19 | inline float setValueInUnits(float v, bool hard) { 20 | auto fromUnits = util::convertFromUnits(v, mUnits); 21 | 22 | if (hard) mFollowable.hardSet(fromUnits); 23 | else mFollowable.softSet(fromUnits); 24 | 25 | return fromUnits; 26 | } 27 | 28 | inline void setShowTarget(bool v) { 29 | mForceRefresh = true; 30 | mShowTarget = v; 31 | } 32 | 33 | inline void setUnits(util::Units v) { 34 | mForceRefresh = true; 35 | mUnits = v; 36 | } 37 | 38 | inline bool refresh() { 39 | auto current = currentValue(); 40 | 41 | auto isChanged = mLastValue != current; 42 | auto shouldRefresh = mForceRefresh || isChanged; 43 | if (!shouldRefresh) return false; 44 | 45 | mForceRefresh = false; 46 | mLastValue = current; 47 | mLastValueInUnits = util::convertToUnits(mLastValue, mUnits); 48 | 49 | return true; 50 | } 51 | 52 | inline bool hasMoved() { 53 | return mLastValue != currentValue(); 54 | } 55 | 56 | inline float lastValueInUnits() { 57 | return mLastValueInUnits; 58 | } 59 | 60 | inline std::string toString(int precision, bool suppressZeros) { 61 | return util::formatQuantity(mLastValueInUnits, mUnits, precision, suppressZeros); 62 | } 63 | 64 | private: 65 | inline float currentValue() { 66 | return mShowTarget ? mFollowable.target() : mFollowable.value(); 67 | } 68 | 69 | od::Followable &mFollowable; 70 | bool mForceRefresh = true; 71 | float mLastValue = 0; 72 | float mLastValueInUnits = 0; 73 | 74 | bool mShowTarget = false; 75 | util::Units mUnits = util::unitNone; 76 | }; 77 | } -------------------------------------------------------------------------------- /common/graphics/composites/GateList.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace graphics { 10 | class GateList { 11 | public: 12 | GateList() {} 13 | virtual ~GateList() {} 14 | 15 | void addItem(std::string name) { 16 | mItems.push_back(Item { name }); 17 | } 18 | 19 | void draw(od::FrameBuffer &fb, const Box &world) { 20 | auto length = mItems.size(); 21 | auto current = 0; 22 | 23 | auto rightWorld = world.splitRight(0.5); 24 | auto vertical = world.vertical(); 25 | 26 | auto primary = ListWindow::from(vertical, vertical.width(), 2) 27 | .scrollTo(current, length, od::justifyRight); 28 | 29 | for (int i = 0; i < length; i++) { 30 | if (!primary.vVisibleIndex(i)) continue; 31 | auto box = primary.vBox(world, i); 32 | 33 | Circle::cr( 34 | box.inLeft(mPrimaryRadius * 2).center(), 35 | mPrimaryRadius 36 | ).trace(fb, GRAY5); 37 | 38 | auto name = Text { "reset", 10 }; 39 | name.draw(fb, WHITE, box, LEFT_BOTTOM); 40 | 41 | // auto item = mItems.at(i); 42 | // item.mName.draw(fb, WHITE, box, CENTER_MIDDLE); 43 | // item.mPrimary.draw(fb, WHITE, box, LEFT_BOTTOM); 44 | } 45 | 46 | auto secondary = ListWindow::from(vertical, 6, 2) 47 | .scrollTo(current, length, od::justifyRight); 48 | 49 | for (int i = 0; i < length; i++) { 50 | if (!secondary.vVisibleIndex(i)) continue; 51 | auto box = secondary.vBox(rightWorld, i); 52 | 53 | auto color = i == current ? WHITE : GRAY5; 54 | box.minCircle().trace(fb, color); 55 | //box.trace(fb, WHITE); 56 | 57 | //auto item = mItems.at(i); 58 | //item.mSecondary.draw(fb, WHITE, box); 59 | } 60 | 61 | //rightWorld.trace(fb, WHITE); 62 | } 63 | 64 | private: 65 | struct Item { 66 | Item(std::string name) : 67 | mName(name, 12) {} 68 | 69 | Text mName; 70 | }; 71 | 72 | int mPrimaryRadius = 8; 73 | 74 | std::vector mItems; 75 | }; 76 | } -------------------------------------------------------------------------------- /common/graphics/composites/Grid.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace graphics { 6 | struct Grid { 7 | inline Grid(const Box &topLeft, int _cols, int _rows, float pad) : 8 | mark(topLeft.quantize().pad(pad)), 9 | cols(_cols), 10 | rows(_rows), 11 | cStep(topLeft.widthHeight().x()), 12 | rStep(-topLeft.widthHeight().y()) { } 13 | 14 | static inline Grid create(const Box& world, int cols, int rows, float pad) { 15 | float iCols = 1.0f / cols; 16 | float iRows = 1.0f / rows; 17 | 18 | auto corner = world.topLeftCorner(iCols, iRows).minSquare().quantizeSize(); 19 | auto grid = corner.scaleDiscrete(cols, rows).recenterOn(world).quantizeCenter(); 20 | 21 | return Grid(grid.topLeftCorner(iCols, iRows), cols, rows, pad); 22 | } 23 | 24 | static inline Grid createRect(const Box& world, int cols, int rows, float pad) { 25 | float iCols = 1.0f / cols; 26 | float iRows = 1.0f / rows; 27 | 28 | auto corner = world.topLeftCorner(iCols, iRows).quantizeSize(); 29 | auto grid = corner.scaleDiscrete(cols, rows).recenterOn(world).quantizeCenter(); 30 | 31 | return Grid(grid.topLeftCorner(iCols, iRows), cols, rows, pad); 32 | } 33 | 34 | inline int index(int c, int r) const { 35 | return c * rows + r; 36 | } 37 | 38 | inline Box cell(int c, int r) const { 39 | return mark.offset(cStep * c, rStep * r); 40 | } 41 | 42 | const Box mark; 43 | const int cols; 44 | const int rows; 45 | const float cStep; 46 | const float rStep; 47 | }; 48 | } -------------------------------------------------------------------------------- /common/graphics/composites/ReadoutList.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace graphics { 12 | class ReadoutList { 13 | public: 14 | ReadoutList() {} 15 | virtual ~ReadoutList() {} 16 | 17 | // graphics::ReadoutView* getCursorController(int i) { 18 | // if (i >= mItems.size()) return nullptr; 19 | // return &(mItems.at(i).mPrimary); 20 | // } 21 | 22 | void addItem(std::string name, od::Followable &followable) { 23 | mItems.push_back(Item { name, followable }); 24 | } 25 | 26 | void draw(od::FrameBuffer &fb, const Box &world) { 27 | auto length = mItems.size(); 28 | auto current = 0; 29 | 30 | auto rightWorld = world.splitRight(0.5); 31 | auto vertical = world.vertical(); 32 | 33 | auto primary = ListWindow::from(vertical, vertical.width(), 2) 34 | .scrollTo(current, length, od::justifyRight); 35 | 36 | for (int i = 0; i < length; i++) { 37 | if (!primary.vVisibleIndex(i)) continue; 38 | auto box = primary.vBox(world, i); 39 | 40 | auto text = Text { "16", 12 }; 41 | text.draw(fb, WHITE, box, LEFT_MIDDLE); 42 | 43 | auto name = Text { "length", 10 }; 44 | name.draw(fb, WHITE, box, LEFT_BOTTOM); 45 | 46 | // auto item = mItems.at(i); 47 | // item.mName.draw(fb, WHITE, box, CENTER_MIDDLE); 48 | // item.mPrimary.draw(fb, WHITE, box, CENTER_MIDDLE); 49 | } 50 | 51 | auto secondary = ListWindow::from(vertical, 6, 2) 52 | .scrollTo(current, length, od::justifyRight); 53 | 54 | for (int i = 0; i < length; i++) { 55 | if (!secondary.vVisibleIndex(i)) continue; 56 | auto box = secondary.vBox(rightWorld, i); 57 | 58 | auto color = i == current ? WHITE : GRAY5; 59 | auto text = Text { "16", 5 }; 60 | text.draw(fb, color, box, RIGHT_MIDDLE); 61 | 62 | //box.trace(fb, WHITE); 63 | 64 | //auto item = mItems.at(i); 65 | //item.mSecondary.draw(fb, WHITE, box, RIGHT_MIDDLE); 66 | } 67 | } 68 | 69 | private: 70 | struct Item { 71 | Item(std::string name, od::Followable &followable) : 72 | mName(name, 12), 73 | mValue(followable), 74 | mPrimary(mValue, 16), 75 | mSecondary(mValue, 5) { } 76 | 77 | Text mName; 78 | FollowableValue mValue; 79 | 80 | FollowableText mPrimary; 81 | FollowableText mSecondary; 82 | }; 83 | 84 | int mPrimaryTextSize = 16; 85 | int mSecondaryTextSize = 5; 86 | 87 | std::vector mItems; 88 | }; 89 | } -------------------------------------------------------------------------------- /common/graphics/composites/ScaleList.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | namespace graphics { 12 | class ScaleList { 13 | public: 14 | inline ScaleList(HasScaleBook &data) : mData(data) { 15 | mData.attach(); 16 | } 17 | 18 | inline ~ScaleList() { 19 | mData.release(); 20 | } 21 | 22 | Box draw( 23 | od::FrameBuffer &fb, 24 | const Box &world, 25 | int size 26 | ) { 27 | auto length = mData.getScaleBookSize(); 28 | auto current = mData.getScaleBookIndex(); 29 | 30 | auto slewCurrent = mIndexSlew.process(mIndexSlewRate, current); 31 | 32 | auto window = ListWindow::from(world.vertical(), size, 2) 33 | .scrollTo(slewCurrent, length); 34 | 35 | auto currentLb = world.leftBottom(); 36 | auto currentRt = world.rightTop(); 37 | 38 | for (int i = 0; i < length; i++) { 39 | auto y = window.globalCenterFromRight(i); 40 | if (!window.visible(y)) continue; 41 | 42 | auto name = mData.getScaleName(i); 43 | auto scaleLength = mData.getScaleSize(i); 44 | 45 | auto wh = world.widthHeight().atY(size); 46 | auto xy = world.leftCenter().atY(y); 47 | 48 | auto color = GRAY10; 49 | 50 | auto isCurrent = i == current; 51 | auto box = Box::lbwh(xy, wh); 52 | if (isCurrent) { 53 | currentLb = box.leftBottom(); 54 | currentRt = box.rightTop(); 55 | color = WHITE; 56 | } 57 | 58 | Text(name, size).draw(fb, color, box, LEFT_MIDDLE); 59 | } 60 | 61 | return Box::lbrt(currentLb, currentRt); 62 | } 63 | 64 | private: 65 | slew::SlewRate mIndexSlewRate = slew::SlewRate::fromRate(0.1, GRAPHICS_REFRESH_PERIOD); 66 | slew::Slew mIndexSlew; 67 | HasScaleBook &mData; 68 | }; 69 | } -------------------------------------------------------------------------------- /common/graphics/composites/Text.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | 9 | namespace graphics { 10 | class Text { 11 | public: 12 | inline Text(std::string str) { 13 | mRefreshDimensions = true; 14 | mText = str; 15 | } 16 | 17 | inline Text(std::string str, int size) { 18 | mRefreshDimensions = true; 19 | mText = str; 20 | mSize = size; 21 | } 22 | 23 | void setText(std::string str) { 24 | mRefreshDimensions = true; 25 | mText = str; 26 | } 27 | 28 | void setSize(int size) { 29 | mRefreshDimensions = true; 30 | mSize = size; 31 | } 32 | 33 | void setClear(bool clear) { 34 | mClear = clear; 35 | } 36 | 37 | void setOutline(bool outline) { 38 | mOutline = outline; 39 | } 40 | 41 | void setVertical(bool vertical) { 42 | mRefreshDimensions = true; 43 | mVertical = vertical; 44 | } 45 | 46 | inline Box draw( 47 | od::FrameBuffer &fb, 48 | od::Color color, 49 | const Box &world, 50 | JustifyAlign justifyAlign 51 | ) { 52 | updateDimensions(); 53 | 54 | auto _bounds = bounds(world, justifyAlign); 55 | if (mClear) _bounds.clear(fb); 56 | 57 | if (mVertical) fb.vtext(color, _bounds.left(), _bounds.bottom(), mText.c_str(), mSize); 58 | else fb.text(color, _bounds.left(), _bounds.bottom(), mText.c_str(), mSize); 59 | 60 | if (mOutline) _bounds.outline(fb, WHITE, 2); 61 | return _bounds; 62 | } 63 | 64 | private: 65 | inline Box bounds(const Box &world, JustifyAlign justifyAlign) const { 66 | auto dimensions = mVertical ? mDimensions.swap() : mDimensions; 67 | return Box::wh(dimensions).justifyAlign(world, justifyAlign); 68 | } 69 | 70 | inline void updateDimensions() { 71 | if (!mRefreshDimensions) return; 72 | mRefreshDimensions = false; 73 | 74 | int width, height; 75 | od::getTextSize(mText.c_str(), &width, &height, mSize); 76 | mDimensions = v2d::of(width, height); 77 | } 78 | 79 | std::string mText; 80 | 81 | int mSize = 10; 82 | bool mClear = true; 83 | bool mOutline = false; 84 | bool mVertical = false; 85 | 86 | bool mRefreshDimensions = true; 87 | v2d mDimensions; 88 | }; 89 | } 90 | -------------------------------------------------------------------------------- /common/graphics/controls/MainControl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace graphics { 6 | class MainControl : public od::Graphic { 7 | public: 8 | MainControl(int plyWidth) : 9 | Graphic(0, 0, plyWidth * SECTION_PLY, SCREEN_HEIGHT), 10 | mPlyWidth(plyWidth) { } 11 | 12 | virtual ~MainControl() { } 13 | 14 | int plyWidth() { return mPlyWidth; } 15 | 16 | private: 17 | int mPlyWidth; 18 | }; 19 | } -------------------------------------------------------------------------------- /common/graphics/controls/ParameterControl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace graphics { 8 | class ParameterControl { 9 | public: 10 | inline ParameterControl(od::Parameter *param) : 11 | mpParameter(param) { 12 | mpParameter->attach(); 13 | } 14 | 15 | inline ~ParameterControl() { 16 | mpParameter->release(); 17 | } 18 | 19 | void setValueInUnits(float value) { 20 | mDisplay.setValueInUnits(value, true); 21 | } 22 | 23 | void save() { 24 | mDialState.save(); 25 | } 26 | 27 | void zero() { 28 | mDialState.zero(); 29 | setValueInUnits(mDialState.value()); 30 | } 31 | 32 | void restore() { 33 | mDialState.restore(); 34 | setValueInUnits(mDialState.value()); 35 | } 36 | 37 | void encoder(int change, bool shifted, bool fine) { 38 | if (mDisplay.hasMoved()) { 39 | mDialState.set(mDisplay.lastValueInUnits()); 40 | } 41 | mDialState.move(change, shifted, fine); 42 | setValueInUnits(mDialState.value()); 43 | } 44 | 45 | private: 46 | od::Parameter *mpParameter; 47 | ui::dial::State mDialState; 48 | }; 49 | } -------------------------------------------------------------------------------- /common/graphics/controls/ReadoutView.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace graphics { 4 | ReadoutView::ReadoutView(od::Parameter *param) : 5 | od::Graphic(0, 0, 20, 20), 6 | mDisplay(*param) { 7 | setCursorOrientation(od::cursorRight); 8 | } 9 | 10 | ReadoutView::~ReadoutView() { } 11 | } -------------------------------------------------------------------------------- /common/graphics/controls/ReadoutView.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | namespace graphics { 18 | class ReadoutView : public od::Graphic { 19 | public: 20 | ReadoutView(od::Parameter *param); 21 | 22 | virtual ~ReadoutView(); 23 | 24 | void setAttributes(util::Units units, ui::DialMap *map) { 25 | mDisplay.setUnits(units); 26 | mDialState.setMap(map); 27 | } 28 | 29 | void setPrecision(int precision) { 30 | mText.setPrecision(precision); 31 | } 32 | 33 | float getValueInUnits() { 34 | return mDisplay.lastValueInUnits(); 35 | } 36 | 37 | void setValueInUnits(float value) { 38 | mDisplay.setValueInUnits(value, true); 39 | } 40 | 41 | void setFontSize(int size) { 42 | mText.setSize(size); 43 | } 44 | 45 | void setJustifyAlign(graphics::JustifyAlign ja) { 46 | mJustifyAlign = ja; 47 | } 48 | 49 | void setCursorOrientation(od::CursorOrientation orientation) { 50 | mCursorState.orientation = orientation; 51 | } 52 | 53 | void save() { 54 | mDialState.save(); 55 | } 56 | 57 | void zero() { 58 | mDialState.zero(); 59 | setValueInUnits(mDialState.value()); 60 | } 61 | 62 | void restore() { 63 | mDialState.restore(); 64 | setValueInUnits(mDialState.value()); 65 | } 66 | 67 | void encoder(int change, bool shifted, bool fine) { 68 | if (mDisplay.hasMoved()) { 69 | mDialState.set(mDisplay.lastValueInUnits()); 70 | } 71 | mDialState.move(change, shifted, fine); 72 | setValueInUnits(mDialState.value()); 73 | } 74 | 75 | #ifndef SWIGLUA 76 | void draw(od::FrameBuffer &fb) { 77 | Graphic::draw(fb); 78 | 79 | auto world = Box::extractWorld(*this); 80 | auto bounds = mText.draw(fb, WHITE, world, mJustifyAlign); 81 | bounds.pointAtMe(mCursorState); 82 | } 83 | #endif 84 | 85 | private: 86 | FollowableValue mDisplay; 87 | FollowableText mText { mDisplay }; 88 | ui::dial::State mDialState; 89 | JustifyAlign mJustifyAlign = CENTER_MIDDLE; 90 | }; 91 | } -------------------------------------------------------------------------------- /common/graphics/controls/ScaleListView.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace graphics { 4 | ScaleListView::ScaleListView(HasScaleBook &data, od::Followable &followable) : 5 | od::Graphic(0, 0, 40, 40), 6 | mValue(followable), 7 | mView(data) { 8 | mCursorState.orientation = od::cursorRight; 9 | } 10 | 11 | ScaleListView::~ScaleListView() { } 12 | } -------------------------------------------------------------------------------- /common/graphics/controls/ScaleListView.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #include 13 | 14 | namespace graphics { 15 | class ScaleListView : public od::Graphic { 16 | public: 17 | ScaleListView(HasScaleBook &data, od::Followable &followable); 18 | virtual ~ScaleListView(); 19 | 20 | void setAttributes(util::Units units, ui::DialMap *map) { 21 | mValue.setUnits(units); 22 | mDialState.setMap(map); 23 | } 24 | 25 | void setValueInUnits(float value) { 26 | mValue.setValueInUnits(value, true); 27 | } 28 | 29 | void encoder(int change, bool shifted, bool fine) { 30 | // if (mDisplay.hasMoved()) { 31 | // mDialState.set(mDisplay.lastValueInUnits()); 32 | // } 33 | mDialState.move(change, shifted, fine); 34 | setValueInUnits(mDialState.value()); 35 | } 36 | 37 | #ifndef SWIGLUA 38 | void draw(od::FrameBuffer &fb) { 39 | Graphic::draw(fb); 40 | 41 | auto world = Box::extractWorld(*this); 42 | auto bounds = mView.draw(fb, world, 5); 43 | bounds.pointAtMe(mCursorState); 44 | } 45 | #endif 46 | 47 | private: 48 | FollowableValue mValue; 49 | ui::dial::State mDialState; 50 | ScaleList mView; 51 | }; 52 | } -------------------------------------------------------------------------------- /common/graphics/interfaces/all.cpp: -------------------------------------------------------------------------------- 1 | #include "all.h" 2 | 3 | namespace graphics { 4 | HasChart::HasChart() { } 5 | HasChart::~HasChart() { } 6 | 7 | HasScale::HasScale() { } 8 | HasScale::~HasScale() { } 9 | 10 | HasScaleBook::HasScaleBook() { } 11 | HasScaleBook::~HasScaleBook() { } 12 | 13 | Quantizer::Quantizer() { } 14 | Quantizer::~Quantizer() { } 15 | } -------------------------------------------------------------------------------- /common/graphics/interfaces/all.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace graphics { 6 | class HasChart { 7 | public: 8 | HasChart(); 9 | virtual ~HasChart(); 10 | 11 | virtual int getChartSize() = 0; 12 | virtual int getChartCurrentIndex() = 0; 13 | virtual int getChartBaseIndex() = 0; 14 | virtual float getChartValue(int i) = 0; 15 | 16 | virtual void attach() = 0; 17 | virtual void release() = 0; 18 | }; 19 | 20 | class HasScale { 21 | public: 22 | HasScale(); 23 | virtual ~HasScale(); 24 | 25 | virtual std::string getScaleName() = 0; 26 | virtual int getScaleSize() = 0; 27 | virtual float getScaleCentValue(int i) = 0; 28 | 29 | virtual float getDetectedCentValue() = 0; 30 | virtual int getDetectedOctaveValue() = 0; 31 | virtual float getQuantizedCentValue() = 0; 32 | 33 | virtual void attach() = 0; 34 | virtual void release() = 0; 35 | }; 36 | 37 | class HasScaleBook { 38 | public: 39 | HasScaleBook(); 40 | virtual ~HasScaleBook(); 41 | 42 | virtual int getScaleBookIndex() = 0; 43 | virtual int getScaleBookSize() = 0; 44 | virtual std::string getScaleName(int i) = 0; 45 | virtual int getScaleSize(int i) = 0; 46 | 47 | virtual void attach() = 0; 48 | virtual void release() = 0; 49 | }; 50 | 51 | class Quantizer : public HasScale, public HasScaleBook { 52 | public: 53 | Quantizer(); 54 | virtual ~Quantizer(); 55 | }; 56 | } -------------------------------------------------------------------------------- /common/graphics/primitives/Circle.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace graphics { 7 | struct Circle { 8 | inline Circle(const v2d &c, float r) : 9 | center(c), 10 | radius(r) { } 11 | 12 | static inline Circle cr(const v2d &c, float r) { 13 | return Circle(c, r); 14 | } 15 | 16 | inline Circle offset(const v2d &by) const { 17 | return cr(center + by, radius); 18 | } 19 | 20 | inline Circle scale(float by) const { 21 | return cr(center, radius * by); 22 | } 23 | 24 | inline Circle grow(float by) const { 25 | return cr(center, radius + by); 26 | } 27 | 28 | inline Circle quantize() const { 29 | return cr(center.quantize(), radius); 30 | } 31 | 32 | inline v2d pointAtTheta(float theta) const { 33 | return center + v2d::ar(theta, radius); 34 | } 35 | 36 | inline void trace(od::FrameBuffer &fb, od::Color color) const { 37 | fb.circle(color, center.x(), center.y(), radius); 38 | } 39 | 40 | inline void fill(od::FrameBuffer &fb, od::Color color) const { 41 | fb.fillCircle(color, center.x(), center.y(), radius); 42 | } 43 | 44 | const v2d center; 45 | const float radius; 46 | }; 47 | } -------------------------------------------------------------------------------- /common/graphics/primitives/Line.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace graphics { 6 | class Line { 7 | public: 8 | inline Line(const v2d &f, const v2d &t) : 9 | from(f), to(t) { } 10 | 11 | static inline Line of(const v2d &f, const v2d &t) { return Line(f, t); } 12 | 13 | inline Line retreat(const v2d &f) const { return of(f, from); } 14 | inline Line advance(const v2d &t) const { return of(to, t); } 15 | 16 | inline void trace(od::FrameBuffer &fb, od::Color color) const { 17 | fb.line(color, from.x(), from.y(), to.x(), to.y()); 18 | } 19 | 20 | private: 21 | v2d from; 22 | v2d to; 23 | }; 24 | } -------------------------------------------------------------------------------- /common/graphics/primitives/all.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | -------------------------------------------------------------------------------- /common/graphics/primitives/constants.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace graphics { 4 | struct JustifyAlign { 5 | inline JustifyAlign() : 6 | JustifyAlign(od::justifyLeft, od::alignMiddle) { } 7 | 8 | inline JustifyAlign(od::Justification j, od::Alignment a) : 9 | justify(j), align(a) { } 10 | 11 | od::Justification justify; 12 | od::Alignment align; 13 | }; 14 | 15 | #define LEFT_BOTTOM graphics::JustifyAlign(od::justifyLeft, od::alignBottom) 16 | #define LEFT_MIDDLE graphics::JustifyAlign(od::justifyLeft, od::alignMiddle) 17 | #define LEFT_TOP graphics::JustifyAlign(od::justifyLeft, od::alignTop) 18 | #define CENTER_BOTTOM graphics::JustifyAlign(od::justifyCenter, od::alignBottom) 19 | #define CENTER_MIDDLE graphics::JustifyAlign(od::justifyCenter, od::alignMiddle) 20 | #define CENTER_TOP graphics::JustifyAlign(od::justifyCenter, od::alignTop) 21 | #define RIGHT_BOTTOM graphics::JustifyAlign(od::justifyRight, od::alignBottom) 22 | #define RIGHT_MIDDLE graphics::JustifyAlign(od::justifyRight, od::alignMiddle) 23 | #define RIGHT_TOP graphics::JustifyAlign(od::justifyRight, od::alignTop) 24 | } -------------------------------------------------------------------------------- /common/sense.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define INPUT_SENSE_LOW 1 6 | #define INPUT_SENSE_HIGH 2 7 | 8 | #define SENSE_LOW 0.1f 9 | #define SENSE_HIGH 0.0f 10 | 11 | namespace common { 12 | inline float getSense(od::Option &sense) { 13 | switch (sense.value()) { 14 | case INPUT_SENSE_LOW: return SENSE_LOW; 15 | case INPUT_SENSE_HIGH: return SENSE_HIGH; 16 | default: return SENSE_HIGH; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /common/ui/DialMap.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace ui { 4 | DialMap::DialMap() { } 5 | DialMap::~DialMap() { } 6 | } 7 | -------------------------------------------------------------------------------- /common/ui/DialMap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace ui { 7 | class DialMap : public od::ReferenceCounted { 8 | public: 9 | DialMap(); 10 | virtual ~DialMap(); 11 | 12 | virtual float valueAt(const ui::dial::Position &p) const = 0; 13 | virtual ui::dial::Position positionAt(float value) const = 0; 14 | virtual ui::dial::Position zero() const = 0; 15 | virtual void move(ui::dial::Position &p, int change, bool shift, bool fine) = 0; 16 | }; 17 | } -------------------------------------------------------------------------------- /common/ui/LinearDialMap.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace ui { 4 | LinearDialMap::LinearDialMap( 5 | float min, 6 | float max, 7 | float zero, 8 | float rounding, 9 | bool wrap, 10 | 11 | float sc, 12 | float c, 13 | float f, 14 | float sf 15 | ) : 16 | mRange(dial::Range(min, max)), 17 | mRadix(dial::Steps(sc, c, f, sf).proportionalRadixSet(mRange)), 18 | mRounding(rounding), 19 | mZero(zero), 20 | mWrap(wrap) { } 21 | 22 | LinearDialMap::~LinearDialMap() { } 23 | } -------------------------------------------------------------------------------- /common/ui/LinearDialMap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace ui { 7 | class LinearDialMap : public DialMap { 8 | public: 9 | LinearDialMap( 10 | float min, 11 | float max, 12 | float zero, 13 | float rounding, 14 | bool wrap, 15 | 16 | float superCoarse, 17 | float coarse, 18 | float fine, 19 | float superFine 20 | ); 21 | 22 | virtual ~LinearDialMap(); 23 | 24 | virtual float valueAt(const ui::dial::Position &p) const { 25 | float result = mRange.min() + mRadix.valueAt(p); 26 | return util::frt(result, mRounding); 27 | } 28 | 29 | virtual ui::dial::Position positionAt(float value) const { 30 | if (value < mRange.min()) return ui::dial::Position::min(); 31 | if (value > mRange.max()) return ui::dial::Position::max(); 32 | return mRadix.positionAt(value - mRange.min()); 33 | } 34 | 35 | virtual ui::dial::Position zero() const { 36 | return positionAt(mZero); 37 | } 38 | 39 | virtual void move(ui::dial::Position &p, int change, bool shift, bool fine) { 40 | mRadix.move(p, change, shift, fine, mWrap); 41 | mRadix.print(); 42 | p.print(); 43 | } 44 | 45 | private: 46 | dial::Range mRange; 47 | dial::RadixSet mRadix; 48 | float mRounding; 49 | float mZero; 50 | bool mWrap; 51 | }; 52 | } -------------------------------------------------------------------------------- /common/ui/dial/state.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace ui { 7 | namespace dial { 8 | class State { 9 | public: 10 | inline State() { } 11 | 12 | void setMap(DialMap *map) { 13 | if (mpMap) mpMap->release(); 14 | mpMap = map; 15 | if (mpMap) mpMap->attach(); 16 | } 17 | 18 | void save() { 19 | mPrevious = mCurrent; 20 | } 21 | 22 | void restore() { 23 | auto saved = mPrevious; 24 | save(); 25 | mCurrent = saved; 26 | } 27 | 28 | void set(float value) { 29 | if (!mpMap) return; 30 | set(mpMap->positionAt(value)); 31 | } 32 | 33 | void set(Position p) { 34 | mCurrent = p; 35 | } 36 | 37 | void zero() { 38 | if (!mpMap) return; 39 | set(mpMap->zero()); 40 | } 41 | 42 | float value() { 43 | if (!mpMap) return 0; 44 | return mpMap->valueAt(mCurrent); 45 | } 46 | 47 | void move(int change, bool shifted, bool fine) { 48 | if (!mpMap) return; 49 | 50 | change = mHysteresis.process(change); 51 | if (change == 0) return; 52 | 53 | mpMap->move(mCurrent, change, shifted, fine); 54 | } 55 | 56 | private: 57 | DialMap *mpMap = 0; 58 | 59 | Position mCurrent; 60 | Position mPrevious; 61 | Hysteresis mHysteresis; 62 | }; 63 | } 64 | } -------------------------------------------------------------------------------- /docker/er-301-am335x-build-env/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:xenial 2 | ENV DEBIAN_FRONTEND noninteractive 3 | 4 | # Add the git ppa so we can get an update to date version to use with Github Actions. 5 | RUN apt-get update 6 | RUN apt install software-properties-common -y 7 | RUN add-apt-repository ppa:git-core/ppa -y 8 | 9 | RUN apt-get update 10 | RUN apt-get install -y build-essential swig python3 gcc-multilib zip fonts-freefont-ttf git vim wget rsync 11 | 12 | RUN wget https://software-dl.ti.com/processor-sdk-rtos/esd/AM335X/04_01_00_06/exports/ti-processor-sdk-rtos-am335x-evm-04.01.00.06-Linux-x86-Install.bin 13 | RUN chmod +x ti-processor-sdk-rtos-am335x-evm-04.01.00.06-Linux-x86-Install.bin 14 | RUN ./ti-processor-sdk-rtos-am335x-evm-04.01.00.06-Linux-x86-Install.bin --prefix ~/ti --mode unattended 15 | -------------------------------------------------------------------------------- /mods/lojik/And.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace lojik { 4 | void And::process() { 5 | processInternal(); 6 | } 7 | } -------------------------------------------------------------------------------- /mods/lojik/And.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace lojik { 9 | class And : public od::Object { 10 | public: 11 | And() { 12 | addInput(mIn); 13 | addOutput(mOut); 14 | addInput(mGate); 15 | addOption(mSense); 16 | } 17 | 18 | virtual ~And() { } 19 | 20 | #ifndef SWIGLUA 21 | virtual void process(); 22 | 23 | void processInternal() { 24 | const float *in = mIn.buffer(); 25 | const float *gate = mGate.buffer(); 26 | float *out = mOut.buffer(); 27 | 28 | const auto sense = common::getSense(mSense); 29 | 30 | for (int i = 0; i < FRAMELENGTH; i += 4) { 31 | const auto _and = vandq_u32( 32 | vcgtq_f32(vld1q_f32(in + i), vdupq_n_f32(sense)), 33 | vcgtq_f32(vld1q_f32(gate + i), vdupq_n_f32(0)) 34 | ); 35 | 36 | vst1q_f32(out + i, vcvtq_n_f32_u32(_and, 32)); 37 | } 38 | } 39 | 40 | od::Inlet mIn { "In" }; 41 | od::Outlet mOut { "Out" }; 42 | od::Inlet mGate { "Gate" }; 43 | od::Option mSense { "Sense", INPUT_SENSE_LOW }; 44 | #endif 45 | }; 46 | } 47 | -------------------------------------------------------------------------------- /mods/lojik/Chance.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace lojik { 4 | void Chance::process() { 5 | processInternal(); 6 | } 7 | } -------------------------------------------------------------------------------- /mods/lojik/Chance.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define CHANCE_MODE_TRIGGER 1 11 | #define CHANCE_MODE_GATE 2 12 | #define CHANCE_MODE_PASSTHROUGH 3 13 | 14 | namespace lojik { 15 | class Chance : public od::Object { 16 | public: 17 | Chance() { 18 | addInput(mIn); 19 | addInput(mChance); 20 | addOutput(mOut); 21 | 22 | addOption(mMode); 23 | addOption(mSense); 24 | } 25 | 26 | virtual ~Chance() { } 27 | 28 | #ifndef SWIGLUA 29 | virtual void process(); 30 | 31 | void processInternal() { 32 | float *in = mIn.buffer(); 33 | float *chance = mChance.buffer(); 34 | float *out = mOut.buffer(); 35 | 36 | float32x4_t sense = vdupq_n_f32(common::getSense(mSense)); 37 | 38 | int mode = mMode.value(); 39 | 40 | OneTime trigSwitch { mTrigSwitch, false }; 41 | bool allow = mAllow; 42 | 43 | for (int i = 0; i < FRAMELENGTH; i += 4) { 44 | float32x4_t loadIn = vld1q_f32(in + i); 45 | float32x4_t loadChance = vld1q_f32(chance + i); 46 | 47 | uint32_t isInHigh[4]; 48 | vst1q_u32(isInHigh, vcgtq_f32(loadIn, sense)); 49 | 50 | for (int j = 0; j < 4; j++) { 51 | trigSwitch.mark(isInHigh[j]); 52 | bool doCapture = trigSwitch.read(); 53 | 54 | if (doCapture) { 55 | allow = loadChance[j] > od::Random::generateFloat(0.0f, 1.0f); 56 | } 57 | 58 | float value = 0.0f; 59 | if (allow) { 60 | switch (mode) { 61 | case CHANCE_MODE_TRIGGER: 62 | value = doCapture ? 1.0f : 0.0f; 63 | break; 64 | 65 | case CHANCE_MODE_GATE: 66 | value = isInHigh[j] ? 1.0f : 0.0f; 67 | break; 68 | 69 | case CHANCE_MODE_PASSTHROUGH: 70 | value = loadIn[j]; 71 | break; 72 | } 73 | } 74 | 75 | out[i + j] = value; 76 | } 77 | } 78 | 79 | mTrigSwitch = trigSwitch; 80 | mAllow = allow; 81 | } 82 | 83 | od::Inlet mIn { "In" }; 84 | od::Inlet mChance { "Chance" }; 85 | od::Outlet mOut { "Out" }; 86 | 87 | od::Option mMode { "Mode", CHANCE_MODE_GATE }; 88 | od::Option mSense { "Sense", INPUT_SENSE_LOW }; 89 | #endif 90 | 91 | private: 92 | OneTime mTrigSwitch; 93 | bool mAllow = false; 94 | }; 95 | } 96 | -------------------------------------------------------------------------------- /mods/lojik/DLatch.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace lojik { 4 | void DLatch::process() { 5 | processInternal(); 6 | } 7 | } -------------------------------------------------------------------------------- /mods/lojik/DLatch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace lojik { 8 | class DLatch : public od::Object { 9 | public: 10 | DLatch() { 11 | addInput(mIn); 12 | addInput(mClock); 13 | addInput(mReset); 14 | addOutput(mOut); 15 | } 16 | 17 | virtual ~DLatch() { } 18 | 19 | #ifndef SWIGLUA 20 | virtual void process(); 21 | 22 | void processInternal() { 23 | float *in = mIn.buffer(); 24 | float *clock = mClock.buffer(); 25 | float *reset = mReset.buffer(); 26 | float *out = mOut.buffer(); 27 | 28 | float32x4_t zero = vdupq_n_f32(0.0f); 29 | 30 | for (int i = 0; i < FRAMELENGTH; i += 4) { 31 | float32x4_t c = vld1q_f32(clock + i); 32 | float32x4_t r = vld1q_f32(reset + i); 33 | 34 | uint32_t cc[4], rc[4]; 35 | vst1q_u32(cc, vcgtq_f32(c, zero)); 36 | vst1q_u32(rc, vcgtq_f32(r, zero)); 37 | 38 | for (int j = 0; j < 4; j++) { 39 | if (!cc[j] || rc[j]) { 40 | mCatch = true; 41 | } 42 | 43 | out[i + j] = mCurrent; 44 | 45 | // Delay by one sample so we can form a chain. 46 | if (cc[j] && mCatch) { 47 | mCatch = false; 48 | mCurrent = in[i + j]; 49 | } 50 | } 51 | } 52 | } 53 | 54 | od::Inlet mIn { "In" }; 55 | od::Inlet mClock { "Clock" }; 56 | od::Inlet mReset { "Reset" }; 57 | od::Outlet mOut { "Out" }; 58 | #endif 59 | 60 | private: 61 | float mCurrent = 0.0f; 62 | bool mCatch = true; 63 | }; 64 | } -------------------------------------------------------------------------------- /mods/lojik/Euclid.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace lojik { 4 | void Euclid::process() { 5 | processInternal(); 6 | } 7 | } -------------------------------------------------------------------------------- /mods/lojik/Latch.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace lojik { 4 | void Latch::process() { 5 | processInternal(); 6 | } 7 | } -------------------------------------------------------------------------------- /mods/lojik/Latch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace lojik { 9 | class Latch : public od::Object { 10 | public: 11 | Latch() { 12 | addInput(mIn); 13 | addInput(mReset); 14 | addOutput(mOut); 15 | 16 | addOption(mSense); 17 | } 18 | 19 | virtual ~Latch() { } 20 | 21 | #ifndef SWIGLUA 22 | virtual void process(); 23 | 24 | void processInternal() { 25 | float *in = mIn.buffer(); 26 | float *reset = mReset.buffer(); 27 | float *out = mOut.buffer(); 28 | 29 | float32x4_t sense = vdupq_n_f32(common::getSense(mSense)); 30 | float32x4_t zero = vdupq_n_f32(0.0f); 31 | 32 | for (int i = 0; i < FRAMELENGTH; i += 4) { 33 | float32x4_t loadIn = vld1q_f32(in + i); 34 | float32x4_t loadReset = vld1q_f32(reset + i); 35 | 36 | uint32_t isInHigh[4], isResetHigh[4]; 37 | vst1q_u32(isInHigh, vcgtq_f32(loadIn, sense)); 38 | vst1q_u32(isResetHigh, vcgtq_f32(loadReset, zero)); 39 | 40 | for (int j = 0; j < 4; j++) { 41 | if (isResetHigh[j]) mCurrent = 0.0f; 42 | if (isInHigh[j]) mCurrent = 1.0f; 43 | 44 | out[i + j] = mCurrent; 45 | } 46 | } 47 | } 48 | 49 | od::Inlet mIn { "In" }; 50 | od::Inlet mReset { "Reset" }; 51 | od::Outlet mOut { "Out" }; 52 | 53 | od::Option mSense { "Sense", INPUT_SENSE_LOW }; 54 | #endif 55 | 56 | private: 57 | float mCurrent = 0.0f; 58 | }; 59 | } -------------------------------------------------------------------------------- /mods/lojik/Not.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace lojik { 4 | void Not::process() { 5 | processInternal(); 6 | } 7 | } -------------------------------------------------------------------------------- /mods/lojik/Not.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace lojik { 9 | class Not : public od::Object { 10 | public: 11 | Not() { 12 | addInput(mIn); 13 | addOutput(mOut); 14 | addOption(mSense); 15 | } 16 | 17 | virtual ~Not() { } 18 | 19 | #ifndef SWIGLUA 20 | virtual void process(); 21 | 22 | void processInternal() { 23 | const float *in = mIn.buffer(); 24 | float *out = mOut.buffer(); 25 | 26 | const auto sense = common::getSense(mSense); 27 | 28 | for (int i = 0; i < FRAMELENGTH; i += 4) { 29 | auto _not = vmvnq_u32(vcgtq_f32(vld1q_f32(in + i), vdupq_n_f32(sense))); 30 | vst1q_f32(out + i, vcvtq_n_f32_u32(_not, 32)); 31 | } 32 | } 33 | 34 | od::Inlet mIn { "In" }; 35 | od::Outlet mOut { "Out" }; 36 | od::Option mSense { "Sense", INPUT_SENSE_LOW }; 37 | #endif 38 | }; 39 | } 40 | -------------------------------------------------------------------------------- /mods/lojik/OneTime.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace lojik { 4 | class OneTime { 5 | public: 6 | OneTime() {} 7 | 8 | OneTime(const OneTime &other) { 9 | this->synced = other.synced; 10 | this->enabled = other.enabled; 11 | this->triggered = other.triggered; 12 | } 13 | 14 | OneTime(const OneTime &other, bool synced) { 15 | this->synced = synced; 16 | this->enabled = other.enabled; 17 | this->triggered = other.triggered; 18 | } 19 | 20 | void mark(bool high, bool reset = false) { 21 | if (!high || reset) { 22 | enabled = true; 23 | } 24 | 25 | if (enabled && high) { 26 | triggered = true; 27 | } 28 | } 29 | 30 | bool read(bool clock = false) { 31 | bool result = enabled && triggered && (!synced || clock); 32 | 33 | if (result) { 34 | triggered = false; 35 | enabled = false; 36 | } 37 | 38 | return result; 39 | } 40 | 41 | private: 42 | bool synced = false; 43 | bool enabled = true; 44 | bool triggered = false; 45 | }; 46 | } -------------------------------------------------------------------------------- /mods/lojik/Or.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace lojik { 4 | void Or::process() { 5 | processInternal(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /mods/lojik/Or.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace lojik { 9 | class Or : public od::Object { 10 | public: 11 | Or() { 12 | addInput(mIn); 13 | addOutput(mOut); 14 | addInput(mGate); 15 | addOption(mSense); 16 | } 17 | 18 | virtual ~Or() { } 19 | 20 | #ifndef SWIGLUA 21 | virtual void process(); 22 | 23 | void processInternal() { 24 | const float *in = mIn.buffer(); 25 | const float *gate = mGate.buffer(); 26 | float *out = mOut.buffer(); 27 | 28 | const auto sense = common::getSense(mSense); 29 | 30 | for (int i = 0; i < FRAMELENGTH; i += 4) { 31 | const auto _or = vorrq_u32( 32 | vcgtq_f32(vld1q_f32(in + i), vdupq_n_f32(sense)), 33 | vcgtq_f32(vld1q_f32(gate + i), vdupq_n_f32(0)) 34 | ); 35 | 36 | vst1q_f32(out + i, vcvtq_n_f32_u32(_or, 32)); 37 | } 38 | } 39 | 40 | od::Inlet mIn { "In" }; 41 | od::Outlet mOut { "Out" }; 42 | od::Inlet mGate { "Gate" }; 43 | od::Option mSense { "Sense", INPUT_SENSE_LOW }; 44 | #endif 45 | }; 46 | } 47 | -------------------------------------------------------------------------------- /mods/lojik/Pick.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace lojik { 4 | void Pick::process() { 5 | processInternal(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /mods/lojik/Pick.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace lojik { 8 | class Pick : public od::Object { 9 | public: 10 | Pick() { 11 | addInput(mIn); 12 | addOutput(mOut); 13 | addInput(mAlt); 14 | addInput(mPick); 15 | } 16 | 17 | virtual ~Pick() { } 18 | 19 | #ifndef SWIGLUA 20 | virtual void process(); 21 | 22 | void processInternal() { 23 | float *in = mIn.buffer(); 24 | float *alt = mAlt.buffer(); 25 | float *pick = mPick.buffer(); 26 | float *out = mOut.buffer(); 27 | 28 | for (int i = 0; i < FRAMELENGTH; i += 4) { 29 | auto p = vbslq_f32( 30 | vcgtq_f32(vld1q_f32(pick + i), vdupq_n_f32(0)), 31 | vld1q_f32(in + i), 32 | vld1q_f32(alt + i) 33 | ); 34 | 35 | vst1q_f32(out + i, p); 36 | } 37 | } 38 | 39 | od::Inlet mIn { "In" }; 40 | od::Outlet mOut { "Out" }; 41 | od::Inlet mAlt { "Alt" }; 42 | od::Inlet mPick { "Pick" }; 43 | #endif 44 | }; 45 | } 46 | -------------------------------------------------------------------------------- /mods/lojik/Pulse.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace lojik { 4 | void Pulse::process() { 5 | processInternal(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /mods/lojik/Pulse.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | namespace lojik { 10 | class Pulse : public od::Object { 11 | public: 12 | Pulse() { 13 | addInput(mVPO); 14 | addInput(mFreq); 15 | addInput(mSync); 16 | addInput(mWidth); 17 | addInput(mGain); 18 | addOutput(mOut); 19 | } 20 | 21 | virtual ~Pulse() { } 22 | 23 | #ifndef SWIGLUA 24 | virtual void process(); 25 | 26 | void processInternal() { 27 | float *vpo = mVPO.buffer(); 28 | float *freq = mFreq.buffer(); 29 | float *sync = mSync.buffer(); 30 | float *width = mWidth.buffer(); 31 | float *gain = mGain.buffer(); 32 | float *out = mOut.buffer(); 33 | 34 | float32x4_t negOne = vdupq_n_f32(-1.0f); 35 | float32x4_t one = vdupq_n_f32(1.0f); 36 | float32x4_t glog2 = vdupq_n_f32(FULLSCALE_IN_VOLTS * logf(2.0f)); 37 | float32x4_t sp = vdupq_n_f32(globalConfig.samplePeriod); 38 | 39 | for (int i = 0; i < FRAMELENGTH; i += 4) { 40 | float32x4_t loadVpo = vld1q_f32(vpo + i); 41 | float32x4_t loadFreq = vld1q_f32(freq + i); 42 | float32x4_t loadSync = vld1q_f32(sync + i); 43 | float32x4_t loadWidth = vld1q_f32(width + i); 44 | float32x4_t loadGain = vld1q_f32(gain + i); 45 | 46 | float32x4_t clampVpo = vmaxq_f32(negOne, vminq_f32(one, loadVpo)); 47 | float32x4_t tune = simd_exp(clampVpo * glog2); 48 | float32x4_t delta = loadFreq * sp * tune; 49 | 50 | auto p = vdupq_n_f32(mPhase) + delta * cScale; 51 | 52 | uint32_t _sync[4], _syncDelta[4]; 53 | vst1q_u32(_sync, vcgtq_f32(loadSync, vdupq_n_f32(0))); 54 | vst1q_u32(_syncDelta, vreinterpretq_u32_f32(p)); 55 | 56 | uint32_t _offset[4], _o = 0; 57 | for (int i = 0; i < 4; i++) { 58 | if (_sync[i]) { _o = _syncDelta[i]; } 59 | _offset[i] = _o; 60 | } 61 | 62 | p = p - vreinterpretq_f32_u32(vld1q_u32(_offset)); 63 | p = p - util::four::floor(p); 64 | mPhase = vgetq_lane_f32(p, 3); 65 | 66 | auto final = vbslq_f32(vcltq_f32(p, loadWidth), negOne, one); 67 | vst1q_f32(out + i, final * loadGain); 68 | } 69 | } 70 | 71 | od::Inlet mVPO { "V/Oct" }; 72 | od::Inlet mFreq { "Frequency" }; 73 | od::Inlet mSync { "Sync" }; 74 | od::Inlet mWidth { "Width" }; 75 | od::Inlet mGain { "Gain" }; 76 | od::Outlet mOut { "Out" }; 77 | #endif 78 | private: 79 | const float32x4_t cScale = util::four::make(1, 2, 3, 4); 80 | float mPhase = 0.0f; 81 | }; 82 | } 83 | -------------------------------------------------------------------------------- /mods/lojik/Register.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace lojik { 11 | 12 | #define MODE_NORMAL 1 13 | #define MODE_SEQ 2 14 | 15 | class Register : public od::Object { 16 | public: 17 | Register(int max, float randomize); 18 | virtual ~Register(); 19 | 20 | #ifndef SWIGLUA 21 | virtual void process(); 22 | void processNormal(); 23 | void processSeq(); 24 | 25 | od::Inlet mIn { "In" }; 26 | od::Outlet mOut { "Out" }; 27 | 28 | od::Inlet mLength { "Length" }; 29 | od::Inlet mStride { "Stride" }; 30 | 31 | od::Inlet mClock { "Clock" }; 32 | od::Inlet mCapture { "Capture" }; 33 | od::Inlet mShift { "Shift" }; 34 | od::Inlet mReset { "Reset" }; 35 | 36 | od::Parameter mScatter { "Scatter", 0.0f }; 37 | od::Parameter mDrift { "Drift", 0.0f }; 38 | od::Parameter mInputGain { "Input Gain", 1.0f }; 39 | od::Parameter mInputBias { "Input Bias", 0.0f }; 40 | 41 | od::Option mMode { "Mode", MODE_NORMAL }; 42 | od::Option mSync { "Sync", 0b111 }; 43 | #endif 44 | 45 | int getMax() { return mState.max(); } 46 | int getLength() { return mState.limit(); } 47 | int getSeqLength() { return mSequenceLength; } 48 | int getStep() { return mState.step(); } 49 | int getShift() { return mState.shift(); } 50 | float getData(int32_t i) { return mState.data(i); } 51 | 52 | void setMax(int v) { mBuffer.setMax(v); } 53 | void setSeqLength(int v) { mSequenceLength = v; } 54 | void setStep(int v) { mBuffer.setStep(v); } 55 | void setShift(int v) { mBuffer.setShift(v); } 56 | void setData(int i, float v) { mBuffer.setData(i, v); } 57 | 58 | void triggerDeserialize() { mDeserialize = true; } 59 | 60 | void triggerZeroWindow() { mState.markZeroWindow(); } 61 | void triggerScatterWindow() { mState.markScatterWindow(); } 62 | void triggerRandomizeWindow() { mState.markRandomizeWindow(); } 63 | 64 | void triggerZeroAll() { mState.markZeroAll(); } 65 | void triggerScatterAll() { mState.markScatterAll(); } 66 | void triggerRandomizeAll() { mState.markRandomizeAll(); } 67 | 68 | private: 69 | RegisterState mState; 70 | RegisterState mBuffer; 71 | 72 | OneTime mClockSwitch; 73 | OneTime mShiftSwitch; 74 | OneTime mCaptureSwitch; 75 | OneTime mResetSwitch; 76 | 77 | int mSequenceLength = 0; 78 | bool mRecordSequence = false; 79 | bool mDeserialize = false; 80 | 81 | void processTriggers() { 82 | mState.processTriggers(); 83 | 84 | if (mDeserialize) { 85 | mState = mBuffer; 86 | mBuffer = {}; 87 | mDeserialize = false; 88 | } 89 | } 90 | }; 91 | } -------------------------------------------------------------------------------- /mods/lojik/Register2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace lojik { 4 | void Register2::process() { 5 | processInternal(); 6 | } 7 | } -------------------------------------------------------------------------------- /mods/lojik/RegisterMainView.cpp: -------------------------------------------------------------------------------- 1 | #include "RegisterMainView.h" 2 | 3 | namespace lojik { 4 | RegisterMainView::RegisterMainView(Register2 &data) : 5 | graphics::MainControl(2), 6 | mChart(data), 7 | mCircleChart(data, 0.5), 8 | mKeyboard(data), 9 | mScaleList(data) { 10 | mReadoutList.addItem("Length", *data.getParameter("Length")); 11 | mReadoutList.addItem("Stride", *data.getParameter("Stride")); 12 | mReadoutList.addItem("Shift", *data.getParameter("Shift")); 13 | mReadoutList.addItem("Offset", *data.getParameter("Offset")); 14 | mGateList.addItem("reset"); 15 | mGateList.addItem("write"); 16 | mGateList.addItem("shift"); 17 | } 18 | } -------------------------------------------------------------------------------- /mods/lojik/TLatch.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace lojik { 4 | void TLatch::process() { 5 | processInternal(); 6 | } 7 | } -------------------------------------------------------------------------------- /mods/lojik/TLatch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace lojik { 10 | class TLatch : public od::Object { 11 | public: 12 | TLatch() { 13 | addInput(mIn); 14 | addInput(mDuration); 15 | addInput(mReset); 16 | addOutput(mOut); 17 | 18 | addOption(mSense); 19 | } 20 | 21 | virtual ~TLatch() { } 22 | 23 | #ifndef SWIGLUA 24 | virtual void process(); 25 | 26 | void processInternal() { 27 | float *in = mIn.buffer(); 28 | float *duration = mDuration.buffer(); 29 | float *reset = mReset.buffer(); 30 | float *out = mOut.buffer(); 31 | 32 | auto sense = vdupq_n_f32(common::getSense(mSense)); 33 | auto zero = vdupq_n_f32(0.0f); 34 | auto sp = vdupq_n_f32(globalConfig.samplePeriod); 35 | 36 | float _phase = mPhase; 37 | 38 | for (int i = 0; i < FRAMELENGTH; i += 4) { 39 | float32x4_t _in = vld1q_f32(in + i); 40 | float32x4_t _duration = vld1q_f32(duration + i); 41 | float32x4_t _reset = vld1q_f32(reset + i); 42 | 43 | auto highIn = vcgtq_f32(_in, sense); 44 | auto highReset = vcgtq_f32(_reset, zero); 45 | 46 | uint32_t _highIn[4], _highReset[4]; 47 | vst1q_u32(_highIn, highIn); 48 | vst1q_u32(_highReset, highReset); 49 | 50 | float _delta[4]; 51 | vst1q_f32(_delta, sp * util::four::invert(vmaxq_f32(_duration, sp))); 52 | 53 | for (int j = 0; j < 4; j++) { 54 | _phase -= _delta[j]; 55 | if (_highIn[j]) _phase = 1.0f; 56 | if (_highReset[j]) _phase = 0.0f; 57 | _delta[j] = _phase; 58 | } 59 | 60 | auto phase = vmaxq_f32(vld1q_f32(_delta), zero); 61 | _phase = vgetq_lane_f32(phase, 3); 62 | vst1q_f32(out + i, vcvtq_n_f32_u32(vcgtq_f32(phase, zero), 32)); 63 | } 64 | 65 | mPhase = _phase; 66 | } 67 | 68 | od::Inlet mIn { "In" }; 69 | od::Inlet mDuration { "Duration" }; 70 | od::Inlet mReset { "Reset" }; 71 | od::Outlet mOut { "Out" }; 72 | 73 | od::Option mSense { "Sense", INPUT_SENSE_LOW }; 74 | #endif 75 | 76 | private: 77 | float mPhase = 0.0f; 78 | }; 79 | } -------------------------------------------------------------------------------- /mods/lojik/Trig.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace lojik { 4 | void Trig::process() { 5 | processInternal(); 6 | } 7 | } -------------------------------------------------------------------------------- /mods/lojik/Trig.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace lojik { 10 | class Trig : public od::Object { 11 | public: 12 | Trig() { 13 | addInput(mIn); 14 | addOutput(mOut); 15 | addOption(mSense); 16 | } 17 | 18 | virtual ~Trig() { } 19 | 20 | #ifndef SWIGLUA 21 | virtual void process(); 22 | 23 | void processInternal() { 24 | const float *in = mIn.buffer(); 25 | float *out = mOut.buffer(); 26 | 27 | const auto sense = vdupq_n_f32(common::getSense(mSense)); 28 | 29 | auto enable = mEnable; 30 | auto trigger = mTrigger; 31 | 32 | for (int i = 0; i < FRAMELENGTH; i += 4) { 33 | auto _in = vld1q_f32(in + i); 34 | 35 | uint32_t _high[4]; 36 | vst1q_u32(_high, vcgtq_f32(_in, sense)); 37 | 38 | uint32_t _trig[4]; 39 | for (int i = 0; i < 4; i++) { 40 | auto high = _high[i]; 41 | 42 | trigger = high & enable; 43 | enable = ~high; 44 | 45 | _trig[i] = trigger; 46 | } 47 | 48 | auto _out = vcvtq_n_f32_u32(vld1q_u32(_trig), 32); 49 | vst1q_f32(out + i, _out); 50 | } 51 | 52 | mEnable = enable; 53 | mTrigger = trigger; 54 | } 55 | 56 | od::Inlet mIn { "In" }; 57 | od::Outlet mOut { "Out" }; 58 | od::Option mSense { "Sense", INPUT_SENSE_LOW }; 59 | #endif 60 | 61 | private: 62 | uint32_t mEnable = 0; 63 | uint32_t mTrigger = 0; 64 | }; 65 | } 66 | -------------------------------------------------------------------------------- /mods/lojik/Wait.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace lojik { 4 | void Wait::process() { 5 | processInternal(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /mods/lojik/assets/Divide/Chance.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local lojik = require "lojik.liblojik" 3 | local Class = require "Base.Class" 4 | local Encoder = require "Encoder" 5 | local Unit = require "Unit" 6 | local GainBias = require "Unit.ViewControl.GainBias" 7 | local OptionControl = require "Unit.MenuControl.OptionControl" 8 | local UnitShared = require "common.assets.UnitShared" 9 | 10 | local Chance = Class {} 11 | Chance:include(Unit) 12 | Chance:include(UnitShared) 13 | 14 | function Chance:init(args) 15 | args.title = "Chance" 16 | args.mnemonic = "?" 17 | Unit.init(self, args) 18 | end 19 | 20 | function Chance:onLoadGraph(channelCount) 21 | local chance = self:addGainBiasControl("chance") 22 | 23 | local op = self:addObject("op", lojik.Chance()) 24 | connect(self, "In1", op, "In") 25 | connect(chance, "Out", op, "Chance") 26 | 27 | for i = 1, channelCount do 28 | connect(op, "Out", self, "Out"..i) 29 | end 30 | end 31 | 32 | function Chance:onShowMenu(objects) 33 | return { 34 | mode = OptionControl { 35 | description = "Output Mode", 36 | option = objects.op:getOption("Mode"), 37 | choices = { "trigger", "gate", "through" } 38 | }, 39 | sensitivity = self.senseOptionControl(objects.op) 40 | }, { "mode", "sensitivity" } 41 | end 42 | 43 | function Chance:onLoadViews() 44 | return { 45 | chance = GainBias { 46 | button = "chance", 47 | description = "Chance", 48 | branch = self.branches.chance, 49 | gainbias = self.objects.chance, 50 | range = self.objects.chanceRange, 51 | biasMap = Encoder.getMap("[0,1]"), 52 | biasUnits = app.unitNone, 53 | biasPrecision = 2, 54 | initialBias = 0.5 55 | } 56 | }, { 57 | expanded = { "chance" }, 58 | collapsed = {} 59 | } 60 | end 61 | 62 | return Chance 63 | -------------------------------------------------------------------------------- /mods/lojik/assets/Divide/Wait.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local lojik = require "lojik.liblojik" 3 | local Class = require "Base.Class" 4 | local Unit = require "Unit" 5 | local GainBias = require "Unit.ViewControl.GainBias" 6 | local UnitShared = require "common.assets.UnitShared" 7 | 8 | local Wait = Class {} 9 | Wait:include(Unit) 10 | Wait:include(UnitShared) 11 | 12 | function Wait:init(args) 13 | args.title = "Wait" 14 | args.mnemonic = "W" 15 | self.max = 64 16 | Unit.init(self, args) 17 | end 18 | 19 | function Wait:onLoadGraph(channelCount) 20 | local count = self:addGainBiasControl("count") 21 | local invert = self:addComparatorControl("invert", app.COMPARATOR_TOGGLE) 22 | local arm = self:addComparatorControl("arm", app.COMPARATOR_TOGGLE) 23 | 24 | for i = 1, channelCount do 25 | local op = self:addObject("op"..i, lojik.Wait()) 26 | connect(self, "In"..i, op, "In") 27 | 28 | connect(count, "Out", op, "Count") 29 | connect(invert, "Out", op, "Invert") 30 | connect(arm, "Out", op, "Arm") 31 | 32 | if i > 1 then 33 | tie(op, "Input Sense", self.objects.op1, "Input Sense") 34 | end 35 | 36 | connect(op, "Out", self, "Out"..i) 37 | end 38 | end 39 | 40 | 41 | function Wait:onShowMenu(objects) 42 | return { 43 | sensitivity = self.senseOptionControl(objects.op1) 44 | }, { "mode", "sensitivity" } 45 | end 46 | 47 | function Wait:onLoadViews() 48 | return { 49 | count = GainBias { 50 | button = "count", 51 | description = "Count", 52 | branch = self.branches.count, 53 | gainbias = self.objects.count, 54 | range = self.objects.countRange, 55 | gainMap = self.intMap(-self.max, self.max), 56 | biasMap = self.intMap(0, self.max), 57 | biasPrecision = 0, 58 | initialBias = 4 59 | }, 60 | invert = self:gateView("invert", "Invert"), 61 | arm = self:gateView("arm", "Arm") 62 | }, { 63 | expanded = { "count", "invert", "arm" }, 64 | collapsed = {} 65 | } 66 | end 67 | 68 | return Wait 69 | -------------------------------------------------------------------------------- /mods/lojik/assets/Latch/DLatch.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local lojik = require "lojik.liblojik" 3 | local Class = require "Base.Class" 4 | local Unit = require "Unit" 5 | local UnitShared = require "common.assets.UnitShared" 6 | 7 | local DLatch = Class {} 8 | DLatch:include(Unit) 9 | DLatch:include(UnitShared) 10 | 11 | function DLatch:init(args) 12 | args.title = "DLatch" 13 | args.mnemonic = "DLx" 14 | Unit.init(self, args) 15 | end 16 | 17 | function DLatch:onLoadGraph(channelCount) 18 | local clock = self:addComparatorControl("clock", app.COMPARATOR_GATE) 19 | local reset = self:addComparatorControl("reset", app.COMPARATOR_GATE) 20 | 21 | local op = self:addObject("op", lojik.DLatch()) 22 | connect(self, "In1", op, "In") 23 | connect(clock, "Out", op, "Clock") 24 | connect(reset, "Out", op, "Reset") 25 | 26 | for i = 1, channelCount do 27 | connect(op, "Out", self, "Out"..i) 28 | end 29 | end 30 | 31 | function DLatch:onLoadViews() 32 | return { 33 | clock = self:gateView("clock", "Clock"), 34 | reset = self:gateView("reset", "Reset") 35 | }, { 36 | expanded = { "clock", "reset" }, 37 | collapsed = {} 38 | } 39 | end 40 | 41 | return DLatch 42 | -------------------------------------------------------------------------------- /mods/lojik/assets/Latch/Latch.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local lojik = require "lojik.liblojik" 3 | local Class = require "Base.Class" 4 | local Unit = require "Unit" 5 | local UnitShared = require "common.assets.UnitShared" 6 | 7 | local Latch = Class {} 8 | Latch:include(Unit) 9 | Latch:include(UnitShared) 10 | 11 | function Latch:init(args) 12 | args.title = "Latch" 13 | args.mnemonic = "Lx" 14 | Unit.init(self, args) 15 | end 16 | 17 | function Latch:onLoadGraph(channelCount) 18 | local reset = self:addComparatorControl("reset", app.COMPARATOR_TRIGGER_ON_RISE) 19 | 20 | local op = self:addObject("op", lojik.Latch()) 21 | connect(self, "In1", op, "In") 22 | connect(reset, "Out", op, "Reset") 23 | 24 | for i = 1, channelCount do 25 | connect(op, "Out", self, "Out"..i) 26 | end 27 | end 28 | 29 | function Latch:onShowMenu(objects) 30 | return { 31 | sensitivity = self.senseOptionControl(objects.op) 32 | }, { "sensitivity" } 33 | end 34 | 35 | function Latch:onLoadViews() 36 | return { 37 | reset = self:gateView("reset", "Reset") 38 | }, { 39 | expanded = { "reset" }, 40 | collapsed = {} 41 | } 42 | end 43 | 44 | return Latch 45 | -------------------------------------------------------------------------------- /mods/lojik/assets/Latch/Pick.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local lojik = require "lojik.liblojik" 3 | local Class = require "Base.Class" 4 | local Encoder = require "Encoder" 5 | local Unit = require "Unit" 6 | local GainBias = require "Unit.ViewControl.GainBias" 7 | local UnitShared = require "common.assets.UnitShared" 8 | 9 | local Pick = Class {} 10 | Pick:include(Unit) 11 | Pick:include(UnitShared) 12 | 13 | function Pick:init(args) 14 | args.title = "Pick" 15 | args.mnemonic = "P" 16 | Unit.init(self, args) 17 | end 18 | 19 | function Pick:onLoadGraph(channelCount) 20 | local alt = self:addGainBiasControl("alt") 21 | local pick = self:addComparatorControl("pick", app.COMPARATOR_GATE) 22 | 23 | local op = self:addObject("op", lojik.Pick()) 24 | connect(self, "In1", op, "In") 25 | connect(alt, "Out", op, "Alt") 26 | connect(pick, "Out", op, "Pick") 27 | 28 | for i = 1, channelCount do 29 | connect(op, "Out", self, "Out"..i) 30 | end 31 | end 32 | 33 | function Pick:onLoadViews() 34 | return { 35 | alt = GainBias { 36 | button = "alt", 37 | description = "Alternate", 38 | branch = self.branches.alt, 39 | gainbias = self.objects.alt, 40 | range = self.objects.altRange, 41 | biasMap = Encoder.getMap("[-1,1]"), 42 | biasUnits = app.unitNone, 43 | biasPrecision = 2, 44 | initialGain = 1 45 | }, 46 | pick = self:gateView("pick", "Pick") 47 | }, { 48 | expanded = { "alt", "pick" }, 49 | collapsed = {} 50 | } 51 | end 52 | 53 | return Pick 54 | -------------------------------------------------------------------------------- /mods/lojik/assets/Latch/TLatch.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local lojik = require "lojik.liblojik" 3 | local Class = require "Base.Class" 4 | local Unit = require "Unit" 5 | local GainBias = require "Unit.ViewControl.GainBias" 6 | local UnitShared = require "common.assets.UnitShared" 7 | 8 | local TLatch = Class {} 9 | TLatch:include(Unit) 10 | TLatch:include(UnitShared) 11 | 12 | function TLatch:init(args) 13 | args.title = "TLatch" 14 | args.mnemonic = "TLx" 15 | Unit.init(self, args) 16 | end 17 | 18 | function TLatch:onLoadGraph(channelCount) 19 | local duration = self:addGainBiasControl("duration") 20 | local reset = self:addComparatorControl("reset", app.COMPARATOR_GATE) 21 | 22 | local op = self:addObject("op", lojik.TLatch()) 23 | connect(self, "In1", op, "In") 24 | connect(duration, "Out", op, "Duration") 25 | connect(reset, "Out", op, "Reset") 26 | 27 | for i = 1, channelCount do 28 | connect(op, "Out", self, "Out"..i) 29 | end 30 | end 31 | 32 | function TLatch:onShowMenu(objects) 33 | return { 34 | sensitivity = self.senseOptionControl(objects.op) 35 | }, { "sensitivity" } 36 | end 37 | 38 | function TLatch:onLoadViews() 39 | return { 40 | time = GainBias { 41 | button = "time", 42 | branch = self.branches.duration, 43 | description = "Duration", 44 | gainbias = self.objects.duration, 45 | range = self.objects.durationRange, 46 | biasMap = self.linMap(0, 10, 0.1, 0.01, 0.001, 0.001), 47 | biasUnits = app.unitSecs, 48 | initialBias = 0.1 49 | }, 50 | reset = self:gateView("reset", "Reset") 51 | }, { 52 | expanded = { "time", "reset" }, 53 | collapsed = {} 54 | } 55 | end 56 | 57 | return TLatch 58 | -------------------------------------------------------------------------------- /mods/lojik/assets/Logic/And.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local lojik = require "lojik.liblojik" 3 | local Class = require "Base.Class" 4 | local Unit = require "Unit" 5 | local UnitShared = require "common.assets.UnitShared" 6 | 7 | local And = Class {} 8 | And:include(Unit) 9 | And:include(UnitShared) 10 | 11 | function And:init(args) 12 | args.title = "And" 13 | args.mnemonic = "&&" 14 | Unit.init(self, args) 15 | end 16 | 17 | function And:onLoadGraph(channelCount) 18 | local gate = self:addComparatorControl("gate", app.COMPARATOR_GATE) 19 | 20 | local op = self:addObject("op", lojik.And()) 21 | connect(self, "In1", op, "In") 22 | connect(gate, "Out", op, "Gate") 23 | 24 | for i = 1, channelCount do 25 | connect(op, "Out", self, "Out"..i) 26 | end 27 | end 28 | 29 | function And:onShowMenu(objects) 30 | return { 31 | sensitivity = self.senseOptionControl(objects.op) 32 | }, { "sensitivity" } 33 | end 34 | 35 | function And:onLoadViews() 36 | return { 37 | gate = self:gateView("gate", "Gate") 38 | }, { 39 | expanded = { "gate" }, 40 | collapsed = {} 41 | } 42 | end 43 | 44 | return And 45 | -------------------------------------------------------------------------------- /mods/lojik/assets/Logic/Not.lua: -------------------------------------------------------------------------------- 1 | local lojik = require "lojik.liblojik" 2 | local Class = require "Base.Class" 3 | local Unit = require "Unit" 4 | local UnitShared = require "common.assets.UnitShared" 5 | 6 | local Not = Class {} 7 | Not:include(Unit) 8 | Not:include(UnitShared) 9 | 10 | function Not:init(args) 11 | args.title = "Not" 12 | args.mnemonic = "!" 13 | Unit.init(self, args) 14 | end 15 | 16 | function Not:onLoadGraph(channelCount) 17 | local op = self:addObject("op", lojik.Not()) 18 | connect(self, "In1", op, "In") 19 | 20 | for i = 1, channelCount do 21 | connect(op, "Out", self, "Out"..i) 22 | end 23 | end 24 | 25 | function Not:onShowMenu(objects) 26 | return { 27 | sensitivity = self.senseOptionControl(objects.op) 28 | }, { "sensitivity" } 29 | end 30 | 31 | function Not:onLoadViews() 32 | return 33 | end 34 | 35 | return Not 36 | -------------------------------------------------------------------------------- /mods/lojik/assets/Logic/Or.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local lojik = require "lojik.liblojik" 3 | local Class = require "Base.Class" 4 | local Unit = require "Unit" 5 | local UnitShared = require "common.assets.UnitShared" 6 | 7 | local Or = Class {} 8 | Or:include(Unit) 9 | Or:include(UnitShared) 10 | 11 | function Or:init(args) 12 | args.title = "Or" 13 | args.mnemonic = "||" 14 | Unit.init(self, args) 15 | end 16 | 17 | function Or:onLoadGraph(channelCount) 18 | local gate = self:addComparatorControl("gate", app.COMPARATOR_GATE) 19 | 20 | local op = self:addObject("op", lojik.Or()) 21 | connect(self, "In1", op, "In") 22 | connect(gate, "Out", op, "Gate") 23 | 24 | for i = 1, channelCount do 25 | connect(op, "Out", self, "Out"..i) 26 | end 27 | end 28 | 29 | function Or:onShowMenu(objects) 30 | return { 31 | sensitivity = self.senseOptionControl(objects.op) 32 | }, { "sensitivity" } 33 | end 34 | 35 | function Or:onLoadViews() 36 | return { 37 | gate = self:gateView("gate", "Gate") 38 | }, { 39 | expanded = { "gate" }, 40 | collapsed = {} 41 | } 42 | end 43 | 44 | return Or 45 | -------------------------------------------------------------------------------- /mods/lojik/assets/Logic/Trig.lua: -------------------------------------------------------------------------------- 1 | local lojik = require "lojik.liblojik" 2 | local Class = require "Base.Class" 3 | local Unit = require "Unit" 4 | local UnitShared = require "common.assets.UnitShared" 5 | 6 | local Trig = Class {} 7 | Trig:include(Unit) 8 | Trig:include(UnitShared) 9 | 10 | function Trig:init(args) 11 | args.title = "Trig" 12 | args.mnemonic = "T" 13 | Unit.init(self, args) 14 | end 15 | 16 | function Trig:onShowMenu(objects) 17 | return { 18 | sensitivity = self.senseOptionControl(objects.op) 19 | }, { "sensitivity" } 20 | end 21 | 22 | function Trig:onLoadGraph(channelCount) 23 | local op = self:addObject("op", lojik.Trig()) 24 | connect(self, "In1", op, "In") 25 | 26 | for i = 1, channelCount do 27 | connect(op, "Out", self, "Out"..i) 28 | end 29 | end 30 | 31 | function Trig:onLoadViews() 32 | return 33 | end 34 | 35 | return Trig 36 | -------------------------------------------------------------------------------- /mods/lojik/assets/Pulse.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local lojik = require "lojik.liblojik" 3 | local Class = require "Base.Class" 4 | local Unit = require "Unit" 5 | local Encoder = require "Encoder" 6 | local Pitch = require "Unit.ViewControl.Pitch" 7 | local GainBias = require "Unit.ViewControl.GainBias" 8 | local UnitShared = require "common.assets.UnitShared" 9 | 10 | local Pulse = Class {} 11 | Pulse:include(Unit) 12 | Pulse:include(UnitShared) 13 | 14 | function Pulse:init(args) 15 | args.title = "Pulse" 16 | args.mnemonic = "P" 17 | Unit.init(self, args) 18 | end 19 | 20 | function Pulse:onLoadGraph(channelCount) 21 | local tune = self:addConstantOffsetControl("tune") 22 | local freq = self:addGainBiasControl("freq") 23 | local sync = self:addComparatorControl("sync", app.COMPARATOR_TRIGGER_ON_RISE) 24 | local width = self:addGainBiasControl("width") 25 | local gain = self:addGainBiasControl("gain") 26 | 27 | local op = self:addObject("op", lojik.Pulse()) 28 | connect(tune, "Out", op, "V/Oct") 29 | connect(freq, "Out", op, "Frequency") 30 | connect(sync, "Out", op, "Sync") 31 | connect(width, "Out", op, "Width") 32 | connect(gain, "Out", op, "Gain") 33 | 34 | for i = 1, channelCount do 35 | connect(op, "Out", self, "Out"..i) 36 | end 37 | end 38 | 39 | function Pulse:onLoadViews() 40 | return { 41 | tune = Pitch { 42 | button = "V/oct", 43 | branch = self.branches.tune, 44 | description = "V/oct", 45 | offset = self.objects.tune, 46 | range = self.objects.tuneRange 47 | }, 48 | freq = GainBias { 49 | button = "freq", 50 | description = "Frequency", 51 | branch = self.branches.freq, 52 | gainbias = self.objects.freq, 53 | range = self.objects.freqRange, 54 | biasMap = Encoder.getMap("oscFreq"), 55 | biasUnits = app.unitHertz, 56 | initialBias = 27.5, 57 | gainMap = Encoder.getMap("freqGain"), 58 | scaling = app.octaveScaling 59 | }, 60 | sync = self:gateView("sync", "Sync"), 61 | width = GainBias { 62 | button = "width", 63 | description = "Width", 64 | branch = self.branches.width, 65 | gainbias = self.objects.width, 66 | range = self.objects.widthRange, 67 | biasMap = Encoder.getMap("[0,1]"), 68 | biasUnits = app.unitNone, 69 | biasPrecision = 2, 70 | initialBias = 0.5 71 | }, 72 | gain = GainBias { 73 | button = "gain", 74 | description = "Gain", 75 | branch = self.branches.gain, 76 | gainbias = self.objects.gain, 77 | range = self.objects.gain, 78 | biasMap = Encoder.getMap("[-1,1]"), 79 | biasUnits = app.unitNone, 80 | biasPrecision = 2, 81 | initialBias = 0.5 82 | } 83 | }, { 84 | expanded = { "tune", "freq", "sync", "width", "gain" }, 85 | collapsed = { } 86 | } 87 | end 88 | 89 | return Pulse 90 | -------------------------------------------------------------------------------- /mods/lojik/assets/Sequencer/RegisterShared.lua: -------------------------------------------------------------------------------- 1 | local RegisterShared = {} 2 | 3 | function RegisterShared.serializeRegister(register) 4 | local max = register:getMax() 5 | local data = {} 6 | 7 | for i = 1, max do 8 | data[i] = register:getData(i - 1); 9 | end 10 | 11 | return { 12 | max = max, 13 | step = register:getStep(), 14 | shift = register:getShift(), 15 | length = register:getSeqLength(), 16 | data = data 17 | } 18 | end 19 | 20 | function RegisterShared.deserializeRegister(register, t) 21 | register:setMax(t.max) 22 | register:setStep(t.step) 23 | register:setShift(t.shift) 24 | register:setSeqLength(t.length) 25 | 26 | for i, v in ipairs(t.data) do 27 | register:setData(i - 1, v) 28 | end 29 | 30 | register:triggerDeserialize(); 31 | end 32 | 33 | return RegisterShared 34 | -------------------------------------------------------------------------------- /mods/lojik/assets/ViewControl/Quantizer.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local lojik = require "lojik.liblojik" 3 | local Class = require "Base.Class" 4 | 5 | local Base = require "common.assets.ViewControl.Split.Paged" 6 | local Scale = require "common.assets.ViewControl.SubView.Scale" 7 | 8 | local Quantizer = Class { 9 | type = "Quantizer", 10 | canEdit = false, 11 | canMove = true 12 | } 13 | Quantizer:include(Base) 14 | 15 | function Quantizer:init(args) 16 | Base.init(self, args) 17 | self.quantizer = args.quantizer or app.logError("%s.init: missing quantizer.", self) 18 | 19 | self:addSubView(Scale { 20 | name = "Scale", 21 | branch = args.branch, 22 | gainBias = args.gainBias, 23 | gainDialMap = args.gainDialMap, 24 | scaleSource = self.quantizer 25 | }) 26 | 27 | self.graphic = lojik.QuantizerView(self.quantizer) 28 | self:setControlGraphic(self.graphic) 29 | 30 | for i = 1, self.graphic:plyWidth() do 31 | self:addSpotDescriptor { 32 | center = (i - 0.5) * app.SECTION_PLY 33 | } 34 | end 35 | end 36 | 37 | return Quantizer -------------------------------------------------------------------------------- /mods/lojik/assets/ViewControl/RegisterView.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local lojik = require "lojik.liblojik" 3 | local Class = require "Base.Class" 4 | 5 | local UnitShared = require "common.assets.UnitShared" 6 | local Base = require "common.assets.ViewControl.Split.Paged" 7 | local GainBias = require "common.assets.ViewControl.SubView.GainBias" 8 | local Scale = require "common.assets.ViewControl.SubView.Scale" 9 | 10 | local ply = app.SECTION_PLY 11 | 12 | local RegisterView = Class { 13 | type = "RegisterView", 14 | canEdit = false, 15 | canMove = true 16 | } 17 | RegisterView:include(Base) 18 | RegisterView:include(UnitShared) 19 | 20 | function RegisterView:init(args) 21 | Base.init(self, args) 22 | self.register = args.register or app.logError("%s.init: missing register instance.", self) 23 | 24 | self:addSubView(GainBias { 25 | name = "Offset", 26 | branch = args.offset.branch, 27 | precision = 0, 28 | gainBias = args.offset.gainBias, 29 | gainDialMap = args.offset.gainDialMap, 30 | biasDialMap = args.offset.biasDialMap, 31 | }) 32 | 33 | self:addSubView(GainBias { 34 | name = "Shift", 35 | branch = args.shift.branch, 36 | precision = 0, 37 | gainBias = args.shift.gainBias, 38 | gainDialMap = args.shift.gainDialMap, 39 | biasDialMap = args.shift.biasDialMap, 40 | }) 41 | 42 | self:addSubView(GainBias { 43 | name = "Length", 44 | branch = args.length.branch, 45 | precision = 0, 46 | gainBias = args.length.gainBias, 47 | gainDialMap = args.length.gainDialMap, 48 | biasDialMap = args.length.biasDialMap, 49 | }) 50 | 51 | self:addSubView(GainBias { 52 | name = "Stride", 53 | branch = args.stride.branch, 54 | precision = 0, 55 | gainBias = args.stride.gainBias, 56 | gainDialMap = args.stride.gainDialMap, 57 | biasDialMap = args.stride.biasDialMap, 58 | }) 59 | 60 | self:addSubView(Scale { 61 | name = "Quantize", 62 | branch = args.quantize.branch, 63 | precision = 0, 64 | gainBias = args.quantize.gainBias, 65 | gainDialMap = args.quantize.gainDialMap, 66 | scaleSource = self.register 67 | }) 68 | 69 | self.graphic = lojik.RegisterMainView(self.register) 70 | self:setControlGraphic(self.graphic) 71 | 72 | for i = 1, self.graphic:plyWidth() do 73 | self:addSpotDescriptor { 74 | center = (i - 0.5) * ply 75 | } 76 | end 77 | end 78 | 79 | function RegisterView:updatePageIndex(pageIndex, propogate) 80 | self:setMainCursorController(self.graphic:getCursorController(pageIndex - 1)); 81 | Base.updatePageIndex(self, pageIndex, propogate) 82 | end 83 | 84 | return RegisterView -------------------------------------------------------------------------------- /mods/lojik/assets/toc.lua: -------------------------------------------------------------------------------- 1 | return { 2 | name = "Lojik", 3 | title = "Lojik", 4 | keyword = "Lojik", 5 | contact = "tomj.fiset@gmail.com", 6 | author = "tomf", 7 | units = { 8 | { 9 | title = "Register", 10 | moduleName = "Sequencer.Register", 11 | keywords = "sequencer, register" 12 | }, 13 | { 14 | title = "Register2", 15 | moduleName = "Sequencer.Register2", 16 | keywords = "sequencer, register" 17 | }, 18 | { 19 | title = "Turing", 20 | moduleName = "Sequencer.Turing", 21 | keywords = "sequencer, turing" 22 | }, 23 | { 24 | title = "Seq", 25 | moduleName = "Sequencer.Seq", 26 | keywords = "sequencer" 27 | }, 28 | { 29 | title = "Latch", 30 | moduleName = "Latch.Latch", 31 | keywords = "latch" 32 | }, 33 | { 34 | title = "DLatch", 35 | moduleName = "Latch.DLatch", 36 | keywords = "latch" 37 | }, 38 | { 39 | title = "TLatch", 40 | moduleName = "Latch.TLatch", 41 | keywords = "latch" 42 | }, 43 | { 44 | title = "Pick", 45 | moduleName = "Latch.Pick", 46 | keywords = "pick" 47 | }, 48 | { 49 | title = "Pulse", 50 | moduleName = "Pulse", 51 | keywords = "pulse, square" 52 | }, 53 | { 54 | title = "Wait", 55 | moduleName = "Divide.Wait", 56 | keywords = "divide, delay" 57 | }, 58 | { 59 | title = "Euclid", 60 | moduleName = "Divide.Euclid", 61 | keyword = "rhythm, clock" 62 | }, 63 | { 64 | title = "Chance", 65 | moduleName = "Divide.Chance", 66 | keyword = "rhythm, clock" 67 | }, 68 | { 69 | title = "And", 70 | moduleName = "Logic.And", 71 | keywords = "and" 72 | }, 73 | { 74 | title = "Or", 75 | moduleName = "Logic.Or", 76 | keywords = "or" 77 | }, 78 | { 79 | title = "Not", 80 | moduleName = "Logic.Not", 81 | keywords = "not" 82 | }, 83 | { 84 | title = "Trig", 85 | moduleName = "Logic.Trig", 86 | keywords = "trig" 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /mods/lojik/controls/main/QuantizerView.cpp: -------------------------------------------------------------------------------- 1 | #include "QuantizerView.h" 2 | 3 | namespace lojik { 4 | QuantizerView::QuantizerView( 5 | graphics::Quantizer &data 6 | ) : 7 | graphics::MainControl(1), 8 | mKeyboard(data), 9 | mScaleList(data) { } 10 | } -------------------------------------------------------------------------------- /mods/lojik/controls/main/QuantizerView.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace lojik { 10 | class QuantizerView : public graphics::MainControl { 11 | public: 12 | QuantizerView(graphics::Quantizer &data); 13 | virtual ~QuantizerView() { } 14 | 15 | private: 16 | void draw(od::FrameBuffer &fb) { 17 | Graphic::draw(fb); 18 | 19 | auto world = graphics::Box::extractWorld(*this); 20 | mKeyboard.draw(fb, world.splitLeft(0.5), 0.85); 21 | mScaleList.draw(fb, world.splitRight(0.5).padLeft(5).padY(10), 5); 22 | } 23 | 24 | graphics::IKeyboard mKeyboard; 25 | graphics::ScaleList mScaleList; 26 | }; 27 | } -------------------------------------------------------------------------------- /mods/lojik/docs/units.md: -------------------------------------------------------------------------------- 1 | ## And 2 | 3 | A logical `And` gate. 4 | 5 | Output goes high when the **unit input** _and_ the **gate** input are greater than zero. 6 | 7 | | Input | Description | 8 | | --- | --- | 9 | | **_unit input_** | The left input to the `and` gate. | 10 | | **gate** | The right input to the `and` gate. | 11 | 12 | ## Or 13 | 14 | A logical `Or` gate. 15 | 16 | Output goes high when either the **unit input** _or_ the **gate** input is greater than zero. 17 | 18 | | Input | Description | 19 | | --- | --- | 20 | | **_unit input_** | The left input to the `or` gate. | 21 | | **gate** | The right input to the `or` gate. | 22 | 23 | ## Not 24 | 25 | A logical `Not` gate. 26 | 27 | Output goes high when the **unit input** is less than or equal to zero. 28 | 29 | | Input | Description | 30 | | --- | --- | 31 | | **_unit input_** | The input to the `not` gate. | 32 | 33 | ## Trig 34 | 35 | Convert the input signal to a trigger. 36 | 37 | Outputs a trigger when the **unit input** is greater than zero. 38 | 39 | | Input | Description | 40 | | --- | --- | 41 | | **_unit input_** | The input to the trigger converter. | 42 | 43 | ## Latch 44 | 45 | An SR Latch. 46 | 47 | Output is latched high when the **unit input** is greater than zero. 48 | 49 | | Input | Description | 50 | | --- | --- | 51 | | **_unit input_** | The latch is set when the **unit input** is greater than zero. | 52 | | **reset** | Reset the latch to zero on gate high unless the **unit input** is greater than zero. | 53 | 54 | ## DLatch 55 | 56 | A data latch. 57 | 58 | Samples the **unit input** when the **clock** gate goes high. Re-samples the **unit input** when **reset** is high. 59 | 60 | ### Patch Ideas 61 | 1. _Sample and Hold_

Patch a signal into the **clock** input to create a sample and hold function. 62 | 63 | 2. _Track and Hold_

Patch a signal into the **clock** input and hold **reset** high to creatte a track and hold function. 64 | 65 | 3. _Shift Register_

Patch several `DLatches` in a row and give them all the same **clock** signal. The left-most **unit input** will be shifted through the `DLatches` on each clock tick. 66 | 67 | | Input | Description | 68 | | --- | --- | 69 | | **_unit input_** | The input to be sampled. | 70 | | **clock** | Sample the **unit input** on gate high. | 71 | | **reset** | Reset the internal latch to cause the **unit input** to be re-sampled on the clock. | 72 | 73 | ## Pick 74 | 75 | A VC switch. 76 | 77 | Output the **unit input** when **pick** is low and the **alt** signal when **pick** is high. 78 | 79 | ### Patch Ideas 80 | 1. _Sequence Swap_

Pass in two sequences or CV sources and switch between them at will with the **pick** gate. 81 | 82 | 2. _Audio Rate Slice_

Rapidly switch between two signals with an audio-rate **pick** input. 83 | 84 | | Input | Description | 85 | | --- | --- | 86 | | **_unit input_** | The left input, output when **pick** is low. | 87 | | **alt** | The right input, output when **pick** is high. | 88 | | **pick** | Output the **unit input** when low and **alt** when high. | -------------------------------------------------------------------------------- /mods/lojik/lojik.cpp.swig: -------------------------------------------------------------------------------- 1 | %module lojik_liblojik 2 | %include 3 | %include 4 | 5 | %{ 6 | 7 | #undef SWIGLUA 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #define SWIGLUA 28 | 29 | %} 30 | 31 | %include 32 | %include 33 | %include 34 | %include 35 | %include 36 | %include 37 | %include 38 | %include 39 | %include 40 | %include 41 | %include 42 | %include 43 | %include 44 | %include 45 | %include 46 | %include 47 | %include 48 | -------------------------------------------------------------------------------- /mods/lojik/mod.mk: -------------------------------------------------------------------------------- 1 | PKGVERSION = 1.2.0 2 | include scripts/mod-builder.mk 3 | -------------------------------------------------------------------------------- /mods/polygon/Duodecet.cpp: -------------------------------------------------------------------------------- 1 | #include "Polygon.h" 2 | 3 | namespace polygon { 4 | template class Polygon<3>; 5 | } -------------------------------------------------------------------------------- /mods/polygon/Observable.cpp: -------------------------------------------------------------------------------- 1 | #include "Observable.h" 2 | 3 | namespace polygon { 4 | Observable::Observable() {} 5 | Observable::~Observable() {} 6 | } -------------------------------------------------------------------------------- /mods/polygon/Observable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace polygon { 6 | class Observable : public od::Object { 7 | public: 8 | Observable(); 9 | virtual ~Observable(); 10 | 11 | virtual bool isVoiceArmed(int i) = 0; 12 | virtual bool isVoiceNext(int i) = 0; 13 | virtual int groups() = 0; 14 | virtual int voices() = 0; 15 | 16 | virtual od::Parameter* vpoDirect(int voice) = 0; 17 | virtual od::Parameter* vpoOffset(int voice) = 0; 18 | 19 | virtual float envLevel(int voice) = 0; 20 | }; 21 | } -------------------------------------------------------------------------------- /mods/polygon/Octet.cpp: -------------------------------------------------------------------------------- 1 | #include "Polygon.h" 2 | 3 | namespace polygon { 4 | template class Polygon<2>; 5 | } -------------------------------------------------------------------------------- /mods/polygon/Polygon.cpp.swig: -------------------------------------------------------------------------------- 1 | %module polygon_libpolygon 2 | %include 3 | %include 4 | 5 | %{ 6 | 7 | #undef SWIGLUA 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #define SWIGLUA 14 | 15 | %} 16 | 17 | %include 18 | %include 19 | %include 20 | %include 21 | 22 | %template(Quartet) polygon::Polygon<1>; 23 | %template(Octet) polygon::Polygon<2>; 24 | %template(Duodecet) polygon::Polygon<3>; -------------------------------------------------------------------------------- /mods/polygon/Quartet.cpp: -------------------------------------------------------------------------------- 1 | #include "Polygon.h" 2 | 3 | namespace polygon { 4 | template class Polygon<1>; 5 | } -------------------------------------------------------------------------------- /mods/polygon/RoundRobinGateView.cpp: -------------------------------------------------------------------------------- 1 | #include "RoundRobinGateView.h" 2 | 3 | namespace polygon { 4 | RoundRobinGateView::RoundRobinGateView(Observable &observable, int left, int bottom, int width, int height) : 5 | od::Graphic(left, bottom, width, height), 6 | mObservable(observable) { 7 | mObservable.attach(); 8 | } 9 | } -------------------------------------------------------------------------------- /mods/polygon/RoundRobinGateView.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Observable.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace polygon; 11 | 12 | namespace polygon { 13 | class RoundRobinGateView : public od::Graphic { 14 | public: 15 | RoundRobinGateView(Observable &observable, int left, int bottom, int width, int height); 16 | 17 | virtual ~RoundRobinGateView() { 18 | mObservable.release(); 19 | } 20 | 21 | void setCursorSelection(int value) { 22 | mCursorSelection = value; 23 | } 24 | 25 | private: 26 | void draw(od::FrameBuffer &fb) { 27 | Graphic::draw(fb); 28 | 29 | auto world = graphics::Box::extractWorld(*this); 30 | auto grid = graphics::Grid::create(world.pad(2), mObservable.groups(), 4, 1); 31 | 32 | for (int c = 0; c < grid.cols; c++) { 33 | for (int r = 0; r < grid.rows; r++) { 34 | auto box = grid.cell(c, r); 35 | 36 | auto index = grid.index(c, r); 37 | auto fillColor = mObservable.envLevel(index); 38 | 39 | auto primaryColor = pColor(index + 1); 40 | auto secondaryColor = sColor(index + 1); 41 | 42 | auto boxCircle = box.minCircle(); 43 | boxCircle.fill(fb, primaryColor * fillColor); 44 | boxCircle.trace(fb, secondaryColor); 45 | 46 | auto point = graphics::Point(box.center()); 47 | 48 | if (mObservable.isVoiceArmed(index)) { 49 | point.diamond(fb, primaryColor); 50 | } 51 | 52 | if (mObservable.isVoiceNext(index)) { 53 | point.dot(fb, primaryColor); 54 | } 55 | } 56 | } 57 | } 58 | 59 | bool isFaded(int i) const { 60 | if (mCursorSelection == 0) return false; 61 | return mCursorSelection != i; 62 | } 63 | 64 | int pColor(int i) const { 65 | return isFaded(i) ? mPrimaryColor - mColorFade : mPrimaryColor; 66 | } 67 | 68 | int sColor(int i) const { 69 | return isFaded(i) ? mSecondaryColor - mColorFade : mSecondaryColor; 70 | } 71 | 72 | int mPrimaryColor = WHITE; 73 | int mSecondaryColor = GRAY10; 74 | int mColorFade = 5; 75 | 76 | int mCursorSelection = 0; 77 | 78 | Observable &mObservable; 79 | }; 80 | } -------------------------------------------------------------------------------- /mods/polygon/RoundRobinPitchView.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace polygon { 4 | RoundRobinPitchView::RoundRobinPitchView(Observable &observable, int left, int bottom, int width, int height) : 5 | od::Graphic(left, bottom, width, height), 6 | mObservable(observable) { 7 | mObservable.attach(); 8 | } 9 | } -------------------------------------------------------------------------------- /mods/polygon/assets/Duodecet.lua: -------------------------------------------------------------------------------- 1 | local polygon = require "polygon.libpolygon" 2 | local Class = require "Base.Class" 3 | local Polygon = require "polygon.Polygon" 4 | 5 | local Duodecet = Class {} 6 | Duodecet:include(Polygon) 7 | 8 | function Duodecet:init(args) 9 | args.title = "12-tet" 10 | args.mnemonic = "12tet" 11 | args.ctor = polygon.Duodecet 12 | Polygon.init(self, args) 13 | end 14 | 15 | return Duodecet -------------------------------------------------------------------------------- /mods/polygon/assets/Octet.lua: -------------------------------------------------------------------------------- 1 | local polygon = require "polygon.libpolygon" 2 | local Class = require "Base.Class" 3 | local Polygon = require "polygon.Polygon" 4 | 5 | local Octet = Class {} 6 | Octet:include(Polygon) 7 | 8 | function Octet:init(args) 9 | args.title = "8-tet" 10 | args.mnemonic = "8tet" 11 | args.ctor = polygon.Octet 12 | Polygon.init(self, args) 13 | end 14 | 15 | return Octet -------------------------------------------------------------------------------- /mods/polygon/assets/Quartet.lua: -------------------------------------------------------------------------------- 1 | local polygon = require "polygon.libpolygon" 2 | local Class = require "Base.Class" 3 | local Polygon = require "polygon.Polygon" 4 | 5 | local Quartet = Class {} 6 | Quartet:include(Polygon) 7 | 8 | function Quartet:init(args) 9 | args.title = "4-tet" 10 | args.mnemonic = "4tet" 11 | args.ctor = polygon.Quartet 12 | Polygon.init(self, args) 13 | end 14 | 15 | return Quartet -------------------------------------------------------------------------------- /mods/polygon/assets/ViewControl/RoundRobinGate.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local polygon = require "polygon.libpolygon" 3 | local Class = require "Base.Class" 4 | 5 | local Base = require "common.assets.ViewControl.Split.Paged" 6 | local Gate = require "common.assets.ViewControl.SubView.Gate" 7 | 8 | local ply = app.SECTION_PLY 9 | 10 | local RoundRobinGate = Class { 11 | type = "RoundRobinGate", 12 | canEdit = false, 13 | canMove = true 14 | } 15 | RoundRobinGate:include(Base) 16 | 17 | function RoundRobinGate:init(args) 18 | Base.init(self, args) 19 | self.polygon = args.polygon or app.logError("%s.init: missing polygon instance.", self) 20 | self.branch = args.branch or app.logError("%s.init: missing branch.", self) 21 | 22 | local threshold = self.polygon:getParameter("Gate Threshold") 23 | local onReleaseFire = function () self.polygon:releaseManualGates() end 24 | 25 | self:addSubView(Gate { 26 | name = "Round Robin", 27 | branch = self.branch, 28 | threshold = threshold, 29 | onPressFire = function () self.polygon:markManualGate(0) end, 30 | onReleaseFire = onReleaseFire 31 | }) 32 | 33 | for i, voice in ipairs(args.voices) do 34 | self:addSubView(Gate { 35 | name = "Voice "..i, 36 | branch = voice.gateBranch, 37 | threshold = threshold, 38 | onPressFire = function () self.polygon:markManualGate(i) end, 39 | onReleaseFire = onReleaseFire 40 | }) 41 | end 42 | 43 | self.graphic = polygon.RoundRobinGateView(self.polygon, 0, 0, ply, 64) 44 | self:setControlGraphic(self.graphic) 45 | self:addSpotDescriptor { 46 | center = 0.5 * ply 47 | } 48 | end 49 | 50 | function RoundRobinGate:updatePageIndex(pageIndex, propogate) 51 | self.graphic:setCursorSelection(pageIndex - 1) 52 | Base.updatePageIndex(self, pageIndex, propogate) 53 | end 54 | 55 | return RoundRobinGate 56 | -------------------------------------------------------------------------------- /mods/polygon/assets/ViewControl/RoundRobinPitch.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local polygon = require "polygon.libpolygon" 3 | local Class = require "Base.Class" 4 | 5 | local Base = require "common.assets.ViewControl.Split.Paged" 6 | local Pitch = require "common.assets.ViewControl.SubView.Pitch" 7 | local PitchTrack = require "common.assets.ViewControl.SubView.PitchTrack" 8 | 9 | local ply = app.SECTION_PLY 10 | 11 | local RoundRobinPitch = Class { 12 | type = "RoundRobinPitch", 13 | canEdit = false, 14 | canMove = true 15 | } 16 | RoundRobinPitch:include(Base) 17 | 18 | function RoundRobinPitch:init(args) 19 | Base.init(self, args) 20 | self.polygon = args.polygon or app.logError("%s.init: missing polygon instance.", self) 21 | self.branch = args.branch or app.logError("%s.init: missing branch.", self) 22 | 23 | local biasMap = args.biasMap or app.logError("%s.init: missing bias map.", self) 24 | 25 | self:addSubView(PitchTrack { 26 | name = "Round Robin", 27 | branch = self.branch, 28 | tune = args.tune, 29 | option = self.polygon:getOption("RR V/Oct Track") 30 | }) 31 | 32 | for i, voice in ipairs(args.voices) do 33 | self:addSubView(Pitch { 34 | name = "Voice "..i, 35 | branch = voice.pitchBranch, 36 | tune = voice.pitchOffset 37 | }) 38 | end 39 | 40 | self.graphic = polygon.RoundRobinPitchView(self.polygon, 0, 0, ply, 64) 41 | self.graphic:setScale(biasMap) 42 | self:setMainCursorController(self.graphic) 43 | self:setControlGraphic(self.graphic) 44 | self:addSpotDescriptor { 45 | center = 0.5 * ply 46 | } 47 | end 48 | 49 | function RoundRobinPitch:updatePageIndex(pageIndex, propogate) 50 | self.graphic:setCursorSelection(pageIndex - 1) 51 | Base.updatePageIndex(self, pageIndex, propogate) 52 | end 53 | 54 | return RoundRobinPitch 55 | -------------------------------------------------------------------------------- /mods/polygon/assets/ViewControl/TrackablePitch.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local Class = require "Base.Class" 3 | local Encoder = require "Encoder" 4 | local Settings = require "Settings" 5 | 6 | local Base = require "common.assets.ViewControl.Split" 7 | local PitchTrack = require "common.assets.ViewControl.SubView.PitchTrack" 8 | 9 | local TrackablePitch = Class { 10 | type = "TrackablePitch", 11 | canEdit = false, 12 | canMove = false 13 | } 14 | TrackablePitch:include(Base) 15 | 16 | function TrackablePitch:init(args) 17 | Base.init(self, args) 18 | 19 | local offset = args.offset or app.logError("%s.init: offset is missing.", self) 20 | local range = args.range or app.logError("%s.init: range is missing.", self) 21 | self.branch = args.branch or app.logError("%s.init: missing branch.", self) 22 | 23 | local faderParam = offset:getParameter("Offset") or offset:getParameter("Bias") 24 | local readoutParam 25 | if Settings.get("unitControlReadoutSource") == "actual" then 26 | readoutParam = range:getParameter("Center") or faderParam 27 | else 28 | readoutParam = faderParam 29 | end 30 | 31 | self:addSubView(PitchTrack { 32 | name = args.name, 33 | branch = self.branch, 34 | tune = faderParam, 35 | option = args.track 36 | }) 37 | 38 | self.graphic = app.Fader(0, 0, app.SECTION_PLY, 64) 39 | self.graphic:setTargetParameter(faderParam) 40 | self.graphic:setValueParameter(faderParam) 41 | self.graphic:setControlParameter(readoutParam) 42 | self.graphic:setRangeObject(range) 43 | self.graphic:setLabel(args.name) 44 | self.graphic:setAttributes(app.unitCents, Encoder.getMap("cents")) 45 | self.graphic:setPrecision(0) 46 | 47 | self:setMainCursorController(self.graphic) 48 | self:setControlGraphic(self.graphic) 49 | self:addSpotDescriptor { 50 | center = 0.5 * app.SECTION_PLY 51 | } 52 | end 53 | 54 | return TrackablePitch 55 | -------------------------------------------------------------------------------- /mods/polygon/assets/toc.lua: -------------------------------------------------------------------------------- 1 | return { 2 | name = "Polygon", 3 | title = "Polygon", 4 | keyword = "Polygon", 5 | contact = "tomj.fiset@gmail.com", 6 | author = "tomf", 7 | units = { 8 | { 9 | title = "4-tet", 10 | moduleName = "Quartet" 11 | }, 12 | { 13 | title = "8-tet", 14 | moduleName = "Octet" 15 | }, 16 | { 17 | title = "12-tet", 18 | moduleName = "Duodecet" 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /mods/polygon/docs/images/reverse-sync-fast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmfset/er-301-custom-units/ee2855768083a2193114d2d36f1bfd3f2fcbbbe4/mods/polygon/docs/images/reverse-sync-fast.png -------------------------------------------------------------------------------- /mods/polygon/docs/images/reverse-sync.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmfset/er-301-custom-units/ee2855768083a2193114d2d36f1bfd3f2fcbbbe4/mods/polygon/docs/images/reverse-sync.gif -------------------------------------------------------------------------------- /mods/polygon/docs/images/screen-detune.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmfset/er-301-custom-units/ee2855768083a2193114d2d36f1bfd3f2fcbbbe4/mods/polygon/docs/images/screen-detune.png -------------------------------------------------------------------------------- /mods/polygon/docs/images/screen-fall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmfset/er-301-custom-units/ee2855768083a2193114d2d36f1bfd3f2fcbbbe4/mods/polygon/docs/images/screen-fall.png -------------------------------------------------------------------------------- /mods/polygon/docs/images/screen-ff0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmfset/er-301-custom-units/ee2855768083a2193114d2d36f1bfd3f2fcbbbe4/mods/polygon/docs/images/screen-ff0.png -------------------------------------------------------------------------------- /mods/polygon/docs/images/screen-gate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmfset/er-301-custom-units/ee2855768083a2193114d2d36f1bfd3f2fcbbbe4/mods/polygon/docs/images/screen-gate.png -------------------------------------------------------------------------------- /mods/polygon/docs/images/screen-main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmfset/er-301-custom-units/ee2855768083a2193114d2d36f1bfd3f2fcbbbe4/mods/polygon/docs/images/screen-main.png -------------------------------------------------------------------------------- /mods/polygon/docs/images/screen-output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmfset/er-301-custom-units/ee2855768083a2193114d2d36f1bfd3f2fcbbbe4/mods/polygon/docs/images/screen-output.png -------------------------------------------------------------------------------- /mods/polygon/docs/images/screen-shape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmfset/er-301-custom-units/ee2855768083a2193114d2d36f1bfd3f2fcbbbe4/mods/polygon/docs/images/screen-shape.png -------------------------------------------------------------------------------- /mods/polygon/docs/images/screen-vpo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmfset/er-301-custom-units/ee2855768083a2193114d2d36f1bfd3f2fcbbbe4/mods/polygon/docs/images/screen-vpo.png -------------------------------------------------------------------------------- /mods/polygon/docs/images/voice-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmfset/er-301-custom-units/ee2855768083a2193114d2d36f1bfd3f2fcbbbe4/mods/polygon/docs/images/voice-diagram.png -------------------------------------------------------------------------------- /mods/polygon/docs/images/voice-switch.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmfset/er-301-custom-units/ee2855768083a2193114d2d36f1bfd3f2fcbbbe4/mods/polygon/docs/images/voice-switch.gif -------------------------------------------------------------------------------- /mods/polygon/docs/images/voice-track.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmfset/er-301-custom-units/ee2855768083a2193114d2d36f1bfd3f2fcbbbe4/mods/polygon/docs/images/voice-track.gif -------------------------------------------------------------------------------- /mods/polygon/mod.mk: -------------------------------------------------------------------------------- 1 | PKGVERSION = 1.0.0 2 | include scripts/mod-builder.mk -------------------------------------------------------------------------------- /mods/scratch/Curl.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define BUILDOPT_VERBOSE 9 | #define BUILDOPT_DEBUG_LEVEL 10 10 | #include 11 | 12 | namespace lojik { 13 | Curl::Curl() { 14 | addInput(mIn); 15 | addInput(mGain); 16 | addInput(mBias); 17 | addInput(mFold); 18 | addOutput(mOut); 19 | } 20 | 21 | Curl::~Curl() { } 22 | 23 | void Curl::process() { 24 | float *in = mIn.buffer(); 25 | float *gain = mGain.buffer(); 26 | float *bias = mBias.buffer(); 27 | float *fold = mFold.buffer(); 28 | float *out = mOut.buffer(); 29 | 30 | for (int i = 0; i < FRAMELENGTH; i ++) { 31 | float x = (in[i] * gain[i]) + bias[i]; 32 | float g = fold[i]; 33 | out[i] = tanh(x) - (g * sin(x * 2.0f * (M_PI))); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /mods/scratch/Curl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace lojik { 6 | class Curl : public od::Object { 7 | public: 8 | Curl(); 9 | virtual ~Curl(); 10 | 11 | #ifndef SWIGLUA 12 | virtual void process(); 13 | od::Inlet mIn { "In" }; 14 | od::Inlet mGain { "Gain" }; 15 | od::Inlet mBias { "Bias" }; 16 | od::Inlet mFold { "Fold" }; 17 | od::Outlet mOut { "Out" }; 18 | #endif 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /mods/scratch/assets/Curl.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local lojik = require "lojik.liblojik" 3 | local Class = require "Base.Class" 4 | local Unit = require "Unit" 5 | local Encoder = require "Encoder" 6 | local GainBias = require "Unit.ViewControl.GainBias" 7 | local UnitShared = require "common.assets.UnitShared" 8 | 9 | local Curl = Class {} 10 | Curl:include(Unit) 11 | Curl:include(UnitShared) 12 | 13 | function Curl:init(args) 14 | args.title = "Curl" 15 | args.mnemonic = "C" 16 | Unit.init(self, args) 17 | end 18 | 19 | function Curl:onLoadGraph(channelCount) 20 | local gain = self:addGainBiasControl("gain") 21 | local bias = self:addGainBiasControl("bias") 22 | local fold = self:addGainBiasControl("fold") 23 | 24 | for i = 1, channelCount do 25 | local op = self:addObject("op", lojik.Curl()) 26 | connect(gain, "Out", op, "Gain") 27 | connect(bias, "Out", op, "Bias") 28 | connect(fold, "Out", op, "Fold") 29 | connect(self, "In"..i, op, "In") 30 | 31 | connect(op, "Out", self, "Out"..i) 32 | end 33 | end 34 | 35 | function Curl:onLoadViews() 36 | return { 37 | gain = GainBias { 38 | button = "gain", 39 | description = "Input Gain", 40 | branch = self.branches.gain, 41 | gainbias = self.objects.gain, 42 | range = self.objects.gain, 43 | biasMap = Encoder.getMap("[-20,20]"), 44 | biasUnits = app.unitNone, 45 | biasPrecision = 2, 46 | initialBias = 1 47 | }, 48 | bias = GainBias { 49 | button = "bias", 50 | description = "Input Bias", 51 | branch = self.branches.bias, 52 | gainbias = self.objects.bias, 53 | range = self.objects.bias, 54 | biasMap = Encoder.getMap("[-20,20]"), 55 | biasUnits = app.unitNone, 56 | biasPrecision = 2, 57 | initialBias = 0 58 | }, 59 | fold = GainBias { 60 | button = "fold", 61 | description = "Fold", 62 | branch = self.branches.fold, 63 | gainbias = self.objects.fold, 64 | range = self.objects.fold, 65 | biasMap = Encoder.getMap("[0,1]"), 66 | biasUnits = app.unitNone, 67 | biasPrecision = 2, 68 | initialBias = 0 69 | } 70 | }, { 71 | expanded = { "gain", "bias", "fold" }, 72 | collapsed = {} 73 | } 74 | end 75 | 76 | return Curl 77 | -------------------------------------------------------------------------------- /mods/sloop/ClockMarks.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace sloop { 8 | #define MAX_CLOCK_MARKS 1024 9 | 10 | class ClockMarks { 11 | public: 12 | ClockMarks() {} 13 | virtual ~ClockMarks() {} 14 | 15 | void set(int step, int position) { 16 | int incSize = util::max(step + 1, size()); 17 | marks.resize(util::clamp(incSize, 0, MAX_CLOCK_MARKS)); 18 | if (step < size()) marks[step] = position; 19 | } 20 | 21 | int size() const { 22 | return marks.size(); 23 | } 24 | 25 | int get(int step) const { 26 | if (step < 0) return 0; 27 | if (step < size()) return marks.at(step); 28 | return 0; 29 | } 30 | 31 | private: 32 | std::vector marks; 33 | od::Slices *mpSlices = 0; 34 | }; 35 | } -------------------------------------------------------------------------------- /mods/sloop/Slew.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | // #define BUILDOPT_VERBOSE 7 | // #define BUILDOPT_DEBUG_LEVEL 10 8 | // #include 9 | 10 | namespace sloop { 11 | class Slew { 12 | public: 13 | Slew() {} 14 | 15 | Slew(float value, float rise, float fall) : 16 | mValue(value), 17 | mRise(rise), 18 | mFall(fall) {} 19 | 20 | Slew(const Slew &other, float rise, float fall) : 21 | mValue(other.mValue), 22 | mRise(rise), 23 | mFall(fall) {} 24 | 25 | // inline float32x4_t moveNeon(float32x4_t target) { 26 | // float fValue[4], fTarget[4]; 27 | // vst1q_f32(fTarget, target); 28 | // for (int i = 0; i < 4; i++) { 29 | // float target = fTarget[i]; 30 | // if (mValue == target) { 31 | // fValue[i] = target; 32 | // continue; 33 | // } 34 | 35 | // mValue = fValue[i] = mValue < target ? 36 | // fmin(target, mValue + mRise) : 37 | // fmax(target, mValue - mFall); 38 | 39 | // //logDebug(1, "t=%f v=%f", target, mValue); 40 | // } 41 | // return vld1q_f32(fValue); 42 | // } 43 | 44 | inline float move(float target) { 45 | if (mValue != target) { 46 | mValue = mValue < target ? 47 | fmin(target, mValue + mRise) : 48 | fmax(target, mValue - mFall); 49 | } 50 | 51 | return mValue; 52 | } 53 | 54 | float value() { return mValue; } 55 | 56 | void setValue(float v) { 57 | mValue = v; 58 | } 59 | 60 | void setRiseFall(float rise, float fall) { 61 | mRise = rise; 62 | mFall = fall; 63 | } 64 | 65 | private: 66 | float mValue = 0; 67 | float mRise = 1; 68 | float mFall = 1; 69 | }; 70 | } -------------------------------------------------------------------------------- /mods/sloop/Sloop.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define BUILDOPT_VERBOSE 8 | #define BUILDOPT_DEBUG_LEVEL 10 9 | #include 10 | 11 | namespace sloop { 12 | void Sloop::process() { 13 | if (mpSample == 0) return; 14 | 15 | updateFades(); 16 | updateManualResetStep(false); 17 | 18 | Buffers buffers { *this }; 19 | Constants constants { *this }; 20 | 21 | if (channels() < 2) { 22 | if (mpSample->mChannelCount < 2) { 23 | // Mono unit + mono sample 24 | for (int i = 0; i < FRAMELENGTH; i += 4) { 25 | auto p = processInput(buffers, constants, i); 26 | processResetOut(buffers, p, i); 27 | processMonoToMono(buffers, constants, p, i); 28 | } 29 | } else { 30 | // Mono unit + stereo sample 31 | for (int i = 0; i < FRAMELENGTH; i += 4) { 32 | auto p = processInput(buffers, constants, i); 33 | processResetOut(buffers, p, i); 34 | processMonoToStereo(buffers, constants, p, i); 35 | } 36 | } 37 | } else { 38 | if (mpSample->mChannelCount < 2) { 39 | // Stereo unit + mono sample 40 | for (int i = 0; i < FRAMELENGTH; i += 4) { 41 | auto p = processInput(buffers, constants, i); 42 | processResetOut(buffers, p, i); 43 | processStereoToMono(buffers, constants, p, i); 44 | } 45 | } else { 46 | // Stereo unit + stereo sample 47 | for (int i = 0; i < FRAMELENGTH; i += 4) { 48 | auto p = processInput(buffers, constants, i); 49 | processResetOut(buffers, p, i); 50 | processStereoToStereo(buffers, constants, p, i); 51 | } 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /mods/sloop/Sloop.cpp.swig: -------------------------------------------------------------------------------- 1 | %module sloop_libsloop 2 | %include 3 | 4 | %{ 5 | 6 | #undef SWIGLUA 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define SWIGLUA 14 | 15 | %} 16 | 17 | %include 18 | %include 19 | %include 20 | %include 21 | -------------------------------------------------------------------------------- /mods/sloop/Sloop2.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace sloop { 6 | class Sloop2 : public od::TapeHead { 7 | public: 8 | Sloop2(bool stereo) { 9 | 10 | } 11 | 12 | #ifndef SWIGLUA 13 | virtual void process(); 14 | 15 | od::Inlet mTap { "Tap" }; 16 | od::Inlet mEngage { "Engage" }; 17 | od::Inlet mClear { "Clear" }; 18 | od::Inlet mWrite { "Write" }; 19 | od::Inlet mReset { "Reset" }; 20 | 21 | od::Outlet mResetOut { "Reset Out" }; 22 | od::Outlet mClockOut { "Clock Out" }; 23 | 24 | od::Parameter mLength { "Length", 4 }; 25 | od::Parameter mThrough { "Through", 1.0 }; 26 | od::Parameter mFeedback { "Feedback", 1.0 }; 27 | od::Parameter mFade { "Fade", 0.005 }; 28 | od::Parameter mResetTo { "Reset To", 0 }; 29 | #endif 30 | private: 31 | inline void processInternal() { 32 | const float *tap = mTap.buffer(); 33 | const float *engage = mEngage.buffer(); 34 | const float *clear = mClear.buffer(); 35 | const float *write = mWrite.buffer(); 36 | const float *reset = mReset.buffer(); 37 | } 38 | }; 39 | } -------------------------------------------------------------------------------- /mods/sloop/SloopHeadSubDisplay.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace sloop { 9 | class SloopHeadSubDisplay : public od::Graphic { 10 | public: 11 | SloopHeadSubDisplay(Sloop *sloop) : 12 | od::Graphic(0, 0, 128, 64) { 13 | mpSloop = sloop; 14 | if (mpSloop) mpSloop->attach(); 15 | } 16 | 17 | virtual ~SloopHeadSubDisplay() { 18 | if (mpSloop) mpSloop->release(); 19 | mpSloop = 0; 20 | } 21 | 22 | private: 23 | Sloop *mpSloop = 0; 24 | std::string mHeadText { "00:00.000" }; 25 | std::string mLoopText { "00:00.000" }; 26 | 27 | static void timeString(float totalSecs, std::string &result) { 28 | int hours = totalSecs / 3600; 29 | int mins = totalSecs / 60; 30 | int secs = totalSecs; 31 | 32 | totalSecs -= hours * 3600; 33 | totalSecs -= mins * 60; 34 | totalSecs -= secs; 35 | 36 | int ms = 1000 * totalSecs; 37 | char tmp[32]; 38 | snprintf(tmp, sizeof(tmp), "%02d:%02d.%03d", mins, secs, ms); 39 | result = tmp; 40 | } 41 | 42 | void updateText() { 43 | if (!mpSloop) return; 44 | 45 | od::Sample *pSample = mpSloop->getSample(); 46 | if (!pSample) return; 47 | 48 | int head = mpSloop->getPosition(); 49 | int loop = mpSloop->lastPosition(); 50 | float sr = pSample->mSampleRate; 51 | 52 | timeString(head / sr, mHeadText); 53 | timeString(loop / sr, mLoopText); 54 | } 55 | 56 | void draw(od::FrameBuffer &fb) { 57 | updateText(); 58 | 59 | fb.text(WHITE, 5, 40, "head:", 10); 60 | fb.text(WHITE, 5, 30, "length:", 10); 61 | 62 | fb.text(WHITE, 83, 40, mHeadText.c_str(), 10); 63 | fb.text(WHITE, 83, 30, mLoopText.c_str(), 10); 64 | } 65 | }; 66 | } -------------------------------------------------------------------------------- /mods/sloop/SyncLatch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define BUILDOPT_VERBOSE 4 | #define BUILDOPT_DEBUG_LEVEL 10 5 | #include 6 | 7 | namespace sloop { 8 | class SyncLatch { 9 | public: 10 | SyncLatch() {} 11 | 12 | SyncLatch(const SyncLatch &other) { 13 | this->mEnable = other.mEnable; 14 | this->mTrigger = other.mTrigger; 15 | this->mState = other.mState; 16 | } 17 | 18 | inline bool readSyncCount(bool high, bool sync) { 19 | mCount += mState && sync; 20 | 21 | if (mState) mEnable = true; 22 | else mEnable = (mEnable || !high) && !mTrigger; 23 | 24 | if (mEnable) mTrigger = high; 25 | if (sync) mState = mTrigger; 26 | 27 | return mState; 28 | } 29 | 30 | inline bool readSyncCountMax(bool high, bool sync, int max) { 31 | mCount = mState ? mCount + sync : 0; 32 | 33 | if (mState) mEnable = mCount >= max; 34 | else mEnable = (mEnable || !high) && !mTrigger; 35 | 36 | if (mEnable) mTrigger = high; 37 | if (sync) mState = mTrigger; 38 | 39 | return mState; 40 | } 41 | 42 | inline bool readSync(bool high, bool sync) { 43 | mEnable = (mEnable || !high) && !mTrigger; 44 | 45 | if (mEnable) mTrigger = high; 46 | if (sync) mState = mTrigger; 47 | 48 | return mState; 49 | } 50 | 51 | inline bool read(bool high) { 52 | if (mState) mEnable = true; 53 | else mEnable = mEnable || !high; 54 | 55 | if (mEnable) mState = mTrigger = high; 56 | 57 | return mState; 58 | } 59 | 60 | inline bool readGateSyncCount(bool high, bool sync) { 61 | mCount += mState && sync; 62 | mTrigger = (sync && high) || (!sync && mState); 63 | mState = mTrigger; 64 | mEnable = true; 65 | return mState; 66 | } 67 | 68 | inline bool readTrigger(bool high) { 69 | mTrigger = mEnable && high; 70 | mState = mTrigger; 71 | mEnable = !mTrigger && !high; 72 | return mState; 73 | } 74 | 75 | inline bool readTriggerSync(bool high, bool sync) { 76 | mTrigger = (mEnable && high) || (mTrigger && !mState); 77 | mState = sync && mTrigger; 78 | mEnable = (!mTrigger && !high) || mState; 79 | return mState; 80 | } 81 | 82 | inline bool state() const { return mState; } 83 | inline bool triggered() const { return mTrigger; } 84 | 85 | inline int count() const { return mCount; } 86 | inline void setCount(int count) { mCount = count; } 87 | 88 | inline bool first() const { return mCount == 0; } 89 | inline bool firstOrLast() const { return mState ? first() : !first(); } 90 | 91 | private: 92 | int mCount = 0; 93 | bool mEnable = true; 94 | bool mTrigger = false; 95 | bool mState = false; 96 | }; 97 | } -------------------------------------------------------------------------------- /mods/sloop/assets/Sloop-10s.unit: -------------------------------------------------------------------------------- 1 | local shared = { 2 | {}; 3 | } -- shared 4 | shared[1]["active"] = false; 5 | shared[1]["enter"] = nil --[[functions with upvalue not supported]]; 6 | shared[1]["__className__"] = "State"; 7 | shared[1]["__instanceName__"] = "Fine"; 8 | shared[1]["paused"] = false; 9 | local obj1 = { 10 | ["activeView"] = "expanded"; 11 | ["loadInfo"] = { 12 | ["moduleName"] = "Sloop"; 13 | ["id"] = "Sloop"; 14 | ["category"] = "Recording and Looping"; 15 | ["libraryName"] = "sloop"; 16 | ["title"] = "Sloop"; 17 | }; 18 | ["objects"] = { }; 19 | ["sample"] = { 20 | ["opts"] = { 21 | ["type"] = "buffer"; 22 | ["secs"] = 10; 23 | }; 24 | }; 25 | ["firmwareVersion"] = "0.6.09"; 26 | ["unitVersion"] = 1; 27 | ["controlBranches"] = { 28 | }; 29 | ["branches"] = { }; 30 | ["bypass"] = false; 31 | } 32 | return obj1 33 | -------------------------------------------------------------------------------- /mods/sloop/assets/Sloop-15s.unit: -------------------------------------------------------------------------------- 1 | local shared = { 2 | {}; 3 | } -- shared 4 | shared[1]["active"] = false; 5 | shared[1]["enter"] = nil --[[functions with upvalue not supported]]; 6 | shared[1]["__className__"] = "State"; 7 | shared[1]["__instanceName__"] = "Fine"; 8 | shared[1]["paused"] = false; 9 | local obj1 = { 10 | ["activeView"] = "expanded"; 11 | ["loadInfo"] = { 12 | ["moduleName"] = "Sloop"; 13 | ["id"] = "Sloop"; 14 | ["category"] = "Recording and Looping"; 15 | ["libraryName"] = "sloop"; 16 | ["title"] = "Sloop"; 17 | }; 18 | ["objects"] = { }; 19 | ["sample"] = { 20 | ["opts"] = { 21 | ["type"] = "buffer"; 22 | ["secs"] = 15; 23 | }; 24 | }; 25 | ["firmwareVersion"] = "0.6.09"; 26 | ["unitVersion"] = 1; 27 | ["controlBranches"] = { 28 | }; 29 | ["branches"] = { }; 30 | ["bypass"] = false; 31 | } 32 | return obj1 33 | -------------------------------------------------------------------------------- /mods/sloop/assets/Sloop-5s.unit: -------------------------------------------------------------------------------- 1 | local shared = { 2 | {}; 3 | } -- shared 4 | shared[1]["active"] = false; 5 | shared[1]["enter"] = nil --[[functions with upvalue not supported]]; 6 | shared[1]["__className__"] = "State"; 7 | shared[1]["__instanceName__"] = "Fine"; 8 | shared[1]["paused"] = false; 9 | local obj1 = { 10 | ["activeView"] = "expanded"; 11 | ["loadInfo"] = { 12 | ["moduleName"] = "Sloop"; 13 | ["id"] = "Sloop"; 14 | ["category"] = "Recording and Looping"; 15 | ["libraryName"] = "sloop"; 16 | ["title"] = "Sloop"; 17 | }; 18 | ["objects"] = { }; 19 | ["sample"] = { 20 | ["opts"] = { 21 | ["type"] = "buffer"; 22 | ["secs"] = 5; 23 | }; 24 | }; 25 | ["firmwareVersion"] = "0.6.09"; 26 | ["unitVersion"] = 1; 27 | ["controlBranches"] = { 28 | }; 29 | ["branches"] = { }; 30 | ["bypass"] = false; 31 | } 32 | return obj1 33 | -------------------------------------------------------------------------------- /mods/sloop/assets/SloopFlagControl.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local Class = require "Base.Class" 3 | local MenuControl = require "Unit.MenuControl" 4 | local ply = app.SECTION_PLY 5 | 6 | local SloopFlagControl = Class {} 7 | SloopFlagControl:include(MenuControl) 8 | 9 | function SloopFlagControl:init(args) 10 | MenuControl.init(self) 11 | self:setClassName("Sloop.SloopFlagControl") 12 | local description = args.description or 13 | app.logError("%s.init: description is missing.", self) 14 | self:setInstanceName(description) 15 | local option = args.option or app.logError("%s.init: option is missing.", self) 16 | local flags = args.flags or app.logError("%s.init: 'flags' is missing.", self) 17 | local descWidth = args.descriptionWidth or 1 18 | local flagsPad = args.flagsPadding or 0 19 | 20 | option:enableSerialization() 21 | 22 | self.description = description 23 | self.flags = flags 24 | self.option = option 25 | self.onUpdate = args.onUpdate 26 | self.descWidth = descWidth 27 | self.flagsPad = flagsPad 28 | 29 | local graphic = app.RichTextBox(description, 10) 30 | -- graphic:setBorder(1) 31 | -- graphic:setBorderColor(app.GRAY7) 32 | -- graphic:setCornerRadius(3, 3, 3, 3) 33 | graphic:setMargins(3, 1, (#flags + flagsPad) * ply, 1) 34 | graphic:fitHeight((#flags + flagsPad + descWidth) * ply - 2) 35 | 36 | local h = graphic.mHeight // 2 37 | local labels = {} 38 | for i, flag in ipairs(flags) do 39 | local label = app.RichTextBox(flag, 10) 40 | label:fitHeight(ply - 2) 41 | labels[flag] = label 42 | graphic:addChild(label) 43 | label:setForegroundColor(app.GRAY5) 44 | label:setJustification(app.justifyCenter) 45 | label:setCenter(app.getButtonCenter(i + descWidth), h) 46 | end 47 | 48 | -- local instructions = app.DrawingInstructions() 49 | -- instructions:color(app.GRAY7) 50 | -- instructions:vline(descWidth * ply - 2, 1, graphic.mHeight) 51 | local drawing = app.Drawing(0, 0, 256, 64) 52 | -- drawing:add(instructions) 53 | graphic:addChild(drawing) 54 | 55 | self:setControlGraphic(graphic) 56 | self.labels = labels 57 | self:update() 58 | end 59 | 60 | function SloopFlagControl:onReleased(i, shifted) 61 | if shifted then return false end 62 | if i > self.descWidth then 63 | local flag = i - self.descWidth - 1 64 | if flag < #self.flags then 65 | self.option:toggleFlag(flag) 66 | self:update() 67 | end 68 | end 69 | return true 70 | end 71 | 72 | function SloopFlagControl:update() 73 | for flag, name in ipairs(self.flags) do 74 | local label = self.labels[name] 75 | if self.option:getFlag(flag - 1) then 76 | label:setForegroundColor(app.WHITE) 77 | else 78 | label:setForegroundColor(app.GRAY5) 79 | end 80 | end 81 | if self.onUpdate then self.onUpdate(self.option) end 82 | end 83 | 84 | return SloopFlagControl 85 | -------------------------------------------------------------------------------- /mods/sloop/assets/SloopHeader.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local Class = require "Base.Class" 3 | local Control = require "Package.Menu.Control" 4 | 5 | local SloopHeader = Class {} 6 | SloopHeader:include(Control) 7 | 8 | function SloopHeader:init(args) 9 | Control.init(self) 10 | self:setClassName("Sloop.SloopHeader") 11 | 12 | local description = args.description or "Sloop! : Synchronized Looper" 13 | self:setInstanceName(description) 14 | 15 | local graphic = app.RichTextBox(description, 10) 16 | graphic:setJustification(app.justifyCenter) 17 | graphic:setBorder(0) 18 | graphic:fitHeight(6 * app.SECTION_PLY + 20) 19 | self:setControlGraphic(graphic) 20 | 21 | self.isHeader = true 22 | end 23 | 24 | return SloopHeader 25 | -------------------------------------------------------------------------------- /mods/sloop/assets/TaskListControl.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local Class = require "Base.Class" 3 | local MenuControl = require "Unit.MenuControl" 4 | local ply = app.SECTION_PLY 5 | 6 | local TaskListControl = Class {} 7 | TaskListControl:include(MenuControl) 8 | 9 | function TaskListControl:init(args) 10 | MenuControl.init(self) 11 | self:setClassName("Sloop.TaskListControl") 12 | -- required arguments 13 | local description = args.description or app.logError("%s.init: description is missing.", self) 14 | self:setInstanceName(description) 15 | local tasks = args.tasks or app.logError("%s.init: 'tasks' is missing.", self) 16 | local descWidth = args.descriptionWidth or 1 17 | 18 | self.description = description 19 | self.tasks = tasks 20 | self.descWidth = descWidth 21 | 22 | local graphic = app.RichTextBox(description, 10) 23 | --graphic:setBorder(1) 24 | --graphic:setBorderColor(app.GRAY7) 25 | --graphic:setCornerRadius(3, 3, 3, 3) 26 | graphic:setMargins(3, 1, #tasks * ply, 1) 27 | graphic:fitHeight((#tasks + descWidth) * ply - 2) 28 | 29 | for i, task in ipairs(tasks) do 30 | local button = app.FittedTextBox(task.description) 31 | button:setCenter(app.getButtonCenter(i + descWidth), graphic.mHeight // 2) 32 | graphic:addChild(button) 33 | -- label:fitHeight(ply - 2) 34 | -- graphic:addChild(label) 35 | -- label:setForegroundColor(app.GRAY5) 36 | -- label:setJustification(app.justifyCenter) 37 | -- label:setCenter(app.getButtonCenter(i + descWidth), h) 38 | end 39 | 40 | --local instructions = app.DrawingInstructions() 41 | --instructions:color(app.GRAY7) 42 | --instructions:vline(descWidth * ply - 2, 1, graphic.mHeight - 1) 43 | --local drawing = app.Drawing(0, 0, 256, 64) 44 | --drawing:add(instructions) 45 | --graphic:addChild(drawing) 46 | 47 | self:setControlGraphic(graphic) 48 | end 49 | 50 | function TaskListControl:onReleased(i, shifted) 51 | local task = self.tasks[i - self.descWidth]; 52 | if task then 53 | self:callUp("hide") 54 | task.task(); 55 | end 56 | 57 | -- if shifted then return false end 58 | -- if i > self.descWidth then 59 | -- local t = i - self.descWidth - self.offset 60 | -- if t <= #self.choices then 61 | -- if self.muteOnChange then 62 | -- local chain = self.parent.unit.chain 63 | -- local wasMuted = chain:muteIfNeeded() 64 | -- self.option:set(choice) 65 | -- self:update() 66 | -- chain:unmuteIfNeeded(wasMuted) 67 | -- else 68 | -- self.option:set(choice) 69 | -- self:update() 70 | -- end 71 | -- end 72 | -- end 73 | return true 74 | end 75 | 76 | return TaskListControl 77 | -------------------------------------------------------------------------------- /mods/sloop/assets/toc.lua: -------------------------------------------------------------------------------- 1 | return { 2 | name = "Sloop", 3 | title = "Sloop", 4 | keyword = "Sloop", 5 | contact = "tomj.fiset@gmail.com", 6 | author = "tomf", 7 | units = { 8 | { 9 | title = "Sloop", 10 | moduleName = "Sloop" 11 | } 12 | }, 13 | presets = { 14 | { 15 | title = "Sloop (5s)", 16 | filename = "Sloop-5s.unit" 17 | }, 18 | { 19 | title = "Sloop (10s)", 20 | filename = "Sloop-10s.unit" 21 | }, 22 | { 23 | title = "Sloop (15s)", 24 | filename = "Sloop-15s.unit" 25 | }, 26 | { 27 | title = "Sloop Delay (2s)", 28 | filename = "Sloop-Delay-2s.unit" 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /mods/sloop/mod.mk: -------------------------------------------------------------------------------- 1 | PKGVERSION = 1.0.3 2 | include scripts/mod-builder.mk -------------------------------------------------------------------------------- /mods/strike/Arc.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace strike { 4 | void Arc::process() { 5 | processInternal(); 6 | } 7 | } -------------------------------------------------------------------------------- /mods/strike/Arc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace strike { 9 | class Arc : public od::Object { 10 | public: 11 | Arc() { 12 | addInput(mIn); 13 | addOutput(mOut); 14 | 15 | addOutput(mEof); 16 | addOutput(mEor); 17 | 18 | addInput(mRise); 19 | addInput(mFall); 20 | addInput(mLoop); 21 | addInput(mHeight); 22 | addInput(mBend); 23 | 24 | addOption(mBendMode); 25 | addOption(mSense); 26 | } 27 | 28 | virtual ~Arc() { } 29 | 30 | #ifndef SWIGLUA 31 | virtual void process(); 32 | 33 | inline void processInternal() { 34 | const float *in = mIn.buffer(); 35 | float *out = mOut.buffer(); 36 | 37 | float *eof = mEof.buffer(); 38 | float *eor = mEor.buffer(); 39 | 40 | const float *rise = mRise.buffer(); 41 | const float *fall = mFall.buffer(); 42 | const float *loop = mLoop.buffer(); 43 | const float *height = mHeight.buffer(); 44 | const float *bend = mBend.buffer(); 45 | 46 | osc::shape::BendMode bendMode = static_cast(mBendMode.value()); 47 | const auto sense = vdupq_n_f32(common::getSense(mSense)); 48 | 49 | for (int i = 0; i < FRAMELENGTH; i += 4) { 50 | auto _in = vcgtq_f32(vld1q_f32(in + i), sense); 51 | auto _loop = vld1q_f32(loop + i); 52 | auto _rise = vld1q_f32(rise + i); 53 | auto _fall = vld1q_f32(fall + i); 54 | auto _bend = vld1q_f32(bend + i); 55 | auto _height = vld1q_f32(height + i); 56 | 57 | auto f = osc::Frequency::riseFall( _rise, _fall); 58 | auto b = osc::shape::Bend { bendMode, _bend }; 59 | vst1q_f32(out + i, mEnvelope.process(f, b, _in, _loop) * _height); 60 | 61 | vst1q_f32(eof + i, mEnvelope.eof()); 62 | vst1q_f32(eor + i, mEnvelope.eor()); 63 | } 64 | } 65 | 66 | od::Inlet mIn { "In" }; 67 | od::Outlet mOut { "Out" }; 68 | 69 | od::Outlet mEof { "EOF" }; 70 | od::Outlet mEor { "EOR" }; 71 | 72 | od::Inlet mRise { "Rise" }; 73 | od::Inlet mFall { "Fall" }; 74 | od::Inlet mLoop { "Loop" }; 75 | od::Inlet mHeight { "Height" }; 76 | od::Inlet mBend { "Bend" }; 77 | 78 | od::Option mBendMode { "Bend Mode", osc::shape::BEND_NORMAL }; 79 | od::Option mSense { "Sense", INPUT_SENSE_LOW }; 80 | #endif 81 | private: 82 | osc::AD mEnvelope; 83 | }; 84 | } -------------------------------------------------------------------------------- /mods/strike/Bique.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace strike { 4 | void Bique::process() { 5 | processInternal(); 6 | } 7 | } -------------------------------------------------------------------------------- /mods/strike/Bique.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace strike { 11 | class Bique : public od::Object { 12 | public: 13 | Bique(bool stereo) { 14 | mChannelCount = stereo ? 2 : 1; 15 | 16 | for (int channel = 0; channel < mChannelCount; channel++) { 17 | std::ostringstream inName; 18 | inName << "In" << channel + 1; 19 | addInputFromHeap(new od::Inlet { inName.str() }); 20 | 21 | std::ostringstream outName; 22 | outName << "Out" << channel + 1; 23 | addOutputFromHeap(new od::Outlet { outName.str() }); 24 | 25 | mFilter.push_back(filter::biquad::Filter<4> {}); 26 | } 27 | 28 | addInput(mVpo); 29 | addInput(mF0); 30 | addInput(mGain); 31 | addInput(mQ); 32 | 33 | addOption(mMode); 34 | addOption(mType); 35 | } 36 | 37 | virtual ~Bique() { } 38 | 39 | #ifndef SWIGLUA 40 | virtual void process(); 41 | 42 | inline void processInternal() { 43 | int cc = mChannelCount; 44 | float *in[cc], *out[cc]; 45 | filter::biquad::Filter<4> *filter[cc]; 46 | for (int channel = 0; channel < cc; channel++) { 47 | in[channel] = getInput(channel)->buffer(); 48 | out[channel] = getOutput(channel)->buffer(); 49 | filter[channel] = &mFilter.at(channel); 50 | } 51 | 52 | const float *vpo = mVpo.buffer(); 53 | const float *f0 = mF0.buffer(); 54 | const float *gain = mGain.buffer(); 55 | const float *q = mQ.buffer(); 56 | 57 | const auto type = static_cast(mType.value()); 58 | const auto mode = static_cast(mMode.value()); 59 | 60 | filter::biquad::Coefficients cf; 61 | 62 | for (int i = 0; i < FRAMELENGTH; i += 4) { 63 | cf.update(vld1q_f32(vpo + i), vld1q_f32(f0 + i), vld1q_f32(q + i), type); 64 | 65 | const auto g = vld1q_f32(gain + i); 66 | for (int c = 0; c < cc; c++) { 67 | filter[c]->process(cf, vld1q_f32(in[c] + i) * g); 68 | vst1q_f32(out[c] + i, util::four::tanh(filter[c]->mode(mode))); 69 | } 70 | } 71 | } 72 | 73 | od::Inlet mVpo { "V/Oct" }; 74 | od::Inlet mF0 { "Fundamental" }; 75 | od::Inlet mGain { "Gain" }; 76 | od::Inlet mQ { "Resonance" }; 77 | 78 | od::Option mType { "Type", filter::biquad::LOWPASS }; 79 | od::Option mMode { "Mode", filter::biquad::MODE_24DB }; 80 | #endif 81 | 82 | private: 83 | int mChannelCount = 1; 84 | std::vector> mFilter; 85 | }; 86 | } 87 | -------------------------------------------------------------------------------- /mods/strike/CPR.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace strike { 4 | void CPR::process() { 5 | processInternal(); 6 | } 7 | } -------------------------------------------------------------------------------- /mods/strike/CompressorScope.cpp: -------------------------------------------------------------------------------- 1 | #include "CompressorScope.h" 2 | 3 | namespace strike { 4 | CompressorScope::CompressorScope( 5 | int left, 6 | int bottom, 7 | int width, 8 | int height 9 | ) : od::Graphic(left, bottom, width, height), 10 | mScopeExcite(left, bottom, width, height, WHITE, true), 11 | mScopeReduction(left, bottom, width, height, WHITE, false) { 12 | own(mScopeExcite); 13 | addChild(&mScopeExcite); 14 | 15 | own(mScopeReduction); 16 | addChild(&mScopeReduction); 17 | } 18 | } -------------------------------------------------------------------------------- /mods/strike/Fin.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace strike { 4 | void Fin::process() { 5 | processInternal(); 6 | } 7 | } -------------------------------------------------------------------------------- /mods/strike/Fin.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace strike { 8 | class Fin : public od::Object { 9 | public: 10 | Fin() { 11 | addOutput(mOut); 12 | 13 | addInput(mVpo); 14 | addInput(mFreq); 15 | addInput(mSync); 16 | addInput(mWidth); 17 | addInput(mGain); 18 | addInput(mBend); 19 | 20 | addOption(mBendMode); 21 | } 22 | 23 | virtual ~Fin() { } 24 | 25 | #ifndef SWIGLUA 26 | virtual void process(); 27 | 28 | inline void processInternal() { 29 | float *out = mOut.buffer(); 30 | 31 | const float *vpo = mVpo.buffer(); 32 | const float *freq = mFreq.buffer(); 33 | const float *sync = mSync.buffer(); 34 | const float *width = mWidth.buffer(); 35 | const float *gain = mGain.buffer(); 36 | const float *bend = mBend.buffer(); 37 | 38 | osc::shape::BendMode bendMode = static_cast(mBendMode.value()); 39 | 40 | for (int i = 0; i < FRAMELENGTH; i += 4) { 41 | auto _vpo = vld1q_f32(vpo + i); 42 | auto _f0 = vld1q_f32(freq + i); 43 | auto _width = vld1q_f32(width + i); 44 | auto _sync = vld1q_f32(sync + i); 45 | auto _gain = vld1q_f32(gain + i); 46 | auto _bend = vld1q_f32(bend + i); 47 | 48 | auto f = osc::Frequency::vpoWidth(_vpo, _f0, _width); 49 | auto b = osc::shape::Bend { bendMode, _bend }; 50 | auto o = mOscillator.process(f, b, _sync); 51 | 52 | vst1q_f32(out + i, vmlaq_f32(vnegq_f32(_gain), _gain, o + o)); 53 | } 54 | } 55 | 56 | od::Outlet mOut { "Out" }; 57 | 58 | od::Inlet mVpo { "V/Oct" }; 59 | od::Inlet mFreq { "Frequency" }; 60 | od::Inlet mSync { "Sync" }; 61 | od::Inlet mWidth { "Width" }; 62 | od::Inlet mGain { "Gain" }; 63 | od::Inlet mBend { "Bend" }; 64 | od::Option mBendMode { "Bend Mode", osc::shape::BEND_INVERTED }; 65 | #endif 66 | private: 67 | osc::Fin mOscillator; 68 | }; 69 | } -------------------------------------------------------------------------------- /mods/strike/Formant.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace strike { 4 | void Formant::process() { 5 | processInternal(); 6 | } 7 | } -------------------------------------------------------------------------------- /mods/strike/Formant.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace strike { 8 | #define FORMANT_FIXED 1 9 | #define FORMANT_FREE 2 10 | 11 | class Formant : public od::Object { 12 | public: 13 | Formant() { 14 | addOutput(mOut); 15 | 16 | addInput(mVpo); 17 | addInput(mFreq); 18 | addInput(mSync); 19 | addInput(mFormant); 20 | addInput(mGain); 21 | addInput(mBarrel); 22 | 23 | addOption(mFixed); 24 | } 25 | 26 | virtual ~Formant() { } 27 | 28 | #ifndef SWIGLUA 29 | virtual void process(); 30 | 31 | inline void processInternal() { 32 | float *out = mOut.buffer(); 33 | 34 | const float *vpo = mVpo.buffer(); 35 | const float *freq = mFreq.buffer(); 36 | const float *sync = mSync.buffer(); 37 | const float *formant = mFormant.buffer(); 38 | const float *gain = mGain.buffer(); 39 | const float *barrel = mBarrel.buffer(); 40 | 41 | float fixed = 1.0f; 42 | if (mFixed.value() == FORMANT_FREE) { 43 | fixed = 0.0f; 44 | } 45 | const auto _fixed = vcgtq_f32(vdupq_n_f32(fixed), vdupq_n_f32(0)); 46 | 47 | // Adjust the gain so that it looks approximately correct. 48 | const auto adjust = vdupq_n_f32(1.24f); 49 | 50 | for (int i = 0; i < FRAMELENGTH; i += 4) { 51 | auto o = mOscillator.process( 52 | vld1q_f32(freq + i), 53 | vld1q_f32(vpo + i), 54 | vld1q_f32(formant + i), 55 | vld1q_f32(barrel + i), 56 | vld1q_f32(sync + i), 57 | _fixed 58 | ); 59 | 60 | auto g = vld1q_f32(gain + i); 61 | auto w = util::four::atan(o + o - vdupq_n_f32(1)); 62 | 63 | vst1q_f32(out + i, w * g * adjust); 64 | } 65 | } 66 | 67 | od::Outlet mOut { "Out" }; 68 | 69 | od::Inlet mVpo { "V/Oct" }; 70 | od::Inlet mFreq { "Frequency" }; 71 | od::Inlet mSync { "Sync" }; 72 | od::Inlet mFormant { "Formant" }; 73 | od::Inlet mGain { "Gain" }; 74 | od::Inlet mBarrel { "Barrel" }; 75 | 76 | od::Option mFixed { "Fixed", FORMANT_FIXED }; 77 | #endif 78 | private: 79 | osc::Formant mOscillator; 80 | }; 81 | } -------------------------------------------------------------------------------- /mods/strike/Lift.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace strike { 4 | void Lift::process() { 5 | processInternal(); 6 | } 7 | } -------------------------------------------------------------------------------- /mods/strike/Lift.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace strike { 9 | class Lift : public od::Object { 10 | public: 11 | Lift() { 12 | addInput(mGate); 13 | addInput(mInLeft); 14 | addInput(mInRight); 15 | 16 | addParameter(mRise); 17 | addParameter(mFall); 18 | addParameter(mHeight); 19 | 20 | addOutput(mOutLeft); 21 | addOutput(mOutRight); 22 | addOutput(mEnv); 23 | } 24 | 25 | virtual ~Lift() { } 26 | 27 | #ifndef SWIGLUA 28 | virtual void process(); 29 | 30 | void processInternal() { 31 | const float *gate = mGate.buffer(); 32 | const float *inLeft = mInLeft.buffer(); 33 | const float *inRight = mInRight.buffer(); 34 | 35 | float *outLeft = mOutLeft.buffer(); 36 | float *outRight = mOutRight.buffer(); 37 | float *env = mEnv.buffer(); 38 | 39 | const auto height = vdup_n_f32(mHeight.value()); 40 | 41 | mSlew.setRiseFall( 42 | mRise.value(), 43 | mFall.value() 44 | ); 45 | 46 | for (int i = 0; i < FRAMELENGTH; i++) { 47 | auto _gate = vld1_dup_f32(gate + i); 48 | auto slew = mSlew.process(_gate); 49 | env[i] = vget_lane_f32(slew, 0); 50 | 51 | auto f0 = vmul_f32(slew, height); 52 | mFilter.setFrequency(f0); 53 | 54 | auto _in = util::two::make(inLeft[i], inRight[i]); 55 | auto _out = mFilter.process(vmul_f32(_in, slew)); 56 | 57 | outLeft[i] = vget_lane_f32(_out, 0); 58 | outRight[i] = vget_lane_f32(_out, 1); 59 | } 60 | } 61 | 62 | od::Inlet mGate { "Gate" }; 63 | od::Inlet mInLeft { "In1" }; 64 | od::Inlet mInRight { "In2" }; 65 | 66 | od::Parameter mRise { "Rise", 0.01 }; 67 | od::Parameter mFall { "Fall", 0.2 }; 68 | od::Parameter mHeight { "Height", 1 }; 69 | 70 | od::Outlet mOutLeft { "Out1" }; 71 | od::Outlet mOutRight { "Out2" }; 72 | od::Outlet mEnv { "Env" }; 73 | #endif 74 | 75 | private: 76 | slew::two::Slew mSlew; 77 | filter::onepole::two::Lowpass mFilter; 78 | }; 79 | } -------------------------------------------------------------------------------- /mods/strike/Sieve.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace strike { 4 | void Sieve::process() { 5 | processInternal(); 6 | } 7 | } -------------------------------------------------------------------------------- /mods/strike/Softy.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace strike { 4 | void Softy::process() { 5 | processInternal(); 6 | } 7 | } -------------------------------------------------------------------------------- /mods/strike/Softy.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace strike { 8 | class Softy : public od::Object { 9 | public: 10 | Softy() { 11 | addOutput(mOut); 12 | 13 | addInput(mVpo); 14 | addInput(mFreq); 15 | addInput(mSync); 16 | addInput(mWidth); 17 | addInput(mGain); 18 | } 19 | 20 | virtual ~Softy() { } 21 | 22 | #ifndef SWIGLUA 23 | virtual void process(); 24 | 25 | inline void processInternal() { 26 | float *out = mOut.buffer(); 27 | 28 | const float *vpo = mVpo.buffer(); 29 | const float *freq = mFreq.buffer(); 30 | const float *sync = mSync.buffer(); 31 | const float *width = mWidth.buffer(); 32 | const float *gain = mGain.buffer(); 33 | 34 | // Adjust the gain so that it looks approximately correct. 35 | const auto adjust = vdupq_n_f32(1.24f); 36 | 37 | for (int i = 0; i < FRAMELENGTH; i += 4) { 38 | auto o = mOscillator.process( 39 | vld1q_f32(freq + i), 40 | vld1q_f32(vpo + i), 41 | vld1q_f32(width + i), 42 | vld1q_f32(sync + i) 43 | ); 44 | 45 | auto g = vld1q_f32(gain + i); 46 | auto w = util::four::atan(o + o - vdupq_n_f32(1)); 47 | 48 | vst1q_f32(out + i, w * g * adjust); 49 | } 50 | } 51 | 52 | od::Outlet mOut { "Out" }; 53 | 54 | od::Inlet mVpo { "V/Oct" }; 55 | od::Inlet mFreq { "Frequency" }; 56 | od::Inlet mSync { "Sync" }; 57 | od::Inlet mWidth { "Width" }; 58 | od::Inlet mGain { "Gain" }; 59 | #endif 60 | private: 61 | osc::Softy mOscillator; 62 | }; 63 | } -------------------------------------------------------------------------------- /mods/strike/Strike.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace strike { 4 | void Strike::process() { 5 | processInternal(); 6 | } 7 | } -------------------------------------------------------------------------------- /mods/strike/Tanh.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace strike { 4 | void Tanh::process() { 5 | processInternal(); 6 | } 7 | } -------------------------------------------------------------------------------- /mods/strike/Tanh.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace strike { 8 | class Tanh : public od::Object { 9 | public: 10 | Tanh() { 11 | addOutput(mOut); 12 | addInput(mIn); 13 | addInput(mGain); 14 | } 15 | 16 | virtual ~Tanh() { } 17 | 18 | #ifndef SWIGLUA 19 | virtual void process(); 20 | 21 | inline void processInternal() { 22 | float *out = mOut.buffer(); 23 | const float *in = mIn.buffer(); 24 | const float *gain = mGain.buffer(); 25 | 26 | for (int i = 0; i < FRAMELENGTH; i += 4) { 27 | const auto _in = vld1q_f32(in + i); 28 | const auto _g = vld1q_f32(gain + i); 29 | vst1q_f32(out + i, util::four::tanh(_in * _g)); 30 | } 31 | } 32 | 33 | od::Outlet mOut { "Out" }; 34 | od::Inlet mIn { "In" }; 35 | od::Inlet mGain { "Gain" }; 36 | #endif 37 | }; 38 | } -------------------------------------------------------------------------------- /mods/strike/assets/CPR.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local strike = require "strike.libstrike" 3 | local Class = require "Base.Class" 4 | local Encoder = require "Encoder" 5 | local Unit = require "Unit" 6 | local GainBias = require "Unit.ViewControl.GainBias" 7 | local SidechainMeter = require "strike.SidechainMeter" 8 | local OutputMeter = require "strike.OutputMeter" 9 | local CompressorScope = require "strike.CompressorScope" 10 | local UnitShared = require "common.assets.UnitShared" 11 | 12 | local CPR = Class {} 13 | CPR:include(Unit) 14 | CPR:include(UnitShared) 15 | 16 | function CPR:init(args) 17 | args.title = "CPR" 18 | args.mnemonic = "cpr" 19 | Unit.init(self, args) 20 | end 21 | 22 | function CPR:onLoadGraph(channelCount) 23 | local op = self:addObject("op", strike.CPR()) 24 | 25 | local threshold = self:addParameterAdapterControl("threshold") 26 | tie(op, "Threshold", threshold, "Out") 27 | 28 | for i = 1, channelCount do 29 | connect(self, "In"..i, op, "In"..i) 30 | connect(op, "Out"..i, self, "Out"..i) 31 | end 32 | 33 | self:addMonitorBranch("sidechain", op, "Sidechain") 34 | self:addFreeBranch("reduction", op, "Reduction") 35 | self:addFreeBranch("active", op, "Active") 36 | end 37 | 38 | function CPR:onLoadViews() 39 | return { 40 | input = SidechainMeter { 41 | button = "input", 42 | description = "Input Gain", 43 | branch = self.branches.sidechain, 44 | compressor = self.objects.op, 45 | channelCount = self.channelCount, 46 | map = self.defaultDecibelMap(), 47 | units = app.unitDecibels, 48 | scaling = app.linearScaling 49 | }, 50 | scope = CompressorScope { 51 | description = "Compression", 52 | width = app.SECTION_PLY * 2, 53 | compressor = self.objects.op 54 | }, 55 | threshold = GainBias { 56 | button = "thresh", 57 | description = "Threshold", 58 | branch = self.branches.threshold, 59 | gainbias = self.objects.threshold, 60 | range = self.objects.threshold, 61 | biasMap = Encoder.getMap("[0,1]"), 62 | biasUnits = app.unitNone, 63 | biasPrecision = 2, 64 | initialBias = 0.5 65 | }, 66 | sidechain = self:branchView("sidechain"), 67 | active = self:branchView("active"), 68 | reduction = self:branchView("reduction"), 69 | output = OutputMeter { 70 | button = "output", 71 | description = "Output Gain", 72 | compressor = self.objects.op, 73 | channelCount = self.channelCount, 74 | map = self.defaultDecibelMap(), 75 | units = app.unitDecibels, 76 | scaling = app.linearScaling 77 | } 78 | }, { 79 | scope = { "sidechain", "threshold", "reduction", "active" }, 80 | expanded = { "input", "threshold", "scope", "output" }, 81 | collapsed = { "scope" } 82 | } 83 | end 84 | 85 | return CPR 86 | -------------------------------------------------------------------------------- /mods/strike/assets/Lift.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local strike = require "strike.libstrike" 3 | local Class = require "Base.Class" 4 | local Encoder = require "Encoder" 5 | local Unit = require "Unit" 6 | local GainBias = require "Unit.ViewControl.GainBias" 7 | local Gate = require "Unit.ViewControl.Gate" 8 | local UnitShared = require "common.assets.UnitShared" 9 | 10 | local Lift = Class {} 11 | Lift:include(Unit) 12 | Lift:include(UnitShared) 13 | 14 | function Lift:init(args) 15 | args.title = "Lift" 16 | args.mnemonic = "lpg" 17 | Unit.init(self, args) 18 | end 19 | 20 | function Lift:onLoadGraph(channelCount) 21 | local gate = self:addComparatorControl("gate", app.COMPARATOR_GATE) 22 | local rise = self:addParameterAdapterControl("rise") 23 | local fall = self:addParameterAdapterControl("fall") 24 | local height = self:addParameterAdapterControl("height") 25 | 26 | local op = self:addObject("op", strike.Lift()) 27 | tie(op, "Rise", rise, "Out") 28 | tie(op, "Fall", fall, "Out") 29 | tie(op, "Height", height, "Out") 30 | connect(gate, "Out", op, "Gate") 31 | 32 | for i = 1, channelCount do 33 | connect(self, "In"..i, op, "In"..i) 34 | connect(op, "Out"..i, self, "Out"..i) 35 | end 36 | 37 | self:addFreeBranch("env", self.objects.op, "Env") 38 | end 39 | 40 | function Lift:onLoadViews() 41 | return { 42 | env = self:branchView("env"), 43 | gate = Gate { 44 | button = "gate", 45 | description = "Gate", 46 | branch = self.branches.gate, 47 | comparator = self.objects.gate 48 | }, 49 | height = GainBias { 50 | button = "height", 51 | description = "Height", 52 | branch = self.branches.height, 53 | gainbias = self.objects.height, 54 | range = self.objects.height, 55 | biasMap = Encoder.getMap("oscFreq"), 56 | biasUnits = app.unitHertz, 57 | initialBias = 440, 58 | gainMap = Encoder.getMap("freqGain"), 59 | scaling = app.octaveScaling 60 | }, 61 | rise = GainBias { 62 | button = "rise", 63 | branch = self.branches.rise, 64 | description = "Rise Time", 65 | gainbias = self.objects.rise, 66 | range = self.objects.rise, 67 | biasMap = self.linMap(0, 10, 0.1, 0.01, 0.001, 0.001), 68 | biasUnits = app.unitSecs, 69 | initialBias = 0.001 70 | }, 71 | fall = GainBias { 72 | button = "fall", 73 | branch = self.branches.fall, 74 | description = "Fall Time", 75 | gainbias = self.objects.fall, 76 | range = self.objects.fall, 77 | biasMap = self.linMap(0, 10, 0.1, 0.01, 0.001, 0.001), 78 | biasUnits = app.unitSecs, 79 | initialBias = 0.200 80 | } 81 | }, { 82 | scope = { "env", "gate", "height", "rise", "fall" }, 83 | expanded = { "gate", "height", "rise", "fall" }, 84 | collapsed = {} 85 | } 86 | end 87 | 88 | return Lift 89 | -------------------------------------------------------------------------------- /mods/strike/assets/Tanh.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local strike = require "strike.libstrike" 3 | local Class = require "Base.Class" 4 | local GainBias = require "Unit.ViewControl.GainBias" 5 | local Unit = require "Unit" 6 | local UnitShared = require "common.assets.UnitShared" 7 | 8 | local Tanh = Class {} 9 | Tanh:include(Unit) 10 | Tanh:include(UnitShared) 11 | 12 | function Tanh:init(args) 13 | args.title = "Tanh" 14 | args.mnemonic = "tanh" 15 | Unit.init(self, args) 16 | end 17 | 18 | function Tanh:onLoadGraph(channelCount) 19 | local gain = self:addGainBiasControl("gain") 20 | 21 | for i = 1, channelCount do 22 | local op = self:addObject("op"..i, strike.Tanh()) 23 | connect(self, "In"..i, op, "In") 24 | connect(gain, "Out", op, "Gain") 25 | connect(op, "Out", self, "Out"..i) 26 | end 27 | end 28 | 29 | function Tanh:onLoadViews() 30 | return { 31 | gain = GainBias { 32 | button = "gain", 33 | description = "Input Gain", 34 | branch = self.branches.gain, 35 | gainbias = self.objects.gain, 36 | range = self.objects.gainRange, 37 | biasMap = self.linMap( 0, 10, 0.1, 0.01, 0.001, 0.001), 38 | gainMap = self.linMap(-10, 10, 0.1, 0.01, 0.001, 0.001), 39 | biasUnits = app.unitNone, 40 | biasPrecision = 2, 41 | initialBias = 2 42 | } 43 | }, { 44 | expanded = { "gain" }, 45 | collapsed = {} 46 | } 47 | end 48 | 49 | return Tanh 50 | -------------------------------------------------------------------------------- /mods/strike/assets/toc.lua: -------------------------------------------------------------------------------- 1 | return { 2 | name = "Strike", 3 | title = "Strike", 4 | keyword = "Strike", 5 | contact = "tomj.fiset@gmail.com", 6 | author = "tomf", 7 | units = { 8 | { title = "Arc (AD)", moduleName = "Arc" }, 9 | { title = "Fin (Osc)", moduleName = "Fin" }, 10 | { title = "Formant (Osc)", moduleName = "Formant" }, 11 | { title = "Softy (Osc)", moduleName = "Softy" }, 12 | 13 | { title = "Sieve (SVF)", moduleName = "Sieve" }, 14 | { title = "Bique (BCF)", moduleName = "Bique" }, 15 | { title = "Strike (LPG)", moduleName = "Strike" }, 16 | { title = "Lift (LPG)", moduleName = "Lift" }, 17 | 18 | { title = "Tanh", moduleName = "Tanh" }, 19 | { title = "CPR", moduleName = "CPR" } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /mods/strike/mod.mk: -------------------------------------------------------------------------------- 1 | PKGVERSION = 2.0.0 2 | include scripts/mod-builder.mk -------------------------------------------------------------------------------- /mods/strike/strike.cpp.swig: -------------------------------------------------------------------------------- 1 | %module strike_libstrike 2 | %include 3 | 4 | %{ 5 | 6 | #undef SWIGLUA 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #define SWIGLUA 22 | 23 | %} 24 | 25 | %include 26 | %include 27 | %include 28 | %include 29 | %include 30 | %include 31 | %include 32 | %include 33 | %include 34 | %include 35 | %include 36 | %include -------------------------------------------------------------------------------- /scripts/docker-shell.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | docker run \ 3 | --privileged -it \ 4 | --platform=linux/amd64 \ 5 | -v `pwd`:/er-301-custom-units \ 6 | -w /er-301-custom-units tomjfiset/er-301-am335x-build-env:1.1.2 \ 7 | bash 8 | -------------------------------------------------------------------------------- /scripts/env.mk: -------------------------------------------------------------------------------- 1 | include scripts/utils.mk 2 | 3 | SDKPATH ?= er-301 4 | 5 | # Determine PROFILE if it's not provided... 6 | # testing | release | debug 7 | PROFILE ?= testing 8 | 9 | # Determine ARCH if it's not provided... 10 | # linux | darwin | am335x 11 | ifndef ARCH 12 | SYSTEM_NAME := $(shell uname -s) 13 | ifeq ($(SYSTEM_NAME),Linux) 14 | ARCH = linux 15 | else ifeq ($(SYSTEM_NAME),Darwin) 16 | ARCH = darwin 17 | else 18 | $(error Unsupported system $(SYSTEM_NAME)) 19 | endif 20 | endif 21 | 22 | ifeq ($(ARCH),am335x) 23 | TI_INSTALL_DIR := /root/ti 24 | include $(SDKPATH)/scripts/am335x.mk 25 | endif 26 | 27 | ifeq ($(ARCH),linux) 28 | include $(SDKPATH)/scripts/linux.mk 29 | endif 30 | 31 | ifeq ($(ARCH),darwin) 32 | include $(SDKPATH)/scripts/darwin.mk 33 | endif 34 | -------------------------------------------------------------------------------- /scripts/rules.mk: -------------------------------------------------------------------------------- 1 | DEPFLAGS = -MF $@.d -MT $@ -MMD -MP 2 | 3 | $(out_dir)/%.o: %.c 4 | @echo $(describe_env) C $(describe_input) 5 | @mkdir -p $(@D) 6 | @$(CC) $(DEPFLAGS) $(CFLAGS) -std=gnu11 -c $< -o $@ 7 | 8 | $(out_dir)/%.o: %.cpp 9 | @echo $(describe_env) C++ $(describe_input) 10 | @mkdir -p $(@D) 11 | @$(CPP) $(DEPFLAGS) $(CFLAGS) -std=gnu++11 -c $< -o $@ 12 | 13 | $(out_dir)/%.o: %.S 14 | @echo $(describe_env) ASM $(describe_input) 15 | @mkdir -p $(@D) 16 | @$(CC) $(DEPFLAGS) $(CFLAGS) -c $< -o $@ 17 | 18 | # SWIG 19 | .PRECIOUS: $(out_dir)/%_swig.c $(out_dir)/%_swig.cpp 20 | 21 | $(out_dir)/%_swig.c: %.c.swig 22 | @echo $(describe_env) "SWIG" $(describe_input) 23 | @mkdir -p $(@D) 24 | @$(SWIG) $(DEPFLAGS) $(SWIGFLAGS) -o $@ $< 25 | 26 | $(out_dir)/%_swig.cpp: %.cpp.swig 27 | @echo $(describe_env) "SWIG++" $(describe_input) 28 | @mkdir -p $(@D) 29 | @$(SWIG) $(DEPFLAGS) -fvirtual -fcompact -c++ $(SWIGFLAGS) -o $@ $< 30 | 31 | $(out_dir)/%_swig.o: $(out_dir)/%_swig.c 32 | @echo $(describe_env) "C(SWIG)" $(describe_input) 33 | @mkdir -p $(@D) 34 | @$(CC) $(DEPFLAGS) $(CFLAGS.swig) -std=gnu11 -c $< -o $@ 35 | 36 | $(out_dir)/%_swig.o: $(out_dir)/%_swig.cpp 37 | @echo $(describe_env) "C++(SWIG)" $(describe_input) 38 | @mkdir -p $(@D) 39 | @$(CPP) $(DEPFLAGS) $(CFLAGS.swig) -std=gnu++11 -c $< -o $@ 40 | 41 | # Rules for files generated in the build directory. 42 | 43 | $(out_dir)/%.o: $(out_dir)/%.c 44 | @echo $(describe_env) C $(describe_input) 45 | @mkdir -p $(@D) 46 | @$(CC) $(CFLAGS) -std=c11 -c $< -o $@ 47 | 48 | $(out_dir)/%.o: $(out_dir)/%.cpp 49 | @echo $(describe_env) C++ $(describe_input) 50 | @mkdir -p $(@D) 51 | @$(CPP) $(DEPFLAGS) $(CFLAGS) -std=gnu++11 -c $< -o $@ 52 | 53 | $(out_dir)/%.o: $(out_dir)/%.S 54 | @echo $(describe_env) ASM $(describe_input) 55 | @mkdir -p $(@D) 56 | @$(CC) $(DEPFLAGS) $(CFLAGS) -c $< -o $@ 57 | 58 | # Dependencies 59 | 60 | #DEPFILES = $(objects:%.o=%o.d) 61 | DEPFILES := $(call rwildcard,$(out_dir),*.d) 62 | $(DEPFILES): ; 63 | -include $(DEPFILES) -------------------------------------------------------------------------------- /scripts/utils.mk: -------------------------------------------------------------------------------- 1 | # A recursive version of the wildcard function. 2 | # $(call rwildcard,directory,pattern) 3 | export rwildcard = $(foreach d,$(wildcard $(1:=/*)),$(call rwildcard,$d,$2) $(filter $(subst *,%,$2),$d)) -------------------------------------------------------------------------------- /src/common/assets/ViewControl/Sub/Control/Button.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local Class = require "Base.Class" 3 | local Base = require "common.assets.ViewControl.SubControl" 4 | 5 | local Button = Class {} 6 | Button:include(Base) 7 | 8 | function Button:init(args) 9 | Base.init(self, args) 10 | self.onPress = args.onPress or self.onPress 11 | self.onRelease = args.onRelease or self.onRelease 12 | end 13 | 14 | return Button 15 | -------------------------------------------------------------------------------- /src/common/assets/ViewControl/Sub/Control/Chain.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local Class = require "Base.Class" 3 | local Base = require "common.assets.ViewControl.SubControl" 4 | 5 | local Chain = Class {} 6 | Chain:include(Base) 7 | 8 | function Chain:init(args) 9 | args.name = args.name or "empty" 10 | Base.init(self, args) 11 | 12 | self.branch = args.branch or app.logError("%s.addScope: missing branch.", self) 13 | 14 | local column = args.column or app.BUTTON1_CENTER 15 | local row = args.row or app.GRID5_CENTER1 16 | 17 | self.scope = app.MiniScope(column, row, 40, 45) 18 | self.scope:setBorder(1) 19 | self.scope:setCornerRadius(3, 3, 3, 3) 20 | self:addGraphic(self.scope) 21 | 22 | self.branch:subscribe("contentChanged", self) 23 | end 24 | 25 | function Chain:contentChanged(chain) 26 | if not chain == self.branch then return end 27 | local outlet = chain:getMonitoringOutput(1) 28 | self.scope:watchOutlet(outlet) 29 | self.button:setText(chain:mnemonic()) 30 | end 31 | 32 | function Chain:onRemove() 33 | self.branch:unsubscribe("contentChanged", self) 34 | end 35 | 36 | function Chain:onRelease() 37 | self:unfocusParent() 38 | self.branch:show() 39 | end 40 | 41 | return Chain 42 | -------------------------------------------------------------------------------- /src/common/assets/ViewControl/Sub/Control/Readout.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local ply = app.SECTION_PLY 3 | 4 | local Class = require "Base.Class" 5 | local Encoder = require "Encoder" 6 | local Base = require "common.assets.ViewControl.SubControl" 7 | 8 | local Readout = Class {} 9 | Readout:include(Base) 10 | 11 | function Readout:init(args) 12 | Base.init(self, args) 13 | 14 | local param = args.parameter or app.logError("%s.addReadout: missing parameter.", self) 15 | param:enableSerialization() 16 | 17 | local units = args.units or app.unitNone 18 | local encoderMap = args.encoderMap or Encoder.getMap("default") 19 | local precision = args.precision or 2 20 | local column = args.column or app.BUTTON1_CENTER 21 | local row = args.row or app.GRID5_CENTER1 22 | 23 | self.readout = app.Readout(0, 0, ply, 10) 24 | self.readout:setParameter(param) 25 | self.readout:setAttributes(units, encoderMap) 26 | self.readout:setPrecision(precision) 27 | self.readout:setCenter(column, row) 28 | self:addGraphic(self.readout) 29 | 30 | self.editMessage = args.editMessage or "" 31 | self.commitMessage = args.commitMessage or "" 32 | self.encoderState = args.encoderState or Encoder.Coarse 33 | end 34 | 35 | function Readout:doKeyboardSet() 36 | local Decimal = require "Keyboard.Decimal" 37 | 38 | local keyboard = Decimal { 39 | message = self.editMessage, 40 | commitMessage = self.commitMessage, 41 | initialValue = self.readout:getValueInUnits() 42 | } 43 | 44 | local task = function(value) 45 | if value then 46 | self.readout:save() 47 | self.readout:setValueInUnits(value) 48 | end 49 | end 50 | 51 | keyboard:subscribe("done", task) 52 | keyboard:subscribe("commit", task) 53 | keyboard:show() 54 | end 55 | 56 | function Readout:onFocus() 57 | if not self:hasParentFocus("encoder") then self:focusParent() end 58 | self.readout:save() 59 | end 60 | 61 | function Readout:getCursorController() 62 | return self.readout 63 | end 64 | 65 | function Readout:onZero() 66 | self.readout:zero() 67 | end 68 | 69 | function Readout:onCancel(focused) 70 | self.readout:restore() 71 | end 72 | 73 | function Readout:onRelease(focused) 74 | if focused then self:doKeyboardSet() end 75 | end 76 | 77 | function Readout:onEncoder(change, shifted) 78 | self.readout:encoder(change, shifted, self.encoderState == Encoder.Fine) 79 | end 80 | 81 | return Readout 82 | -------------------------------------------------------------------------------- /src/common/assets/ViewControl/Sub/Control/Toggle.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local Class = require "Base.Class" 3 | local Base = require "common.assets.ViewControl.SubControl" 4 | 5 | local Toggle = Class {} 6 | Toggle:include(Base) 7 | 8 | function Toggle:init(args) 9 | Base.init(self, args) 10 | self.option = args.option 11 | self.option:enableSerialization() 12 | 13 | self.valueOn = args.on 14 | self.valueOff = args.off 15 | 16 | self.onPress = function () 17 | self:toggle() 18 | self:updateViewState() 19 | end 20 | 21 | local column = args.column or app.BUTTON2_CENTER 22 | local row = args.row or app.GRID5_CENTER3 23 | 24 | self.indicator = app.BinaryIndicator(0, 0, 32, 32) 25 | self.indicator:setCenter(column, row) 26 | self:addGraphic(self.indicator) 27 | end 28 | 29 | function Toggle:onCursorEnter() 30 | self:updateViewState() 31 | return Base.onCursorEnter(self) 32 | end 33 | 34 | function Toggle:isOn() 35 | return self.option:value() == self.valueOn 36 | end 37 | 38 | function Toggle:toggle() 39 | if self:isOn() then 40 | self.option:set(self.valueOff) 41 | else 42 | self.option:set(self.valueOn) 43 | end 44 | end 45 | 46 | function Toggle:updateViewState() 47 | if self:isOn() then 48 | self.indicator:on() 49 | else 50 | self.indicator:off() 51 | end 52 | end 53 | 54 | return Toggle 55 | -------------------------------------------------------------------------------- /src/common/assets/ViewControl/Sub/View/Parameter.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local Class = require "Base.Class" 3 | 4 | local Base = require "common.assets.ViewControl.SubView" 5 | local Readout = require "common.assets.ViewControl.SubControl.Readout" 6 | 7 | local Parameter = Class {} 8 | Parameter:include(Base) 9 | 10 | function Parameter:init(args) 11 | Base.init(self, args) 12 | 13 | Readout { 14 | parent = self, 15 | position = 1, 16 | name = args.param1.name, 17 | parameter = args.param1.parameter, 18 | editMessage = args.param1.editMessage, 19 | commitMessage = args.param1.commitMessage, 20 | column = app.BUTTON1_CENTER, 21 | row = app.GRID5_CENTER4 22 | } 23 | 24 | Readout { 25 | parent = self, 26 | position = 2, 27 | name = args.param2.name, 28 | parameter = args.param2.parameter, 29 | editMessage = args.param2.editMessage, 30 | commitMessage = args.param2.commitMessage, 31 | column = app.BUTTON2_CENTER, 32 | row = app.GRID5_CENTER4 33 | } 34 | 35 | Readout { 36 | parent = self, 37 | position = 3, 38 | name = args.param3.name, 39 | parameter = args.param3.parameter, 40 | editMessage = args.param3.editMessage, 41 | commitMessage = args.param3.commitMessage, 42 | column = app.BUTTON3_CENTER, 43 | row = app.GRID5_CENTER4 44 | } 45 | end 46 | 47 | return Parameter 48 | -------------------------------------------------------------------------------- /src/common/assets/ViewControl/Sub/View/Pitch.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local Class = require "Base.Class" 3 | local Encoder = require "Encoder" 4 | 5 | local common = require "common.lib" 6 | local DialMap = require "common.assets.DialMap" 7 | local Base = require "common.assets.ViewControl.SubView" 8 | local Readout = require "common.assets.ViewControl.SubControl.Readout" 9 | local Chain = require "common.assets.ViewControl.SubControl.Chain" 10 | 11 | local col2 = app.BUTTON2_CENTER 12 | local col3 = app.BUTTON3_CENTER 13 | local col2h = col2 + (col3 - col2) / 2 14 | 15 | local Pitch = Class {} 16 | Pitch:include(Base) 17 | 18 | local overlay = (function () 19 | local instructions = app.DrawingInstructions() 20 | 21 | local line1 = app.GRID5_LINE1 22 | local center3 = app.GRID5_CENTER3 23 | local col1 = app.BUTTON1_CENTER 24 | 25 | -- sum 26 | instructions:circle(col2, center3, 8) 27 | instructions:hline(col2 - 5, col2 + 5, center3) 28 | instructions:vline(col2, center3 - 5, center3 + 5) 29 | 30 | -- 2^V 31 | instructions:box(col2h - 8, center3 - 8, 16, 16) 32 | 33 | -- arrow: branch to bias 34 | instructions:hline(col1 + 20, col2 - 9, center3) 35 | instructions:triangle(col2 - 12, center3, 0, 3) 36 | 37 | -- arrow: bias to 2^V 38 | instructions:hline(col2 + 9, col2h - 8, center3) 39 | --instructions:triangle(col2h - 11, center3, 0, 3) 40 | 41 | -- arrow: 2^V to title 42 | instructions:vline(col2h, center3 + 8, line1 - 2) 43 | instructions:triangle(col2h, line1 - 2, 90, 3) 44 | 45 | return instructions 46 | end)() 47 | 48 | function Pitch:addDrawing() 49 | local drawing = app.Drawing(0, 0, 128, 64) 50 | drawing:add(overlay) 51 | self.graphic:addChild(drawing) 52 | 53 | local two = app.Label("2", 12) 54 | two:fitToText(0) 55 | two:setCenter(col2h - 2, app.GRID5_CENTER3) 56 | self.graphic:addChild(two) 57 | 58 | local v = app.Label("v", 10) 59 | v:fitToText(0) 60 | v:setCenter(col2h + 3, app.GRID5_CENTER3 + 5) 61 | self.graphic:addChild(v) 62 | end 63 | 64 | function Pitch:init(args) 65 | Base.init(self, args) 66 | 67 | self:addDrawing() 68 | 69 | Chain { 70 | parent = self, 71 | position = 1, 72 | name = "empty", 73 | branch = args.branch, 74 | column = app.BUTTON1_CENTER - 20, 75 | row = app.GRID5_LINE4 76 | } 77 | 78 | Readout { 79 | parent = self, 80 | position = 2, 81 | name = "tune", 82 | parameter = args.tune, 83 | dialMap = DialMap.cents.threeOctaves, 84 | units = common.unitCents, 85 | precision = 0, 86 | editMessage = "Pitch offset.", 87 | commitMessage = "Updated pitch offset.", 88 | column = app.BUTTON2_CENTER, 89 | row = app.GRID5_CENTER4 90 | } 91 | end 92 | 93 | function Pitch:onFocused() 94 | self:setFocusedPosition(2) 95 | Base.onFocused(self) 96 | end 97 | 98 | return Pitch 99 | -------------------------------------------------------------------------------- /src/common/assets/ViewControl/Sub/View/PitchTrack.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local Class = require "Base.Class" 3 | 4 | local Base = require "common.assets.ViewControl.SubView.Pitch" 5 | local Toggle = require "common.assets.ViewControl.SubControl.Toggle" 6 | 7 | local PitchTrack = Class {} 8 | PitchTrack:include(Base) 9 | 10 | function PitchTrack:init(args) 11 | Base.init(self, args) 12 | 13 | Toggle { 14 | parent = self, 15 | position = 3, 16 | name = "track", 17 | option = args.option, 18 | on = 1, 19 | off = 2, 20 | column = app.BUTTON3_CENTER, 21 | row = app.GRID5_CENTER3 22 | } 23 | end 24 | 25 | return PitchTrack 26 | -------------------------------------------------------------------------------- /src/mods/polygon/assets/ViewControl/RoundRobinGate.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local polygon = require "polygon.libpolygon" 3 | local Class = require "Base.Class" 4 | 5 | local Base = require "common.assets.ViewControl.Split.Paged" 6 | local Gate = require "common.assets.ViewControl.SubView.Gate" 7 | 8 | local ply = app.SECTION_PLY 9 | 10 | local RoundRobinGate = Class { 11 | type = "RoundRobinGate", 12 | canEdit = false, 13 | canMove = true 14 | } 15 | RoundRobinGate:include(Base) 16 | 17 | function RoundRobinGate:init(args) 18 | Base.init(self, args) 19 | self.polygon = args.polygon or app.logError("%s.init: missing polygon instance.", self) 20 | self.branch = args.branch or app.logError("%s.init: missing branch.", self) 21 | 22 | local threshold = self.polygon:getParameter("Gate Threshold") 23 | local onReleaseFire = function () self.polygon:releaseManualGates() end 24 | 25 | self:addSubView(Gate { 26 | name = "Round Robin", 27 | branch = self.branch, 28 | threshold = threshold, 29 | onPressFire = function () self.polygon:markManualGate(0) end, 30 | onReleaseFire = onReleaseFire 31 | }) 32 | 33 | for i, voice in ipairs(args.voices) do 34 | self:addSubView(Gate { 35 | name = "Voice "..i, 36 | branch = voice.gateBranch, 37 | threshold = threshold, 38 | onPressFire = function () self.polygon:markManualGate(i) end, 39 | onReleaseFire = onReleaseFire 40 | }) 41 | end 42 | 43 | self.graphic = polygon.RoundRobinGateView(self.polygon, 0, 0, ply, 64) 44 | self:setControlGraphic(self.graphic) 45 | self:addSpotDescriptor { 46 | center = 0.5 * ply 47 | } 48 | end 49 | 50 | function RoundRobinGate:updatePageIndex(pageIndex, propogate) 51 | self.graphic:setCursorSelection(pageIndex - 1) 52 | Base.updatePageIndex(self, pageIndex, propogate) 53 | end 54 | 55 | return RoundRobinGate 56 | -------------------------------------------------------------------------------- /src/mods/polygon/assets/ViewControl/RoundRobinPitch.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local polygon = require "polygon.libpolygon" 3 | local Class = require "Base.Class" 4 | 5 | local Base = require "common.assets.ViewControl.Split.Paged" 6 | local Pitch = require "common.assets.ViewControl.SubView.Pitch" 7 | local PitchTrack = require "common.assets.ViewControl.SubView.PitchTrack" 8 | 9 | local ply = app.SECTION_PLY 10 | 11 | local RoundRobinPitch = Class { 12 | type = "RoundRobinPitch", 13 | canEdit = false, 14 | canMove = true 15 | } 16 | RoundRobinPitch:include(Base) 17 | 18 | function RoundRobinPitch:init(args) 19 | Base.init(self, args) 20 | self.polygon = args.polygon or app.logError("%s.init: missing polygon instance.", self) 21 | self.branch = args.branch or app.logError("%s.init: missing branch.", self) 22 | 23 | local biasMap = args.biasMap or app.logError("%s.init: missing bias map.", self) 24 | 25 | self:addSubView(PitchTrack { 26 | name = "Round Robin", 27 | branch = self.branch, 28 | tune = args.tune, 29 | option = self.polygon:getOption("RR V/Oct Track") 30 | }) 31 | 32 | for i, voice in ipairs(args.voices) do 33 | self:addSubView(Pitch { 34 | name = "Voice "..i, 35 | branch = voice.pitchBranch, 36 | tune = voice.pitchOffset 37 | }) 38 | end 39 | 40 | self.graphic = polygon.RoundRobinPitchView(self.polygon, 0, 0, ply, 64) 41 | self.graphic:setScale(biasMap) 42 | self:setMainCursorController(self.graphic) 43 | self:setControlGraphic(self.graphic) 44 | self:addSpotDescriptor { 45 | center = 0.5 * ply 46 | } 47 | end 48 | 49 | function RoundRobinPitch:updatePageIndex(pageIndex, propogate) 50 | self.graphic:setCursorSelection(pageIndex - 1) 51 | Base.updatePageIndex(self, pageIndex, propogate) 52 | end 53 | 54 | return RoundRobinPitch 55 | -------------------------------------------------------------------------------- /src/mods/polygon/assets/ViewControl/TrackablePitch.lua: -------------------------------------------------------------------------------- 1 | local app = app 2 | local Class = require "Base.Class" 3 | local Encoder = require "Encoder" 4 | local Settings = require "Settings" 5 | 6 | local Base = require "common.assets.ViewControl.Split" 7 | local PitchTrack = require "common.assets.ViewControl.SubView.PitchTrack" 8 | 9 | local TrackablePitch = Class { 10 | type = "TrackablePitch", 11 | canEdit = false, 12 | canMove = false 13 | } 14 | TrackablePitch:include(Base) 15 | 16 | function TrackablePitch:init(args) 17 | Base.init(self, args) 18 | 19 | local offset = args.offset or app.logError("%s.init: offset is missing.", self) 20 | local range = args.range or app.logError("%s.init: range is missing.", self) 21 | self.branch = args.branch or app.logError("%s.init: missing branch.", self) 22 | 23 | local faderParam = offset:getParameter("Offset") or offset:getParameter("Bias") 24 | local readoutParam 25 | if Settings.get("unitControlReadoutSource") == "actual" then 26 | readoutParam = range:getParameter("Center") or faderParam 27 | else 28 | readoutParam = faderParam 29 | end 30 | 31 | self:addSubView(PitchTrack { 32 | name = args.name, 33 | branch = self.branch, 34 | tune = faderParam, 35 | option = args.track 36 | }) 37 | 38 | self.graphic = app.Fader(0, 0, app.SECTION_PLY, 64) 39 | self.graphic:setTargetParameter(faderParam) 40 | self.graphic:setValueParameter(faderParam) 41 | self.graphic:setControlParameter(readoutParam) 42 | self.graphic:setRangeObject(range) 43 | self.graphic:setLabel(args.name) 44 | self.graphic:setAttributes(app.unitCents, Encoder.getMap("cents")) 45 | self.graphic:setPrecision(0) 46 | 47 | self:setMainCursorController(self.graphic) 48 | self:setControlGraphic(self.graphic) 49 | self:addSpotDescriptor { 50 | center = 0.5 * app.SECTION_PLY 51 | } 52 | end 53 | 54 | return TrackablePitch 55 | --------------------------------------------------------------------------------