Skip to content
This repository has been archived by the owner on Feb 18, 2022. It is now read-only.

Play button responds to spacebar #13

Open
wants to merge 7 commits into
base: 1.0.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 73 additions & 52 deletions demo/demo.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
import React, { Component } from 'react';
import { Analyser, Compressor, Song, Sequencer, Sampler, Synth } from '../src';

import {
Analyser,
Bitcrusher,
Chorus,
Compressor,
Delay,
Filter,
MoogFilter,
Overdrive,
Phaser,
PingPong,
Reverb,
Song,
Sequencer,
Sampler,
Synth,
} from '../src';

import Polysynth from './polysynth';
import Visualization from './visualization';

import './index.css';

Expand All @@ -14,27 +34,8 @@ export default class Demo extends Component {
this.audioProcess = this.audioProcess.bind(this);
this.playToggle = this.playToggle.bind(this);
}
componentDidMount() {
this.ctx = this.canvas.getContext('2d');
}
audioProcess(analyser) {
if (this.ctx) {
const gradient = this.ctx.createLinearGradient(0, 0, 0, 512);
gradient.addColorStop(1, '#000000');
gradient.addColorStop(0.75, '#2ecc71');
gradient.addColorStop(0.25, '#f1c40f');
gradient.addColorStop(0, '#e74c3c');

const array = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(array);
this.ctx.clearRect(0, 0, 800, 512);
this.ctx.fillStyle = gradient;

for (let i = 0; i < (array.length); i++) {
const value = array[i];
this.ctx.fillRect(i * 12, 512, 10, value * -2);
}
}
this.visualization.audioProcess(analyser);
}
playToggle() {
this.setState({
Expand All @@ -46,13 +47,60 @@ export default class Demo extends Component {
<div>
<Song
playing={this.state.playing}
tempo={190}
tempo={90}
>
<Analyser onAudioProcess={this.audioProcess}>
<Sequencer
resolution={16}
bars={1}
>
<Sampler
sample="samples/kick.wav"
steps={[0, 2, 8, 10]}
/>
<Sampler
sample="samples/snare.wav"
steps={[4, 12]}
/>
</Sequencer>
<Sequencer
resolution={16}
bars={2}
>
<Polysynth
type="sine"
steps={[
[0, 1, ['c3', 'd#3', 'g3']],
[2, 1, ['c4']],
[8, 1, ['c3', 'd#3', 'g3']],
[10, 1, ['c4']],
[12, 1, ['c3', 'd#3', 'g3']],
[14, 1, ['d#4']],
[16, 1, ['f3', 'g#3', 'c4']],
[18, 1, ['f3', 'g#3', 'c4']],
[24, 1, ['f3', 'g#3', 'c4']],
[26, 1, ['f3', 'g#3', 'c4']],
[28, 1, ['f3', 'g#3', 'c4']],
[30, 1, ['f3', 'g#3', 'c4']],
]}
/>
</Sequencer>
<Sequencer
resolution={16}
bars={2}
>
<Synth
gain={1}
type="sine"
steps={[
[0, 8, ['c2']],
[8, 4, ['c2']],
[12, 4, ['d#2']],
[16, 8, ['f2']],
[24, 8, ['f1']],
]}
/>
</Sequencer>
<Compressor>
<Sampler
sample="samples/kick.wav"
Expand All @@ -67,44 +115,17 @@ export default class Demo extends Component {
steps={[0, 4, 8, 12, 16, 20, 24, 28]}
/>
</Compressor>
</Sequencer>
<Sequencer
resolution={16}
bars={4}
>
<Synth
type="sine"
gain={0}
steps={[
[0, 1, ['d1']],
[4, 1, ['d1']],
[8, 1, ['f1']],
[12, 1, ['g1']],
[16, 1, ['a1']],
[20, 1, ['d1']],
[32, 1, ['d1']],
[33, 1, ['d1']],
[34, 1, ['d1']],
[40, 1, ['f1']],
[44, 1, ['f1']],
[48, 1, ['d1']],
]}
/>
</Sequencer>
</Analyser>
</Song>

<canvas
className="react-music-canvas"
width={800}
height={512}
ref={(c) => { this.canvas = c; }}
/>
<Visualization ref={(c) => { this.visualization = c; }} />

<button
className="react-music-button"
type="button"
id="play-button"
onClick={this.playToggle}
autofocus
>
{this.state.playing ? 'Stop' : 'Play'}
</button>
Expand Down
2 changes: 1 addition & 1 deletion demo/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ import Demo from './demo';
ReactDOM.render(
<Demo />,
document.getElementById('root')
);
);
45 changes: 45 additions & 0 deletions demo/polysynth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React, { PropTypes } from 'react';

import {
Analyser,
Bitcrusher,
Chorus,
Compressor,
Delay,
Filter,
MoogFilter,
Overdrive,
Phaser,
PingPong,
Reverb,
Song,
Sequencer,
Sampler,
Synth,
} from '../src';

const Polysynth = (props) => (
<Delay>
<Reverb>
<Synth
type="sine"
gain={0.15}
steps={props.steps}
/>
<MoogFilter>
<Synth
type="square"
gain={0.15}
transpose={1}
steps={props.steps}
/>
</MoogFilter>
</Reverb>
</Delay>
);

Polysynth.propTypes = {
steps: PropTypes.array,
};

export default Polysynth;
43 changes: 43 additions & 0 deletions demo/visualization.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React, { Component } from 'react';

export default class Visualization extends Component {
constructor(props) {
super(props);
this.audioProcess = this.audioProcess.bind(this);
}
componentDidMount() {
this.ctx = this.canvas.getContext('2d');
}
componentDidReceiveProps() {

}
audioProcess(analyser) {
if (this.ctx) {
const gradient = this.ctx.createLinearGradient(0, 0, 0, 512);
gradient.addColorStop(1, '#000000');
gradient.addColorStop(0.75, '#2ecc71');
gradient.addColorStop(0.25, '#f1c40f');
gradient.addColorStop(0, '#e74c3c');

const array = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(array);
this.ctx.clearRect(0, 0, 800, 512);
this.ctx.fillStyle = gradient;

for (let i = 0; i < (array.length); i++) {
const value = array[i];
this.ctx.fillRect(i * 12, 512, 10, value * -2);
}
}
}
render() {
return (
<canvas
className="react-music-canvas"
width={800}
height={512}
ref={(c) => { this.canvas = c; }}
/>
);
}
}
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
"audio-contour": "0.0.1",
"envelope-generator": "^3.0.0",
"note-parser": "^2.0.0",
"react-addons-create-fragment": "^15.3.1"
"react-addons-create-fragment": "^15.3.1",
"tunajs": "^0.4.5",
"uuid": "^2.0.2"
},
"peerDependencies": {
"react": "^15.2.1",
Expand Down
18 changes: 18 additions & 0 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,23 @@
<body>
<div id='root'></div>
<script src="bundle.js"></script>
<script>
var button = document.getElementById("play-button");

// Focus on button
button.focus();

// playToggle() is called when spacebar is pressed
document.onkeydown = function(e){
if(e.keyCode == 32) {
button.playToggle();
}
}

//Clicking off button will refocus on button
button.onblur = function(e){
setTimeout(function() {button.focus()}, 10);
}
</script>
</body>
</html>
Binary file added public/reverb/room.wav
Binary file not shown.
9 changes: 0 additions & 9 deletions src/components/analyser.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,7 @@ export default class Sequencer extends Component {
};
static childContextTypes = {
audioContext: PropTypes.object,
bars: PropTypes.number,
barInterval: PropTypes.number,
bufferLoaded: PropTypes.func,
connectNode: PropTypes.object,
registerBuffer: PropTypes.func,
registerInstrument: PropTypes.func,
resolution: PropTypes.number,
scheduler: PropTypes.object,
tempo: PropTypes.number,
totalBars: PropTypes.number,
};
constructor(props, context) {
super(props);
Expand Down
50 changes: 50 additions & 0 deletions src/components/bitcrusher.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/* eslint-disable no-restricted-syntax */
import React, { PropTypes, Component } from 'react';
import Tuna from 'tunajs';

export default class Bitcrusher extends Component {
static propTypes = {
children: PropTypes.node,
bits: PropTypes.number,
normfreq: PropTypes.number,
bufferSize: PropTypes.number,
};
static defaultProps = {
bits: 4,
normfreq: 0.1,
bufferSize: 4096,
};
static contextTypes = {
audioContext: PropTypes.object,
connectNode: PropTypes.object,
};
static childContextTypes = {
audioContext: PropTypes.object,
connectNode: PropTypes.object,
};
constructor(props, context) {
super(props);

const tuna = new Tuna(context.audioContext);

this.connectNode = new tuna.Bitcrusher({
bits: props.bits,
normfreq: props.normfreq,
bufferSize: props.bufferSize,
});

this.connectNode.connect(context.connectNode);
}
getChildContext() {
return {
...this.context,
connectNode: this.connectNode,
};
}
componentWillUnmount() {
this.connectNode.disconnect();
}
render() {
return <span>{this.props.children}</span>;
}
}
Loading