Skip to content

Commit

Permalink
Merge pull request kitodo#5803 from markusweigelt/audio-waveform
Browse files Browse the repository at this point in the history
Visualisation of audio as a waveform
  • Loading branch information
solth authored Nov 3, 2023
2 parents dccde1a + a221260 commit 23235ae
Show file tree
Hide file tree
Showing 18 changed files with 323 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -128,19 +128,24 @@ public class Project extends BaseIndexedBean implements Comparable<Project> {
private Folder preview;

/**
* Folder with media to use for the video preview.
* Folder with media to use for the audio preview.
*/
@ManyToOne
@JoinColumn(name = "preview_audio_folder_id", foreignKey = @ForeignKey(name = "FK_project_preview_audio_folder_id"))
private Folder audioPreview;

/**
* Folder with media to use for the video viewer.
* Folder with media to use for the audio viewer.
*/
@ManyToOne
@JoinColumn(name = "mediaView_audio_folder_id", foreignKey = @ForeignKey(name = "FK_project_mediaView_audio_folder_id"))
private Folder audioMediaView;

/**
* Field to define the status of the audio media view waveform.
*/
@Column(name = "mediaView_audio_waveform")
private Boolean audioMediaViewWaveform = false;

/**
* Folder with media to use for the video preview.
Expand Down Expand Up @@ -553,6 +558,24 @@ public void setAudioMediaView(Folder audioMediaView) {
this.audioMediaView = audioMediaView;
}

/**
* Get the status of the audio media view waveform.
*
* @return True if is active
*/
public boolean isAudioMediaViewWaveform() {
return audioMediaViewWaveform;
}

/**
* Set the status of the audio media view waveform.
*
* @param audioMediaViewWaveform True if is active
*/
public void setAudioMediaViewWaveform(boolean audioMediaViewWaveform) {
this.audioMediaViewWaveform = audioMediaViewWaveform;
}

/**
* Returns the folder to use for video preview.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
--
-- (c) Kitodo. Key to digital objects e. V. <[email protected]>
--
-- This file is part of the Kitodo project.
--
-- It is licensed under GNU General Public License version 3 or later.
--
-- For the full copyright and license information, please read the
-- GPL3-License.txt file that was distributed with this source code.
--

--
-- Migration: Add column for state of audio waveform in media view to project table.
ALTER TABLE project ADD mediaView_audio_waveform TINYINT(1) NOT NULL DEFAULT 0;
19 changes: 19 additions & 0 deletions Kitodo/src/main/java/org/kitodo/production/forms/ProjectForm.java
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,25 @@ public void setAudioMediaView(String audioMediaView) {
project.setAudioMediaView(getFolderMap().get(audioMediaView));
}

/**
* Returns the state of the audio media view waveform.
*
* @return True if enabled
*/
public boolean isAudioMediaViewWaveform() {
return project.isAudioMediaViewWaveform();
}

/**
* Sets the state of the audio media view waveform.
*
* @param audioMediaViewWaveform True if enabled
*
*/
public void setAudioMediaViewWaveform(boolean audioMediaViewWaveform) {
project.setAudioMediaViewWaveform(audioMediaViewWaveform);
}

/**
* Returns the folder to use for the video media view.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,13 @@ public void onPageDrop() {
}
}

/**
* Check if audio media view waveform is activated in project.
*/
public boolean isAudioMediaViewWaveform() {
return dataEditor.getProcess().getProject().isAudioMediaViewWaveform();
}

private boolean dragStripeIndexMatches(String dragId) {
Matcher dragStripeImageMatcher = DRAG_STRIPE_IMAGE.matcher(dragId);
Matcher dragUnstructuredMediaMatcher = DRAG_UNSTRUCTURED_MEDIA.matcher(dragId);
Expand Down Expand Up @@ -829,7 +836,8 @@ private void selectMedia(String physicalDivisionOrder, String stripeIndex, Strin

String scrollScripts = "scrollToSelectedTreeNode();scrollToSelectedPaginationRow();";
if (GalleryViewMode.PREVIEW.equals(galleryViewMode)) {
PrimeFaces.current().executeScript("checkScrollPosition();initializeImage();" + scrollScripts);
PrimeFaces.current().executeScript(
"checkScrollPosition();initializeImage();metadataEditor.gallery.mediaView.update();" + scrollScripts);
} else {
PrimeFaces.current().executeScript(scrollScripts);
}
Expand Down
7 changes: 7 additions & 0 deletions Kitodo/src/main/resources/messages/messages_de.properties
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@
# GPL3-License.txt file that was distributed with this source code.
#
AND=Enth\u00E4lt
audioWaveformToolsCenteredCursor=zentrierter Cursor
audioWaveformToolsJumpForwardFiveSeconds=5 Sekunden vorspringen
audioWaveformToolsJumpForwardOneSecond=1 Sekunde vorspringen
audioWaveformToolsJumpBackFiveSeconds=5 Sekunden zur\u00FCckspringen
audioWaveformToolsJumpBackOneSecond=1 Sekunde zur\u00FCckspringen
audioWaveformToolsZoom=Zoom
authorities=Berechtigungen
authority=Berechtigung
DMSExportByThread=Der Vorgang wird vom Taskmanager ins DMS exportiert\:
Expand Down Expand Up @@ -464,6 +470,7 @@ folderUse.audioPreview=Als Vorschau f\u00fcr Audios verwenden
folderUse.audioPreview.disabled=Vorschau f\u00fcr Audios deaktiviert
folderUse.audioMediaView=F\u00FCr die Medienansicht f\u00fcr Audios verwenden
folderUse.audioMediaView.disabled=Medienansicht f\u00fcr Audios deaktiviert
folderUse.audioMediaViewWaveform=Wellenform in der Medienansicht f\u00fcr Audios anzeigen
folderUse.generatorSource=Als Quelle zum Generieren von Inhalten verwenden
folderUse.generatorSource.disabled=Generieren von Inhalten deaktiviert
folderUse.mediaView=F\u00FCr die Medienansicht verwenden
Expand Down
7 changes: 7 additions & 0 deletions Kitodo/src/main/resources/messages/messages_en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@
# GPL3-License.txt file that was distributed with this source code.
#
AND=Contains
audioWaveformToolsCenteredCursor=Centered cursor
audioWaveformToolsJumpForwardFiveSeconds=Jump forward 5 seconds
audioWaveformToolsJumpForwardOneSecond=Jump forward 1 second
audioWaveformToolsJumpBackFiveSeconds=Jump back 5 seconds
audioWaveformToolsJumpBackOneSecond=Jump back 1 second
audioWaveformToolsZoom=Zoom
authorities=Authorities
authority=Authority
DMSExportByThread=The process is being exported into the DMS by the task manager\:
Expand Down Expand Up @@ -465,6 +471,7 @@ folderUse.audioPreview=Use as preview for audios
folderUse.audioPreview.disabled=Preview for audios disabled
folderUse.audioMediaView=Use for the media view for audios
folderUse.audioMediaView.disabled=Media view for audios disabled
folderUse.audioMediaViewWaveform=Show waveform in media view for audios
folderUse.generatorSource=Use as source to generate contents
folderUse.generatorSource.disabled=Content generation disabled
folderUse.mediaView=Use for the media view
Expand Down
32 changes: 31 additions & 1 deletion Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css
Original file line number Diff line number Diff line change
Expand Up @@ -3061,15 +3061,35 @@ Column content

#imagePreviewForm\:mediaDetail {
display: flex;
width: 100%;
width: calc(100% - 100px);
align-items: center;
justify-content: center;
flex-direction: column;
gap: 24px;
}

#imagePreviewForm\:mediaDetail .mediaPreviewItem {
max-height: 100%;
}

#imagePreviewForm\:mediaDetail #audioWaveformTools{
display: flex;
gap: 30px;
justify-content: center;
align-items: center;
padding: 14px 16px;
background: var(--trans-blue);
border-radius: 3px;
}

