mirror of
https://github.com/generativefm/generative.fm.git
synced 2026-01-09 14:48:14 -05:00
Update pieces to v4
This commit is contained in:
748
package-lock.json
generated
748
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -65,10 +65,11 @@
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.29",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.13.1",
|
||||
"@fortawesome/react-fontawesome": "^0.1.11",
|
||||
"@generative-music/pieces-alex-bainter": "^3.4.0",
|
||||
"@generative-music/samples-alex-bainter": "^1.1.0",
|
||||
"@generative-music/pieces-alex-bainter": "^4.1.2",
|
||||
"@generative-music/samples-alex-bainter": "^1.2.0",
|
||||
"@generative-music/visualizer": "^2.3.5",
|
||||
"@generative-music/web-provider": "^1.1.0",
|
||||
"@generative-music/web-library": "^0.2.0",
|
||||
"@generative-music/web-provider": "^2.0.4",
|
||||
"audiobuffer-to-wav": "^1.0.0",
|
||||
"classnames": "^2.2.6",
|
||||
"clone": "^2.1.2",
|
||||
@@ -81,7 +82,7 @@
|
||||
"react-tiny-popover": "3.4.1",
|
||||
"redux": "^4.0.5",
|
||||
"svg.js": "^2.7.1",
|
||||
"tone": "^13.8.25",
|
||||
"tone": "^14.7.58",
|
||||
"uuid": "^3.4.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,10 @@ const DEFAULT_VISUALIZATION_TYPE = 'squareCut';
|
||||
const pieceLoader = source => {
|
||||
const pieceManifest = JSON.parse(source);
|
||||
const output = `import image from '${pieceManifest.image}';
|
||||
import makePiece from '${pieceManifest.makePiece}';
|
||||
import activate from '${pieceManifest.makePiece}';
|
||||
export default {
|
||||
image,
|
||||
makePiece,
|
||||
activate,
|
||||
title: '${pieceManifest.title}',
|
||||
id: '${pieceManifest.artistId}-${pieceManifest.id}',
|
||||
artist: '${pieceManifest.artistId}',
|
||||
|
||||
@@ -71,7 +71,7 @@ const AboutTabComponent = ({ version, isUpdateAvailable, isOnline }) => {
|
||||
</p>
|
||||
<p>
|
||||
If you enjoy this project, consider supporting it:
|
||||
<div className="support-methods">
|
||||
<span className="support-methods">
|
||||
<a
|
||||
href="https://www.patreon.com/bePatron?u=2484731"
|
||||
target="_blank"
|
||||
@@ -87,7 +87,7 @@ const AboutTabComponent = ({ version, isUpdateAvailable, isOnline }) => {
|
||||
PayPal
|
||||
</a>
|
||||
<span>BTC: 3DMb8BQVTtfVv59pMLmZmHr6xSoJsb3P4Z</span>
|
||||
</div>
|
||||
</span>
|
||||
</p>
|
||||
<p>{`v${version}`}</p>
|
||||
<p>
|
||||
|
||||
@@ -12,9 +12,15 @@ import HelpTabComponent from './help-tab';
|
||||
import TitleNavContainer from '@containers/title-nav.container';
|
||||
import AboutTabContainer from '@containers/about-tab.container';
|
||||
import PiecesTabContainer from '@containers/pieces-tab.container';
|
||||
import RecordTabContainer from '@containers/record-tab.container';
|
||||
import './app.scss';
|
||||
|
||||
const RecordRedirect = () => {
|
||||
useEffect(() => {
|
||||
window.location = 'https://record.generative.fm';
|
||||
});
|
||||
return null;
|
||||
};
|
||||
|
||||
const AppComponent = () => {
|
||||
const [isHoverEnabled, setIsHoverEnabled] = useState(!isMobile);
|
||||
useEffect(() => {
|
||||
@@ -51,7 +57,7 @@ const AppComponent = () => {
|
||||
<Route exact path="/" component={PiecesTabContainer} />
|
||||
<Route exact path="/about" component={AboutTabContainer} />
|
||||
<Route exact path="/help" component={HelpTabComponent} />
|
||||
<Route exact path="/record" component={RecordTabContainer} />
|
||||
<Route exact path="/record" component={RecordRedirect} />
|
||||
<Route
|
||||
path="/music/:pieceId"
|
||||
render={({ match, history }) => (
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react';
|
||||
import propTypes from 'prop-types';
|
||||
import { Redirect } from 'react-router';
|
||||
import pieces from '@pieces';
|
||||
import provider from '@pieces/provider';
|
||||
import library from '@pieces/library';
|
||||
import piecesById from '@pieces/by-id';
|
||||
import LinkButton from '@components/shared/link-button';
|
||||
import PieceFilter from './piece-filter';
|
||||
@@ -72,7 +72,7 @@ const PiecesTabComponent = ({
|
||||
if (!isOnline) {
|
||||
Promise.all(
|
||||
pieces.map(({ id, sampleNames }) =>
|
||||
provider.canProvide(sampleNames).then(result => [id, result])
|
||||
library.canProvide(sampleNames).then(result => [id, result])
|
||||
)
|
||||
).then(results => {
|
||||
setIsPieceCachedMap(new Map(results));
|
||||
|
||||
@@ -1,289 +0,0 @@
|
||||
import React, { useState, useEffect, useMemo } from 'react';
|
||||
import propTypes from 'prop-types';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { faTimes, faPlus } from '@fortawesome/free-solid-svg-icons';
|
||||
import pieces from '@pieces';
|
||||
import piecesById from '@pieces/by-id';
|
||||
import provider from '@pieces/provider';
|
||||
import ControlButtonComponent from '../controls/control-button';
|
||||
import TextButton from '@components/shared/text-button';
|
||||
import isSupported from '@config/is-supported';
|
||||
import './record-tab.scss';
|
||||
|
||||
const MAX_RECORDING_LENGTH_MINUTES = 240;
|
||||
|
||||
const getGeneratedRecordingsQueue = generatedRecordings =>
|
||||
Reflect.ownKeys(generatedRecordings)
|
||||
.map(recordingId => generatedRecordings[recordingId])
|
||||
.sort((a, b) => a.queuedAtTime - b.queuedAtTime);
|
||||
|
||||
const RecordTabComponent = ({
|
||||
selectedPieceId,
|
||||
selectPiece,
|
||||
queueRecordingGeneration,
|
||||
generatedRecordings,
|
||||
lastRecordingGenerationLength,
|
||||
isOnline,
|
||||
removeRecordingGeneration,
|
||||
startRecordingGeneration,
|
||||
}) => {
|
||||
if (selectedPieceId === null) {
|
||||
selectPiece(pieces[0].id);
|
||||
return <div />;
|
||||
}
|
||||
const [recordingLengthInMinutes, setRecordingLengthInMinutes] = useState(
|
||||
lastRecordingGenerationLength
|
||||
);
|
||||
|
||||
const selectedPiece = useMemo(() => piecesById[selectedPieceId], [
|
||||
selectedPieceId,
|
||||
]);
|
||||
|
||||
const [generatedRecordingsQueue, setGeneratedRecordingsQueue] = useState(
|
||||
getGeneratedRecordingsQueue(generatedRecordings)
|
||||
);
|
||||
|
||||
const [isGenerationInProgress, setIsGenerationInProgress] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const queue = getGeneratedRecordingsQueue(generatedRecordings);
|
||||
setGeneratedRecordingsQueue(queue);
|
||||
setIsGenerationInProgress(queue.some(({ isInProgress }) => isInProgress));
|
||||
}, [generatedRecordings]);
|
||||
|
||||
const [isPieceCachedMap, setIsPieceCachedMap] = useState(new Map());
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOnline) {
|
||||
Promise.all(
|
||||
pieces.map(({ id, sampleNames }) =>
|
||||
provider.canProvide(sampleNames).then(result => [id, result])
|
||||
)
|
||||
).then(results => {
|
||||
setIsPieceCachedMap(new Map(results));
|
||||
});
|
||||
}
|
||||
}, [isOnline]);
|
||||
|
||||
const isPieceDisabled = piece =>
|
||||
!piece.isRecordable || (!isOnline && !isPieceCachedMap.get(piece.id));
|
||||
|
||||
const getIsRecordingValid = () =>
|
||||
!isPieceDisabled(selectedPiece) &&
|
||||
recordingLengthInMinutes > 0 &&
|
||||
recordingLengthInMinutes <= MAX_RECORDING_LENGTH_MINUTES;
|
||||
|
||||
const [isRecordingValid, setIsRecordingValid] = useState(
|
||||
getIsRecordingValid()
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setIsRecordingValid(getIsRecordingValid());
|
||||
}, [selectedPiece, recordingLengthInMinutes, isOnline]);
|
||||
|
||||
const handleSubmit = event => {
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="centered-tab record-tab">
|
||||
<a
|
||||
className="alert"
|
||||
href="https://record.generative.fm"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
Try the new{' '}
|
||||
<span className="fake-link">Generative.fm recording app</span>. Add fade
|
||||
ins and outs, with more consistent waiting times—often faster—and
|
||||
smaller file sizes.
|
||||
</a>
|
||||
This page enables you to generate and download recordings. Recording
|
||||
generation may take a while, and longer recordings require more time to
|
||||
generate. Music playback is not supported while recording generation is in
|
||||
progress.
|
||||
{isSupported && (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="form-group recording-entry">
|
||||
<span>
|
||||
Record
|
||||
<input
|
||||
type="number"
|
||||
id="length-input"
|
||||
min="1"
|
||||
max={MAX_RECORDING_LENGTH_MINUTES}
|
||||
value={recordingLengthInMinutes}
|
||||
onChange={event =>
|
||||
setRecordingLengthInMinutes(event.target.value)
|
||||
}
|
||||
title="Length of the recording to generate, in miniutes"
|
||||
required
|
||||
/>
|
||||
minutes of
|
||||
<select
|
||||
id="piece-select"
|
||||
value={selectedPieceId}
|
||||
onChange={event => selectPiece(event.target.value)}
|
||||
title="Piece to record"
|
||||
>
|
||||
{pieces.map(piece => (
|
||||
<option
|
||||
key={piece.id}
|
||||
value={piece.id}
|
||||
disabled={isPieceDisabled(piece)}
|
||||
>
|
||||
{piece.title}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</span>
|
||||
{isRecordingValid && (
|
||||
<ControlButtonComponent
|
||||
faIcon={faPlus}
|
||||
title="Add to queue"
|
||||
onClick={() =>
|
||||
queueRecordingGeneration({
|
||||
pieceId: selectedPieceId,
|
||||
lengthInMinutes: recordingLengthInMinutes,
|
||||
})
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{!isRecordingValid && <div className="btn-spacer" />}
|
||||
</div>
|
||||
{isPieceDisabled(selectedPiece) && (
|
||||
<div className="form-group invalid-msg">
|
||||
{`${selectedPiece.title} is not recordable${
|
||||
selectedPiece.isRecordable ? ' right now.' : '.'
|
||||
}`}
|
||||
</div>
|
||||
)}
|
||||
{(recordingLengthInMinutes <= 0 ||
|
||||
recordingLengthInMinutes > MAX_RECORDING_LENGTH_MINUTES) && (
|
||||
<div className="form-group invalid-msg">
|
||||
Recording length must be between 1 and 240 minutes
|
||||
</div>
|
||||
)}
|
||||
<div className="form-group">
|
||||
{generatedRecordingsQueue.filter(({ url }) => url === '').length >
|
||||
0 &&
|
||||
!isGenerationInProgress && (
|
||||
<TextButton
|
||||
title="Resume previously queued recording generation"
|
||||
onClick={() =>
|
||||
startRecordingGeneration(
|
||||
generatedRecordingsQueue[0].recordingId
|
||||
)
|
||||
}
|
||||
>
|
||||
Resume generation
|
||||
</TextButton>
|
||||
)}
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<ul className="generated-recordings-queue">
|
||||
{generatedRecordingsQueue.map(generatedRecording => {
|
||||
const {
|
||||
recordingId,
|
||||
lengthInMinutes,
|
||||
url,
|
||||
pieceId,
|
||||
isInProgress,
|
||||
} = generatedRecording;
|
||||
let status = '';
|
||||
if (isInProgress) {
|
||||
status = ' - generating...';
|
||||
} else if (url === '') {
|
||||
status = ' - waiting to generate';
|
||||
}
|
||||
const displayText = `${lengthInMinutes} minutes of ${piecesById[pieceId].title}${status}`;
|
||||
return (
|
||||
<li
|
||||
key={recordingId}
|
||||
className="generated-recordings-queue__item"
|
||||
>
|
||||
{url === '' ? (
|
||||
<span>{displayText} </span>
|
||||
) : (
|
||||
<a
|
||||
href={url}
|
||||
download={`${pieceId}-${lengthInMinutes}-minutes.wav`}
|
||||
>
|
||||
{displayText}
|
||||
</a>
|
||||
)}
|
||||
{!isInProgress && (
|
||||
<ControlButtonComponent
|
||||
faIcon={faTimes}
|
||||
onClick={() => removeRecordingGeneration(recordingId)}
|
||||
title="Remove"
|
||||
/>
|
||||
)}
|
||||
{isInProgress && <div className="btn-spacer" />}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</form>
|
||||
)}
|
||||
{!isSupported && (
|
||||
<div>
|
||||
<br />
|
||||
<Link to="/help">Browser not supported</Link>
|
||||
</div>
|
||||
)}
|
||||
{selectedPiece.isRecordable && (
|
||||
<span>
|
||||
<br />
|
||||
<br />
|
||||
<a
|
||||
rel="license noreferrer noopener"
|
||||
href="http://creativecommons.org/licenses/by/4.0/"
|
||||
target="_blank"
|
||||
className="centered-content"
|
||||
>
|
||||
<img
|
||||
alt="Creative Commons License"
|
||||
style={{ borderWidth: 0 }}
|
||||
src="https://i.creativecommons.org/l/by/4.0/80x15.png"
|
||||
/>
|
||||
</a>
|
||||
<br />
|
||||
<span className="centered-content">
|
||||
{selectedPiece.title} (Excerpt) by{' '}
|
||||
<a
|
||||
href="https://alexbainter.com"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
Alex Bainter
|
||||
</a>{' '}
|
||||
is licensed under a{' '}
|
||||
<a
|
||||
rel="license noreferrer noopener"
|
||||
href="http://creativecommons.org/licenses/by/4.0/"
|
||||
target="_blank"
|
||||
>
|
||||
Creative Commons Attribution 4.0 International License
|
||||
</a>
|
||||
.
|
||||
</span>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
RecordTabComponent.propTypes = {
|
||||
selectedPieceId: propTypes.string.isRequired,
|
||||
selectPiece: propTypes.func.isRequired,
|
||||
queueRecordingGeneration: propTypes.func.isRequired,
|
||||
generatedRecordings: propTypes.object.isRequired,
|
||||
lastRecordingGenerationLength: propTypes.string.isRequired,
|
||||
isOnline: propTypes.bool.isRequired,
|
||||
removeRecordingGeneration: propTypes.func.isRequired,
|
||||
startRecordingGeneration: propTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default RecordTabComponent;
|
||||
@@ -1,57 +0,0 @@
|
||||
@import '_colors';
|
||||
@import '_variables';
|
||||
@import '_mixins';
|
||||
|
||||
.record-tab {
|
||||
input:invalid {
|
||||
border: 1px solid $highlightColor;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.recording-entry {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 0.5em;
|
||||
|
||||
input,
|
||||
select {
|
||||
margin: 0 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.generated-recordings-queue {
|
||||
@include borderedList();
|
||||
}
|
||||
|
||||
.btn-spacer {
|
||||
height: $buttonWidth;
|
||||
}
|
||||
|
||||
.invalid-msg {
|
||||
font-style: italic;
|
||||
color: $highlightColor;
|
||||
}
|
||||
|
||||
.centered-content {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.alert {
|
||||
padding: 1em;
|
||||
background-color: $highlightColor;
|
||||
margin-bottom: 1em;
|
||||
display: block;
|
||||
color: $primaryColor;
|
||||
text-decoration: none;
|
||||
|
||||
.fake-link {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import Tone from 'tone';
|
||||
import * as Tone from 'tone';
|
||||
import classNames from 'classnames';
|
||||
import streamDestination from '@store/middleware/stream-destination';
|
||||
import './air-play.scss';
|
||||
@@ -25,7 +25,7 @@ const AirPlay = () => {
|
||||
|
||||
const makeHandleWebkitCurrentPlaybackTargetIsWirelessChanged = audio => () => {
|
||||
const isWireless = Boolean(audio.webkitCurrentPlaybackTargetIsWireless);
|
||||
Tone.master.mute = isWireless;
|
||||
Tone.Destination.mute = isWireless;
|
||||
setIsConnected(isWireless);
|
||||
};
|
||||
|
||||
|
||||
@@ -95,11 +95,16 @@ const TitleNavComponent = ({
|
||||
linkTo="/"
|
||||
isActive={matchRootOrMusic}
|
||||
/>
|
||||
<TitleNavLink
|
||||
text="RECORD"
|
||||
parentClass="title-nav__header__tab-list"
|
||||
linkTo="/record"
|
||||
/>
|
||||
<li className="title-nav__header__tab-list__item">
|
||||
<a
|
||||
href="https://record.generative.fm"
|
||||
className="title-nav__header__tab-list__item__link"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
RECORD
|
||||
</a>
|
||||
</li>
|
||||
<TitleNavLink
|
||||
text="HELP"
|
||||
parentClass="title-nav__header__tab-list"
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import Tone from 'tone';
|
||||
import * as Tone from 'tone';
|
||||
|
||||
export default Tone.supported;
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import { connect } from 'react-redux';
|
||||
import recordTabComponent from '@components/app/record-tab';
|
||||
import selectPiece from '@store/actions/creators/select-piece.creator';
|
||||
import queueRecordingGeneration from '@store/actions/creators/queue-recording-generation.creator';
|
||||
import removeRecordingGeneration from '@store/actions/creators/remove-recording-generation.creator';
|
||||
import startRecordingGeneration from '@store/actions/creators/start-recording-generation.creator';
|
||||
|
||||
const mapStateToProps = ({
|
||||
selectedPieceId,
|
||||
generatedRecordings,
|
||||
lastRecordingGenerationLength,
|
||||
isOnline,
|
||||
}) => ({
|
||||
selectedPieceId,
|
||||
generatedRecordings,
|
||||
lastRecordingGenerationLength,
|
||||
isOnline,
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, {
|
||||
selectPiece,
|
||||
queueRecordingGeneration,
|
||||
removeRecordingGeneration,
|
||||
startRecordingGeneration,
|
||||
})(recordTabComponent);
|
||||
10
src/pieces/library.js
Normal file
10
src/pieces/library.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import getSamplesByFormat from '@generative-music/samples-alex-bainter';
|
||||
import createProvider from '@generative-music/web-provider';
|
||||
import createLibrary from '@generative-music/web-library';
|
||||
import sampleFormat from '@config/sample-format';
|
||||
|
||||
const provider = createProvider();
|
||||
const sampleIndex = getSamplesByFormat()[sampleFormat];
|
||||
const library = createLibrary({ sampleIndex, provider });
|
||||
|
||||
export default library;
|
||||
@@ -1,3 +1,4 @@
|
||||
import piecesById from '@pieces/by-id';
|
||||
import isMobile from '@config/is-mobile';
|
||||
import getOnlineStatus from '@utils/get-online-status';
|
||||
import objToMap from '@utils/obj-to-map';
|
||||
@@ -30,6 +31,9 @@ const getInitialState = storedState => {
|
||||
timer: Object.assign({}, storedState.timer, { remainingMS: 0 }),
|
||||
favorites: new Set(storedState.favorites),
|
||||
isInstallable: false,
|
||||
selectedPieceId: piecesById[storedState.selectedPieceId]
|
||||
? storedState.selectedPieceId
|
||||
: null,
|
||||
});
|
||||
|
||||
if (typeof storedState.globalPlayTime === 'object') {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import Tone from 'tone';
|
||||
import * as Tone from 'tone';
|
||||
import castApplicationId from '@config/cast-application-id';
|
||||
import piecesById from '@pieces/by-id';
|
||||
import artists from '@data/artists';
|
||||
@@ -41,7 +41,7 @@ const updateReceiverMetadata = (castSession, currentPieceId) => {
|
||||
};
|
||||
|
||||
const handleCastStateConnected = (castContext, store) => {
|
||||
Tone.Master.mute = true;
|
||||
Tone.Destination.mute = true;
|
||||
const peerConnection = new RTCPeerConnection(null);
|
||||
const castSession = castContext.getCurrentSession();
|
||||
castSession.addMessageListener(CUSTOM_MESSAGE_NAMESPACE, (ns, message) => {
|
||||
@@ -74,7 +74,7 @@ const handleCastStateConnected = (castContext, store) => {
|
||||
const { cast } = window;
|
||||
const handleCastStateChanged = ({ castState }) => {
|
||||
if (castState === cast.framework.CastState.NOT_CONNECTED) {
|
||||
Tone.Master.mute = false;
|
||||
Tone.Destination.mute = false;
|
||||
castContext.removeEventListener(
|
||||
cast.framework.CastContextEventType.CAST_STATE_CHANGED,
|
||||
handleCastStateChanged
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import Tone from 'tone';
|
||||
import MUTE from '../../actions/types/mute.type';
|
||||
import UNMUTE from '../../actions/types/unmute.type';
|
||||
import NEXT from '../../actions/types/next.type';
|
||||
@@ -19,7 +18,6 @@ import updatePlayTime from '../../actions/creators/update-play-time.creator';
|
||||
const UPDATE_PLAY_TIME_INTERVAL_MS = 5000;
|
||||
|
||||
const piecesMiddleware = store => next => {
|
||||
Tone.context.latencyHint = 'balanced';
|
||||
let playTimeInterval;
|
||||
|
||||
const startTrackingPlayTimeForPieceId = pieceId => {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import Tone from 'tone';
|
||||
import provider from '@pieces/provider';
|
||||
import * as Tone from 'tone';
|
||||
import library from '@pieces/library';
|
||||
import markPieceBuildLoading from '../../actions/creators/mark-piece-build-loading.creator';
|
||||
import markPieceBuildLoaded from '../../actions/creators/mark-piece-build-loaded.creator';
|
||||
import performance from './performance';
|
||||
import stopPerformances from './stop-performances';
|
||||
import convertPctToDb from './convert-pct-to-db';
|
||||
import streamDestination from '../stream-destination';
|
||||
import noop from '@utils/noop';
|
||||
|
||||
let lastBuildId;
|
||||
let isPerformanceBuilding = false;
|
||||
@@ -46,33 +47,34 @@ const makePlayPiece = (store, performances) => {
|
||||
// console.log(maxDb);
|
||||
// }, 1000);
|
||||
|
||||
provider.provide(piece.sampleNames, Tone.context).then(samples => {
|
||||
piece
|
||||
.makePiece({
|
||||
destination: pieceVol,
|
||||
audioContext: Tone.context,
|
||||
samples,
|
||||
})
|
||||
.then(cleanUp => {
|
||||
piecePerformance.addCleanupFn(cleanUp);
|
||||
piecePerformance.isLoaded = true;
|
||||
isPerformanceBuilding = false;
|
||||
const { selectedPieceId, isPlaying } = store.getState();
|
||||
store.dispatch(markPieceBuildLoaded(piecePerformance));
|
||||
if (
|
||||
lastBuildId === piecePerformance.performanceId &&
|
||||
selectedPieceId === piece.id &&
|
||||
isPlaying
|
||||
) {
|
||||
Tone.Transport.start();
|
||||
} else {
|
||||
stopPerformances(performances);
|
||||
}
|
||||
if (isPlaying && queuedPiece !== null) {
|
||||
playPiece(queuedPiece, store);
|
||||
}
|
||||
});
|
||||
});
|
||||
piece
|
||||
.activate({
|
||||
destination: pieceVol,
|
||||
context: Tone.context,
|
||||
sampleLibrary: library,
|
||||
onProgress: noop,
|
||||
})
|
||||
.then(([deactivate, schedule]) => {
|
||||
piecePerformance.addCleanupFn(deactivate);
|
||||
const end = schedule();
|
||||
piecePerformance.addCleanupFn(end);
|
||||
piecePerformance.isLoaded = true;
|
||||
isPerformanceBuilding = false;
|
||||
const { selectedPieceId, isPlaying } = store.getState();
|
||||
store.dispatch(markPieceBuildLoaded(piecePerformance));
|
||||
if (
|
||||
lastBuildId === piecePerformance.performanceId &&
|
||||
selectedPieceId === piece.id &&
|
||||
isPlaying
|
||||
) {
|
||||
Tone.Transport.start();
|
||||
} else {
|
||||
stopPerformances(performances);
|
||||
}
|
||||
if (isPlaying && queuedPiece !== null) {
|
||||
playPiece(queuedPiece, store);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
queuedPiece = piece;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import Tone from 'tone';
|
||||
import * as Tone from 'tone';
|
||||
import uuid from 'uuid';
|
||||
|
||||
const performance = (piece, volumeNode) => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import Tone from 'tone';
|
||||
import * as Tone from 'tone';
|
||||
import toWav from 'audiobuffer-to-wav';
|
||||
import piecesById from '@pieces/by-id';
|
||||
import START_RECORDING_GENERATION from '@store/actions/types/start-recording-generation.type';
|
||||
@@ -8,7 +8,8 @@ import RECORDING_GENERATION_COMPLETE from '@store/actions/types/recording-genera
|
||||
import recordingGenerationComplete from '@store/actions/creators/recording-generation-complete.creator';
|
||||
import startRecordingGeneration from '@store/actions/creators/start-recording-generation.creator';
|
||||
import stop from '@store/actions/creators/stop.creator';
|
||||
import provider from '@pieces/provider';
|
||||
import library from '@pieces/library';
|
||||
import noop from '@utils/noop';
|
||||
|
||||
const renderOffline = (piece, durationInSeconds) => {
|
||||
const { sampleRate } = Tone.context;
|
||||
@@ -21,7 +22,7 @@ const renderOffline = (piece, durationInSeconds) => {
|
||||
durationInSeconds,
|
||||
sampleRate
|
||||
);
|
||||
Tone.context = offlineContext;
|
||||
Tone.setContext(offlineContext);
|
||||
|
||||
/*
|
||||
* SUPER HACK ALERT―TRULY DISGUSTING
|
||||
@@ -31,41 +32,41 @@ const renderOffline = (piece, durationInSeconds) => {
|
||||
* rendered. So, delay those disconnects until after the audio has been rendered.
|
||||
*/
|
||||
|
||||
const fnAttempts = [];
|
||||
const restoreFns = [];
|
||||
const hackFn = (target, fnName, returnValue) => {
|
||||
const originalFn = target[fnName];
|
||||
restoreFns.push(() => {
|
||||
target[fnName] = originalFn;
|
||||
});
|
||||
target[fnName] = function hacked(...args) {
|
||||
fnAttempts.push(originalFn.bind(this, ...args));
|
||||
return returnValue;
|
||||
};
|
||||
};
|
||||
// const fnAttempts = [];
|
||||
// const restoreFns = [];
|
||||
// const hackFn = (target, fnName, returnValue) => {
|
||||
// const originalFn = target[fnName];
|
||||
// restoreFns.push(() => {
|
||||
// target[fnName] = originalFn;
|
||||
// });
|
||||
// target[fnName] = function hacked(...args) {
|
||||
// fnAttempts.push(originalFn.bind(this, ...args));
|
||||
// return returnValue;
|
||||
// };
|
||||
// };
|
||||
//
|
||||
// hackFn(Tone, 'disconnect', Tone);
|
||||
// hackFn(window.AudioBufferSourceNode.prototype, 'disconnect');
|
||||
// hackFn(Tone.ToneBufferSource.prototype, 'dispose');
|
||||
|
||||
hackFn(Tone, 'disconnect', Tone);
|
||||
hackFn(window.AudioBufferSourceNode.prototype, 'disconnect');
|
||||
hackFn(Tone.BufferSource.prototype, 'dispose');
|
||||
|
||||
return provider
|
||||
.provide(piece.sampleNames, offlineContext)
|
||||
.then(samples =>
|
||||
piece.makePiece({
|
||||
samples,
|
||||
audioContext: offlineContext,
|
||||
destination: Tone.Master,
|
||||
})
|
||||
)
|
||||
.then(cleanup => {
|
||||
return piece
|
||||
.activate({
|
||||
context: offlineContext,
|
||||
destination: offlineContext.destination,
|
||||
sampleLibrary: library,
|
||||
onProgress: noop,
|
||||
})
|
||||
.then(([deactivate, schedule]) => {
|
||||
const end = schedule();
|
||||
Tone.Transport.start();
|
||||
const renderPromise = offlineContext.render();
|
||||
return renderPromise.then(recordingBuffer => {
|
||||
fnAttempts.concat(restoreFns).forEach(fn => fn());
|
||||
//fnAttempts.concat(restoreFns).forEach(fn => fn());
|
||||
end();
|
||||
Tone.Transport.stop();
|
||||
Tone.Transport.cancel();
|
||||
cleanup();
|
||||
Tone.context = originalContext;
|
||||
deactivate();
|
||||
Tone.setContext(originalContext);
|
||||
return recordingBuffer;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import Tone from 'tone';
|
||||
import * as Tone from 'tone';
|
||||
|
||||
const streamDestination = Tone.context.createMediaStreamDestination();
|
||||
|
||||
|
||||
5
src/utils/noop.js
Normal file
5
src/utils/noop.js
Normal file
@@ -0,0 +1,5 @@
|
||||
const noop = () => {
|
||||
/* do nothing */
|
||||
};
|
||||
|
||||
export default noop;
|
||||
@@ -9,7 +9,7 @@ const { EnvironmentPlugin } = require('webpack');
|
||||
|
||||
const makeConfig = alias => ({
|
||||
mode: 'development',
|
||||
devtool: 'sourcemap',
|
||||
devtool: 'source-map',
|
||||
entry: ['@babel/polyfill', './src'],
|
||||
output: {
|
||||
path: path.join(__dirname, 'dist'),
|
||||
|
||||
Reference in New Issue
Block a user