synthing

a waveform sequencing synth on the web
Log | Files | Refs | Submodules

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 };