#imagePreviewForm\:mediaDetail #audioWaveformTools > div > button:first-child {
margin-left: 0;
}

#imagePreviewForm\:mediaDetail #audioWaveformTools > div > button {
margin-left: 10px;
}

#imagePreviewForm .mediaListIconItem {
height: 100%;
width: 100%;
Expand Down Expand Up @@ -3944,6 +3964,16 @@ footer {
max-width: 300px;
}

.loader {
width: 30px;
height: 30px;
color: white;
display: flex;
justify-content: center;
align-items: center;
padding: 10px;
}

/*----------------------------------------------------------------------
min 700px
----------------------------------------------------------------------*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
BSD 3-Clause License

Copyright (c) 2012-2023, katspaugh and contributors
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/**
* (c) Kitodo. Key to digital objects e. V. <[email protected]>
*
* This file is part of the Kitodo project.
*
* It is licensed under GNU General Public License version 3 or later.
*
* For the full copyright and license information, please read the
* GPL3-License.txt file that was distributed with this source code.
*/

import WaveSurfer from './libs/wavesurfer/wavesurfer.esm.js.jsf';

class AudioWaveform {
#audioElement;
#wavesurfer;
#buildTimeout;
#loader;
#peaksCache = [];

constructor() {
this.init();
}

init() {
let self = this;
this.#audioElement = document.querySelector('audio.mediaPreviewItem');
if (this.#audioElement && this.#audioElement.getAttribute("data-audio-waveform") !== "initialized") {
this.#audioElement.setAttribute("data-audio-waveform", "initialized");

// add a loader to visualize loading process
this.#loader = document.createElement("div");
this.#loader.innerHTML = '<i class="fa fa-spinner fa-spin"/>';
this.#loader.classList.add('loader');
this.#audioElement.parentNode.insertBefore(this.#loader, this.#audioElement);

// when the user agent can play the media
this.#audioElement && this.#audioElement.addEventListener("canplay", () => {
// Prevent browser crashes during audio decoding when multiple rapid clicks, such as double-clicking, occur.
clearTimeout(this.#buildTimeout);
self.#buildTimeout = setTimeout(function() {
self.#build();
}, 500);
}, {once: true});

}
}

