bbc / r-audio

A library of React components for building Web Audio graphs.
Other
180 stars 14 forks source link

how to use with redux? #3

Open haszari opened 5 years ago

haszari commented 5 years ago

I'm trying to use this with redux, but I'm hitting problems. I'm using RExtensible components for voices in my composition/audio scene/song. I want to make these components dependent on app state using redux.

The normal way to do this is to wrap the components with using connect from react-redux.

This breaks the interface expected by RPipeline:

pipeline.js:50 Uncaught TypeError: this.props.destination is not a function
    at Object.destinationFunction [as destination] (pipeline.js:50)
    at destinationFunction (pipeline.js:50)
    at RGain.connectToAllDestinations (audio-node.js:62)
    at RGain.componentDidMount (audio-node.js:165)
    at commitLifeCycles (react-dom.development.js:14524)
    at commitAllLifeCycles (react-dom.development.js:15738)
    at HTMLUnknownElement.callCallback (react-dom.development.js:145)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:195)
    at invokeGuardedCallback (react-dom.development.js:248)
    at commitRoot (react-dom.development.js:15908)

Here's the node, which plays back a loop.

class SyncLoop extends RExtensible {
  constructor() {
    super();

    this.state = { 
      amp: 0.2,
    };
  }

  renderGraph() {
    const { playing } = this.props; // `playing` comes from app state, via redux

    const { label, buffer, bufferTempo, playbackTempo, loopLengthBeats } = this.props;
    const loopLengthSeconds = ( 60 / bufferTempo ) * loopLengthBeats;
    const playbackRate = playbackTempo / bufferTempo;

    const { amp } = this.state;

    //
    // (would use `playing` here to render nothing, or mute the source, or slow it down)
    // 

    const looper = (
      <RBufferSource 
        buffer={ buffer } 
        playbackRate={ playbackRate }
        loop start={0} loopEnd={ loopLengthSeconds } 
      /> 
    );

    return (
      <RPipeline>
        { looper }
        <RGain gain={ amp } />
      </RPipeline>
    );
};

SyncLoop.defaultProps = {
  label: 'loop',
  buffer: null,
  bufferTempo: 120,
  playbackTempo: 120,
  loopLengthBeats: 4
};

const mapStateToProps = (state /*, ownProps*/) => {
  return createSelector( {
    playing: 'transport.playing',
  } );
};

export default connect(
  mapStateToProps
)(SyncLoop);

Is it possible to use r-audio in conjunction with redux - any ideas?

jakubfiala-bbc commented 5 years ago

Hi @haszari - apologies for the late reply. This is a very good point - I haven't considered the use case where you might need to wrap children in redux connect-style components. The issue here is that r-audio components expect their sub-graphs to be immediate children. Any child that's not a r-audio component will be ignored by the "parent graph".

I think the best way to achieve what you want right now is to simply have your entire audio graph wrapped once, rather than wrapping individual parts of it. I've been thinking about an update to the library which would solve this but I want to avoid recursively checking for r-audio components within non-r-audio components, as that would potentially be very bad for performance.

If you have any suggestions on how this could be achieved or how to get around this issue, please do share, it would be much appreciated!

haszari commented 5 years ago

The goal I have in mind is a graph structure that is dependent on app state – for example, if you add another file to loop, it will add a node for that (etc). I can understand why this kind of thing is out of scope of r-audio for the moment :)