Add initial sample support

This commit is contained in:
Kevin Thomas
2021-08-25 04:05:50 -07:00
parent 880864ee5e
commit b9e340c26a
6 changed files with 390 additions and 17330 deletions

17388
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -69,9 +69,47 @@ router.post('/profiles', (req, res) => {
} }
}) })
}) })
return res.json({ id: profileID })
})
})
})
router.post('/profiles/default', (req, res) => {
if (!req.user) {
return res.sendStatus(401)
}
db.serialize(() => {
db.run(`INSERT INTO profiles (
name,
user,
timer_enabled,
duration,
volume,
noise_color,
filter_enabled,
filter_type,
filter_cutoff,
lfo_filter_cutoff_enabled,
lfo_filter_cutoff_frequency,
lfo_filter_cutoff_low,
lfo_filter_cutoff_high,
tremolo_enabled,
tremolo_frequency,
tremolo_depth)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
'Default', req.user.id, 0, 30, -10, 'pink', 0, 'lowpass', 20000,
0, 0.5, 100, 5000, 0, 0.5, 0.5
],
function (err) {
if (err) {
return res.sendStatus(500)
} else {
return res.json({ id: this.lastID })
}
}) })
}) })
return res.sendStatus(200)
}) })
router.get('/profiles', (req, res) => { router.get('/profiles', (req, res) => {

View File

@@ -18,9 +18,7 @@
<v-list <v-list
nav nav
> >
<v-list-item-group <v-list-item-group>
v-model="group"
>
<v-list-item <v-list-item
@click="home" @click="home"
> >
@@ -70,7 +68,6 @@ export default {
data: () => ({ data: () => ({
drawyer: false, drawyer: false,
group: null,
isAdmin: false isAdmin: false
}), }),
methods: { methods: {

View File

@@ -37,6 +37,7 @@
<v-btn <v-btn
class="mx-3 mb-5" class="mx-3 mb-5"
:disabled="!valid"
@click="login" @click="login"
> >
Login Login

View File

@@ -279,19 +279,14 @@
</h2> </h2>
<v-row <v-row
v-for="(sample, index) in samples" v-for="(sample, index) in loadedSamples"
:key="sample.name" :key="sample.name"
> >
<v-container> <v-container>
<v-row <v-row
justify="center" justify="center"
> >
<v-checkbox {{ sample.name }}
v-model="checkedSamples"
:value="sample.id"
:label="`${sample.name}`"
class="mx-3"
/>
</v-row> </v-row>
<v-row> <v-row>
@@ -307,14 +302,14 @@
<div <div
class="mx-3" class="mx-3"
> >
<p>{{ samples[index].volume }}</p> <p>{{ loadedSamples[index].volume }}</p>
</div> </div>
</v-row> </v-row>
</v-container> </v-container>
</v-row> </v-row>
<v-dialog <v-dialog
v-model="sampleDialog" v-model="addSampleDialog"
max-width="600px" max-width="600px"
> >
<template v-slot:activator="{ on, attrs }"> <template v-slot:activator="{ on, attrs }">
@@ -332,27 +327,24 @@
</v-card-title> </v-card-title>
<v-card-text> <v-card-text>
<v-container> <v-container>
<v-divider /> <v-row v-if="unloadedSamples.length != 0">
<v-row> <v-list-item
<v-file-input v-for="(sample) in unloadedSamples"
v-model="selectedSample" :key="sample.name"
accept="audio/*" >
label="Upload a sample!" <v-list-item-action>
/> <v-checkbox
v-model="checkedSamples"
:value="sample.id"
/>
</v-list-item-action>
<v-list-item-content>
<v-list-item-title>{{ sample.name }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-row> </v-row>
<v-row> <v-row v-else>
<v-col cols="12"> No samples to add
<p><strong>WARNING:</strong> Uploaded samples are publicly accessible.</p>
</v-col>
</v-row>
<v-row>
<v-col cols="12">
<v-text-field
v-model="sampleName"
label="Sample Name"
required
/>
</v-col>
</v-row> </v-row>
</v-container> </v-container>
</v-card-text> </v-card-text>
@@ -360,19 +352,86 @@
<v-spacer /> <v-spacer />
<v-btn <v-btn
text text
@click="sampleDialog = false" @click="addSampleDialog = false"
> >
Close Close
</v-btn> </v-btn>
<v-btn <v-btn
text text
@click="uploadSample" :disabled="unloadedSamples.length === 0"
@click="addSample"
> >
Upload Add
</v-btn> </v-btn>
</v-card-actions> </v-card-actions>
</v-card> </v-card>
</v-dialog> </v-dialog>
<v-dialog
v-model="uploadSampleDialog"
max-width="600px"
>
<template v-slot:activator="{ on, attrs }">
<v-btn
v-bind="attrs"
class="mx-3 mb-5"
v-on="on"
>
Upload Sample
</v-btn>
</template>
<v-form
v-model="isSampleUploadValid"
>
<v-card>
<v-card-title>
<span class="text-h5">Upload Sample</span>
</v-card-title>
<v-card-text>
<v-container>
<v-row>
<v-col cols="12">
<p><strong>WARNING:</strong> Uploaded samples are publicly accessible.</p>
</v-col>
</v-row>
<v-row>
<v-file-input
v-model="selectedSample"
accept="audio/*"
label="Upload a sample!"
:rules="[rules.required()]"
/>
</v-row>
<v-row>
<v-col cols="12">
<v-text-field
v-model="sampleName"
label="Sample Name"
:rules="[rules.required()]"
/>
</v-col>
</v-row>
</v-container>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn
text
@click="uploadSampleDialog = false"
>
Close
</v-btn>
<v-btn
text
:disabled="!isSampleUploadValid"
@click="uploadSample"
>
Upload
</v-btn>
</v-card-actions>
</v-card>
</v-form>
</v-dialog>
</v-col> </v-col>
<v-col cols="12"> <v-col cols="12">
@@ -393,6 +452,7 @@
<v-btn <v-btn
class="mx-3" class="mx-3"
:disabled="profileItems.length < 2"
@click="deleteProfile" @click="deleteProfile"
> >
Delete Profile Delete Profile
@@ -411,39 +471,44 @@
Save Profile Save Profile
</v-btn> </v-btn>
</template> </template>
<v-card> <v-form
<v-card-title> v-model="isProfileValid"
<span class="text-h5">Profile Name</span> >
</v-card-title> <v-card>
<v-card-text> <v-card-title>
<v-container> <span class="text-h5">Profile Name</span>
<v-row> </v-card-title>
<v-col cols="12"> <v-card-text>
<v-text-field <v-container>
v-model="profileName" <v-row>
label="Profile Name" <v-col cols="12">
required <v-text-field
/> v-model="profileName"
</v-col> label="Profile Name"
</v-row> :rules="[rules.required()]"
</v-container> />
</v-card-text> </v-col>
<v-card-actions> </v-row>
<v-spacer /> </v-container>
<v-btn </v-card-text>
text <v-card-actions>
@click="profileDialog = false" <v-spacer />
> <v-btn
Close text
</v-btn> @click="profileDialog = false"
<v-btn >
text Close
@click="saveProfile" </v-btn>
> <v-btn
Save text
</v-btn> :disabled="!isProfileValid"
</v-card-actions> @click="saveProfile"
</v-card> >
Save
</v-btn>
</v-card-actions>
</v-card>
</v-form>
</v-dialog> </v-dialog>
</v-col> </v-col>
</v-row> </v-row>

View File

@@ -9,6 +9,7 @@ export default {
profileItems: [], profileItems: [],
profileDialog: false, profileDialog: false,
profileName: '', profileName: '',
isProfileValid: false,
playDisabled: false, playDisabled: false,
isTimerEnabled: false, isTimerEnabled: false,
hours: 0, hours: 0,
@@ -31,21 +32,39 @@ export default {
isTremoloEnabled: false, isTremoloEnabled: false,
tremoloFrequency: 0.5, tremoloFrequency: 0.5,
tremoloDepth: 0.5, tremoloDepth: 0.5,
samples: [], allSamples: [],
checkedSamples: [], loadedSamples: [],
selectedSample: null, selectedSample: null,
sampleDialog: false, uploadSampleDialog: false,
addSampleDialog: false,
checkedSamples: [],
sampleName: '', sampleName: '',
file: null, file: null,
isSampleUploadValid: false,
rules: { rules: {
lt (n) { lt (n) {
return value => (!isNaN(parseInt(value, 10)) && value < n) || 'Must be less than ' + n return value => (!isNaN(parseInt(value, 10)) && value < n) || 'Must be less than ' + n
}, },
gt (n) { gt (n) {
return value => (!isNaN(parseInt(value, 10)) && value > n) || 'Must be greater than ' + n return value => (!isNaN(parseInt(value, 10)) && value > n) || 'Must be greater than ' + n
},
required () {
return value => !!value || 'Required'
} }
} }
}), }),
computed: {
unloadedSamples: function () {
const samples = []
this.allSamples.forEach(s1 => {
const result = this.loadedSamples.find(s2 => s2.id === s1.id)
if (!result) {
samples.push(s1)
}
})
return samples
}
},
created () { created () {
this.noise = new Noise() this.noise = new Noise()
this.filter = new Filter() this.filter = new Filter()
@@ -88,14 +107,14 @@ export default {
this.transportInterval = setInterval(() => this.stop(), this.duration * 1000 + 100) this.transportInterval = setInterval(() => this.stop(), this.duration * 1000 + 100)
this.timeRemainingInterval = setInterval(() => this.startTimer(), 1000) this.timeRemainingInterval = setInterval(() => this.startTimer(), 1000)
this.checkedSamples.forEach(s => { this.loadedSamples.forEach(s => {
this.players.player(s).unsync().sync().start(0).stop(this.duration) this.players.player(s.id).unsync().sync().start(0).stop(this.duration)
}) })
} else { } else {
this.noise.sync().start(0) this.noise.sync().start(0)
this.checkedSamples.forEach(s => { this.loadedSamples.forEach(s => {
this.players.player(s).unsync().sync().start(0) this.players.player(s.id).unsync().sync().start(0)
}) })
} }
@@ -166,7 +185,23 @@ export default {
this.$http.get('/profiles') this.$http.get('/profiles')
.then(response => { .then(response => {
if (response.status === 200) { if (response.status === 200) {
this.profileItems = response.data.profiles if (response.data.profiles.length === 0) {
this.addDefaultProfile()
} else {
this.profileItems = response.data.profiles
this.selectedProfile = this.profileItems[0]
}
}
})
.catch((error) => {
console.error(error.response)
})
},
addDefaultProfile () {
this.$http.post('/profiles/default')
.then(response => {
if (response.status === 200) {
this.selectedProfile = { id: response.data.id, text: 'Default' }
} }
}) })
.catch((error) => { .catch((error) => {
@@ -190,14 +225,28 @@ export default {
isTremoloEnabled: this.isTremoloEnabled, isTremoloEnabled: this.isTremoloEnabled,
tremoloFrequency: this.tremoloFrequency, tremoloFrequency: this.tremoloFrequency,
tremoloDepth: this.tremoloDepth, tremoloDepth: this.tremoloDepth,
samples: this.samples samples: this.loadedSamples
}).then(response => {
const id = response.data.id
if (response.status === 200) {
this.profileDialog = false
this.populateProfileItems()
this.$http.get('/profiles')
.then(response => {
if (response.status === 200) {
this.profileItems = response.data.profiles
this.selectedProfile = { id: id, text: this.profileName }
}
})
.catch((error) => {
console.error(error.response)
})
}
}) })
.catch((error) => { .catch((error) => {
console.error(error.response) console.error(error.response)
}) })
this.profileDialog = false
this.populateProfileItems()
}, },
loadProfile () { loadProfile () {
this.$http.get('/profiles/'.concat(this.selectedProfile.id)) this.$http.get('/profiles/'.concat(this.selectedProfile.id))
@@ -220,7 +269,7 @@ export default {
this.tremoloFrequency = profile.tremoloFrequency this.tremoloFrequency = profile.tremoloFrequency
this.tremoloDepth = profile.tremoloDepth this.tremoloDepth = profile.tremoloDepth
this.samples = profile.samples this.loadedSamples = profile.samples
} }
}) })
.catch((error) => { .catch((error) => {
@@ -242,8 +291,8 @@ export default {
this.$http.get('/samples') this.$http.get('/samples')
.then(response => { .then(response => {
if (response.status === 200) { if (response.status === 200) {
this.samples = response.data.samples this.allSamples = response.data.samples
this.samples.forEach(s => { this.allSamples.forEach(s => {
if (!this.players.has(s.id)) { if (!this.players.has(s.id)) {
this.players.add(s.id, '/samples/' + s.name).toDestination() this.players.add(s.id, '/samples/' + s.name).toDestination()
} }
@@ -274,10 +323,18 @@ export default {
console.error(error.response) console.error(error.response)
}) })
this.sampleDialog = false this.uploadSampleDialog = false
},
addSample () {
this.checkedSamples.forEach(i => {
const load = this.allSamples.find(e => e.id === i)
this.loadedSamples.push(load)
})
this.addSampleDialog = false
}, },
updateSampleVolume (id, index) { updateSampleVolume (id, index) {
this.players.player(id).volume.value = this.samples[index].volume this.players.player(id).volume.value = this.loadedSamples[index].volume
} }
} }
} }