diff --git a/jam-ui/package-lock.json b/jam-ui/package-lock.json
index 0df8cb0e2..62407236b 100644
--- a/jam-ui/package-lock.json
+++ b/jam-ui/package-lock.json
@@ -3313,6 +3313,21 @@
}
}
},
+ "@fingerprintjs/fingerprintjs": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/@fingerprintjs/fingerprintjs/-/fingerprintjs-4.4.3.tgz",
+ "integrity": "sha512-sm0ZmDp5Oeq8hQTf+bAHKsuuteVAYme/YOY9UPP/GrUBrR5Fzl1P5oOv6F5LvyBrO7qLjU5HQkfU0MmFte/8xA==",
+ "requires": {
+ "tslib": "^2.4.1"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "2.6.3",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz",
+ "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ=="
+ }
+ }
+ },
"@fortawesome/fontawesome-common-types": {
"version": "0.2.35",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.35.tgz",
diff --git a/jam-ui/package.json b/jam-ui/package.json
index cfa16b7d9..352b6c904 100644
--- a/jam-ui/package.json
+++ b/jam-ui/package.json
@@ -4,6 +4,7 @@
"private": true,
"dependencies": {
"@farfetch/react-context-responsive": "^1.5.0",
+ "@fingerprintjs/fingerprintjs": "^4.4.3",
"@fortawesome/fontawesome-free": "^5.15.1",
"@fortawesome/fontawesome-svg-core": "^1.2.30",
"@fortawesome/free-brands-svg-icons": "^5.14.0",
diff --git a/jam-ui/src/components/jamtracks/JKCreateCustomMix.js b/jam-ui/src/components/jamtracks/JKCreateCustomMix.js
new file mode 100644
index 000000000..c67565a38
--- /dev/null
+++ b/jam-ui/src/components/jamtracks/JKCreateCustomMix.js
@@ -0,0 +1,252 @@
+import React, { useState, useEffect } from 'react';
+import { Table, Row, Col, Input, Button } from 'reactstrap';
+import JKInstrumentIcon from '../profile/JKInstrumentIcon';
+import Select from 'react-select';
+import { useForm, Controller } from 'react-hook-form';
+import { createMixdown } from '../../helpers/rest';
+
+const JKCreateCustomMix = ({ jamTrack }) => {
+ const [tracks, setTracks] = useState([]);
+ //const [selectedTracks, setSelectedTracks] = useState([]);
+
+ const TEMPO_OPTIONS = [
+ { value: '0', label: 'Original tempo' },
+ { value: '-5', label: 'Slower by 5%' },
+ { value: '-10', label: 'Slower by 10%' },
+ { value: '-15', label: 'Slower by 15%' },
+ { value: '-20', label: 'Slower by 20%' },
+ { value: '-25', label: 'Slower by 25%' },
+ { value: '-30', label: 'Slower by 30%' },
+ { value: '-35', label: 'Slower by 35%' },
+ { value: '-40', label: 'Slower by 40%' },
+ { value: '-45', label: 'Slower by 45%' },
+ { value: '-50', label: 'Slower by 50%' },
+ { value: '-60', label: 'Slower by 60%' },
+ { value: '-70', label: 'Slower by 70%' },
+ { value: '-80', label: 'Slower by 80%' },
+ { value: '5', label: 'Faster by 5%' },
+ { value: '10', label: 'Faster by 10%' },
+ { value: '15', label: 'Faster by 15%' },
+ { value: '20', label: 'Faster by 20%' },
+ { value: '30', label: 'Faster by 30%' },
+ { value: '40', label: 'Faster by 40%' },
+ { value: '50', label: 'Faster by 50%' }
+ ];
+
+ const PITCH_OPTIONS = [
+ { value: '0', label: 'Original pitch' },
+ { value: '-1', label: 'Down 1 semitone' },
+ { value: '-2', label: 'Down 2 semitone' },
+ { value: '-3', label: 'Down 3 semitone' },
+ { value: '-4', label: 'Down 4 semitone' },
+ { value: '-5', label: 'Down 5 semitone' },
+ { value: '-6', label: 'Down 6 semitone' },
+ { value: '-7', label: 'Down 7 semitone' },
+ { value: '-8', label: 'Down 8 semitone' },
+ { value: '-9', label: 'Down 9 semitone' },
+ { value: '-10', label: 'Down 10 semitone' },
+ { value: '-11', label: 'Down 11 semitone' },
+ { value: '-12', label: 'Down 12 semitone' },
+ { value: '1', label: 'Up 1 semitone' },
+ { value: '2', label: 'Up 2 semitone' },
+ { value: '3', label: 'Up 3 semitone' },
+ { value: '4', label: 'Up 4 semitone' },
+ { value: '5', label: 'Up 5 semitone' },
+ { value: '6', label: 'Up 6 semitone' },
+ { value: '7', label: 'Up 7 semitone' },
+ { value: '8', label: 'Up 8 semitone' },
+ { value: '9', label: 'Up 9 semitone' },
+ { value: '10', label: 'Up 10 semitone' },
+ { value: '11', label: 'Up 11 semitone' },
+ { value: '12', label: 'Up 12 semitone' }
+ ];
+
+ const {
+ control,
+ handleSubmit,
+ formState: { errors },
+ setValue,
+ getValues
+ } = useForm({
+ defaultValues: {
+ mixName: '',
+ tempo: {
+ value: '0',
+ label: 'Original tempo'
+ },
+ pitch: {
+ value: '0',
+ label: 'Original pitch'
+ },
+ mixdownTracks: []
+ }
+ });
+ const onSubmit = data => {
+ console.log('data', data);
+ const _tracks = [];
+ let countIn = false;
+ const selectedTracks = getValues('mixdownTracks');
+ tracks.forEach(track => {
+ const muted = selectedTracks.includes(track.id);
+ if (track.id === 'count-in') {
+ if (countIn === false) {
+ countIn = !muted;
+ }
+ } else {
+ _tracks.push({
+ id: track.id,
+ mute: selectedTracks.includes(track.id)
+ });
+ }
+ });
+
+ setValue('mixdownTracks', _tracks);
+
+ const mixData = {
+ jamTrackID: jamTrack.id,
+ name: data.mixName,
+ settings: { speed: data.tempo.value, pitch: data.pitch.value, 'count-in': countIn, tracks: _tracks }
+ };
+
+ console.log('mixData', mixData);
+
+ createMixdown(mixData)
+ .then(response => {
+ console.log('mixdown created', response);
+ //TODO: add this mixdown to global state of jamtrack mixdowns
+ })
+ .catch(error => {
+ console.error('mixdown create error', error);
+ });
+ };
+
+ const toggleTrack = e => {
+ const trackId = e.target.value;
+ const selectedTracks = getValues('mixdownTracks');
+ if (selectedTracks.includes(trackId)) {
+ //setSelectedTracks(selectedTracks.filter(track => track !== trackId));
+ setValue('mixdownTracks', selectedTracks.filter(track => track !== trackId));
+ } else {
+ //setSelectedTracks([...selectedTracks, trackId]);
+ setValue('mixdownTracks', [...selectedTracks, trackId]);
+ }
+ };
+
+ useEffect(() => {
+ if (jamTrack) {
+ setTracks(jamTrack.tracks.filter(track => track.track_type === 'Track' || track.track_type === 'Click'));
+ }
+ }, [jamTrack]);
+
+ const trackName = track => {
+ if (track.track_type === 'Track' || track.track_type === 'Click') {
+ if (track.track_type === 'Click') {
+ return 'Clicktrack';
+ } else if (track.instrument) {
+ const instrumentDescription = track.instrument.description;
+ let part = '';
+ if (track.part && track.part !== instrumentDescription) {
+ part = `(${track.part})`;
+ }
+ return `${instrumentDescription} ${part}`;
+ }
+ }
+ };
+
+ return (
+ <>
+
+ Mute any tracks you like. Adjust the pitch or tempo of playback. Then give your mix a descriptive name, and
+ click the Create Mix button. It will take few minutes for us to create your custom mix.
+
+
+
+ >
+ );
+};
+
+export default JKCreateCustomMix;
diff --git a/jam-ui/src/components/jamtracks/JKJamTrack.js b/jam-ui/src/components/jamtracks/JKJamTrack.js
index e0d001dda..6acd83bdb 100644
--- a/jam-ui/src/components/jamtracks/JKJamTrack.js
+++ b/jam-ui/src/components/jamtracks/JKJamTrack.js
@@ -6,6 +6,8 @@ import { Card, CardBody, Row, Col, Progress } from 'reactstrap';
import FalconCardHeader from '../common/FalconCardHeader';
import { getJamTrack, getUserDetail, postUserEvent, userOpenedJamTrackWebPlayer } from '../../helpers/rest';
import JKJamTrackPlayer from './JKJamTrackPlayer';
+import JKMyJamTrackMixes from './JKMyJamTrackMixes';
+import JKCreateCustomMix from './JKCreateCustomMix';
import { useAuth } from '../../context/UserAuth';
const JKJamTrack = () => {
@@ -23,6 +25,7 @@ const JKJamTrack = () => {
setLoading(true);
const resp = await getJamTrack({ id });
const data = await resp.json();
+ console.log('jam track 123', data);
setJamTrack(data);
} catch (error) {
console.log('Error when fetching jam track', error);
@@ -58,26 +61,24 @@ const JKJamTrack = () => {
fetchJamTrack();
}, [id]);
-
-
return (
-
- {jamTrack && }
-
+ {jamTrack && }
-
+ {jamTrack && }
-
+
+ { jamTrack && }
+
diff --git a/jam-ui/src/components/jamtracks/JKJamTrackPlayer copy 2.js b/jam-ui/src/components/jamtracks/JKJamTrackPlayer copy 2.js
new file mode 100644
index 000000000..6843d5f66
--- /dev/null
+++ b/jam-ui/src/components/jamtracks/JKJamTrackPlayer copy 2.js
@@ -0,0 +1,101 @@
+import React, { useState, useEffect, useMemo } from 'react';
+import Select from 'react-select';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Row, Col } from 'reactstrap';
+import PropTypes from 'prop-types';
+import { markMixdownActive } from '../../helpers/rest';
+import useJamTrackAudio from '../../hooks/useJamTrackAudio';
+
+const JKJamTrackPlayer = ({ jamTrack }) => {
+ const [mixes, setMixes] = useState([]);
+ const [options, setOptions] = useState([]);
+ const [selectedMix, setSelectedMix] = useState(null);
+ const { audioUrls, loadJamTrack } = useJamTrackAudio(jamTrack);
+
+ const handleChange = selectedOption => {
+ const mix = mixes.find(mix => mix.value === selectedOption.value);
+ setSelectedMix(mix);
+ };
+
+ useEffect(() => {
+ if (jamTrack) {
+ console.log('_JamTrack_ jamTrack', jamTrack);
+ const _opts = jamTrack.mixdowns.map(mix => ({ value: mix.id, label: mix.name }));
+ _opts.unshift({ value: 'original', label: 'Original' });
+ setOptions(_opts);
+
+ //set the default mix to the original
+ const activeMix = jamTrack.mixdowns.find(mix => mix.id === jamTrack.last_mixdown_id)
+
+ console.log('_JamTrack_ activeMix', activeMix);
+
+ setSelectedMix(activeMix);
+ }
+ }, [jamTrack]);
+
+ const activateMasterTrack = async () => {
+ console.log('playing original');
+ await markMixdownActive({ id: jamTrack.id, mixdown_id: null });
+ await loadJamTrack();
+ };
+
+ const activateCustomMix = async () => {
+ console.log('playing mix', selectedMix.value);
+ try {
+ await markMixdownActive({ id: jamTrack.id, mixdown_id: selectedMix.value });
+ await loadJamTrack();
+ }catch(error){
+ console.log('Error when activating custom mix', error);
+ }
+
+ };
+
+ useEffect(() => {
+ if (!selectedMix) {
+ return;
+ }
+
+ console.log('_JamTrack_ selectedMix', selectedMix);
+
+ if (selectedMix.value === 'original') {
+ //console.log('_JAMTRACK_ activating master track');
+ activateMasterTrack().then(() => {
+ //TODO: commiunicate with the client back end. Following is copied from the Rails front end
+ //SessionActions.mixdownActive({id:null})
+ });
+ } else {
+ //console.log('_JAMTRACK_ activating custom mix:', selectedMix);
+ activateCustomMix().then(() => {
+ //TODO: commiunicate with the client back end. Following is copied from the Rails front end
+ //context.jamClient.JamTrackStopPlay();
+ //SessionActions.mixdownActive(mixdown)
+ });
+ }
+ }, [selectedMix]);
+
+ return (
+ <>
+
+ { JSON.stringify(audioUrls)}
+
+
+ {audioUrls.length > 0 && (
+
+
+ {audioUrls.map((url, index) => (
+
+ ))}
+
+
+ )}
+
+
+ >
+ );
+};
+
+JKJamTrackPlayer.propTypes = {
+ jamTrack: PropTypes.object.isRequired
+};
+
+export default JKJamTrackPlayer;
diff --git a/jam-ui/src/components/jamtracks/JKJamTrackPlayer.js b/jam-ui/src/components/jamtracks/JKJamTrackPlayer.js
index c10d2cc90..aeef561bb 100644
--- a/jam-ui/src/components/jamtracks/JKJamTrackPlayer.js
+++ b/jam-ui/src/components/jamtracks/JKJamTrackPlayer.js
@@ -1,73 +1,87 @@
-import React, { useState, useEffect, useMemo } from 'react';
+import React, { useState, useEffect, useRef } from 'react';
import Select from 'react-select';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Row, Col } from 'reactstrap';
import PropTypes from 'prop-types';
import { markMixdownActive } from '../../helpers/rest';
-import useJamTrackAudio from '../../hooks/useJamTrackAudio';
+import FingerprintJS from '@fingerprintjs/fingerprintjs';
const JKJamTrackPlayer = ({ jamTrack }) => {
- const [mixes, setMixes] = useState([]);
- const [selectedMix, setSelectedMix] = useState(null);
- const { audioUrls } = useJamTrackAudio(jamTrack);
-
- const handleChange = selectedOption => {
- setSelectedMix(selectedOption);
- };
+ const [options, setOptions] = useState([]);
+ const [selectedOption, setSelectedOption] = useState(null);
+ const fpPromise = FingerprintJS.load();
+ const [audioUrl, setAudioUrl] = useState(null);
+ const audioRef = useRef(null);
useEffect(() => {
if (jamTrack) {
- const mixes = jamTrack.mixdowns.map(mix => ({ value: mix.id, label: mix.name, mix }));
- mixes.unshift({ value: 'original', label: 'Original', jamTrack });
- setMixes(mixes);
-
- //set the default mix to the original
- setSelectedMix(mixes[0]);
+ console.log('_JamTrackPlayer_ jamTrack', jamTrack);
+ const opts = jamTrack.mixdowns.map(mix => ({ value: mix.id, label: mix.name }));
+ opts.unshift({ value: 'original', label: 'Original' });
+ setOptions(opts);
+ if (jamTrack.last_mixdown_id) {
+ setSelectedOption(opts.find(opt => opt.value === jamTrack.last_mixdown_id));
+ } else {
+ setSelectedOption(opts[0]);
+ }
}
}, [jamTrack]);
- const activateMasterTrack = async () => {
- console.log('playing original');
- await markMixdownActive({ id: selectedMix.jamTrack.id, mixdown_id: null });
- };
-
- const activateCustomMix = async () => {
- console.log('playing mix', selectedMix.value);
- await markMixdownActive({ id: selectedMix.jamTrack.id, mixdown_id: selectedMix.value });
+ const handleOnChange = selectedOption => {
+ const option = options.find(opt => opt.value === selectedOption.value);
+ setSelectedOption(option);
};
useEffect(() => {
- if (!selectedMix) {
+ if (!selectedOption) {
return;
}
-
- if (selectedMix.value === 'original') {
- console.log('activating master track');
- activateMasterTrack().then(() => {
- //TODO: commiunicate with the client back end. Following is copied from the Rails front end
- //SessionActions.mixdownActive({id:null})
- });
+
+ console.log('_JamTrackPlayer_ selectedOption', selectedOption);
+
+ if (selectedOption.value === 'original') {
+ const audioUrl = getMasterTrack();
+ setAudioUrl(audioUrl);
+ if(audioRef.current)
+ audioRef.current.load();
} else {
- console.log('activating custom mix:', selectedMix.value);
- activateCustomMix().then(() => {
- //TODO: commiunicate with the client back end. Following is copied from the Rails front end
- //context.jamClient.JamTrackStopPlay();
- //SessionActions.mixdownActive(mixdown)
+ //it's a mixdown
+ getMixdown().then(audioUrl => {
+ setAudioUrl(audioUrl);
+ if(audioRef.current)
+ audioRef.current.load();
});
}
- }, [selectedMix]);
+ }, [selectedOption]);
+
+ const getMasterTrack = () => {
+ const masterTrack = jamTrack.tracks.find(track => track.track_type === 'Master');
+ console.log('_JamTrackPlayer_ master', masterTrack);
+ if (masterTrack) {
+ const audioUrl = masterTrack.preview_mp3_url;
+ return audioUrl;
+ }
+ };
+
+ const getMixdown = async () => {
+ const activeMix = jamTrack.mixdowns.find(mix => mix.id === selectedOption.value);
+ const fp = await fpPromise;
+ const result = await fp.get();
+ const audioUrl =
+ process.env.REACT_APP_API_BASE_URL +
+ `/mixdowns/${activeMix.id}/download.mp3?file_type=mp3&sample_rate=48&mark=${result.visitorId}`;
+ return audioUrl;
+ };
return (
<>
-
-
+
+
- {audioUrls.length > 0 && (
+ {audioUrl && (
-
- {audioUrls.map((url, index) => (
-
- ))}
+
+
)}
diff --git a/jam-ui/src/components/jamtracks/JKMyJamTrackMixes.js b/jam-ui/src/components/jamtracks/JKMyJamTrackMixes.js
new file mode 100644
index 000000000..f4387f12d
--- /dev/null
+++ b/jam-ui/src/components/jamtracks/JKMyJamTrackMixes.js
@@ -0,0 +1,78 @@
+import React, { useState, useEffect } from 'react';
+import { Table } from 'reactstrap';
+import FingerprintJS from '@fingerprintjs/fingerprintjs';
+
+const JKMyJamTrackMixes = ({ jamTrack }) => {
+ const [mixes, setMixes] = useState([]);
+ const fpPromise = FingerprintJS.load();
+
+ useEffect(() => {
+ if(!jamTrack) {
+ return;
+ }
+ setMixes(jamTrack.mixdowns)
+ }, []);
+
+ const downloadJamTrack = async () => {
+ console.log('Downloading JamTrack');
+ if(!jamTrack.can_download) {
+ console.log('Cannot download JamTrack');
+ return
+ }
+ const fp = await fpPromise;
+ const result = await fp.get();
+ const redirectTo = `${process.env.REACT_APP_API_BASE_URL}/jamtracks/${jamTrack.id}/stems/master/download.mp3?file_type=mp3&download=1&mark=${result.visitorId}`;
+ window.open(redirectTo, '_blank');
+ }
+
+ const downloadMix = async (mixId) => {
+ console.log('Download mixdown')
+ const mixdown = mixes.find(m => m.id === mixId);
+ const mixdownPackage = mixdown.packages.find(p => p.file_type === 'mp3');
+ if(mixdownPackage?.signing_state == 'SIGNED'){
+ const fp = await fpPromise;
+ const result = await fp.get();
+ const redirectTo = `${process.env.REACT_APP_API_BASE_URL}/mixdowns/${mixdown.id}/download.mp3?file_type=mp3&sample_rate=48&download=1&mark=${result.visitorId}`
+ window.open(redirectTo, '_blank');
+ }
+ }
+
+ const deleteMix = (mixId) => {
+ if(window.confirm("Delete this custom mix?")){
+ console.log("Deleting mixdown", mixId)
+ }
+ }
+
+ return (
+ <>
+ You can save a maximum of 5 mixes on JamKazam. If you need to make more mixes, download a mix to save it, then delete it to make more room
+
+
+
+ Mix
+ Actions
+
+
+
+
+ Full JamTrack
+
+ Download
+
+
+ {mixes.map(mix => (
+
+ {mix.name}
+
+ downloadMix(mix.id)}>Download
+ deleteMix(mix.id)}>Delete
+
+
+ ))}
+
+
+ >
+ );
+};
+
+export default JKMyJamTrackMixes;
diff --git a/jam-ui/src/helpers/rest.js b/jam-ui/src/helpers/rest.js
index 456882eaa..e12f09ef0 100644
--- a/jam-ui/src/helpers/rest.js
+++ b/jam-ui/src/helpers/rest.js
@@ -512,3 +512,14 @@ export const markMixdownActive = options => {
.catch(error => reject(error));
});
}
+
+export const createMixdown = options => {
+ return new Promise((resolve, reject) => {
+ apiFetch(`/mixdowns`, {
+ method: 'POST',
+ body: JSON.stringify(options)
+ })
+ .then(response => resolve(response))
+ .catch(error => reject(error));
+ });
+}
\ No newline at end of file
diff --git a/jam-ui/src/hooks/useJamTrackAudio.js b/jam-ui/src/hooks/useJamTrackAudio.js
index 36d16017b..5c45c4f87 100644
--- a/jam-ui/src/hooks/useJamTrackAudio.js
+++ b/jam-ui/src/hooks/useJamTrackAudio.js
@@ -1,33 +1,63 @@
import { useState, useEffect } from 'react';
+import { getJamTrack } from '../helpers/rest';
+import FingerprintJS from '@fingerprintjs/fingerprintjs';
-const useJamTrackAudio = (jamTrack) => {
+const useJamTrackAudio = jamTrack => {
const [audioUrls, setAudioUrls] = useState([]);
+ const [jamTrackRecord, setJamTrackRecord] = useState(jamTrack);
+ const fpPromise = FingerprintJS.load();
- const loadMedia = () => {
- const activeMixdown = jamTrack.mixdowns.find(mixdown => mixdown.id === jamTrack.last_mixdown_id)
- const activeStem = jamTrack.tracks.find(stem => stem.id === jamTrack.last_stem_id);
+ const loadJamTrack = async () => {
+ //console.log('_JAMTRACK_ loading jam track');
+ try {
+ const resp = await getJamTrack({ id: jamTrack.id });
+ const data = await resp.json();
+ setJamTrackRecord(data);
+ } catch (error) {
+ console.log('Error when fetching jam track', error);
+ }
+ };
- if ( activeStem ) {
- } else if ( activeMixdown ) {
-
+ const updateMedia = async () => {
+ //console.log('_JAMTRACK_ updating media', jamTrackRecord);
+ const activeMixdown = jamTrackRecord.mixdowns.find(mixdown => mixdown.id === jamTrackRecord.last_mixdown_id);
+ const activeStem = jamTrackRecord.tracks.find(stem => stem.id === jamTrackRecord.last_stem_id);
+
+
+ if (activeStem) {
+ //console.log('_JAMTRACK_ this is a stem', activeStem);
+ } else if (activeMixdown) {
+ //console.log('_JAMTRACK_ this is a mixdown', activeMixdown);
+ const fp = await fpPromise;
+ const result = await fp.get();
+ const audioUrl =
+ process.env.REACT_APP_API_BASE_URL +
+ `/mixdowns/${activeMixdown.id}/download.mp3?file_type=mp3&sample_rate=48&mark=${result.visitorId}`;
+ console.log('mixdown audioUrl', audioUrl);
+ setAudioUrls([audioUrl]);
} else if (jamTrack) {
const masterTrack = jamTrack.tracks.find(track => track.track_type === 'Master');
+ //console.log('_JAMTRACK_ this is the master track', masterTrack);
if (masterTrack) {
setAudioUrls([masterTrack.preview_mp3_url]);
}
}
+ };
- }
+ // useEffect(() => {
+ // if (!jamTrack) {
+ // return;
+ // }
+ // loadJamTrack();
+ // }, [jamTrack]);
useEffect(() => {
- if (!jamTrack) {
- return;
+ if (jamTrackRecord) {
+ updateMedia();
}
- loadMedia();
-
- }, [jamTrack]);
+ }, [jamTrackRecord]);
- return { audioUrls };
+ return { audioUrls, loadJamTrack };
};
-export default useJamTrackAudio;
\ No newline at end of file
+export default useJamTrackAudio;