Skip to content

Commit

Permalink
Add browse sound #244
Browse files Browse the repository at this point in the history
  • Loading branch information
jlpereira committed Jan 24, 2025
1 parent 4472b51 commit e379c05
Show file tree
Hide file tree
Showing 8 changed files with 229 additions and 123 deletions.
46 changes: 44 additions & 2 deletions app/javascript/vue/components/audio/AudioPlayer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
</template>

<script setup>
import { onMounted, useTemplateRef } from 'vue'
import { onMounted, useTemplateRef, onBeforeUnmount } from 'vue'
import WaveSurfer from 'wavesurfer.js'
import Spectrogram from 'wavesurfer.js/dist/plugins/spectrogram.esm.js'
const props = defineProps({
waveColor: {
Expand All @@ -25,10 +26,30 @@ const props = defineProps({
width: {
type: String,
default: '100%'
},
spectrogram: {
type: Boolean,
default: false
},
timeline: {
type: Boolean,
default: false
},
mediaControls: {
type: Boolean,
default: false
},
sampleRate: {
type: Number,
default: 44100
}
})
const emit = defineEmits(['play', 'pause', 'stop'])
const emit = defineEmits(['load', 'ready', 'finish', 'play', 'pause', 'stop'])
const audioPlayerRef = useTemplateRef('player')
let audioPlayer
Expand All @@ -39,9 +60,30 @@ onMounted(() => {
...props
})
if (props.spectrogram) {
audioPlayer.registerPlugin(
Spectrogram.create({
labels: true,
height: 200,
scale: 'mel',
frequencyMax: 8000,
frequencyMin: 0,
fftSamples: 1024,
labelsBackground: 'rgba(0, 0, 0, 0.1)'
})
)
}
audioPlayer.on('play', () => emit('play'))
audioPlayer.on('stop', () => emit('stop'))
audioPlayer.on('pause', () => emit('pause'))
audioPlayer.on('load', (url) => emit('load', url))
audioPlayer.on('finish', () => emit('finish'))
audioPlayer.on('ready', (duration) => emit('ready', duration))
})
onBeforeUnmount(() => {
audioPlayer.destroy()
})
function play() {
Expand Down
47 changes: 43 additions & 4 deletions app/javascript/vue/tasks/sounds/browse/App.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,46 @@
<template>
<div id="app-container">
<h1>Browse sound</h1>
<div id="app">
<div class="flex-separate middle">
<h1>Browse sound</h1>
<VAutocomplete
v-if="store.sound"
url="/sounds/autocomplete"
param="term"
label="label_html"
placeholder="Search a sound..."
@get-item="(item) => store.loadSound(item.id)"
/>
</div>
<div class="app-container gap-medium">
<HeaderBar
:sound="store.sound"
@select="store.loadSound"
/>
<template v-if="store.sound">
<PanelSound :sound="store.sound" />
<PanelConveyances :conveyances="store.conveyances" />
<PanelAnnotations
:object-id="store.sound.id"
:object-type="store.sound.base_class"
/>
</template>
</div>
</div>
</template>

<script setup>
import useStore from './store/store.js'
import { ref, onBeforeMount } from 'vue'
import { onBeforeMount } from 'vue'
import PanelSound from './components/Panel/PanelSound.vue'
import PanelConveyances from './components/Panel/PanelConveyances.vue'
import PanelAnnotations from './components/Panel/PanelAnnotations.vue'
import { URLParamsToJSON } from '@/helpers'
import HeaderBar from './components/HeaderBar.vue'
import VAutocomplete from '@/components/ui/Autocomplete.vue'
defineOptions({
name: 'BrowseSound'
})
const store = useStore()
onBeforeMount(() => {
Expand All @@ -17,12 +49,19 @@ onBeforeMount(() => {
if (soundId) {
store.loadSound(soundId)
store.loadConveyances(soundId)
}
})
</script>

<style scoped>
#app-container {
#app {
margin: 0 auto;
width: 1240px;
}
.app-container {
display: grid;
grid-column: 1fr 2fr 1fr;
}
</style>
39 changes: 21 additions & 18 deletions app/javascript/vue/tasks/sounds/browse/components/HeaderBar.vue
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
<template>
<NavBar>
<span
v-if="sound"
v-html="sound.object_tag"
/>
<VAutocomplete
v-else
url="/sounds/autocomplete"
param="term"
label="label_html"
@get-item="(item) => emit('select', item.id)"
/>
<ul
v-if="sound"
class="context-menu"
>
<li><RadialAnnotator :global-id="sound.global_id" /></li>
</ul>
<NavBar class="no-margin">
<div class="flex-separate middle">
<span
v-if="sound"
v-html="sound.object_tag"
/>
<VAutocomplete
v-else
url="/sounds/autocomplete"
param="term"
label="label_html"
placeholder="Search a sound..."
@get-item="(item) => emit('select', item.id)"
/>
<ul
v-if="sound"
class="context-menu"
>
<li><RadialAnnotator :global-id="sound.global_id" /></li>
</ul>
</div>
</NavBar>
</template>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,37 @@
<template>
<div class="panel content">
<h3>Annotations</h3>
<div
v-if="hasAnnotations || isLoading"
class="flex-col gap-small"
>
<template
v-for="(list, key) in annotations"
:key="key"
>
<div v-if="list.length">
<h4 class="capitalize">{{ ANNOTATIONS[key].title }}</h4>
<ul>
<li
v-for="item in list"
:key="item.id"
v-html="item[ANNOTATIONS[key].label]"
/>
</ul>
</div>
</template>
</div>
<p
v-else
class=""
>
No annotations found
</p>
</div>
</template>

<script setup>
import { ref, watch } from 'vue'
import { computed, ref, watch } from 'vue'
import {
Citation,
Tag,
Expand All @@ -14,7 +40,7 @@ import {
DataAttribute,
Identifier,
Confidence,
Verifier
Role
} from '@/routes/endpoints'
const props = defineProps({
Expand All @@ -29,79 +55,92 @@ const props = defineProps({
}
})
const ANNOTATIONS = [
{
prefix: 'citation',
title: 'citations',
const ANNOTATIONS = {
Citation: {
label: 'object_tag',
service: Citation
prefix: 'citation',
service: Citation,
title: 'citations'
},
{
prefix: 'tag',
title: 'Tags',
Tag: {
label: 'object_tag',
service: Tag
prefix: 'tag',
service: Tag,
title: 'tags'
},
{
prefix: 'note',
title: 'Notes',
Note: {
label: 'text',
service: Note
prefix: 'note',
service: Note,
title: 'notes'
},
{
prefix: 'identifier',
title: 'identifiers',
Identifier: {
label: 'object_tag',
service: Identifier
prefix: 'identifier',
service: Identifier,
title: 'Identifiers'
},
{
prefix: 'confidence',
title: 'Confidences',
Confidence: {
label: 'object_tag',
service: Confidence
prefix: 'confidence',
service: Confidence,
title: 'Confidences'
},
{
prefix: 'verifier',
title: 'Verifiers',
Verifier: {
label: 'object_tag',
service: Verifier
prefix: 'role',
service: Role,
title: 'Verifiers'
},
{
prefix: 'data_attribute',
title: 'Data attributes',
DataAttribute: {
label: 'object_tag',
service: DataAttribute
idParam: 'attribute_subject_id',
typeParam: 'attribute_subject_type',
service: DataAttribute,
title: 'Data attributes'
},
{
prefix: 'attribution',
title: 'Attribution',
Attribution: {
label: 'object_tag',
service: Attribution
prefix: 'attribution',
service: Attribution,
title: 'Attribution'
}
]
}
const annotations = ref({})
const isLoading = ref(false)
const hasAnnotations = computed(() =>
Object.values(annotations.value).some((list) => list.length)
)
function loadAnnotations() {
ANNOTATIONS.forEach(({ prefix, service }) => {
service
.where({
[`${prefix}_object_id`]: props.objectId,
[`${prefix}_object_type`]: props.objectType
})
.then(({ body }) => {
annotations[prefix] = body
})
.catch(() => {})
isLoading.value = true
const requests = Object.entries(ANNOTATIONS).map(
([key, { prefix, service, typeParam, idParam }]) =>
service
.where({
[idParam || `${prefix}_object_id`]: props.objectId,
[typeParam || `${prefix}_object_type`]: props.objectType
})
.then(({ body }) => {
annotations.value[key] = body
})
.catch(() => {})
)
Promise.all(requests).then(() => {
isLoading.value = false
})
}
watch(
() => props.objectId,
(newVal) => {
if (newVal) {
loadAnnotations
loadAnnotations(newVal)
}
}
},
{ immediate: true }
)
</script>
Loading

0 comments on commit e379c05

Please sign in to comment.