#build() {
let self = this;
// wavesurfer uses the 'src' attribute of the audio element, and we add this attribute based on the browser's current source selection
this.#audioElement.src = this.#audioElement.currentSrc;

// get the media id from the source parameter
const urlParams = new URLSearchParams(this.#audioElement.src);
let mediaId = urlParams.get('mediaId');

let waveContainer = document.createElement("div");
waveContainer.setAttribute("id", "wave-container");
waveContainer.onclick = function () {
self.#wavesurfer.playPause();
};
waveContainer.style.width = "90%";
waveContainer.style.display = "none";
this.#audioElement.parentNode.insertBefore(waveContainer, this.#audioElement);

this.#wavesurfer = WaveSurfer.create({
container: document.getElementById(waveContainer.getAttribute("id")),
height: 100,
waveColor: "#f3f3f3",
progressColor: "#ff4e00",
cursorColor: "#ffffff",
media: this.#audioElement,
minPxPerSec: 0,
peaks: this.#peaksCache[mediaId]
});

this.#wavesurfer.on("decode", function () {
// cache peaks after when audio has been decoded
self.#peaksCache[mediaId] = self.#wavesurfer.getDecodedData().getChannelData(0);
});

this.#wavesurfer.on("ready", function () {
waveContainer.style.display = "block";
self.#loader.style.display = "none";

let waveToolsContainer = document.getElementById("audioWaveformTools");
const waveToolsSlider = waveToolsContainer.querySelector('input[type="range"]');

waveToolsSlider.addEventListener('input', (e) => {
const minPxPerSec = e.target.valueAsNumber;
self.#wavesurfer.zoom(minPxPerSec);
});

waveToolsContainer.querySelectorAll('input[type="checkbox"]').forEach((input) => {
input.onchange = (e) => {
self.#wavesurfer.setOptions({
[input.value]: e.target.checked,
});
}
});
const jumpButtons = document.getElementsByClassName("audio-waveform-tools-jump-button");
Array.from(jumpButtons).forEach(function (jumpButton) {
jumpButton.addEventListener('click', function () {
let jumpSeconds = parseInt(this.getAttribute("data-audio-waveform-tools-jump-seconds"));
self.#wavesurfer.setTime(self.#wavesurfer.getCurrentTime() + jumpSeconds);
});
});
});
}

}

const audioWaveform= new AudioWaveform();

document.addEventListener("kitodo-metadataditor-mediaview-update", function () {
audioWaveform.init();
});
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,13 @@ metadataEditor.gallery = {
}
},

mediaView: {
updateEventName : "kitodo-metadataditor-mediaview-update",
update(){
document.dispatchEvent(new Event(this.updateEventName));
}
},

/**
* Event handlers methods related to gallery stripes
*/
Expand Down
3 changes: 2 additions & 1 deletion Kitodo/src/main/webapp/WEB-INF/resources/js/resize.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* For the full copyright and license information, please read the
* GPL3-License.txt file that was distributed with this source code.
*/
/* globals PF */
/* globals PF, metadataEditor */
// jshint unused:false

var SEPARATOR_WIDTH = 3;
Expand Down Expand Up @@ -523,6 +523,7 @@ function updateMetadataEditorView(showMetadataColumn) {
expandThirdColumn();
scrollToSelectedThumbnail();
initializeImage();
metadataEditor.gallery.mediaView.update();
scrollToSelectedTreeNode();
scrollToSelectedPaginationRow();
}
Expand Down
Loading

0 comments on commit 23235ae

Please sign in to comment.