store.js (4372B)
1 import { createStore, applyMiddleware } from 'redux'; 2 import thunk from 'redux-thunk'; 3 import consts from './consts.js'; 4 import helpers from './helpers.js'; 5 6 const toneShape = { 7 active: false, 8 waveform: consts.initialWave, 9 mix: 0.7, 10 mute: false, 11 solo: false, 12 beats: helpers.boolArray.create(consts.MAX_BEATS) 13 }; 14 15 const initialState = { 16 volume: 0.7, 17 bpm: 120, 18 beat: 0, 19 playing: false, 20 numBeats: 4, 21 editingToneIdx: 0, 22 helpOpen: false, 23 adsr: { 24 attack: 0.3, 25 decay: 1, 26 sustain: 0.4, 27 release: 1 28 }, 29 tones: [Object.assign({}, toneShape, { 30 active: true, 31 beats: helpers.boolArray.update(helpers.boolArray.create(consts.MAX_BEATS), 0, true) 32 })], 33 }; 34 35 36 const adsrReducer = (state, action) => { 37 const updates = {}; 38 const propertiesAllowed = consts.adsrProperties.map(val => val.name); 39 switch (action.type) { 40 case 'SET_ADSR_PROPERTY': 41 if (propertiesAllowed.indexOf(action.property) > -1) { 42 updates[action.property] = action.value 43 } 44 break; 45 default: 46 break; 47 } 48 return Object.assign({}, state, updates); 49 }; 50 51 const tonesReducer = (state = [], action) => { 52 switch (action.type) { 53 case 'ADD_TONE': 54 // idx: optional, defaults to end 55 // waveform: optional, defaults to sine wave 56 // activate: optional, defaults active to false 57 const idx = action.idx === undefined ? state.length : action.idx; 58 59 const newToneProps = {}; 60 if (action.waveform) 61 newToneProps.waveform = action.waveform; 62 if (action.activate) 63 newToneProps.active = true; 64 65 return helpers.immObjArray.add( 66 state, 67 idx, 68 Object.assign({}, toneShape, newToneProps) 69 ); 70 case 'SET_TONE_PROPERTY': 71 // idx: required 72 // property: required 73 // value: required 74 const propertiesAllowed = Object.keys(initialState.tones[0]); 75 if (propertiesAllowed.indexOf(action.property) > -1) { 76 return helpers.immObjArray.update(state, action.idx, { 77 [action.property]: action.value 78 }); 79 } 80 else return state; 81 case 'DELETE_TONE': 82 // idx: required 83 return helpers.immObjArray.remove(state, action.idx); 84 default: 85 return state; 86 } 87 }; 88 89 const globalReducer = (state, action) => { 90 const updates = {}; 91 switch (action.type) { 92 case 'SET_GLOBAL_VOLUME': 93 updates.volume = action.value; 94 break; 95 case 'SET_GLOBAL_BPM': 96 updates.bpm = action.value; 97 break; 98 case 'SET_GLOBAL_BEAT': 99 updates.beat = action.value; 100 break; 101 case 'SET_GLOBAL_NUM_BEATS': 102 updates.numBeats = action.value; 103 break; 104 case 'START_METRO': 105 updates.playing = true; 106 break; 107 case 'TICK_METRO': 108 updates.beat = (state.beat + 1) % state.numBeats; 109 break; 110 case 'STOP_METRO': 111 updates.playing = false; 112 updates.beat = 0; 113 break; 114 case 'SET_EDITING_TONE_IDX': 115 updates.editingToneIdx = action.value; 116 break; 117 case 'DELETE_TONE': 118 updates.editingToneIdx = Math.min( 119 state.editingToneIdx, 120 // 1 for length, 1 for deleted tone. 121 // min length of tones array should be 2 before a delete... 122 state.tones.length - 2 123 ); 124 break; 125 case 'SET_HELP_OPEN': 126 updates.helpOpen = action.value; 127 break; 128 case 'TOGGLE_HELP_OPEN': 129 updates.helpOpen = !state.helpOpen; 130 break; 131 default: 132 break; 133 } 134 return Object.assign({}, state, updates); 135 } 136 137 const reducer = (state = initialState, action) => { 138 return Object.assign({}, state, globalReducer(state, action), { 139 tones: tonesReducer(state.tones, action), 140 adsr: adsrReducer(state.adsr, action) 141 }); 142 }; 143 144 const store = createStore(reducer, initialState, applyMiddleware(thunk)); 145 146 export default store; 147 export { store };