Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | 1x 1x 4x 4x 4x 2x 2x 4x 10x 10x 4x 2x 4x 4x 1x 1x 1x 4x | import { computed } from '@ember/object'; import Beat from './beat'; import Sampler from './sampler'; const beatBank = new WeakMap(); /** * An instance of this class has a single "sound" (comprised of one or multiple * audio sources) but provides methods to play that sound repeatedly, mixed with * "rests," in a rhythmic way. An instance of this class behaves very similarly * to a "lane" on a drum machine. * * @public * @class BeatTrack * @extends Sampler * * @todo need a way to stop a BeatTrack once it's started. Maybe by creating * the times in advance and not calling play until it's the next beat in the * queue? */ const BeatTrack = Sampler.extend({ /** * Determines the number of beats in a BeatTrack instance. * * @public * @property numBeats * @type {number} */ numBeats: 4, /** * If specified, Determines length of time, in milliseconds, before isPlaying * and currentTimeIsPlaying are automatically switched back to false after * having been switched to true for each beat. 100ms is used by default. * * @public * @property duration * @type {number} * @default 100 */ duration: 100, /** * Computed property. An array of Beat instances. The number of Beat instances * in the array is always the same as the `numBeats` property. If 'numBeats' * or duration changes. This property will be recomputed, but any beats that * previously existed are reused so that they will maintain their `active` * state. * * @public * @property beats * @type {array|Beat} */ beats: computed('numBeats', 'duration', function() { let beats = []; let numBeats = this.get('numBeats'); let existingBeats; if (beatBank.has(this)) { existingBeats = beatBank.get(this); numBeats = numBeats - existingBeats.length; } for (let i = 0; i < numBeats; i++) { const beat = Beat.create({ duration: this.get('duration'), _parentPlayIn: this.playIn.bind(this), _parentPlay: this.play.bind(this) }); beats.push(beat); } if (existingBeats) { beats = existingBeats.concat(beats); } beatBank.set(this, beats); return beats; }), /** * Calls play on all Beat instances in the beats array. * * @public * @method playBeats * * @param {number} bpm The tempo at which the beats should be played. * * @param noteType {number} The (rhythmic) length of each beat. Fractions * are suggested here so that it's easy to reason about. For example, for * eighth notes, pass in `1/8`. */ playBeats(bpm, noteType) { this._callPlayMethodOnBeats('playIn', bpm, noteType); }, /** * Calls play on `active` Beat instances in the beats array. Any beat that * is not marked active is effectively a "rest". * * @public * @method playActiveBeats * * @param {number} bpm The tempo at which the beats and rests should be played. * * @param noteType {number} The (rhythmic) length of each beat/rest. Fractions * are suggested here so that it's easy to reason about. For example, for * eighth notes, pass in `1/8`. */ playActiveBeats(bpm, noteType) { this._callPlayMethodOnBeats('ifActivePlayIn', bpm, noteType); }, /** * The underlying method behind playBeats and playActiveBeats. * * @private * @method _callPlayMethodOnBeats * * @param {string} method The method that should be called on each beat. * * @param {number} bpm The tempo that should be used to calculate the length * of a beat/rest. * * @param noteType {number} The (rhythmic) length of each beat/rest that should * be used to calculate the length of a beat/rest in seconds. */ _callPlayMethodOnBeats(method, bpm, noteType=1 / 4) { // http://bradthemad.org/guitar/tempo_explanation.php const duration = (240 * noteType) / bpm; this.get('beats').map((beat, idx) => beat[method](idx * duration)); } }); export default BeatTrack; |