commit 4e546d4d1eb8b9a36722ec4fb65884e2666382fd
parent 07e308b61ee86a9bc39e7f3c2617005597d39d66
Author: Massimo Siboldi <mdsiboldi@gmail.com>
Date: Sun, 11 Mar 2018 22:13:41 -0700
Introducing thunk to handle the metro until it goes into a web worker
Diffstat:
6 files changed, 59 insertions(+), 44 deletions(-)
diff --git a/package-lock.json b/package-lock.json
@@ -8855,6 +8855,11 @@
"symbol-observable": "1.2.0"
}
},
+ "redux-thunk": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.2.0.tgz",
+ "integrity": "sha1-5hWhbha0ehmlFXZhM9Hj6Zt4UuU="
+ },
"regenerate": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.3.tgz",
diff --git a/package.json b/package.json
@@ -11,7 +11,8 @@
"envelope-generator": "^3.0.0",
"preact": "^8.2.6",
"preact-redux": "^2.0.3",
- "redux": "^3.7.2"
+ "redux": "^3.7.2",
+ "redux-thunk": "^2.2.0"
},
"scripts": {
"start": "preact-scripts start",
diff --git a/src/App/index.js b/src/App/index.js
@@ -222,26 +222,6 @@ class App extends Component {
})
}
- //TODO this could be part of the reducer, and sending PLAY action can start it. Wonder how that can be done without side effects? Or if it should.
- metro = () => {
- this.props.setPlaying(true);
- const loop = () => {
- if (this.props.playing) {
- this.props.setBeat((this.props.beat + 1) % this.props.numBeats);
- window.setTimeout(loop, (1 / this.props.bpm) * 60000);
- }
- }
- // give redux a chance to convey it's playing, a good sign of a bad design.
- // i like this https://medium.com/@machadogj/timers-in-react-with-redux-apps-9a5a722162e8#Timers in Actions
- window.setTimeout(loop, 0);
- }
-
- stopMetro = () => {
- //TODO this could be one action handler called STOP, rather than composing everything manually like this.
- this.props.setBeat(0);
- this.props.setPlaying(false);
- }
-
keyHandler(e) {
//TODO handle global commands, maybe some modal stuff even wow
console.log('wow i got through', e.key);
@@ -310,14 +290,14 @@ class App extends Component {
<div class="global-controls">
<CircleButton
active={this.props.playing}
- action={this.metro}
+ action={this.props.startMetro}
disabled={this.props.playing}
>
<div class="triangle"></div>
</CircleButton>
<CircleButton
active={!this.props.playing}
- action={this.stopMetro}
+ action={this.props.stopMetro}
>
<div class="rectangle"></div>
</CircleButton>
diff --git a/src/Synth/index.js b/src/Synth/index.js
@@ -35,11 +35,15 @@ export default class Synth extends Component {
}
}
+const taperOff = (length, val, i) => {
+ return val * (1 - Math.pow(i / length, 0.5));
+}
+
function updateAudio(waveform) {
FFT.forward(waveform);
const periodicWave = ac.createPeriodicWave(
- new Float32Array(FFT.real),
- new Float32Array(FFT.imag)
+ new Float32Array(FFT.real.map(taperOff.bind(null, FFT.real.length))),
+ new Float32Array(FFT.imag.map(taperOff.bind(null, FFT.imag.length)))
);
P.changeWave(periodicWave);
}
diff --git a/src/index.js b/src/index.js
@@ -10,8 +10,23 @@ const ConnectedApp = connect(state => state, {
setVolume: (newVal) => ({type: 'SET_GLOBAL_VOLUME', value: newVal}),
setBpm: (newVal) => ({type: 'SET_GLOBAL_BPM', value: newVal}),
setBeat: (newVal) => ({type: 'SET_GLOBAL_BEAT', value: newVal}),
- setPlaying: (newVal) => ({type: 'SET_GLOBAL_PLAYING', value: newVal}),
- setNumBeats: (newVal) => ({type: 'SET_GLOBAL_NUM_BEATS', value: newVal})
+ setNumBeats: (newVal) => ({type: 'SET_GLOBAL_NUM_BEATS', value: newVal}),
+ startMetro: () => (dispatch, getState) => {
+ const tickMetro = {type: 'TICK_METRO'};
+ const startMetro = {type: 'START_METRO'};
+
+ dispatch(startMetro);
+
+ const loop = () => {
+ if (getState().playing) {
+ dispatch(tickMetro);
+ window.setTimeout(loop, (1 / getState().bpm) * 60000);
+ }
+ }
+
+ loop();
+ },
+ stopMetro: () => ({type: 'STOP_METRO'})
})(App);
const InformedApp = <Provider store={store}><ConnectedApp /></Provider>;
diff --git a/src/store.js b/src/store.js
@@ -1,4 +1,5 @@
-import { createStore } from 'redux';
+import { createStore, applyMiddleware } from 'redux';
+import thunk from 'redux-thunk';
const initialState = {
volume: 0.7,
@@ -11,26 +12,35 @@ const initialState = {
const reducer = (state, action) => {
let updates = {};
switch (action.type) {
- case 'SET_GLOBAL_VOLUME':
- updates.volume = action.value;
- break;
- case 'SET_GLOBAL_BPM':
- updates.bpm = action.value;
- break;
- case 'SET_GLOBAL_BEAT':
- updates.beat = action.value;
- break;
- case 'SET_GLOBAL_PLAYING':
- updates.playing = action.value;
- break;
- case 'SET_GLOBAL_NUM_BEATS':
- updates.numBeats = action.value;
- break;
+ case 'SET_GLOBAL_VOLUME':
+ updates.volume = action.value;
+ break;
+ case 'SET_GLOBAL_BPM':
+ updates.bpm = action.value;
+ break;
+ case 'SET_GLOBAL_BEAT':
+ updates.beat = action.value;
+ break;
+ case 'SET_GLOBAL_NUM_BEATS':
+ updates.numBeats = action.value;
+ break;
+ case 'START_METRO':
+ updates.playing = true;
+ break;
+ case 'TICK_METRO':
+ updates.beat = (state.beat + 1) % state.numBeats;
+ break;
+ case 'STOP_METRO':
+ updates.playing = false;
+ updates.beat = 0;
+ break;
+ default:
+ break;
}
return Object.assign({}, state, updates);
}
-const store = createStore(reducer, initialState);
+const store = createStore(reducer, initialState, applyMiddleware(thunk));
export default store;
export { store };