Ton in Beamer-Präsentation für mehrere Folien einbinden

Ton in Beamer-Präsentation für mehrere Folien einbinden

Ich möchte Ton in eine Beamer-Präsentation einbinden und ich möchte, dass dieser Ton bei einer bestimmten Folie beginnt und über mehrere Overlays oder mehrere Folien anhält. Ich habe versucht, dies mit media9oder zu erreichen multimedia(beides sollte laut Dokumentation funktionieren). Aber in beiden Fällen (siehe MWEs unten) beginnt der Ton, wenn er beginnen soll, aber er stoppt, wenn Punkt 2 angezeigt wird. Wie kann ich dafür sorgen, dass der Ton über mehr als eine Folie anhält (für eines der Pakete media9oder multimedia, welches ist mir egal)?

MWE für media9:

\documentclass{beamer}
\usepackage{media9}
\begin{document}
\begin{frame}{1}
1
\end{frame}

\begin{frame}{2}
\includemedia[label=my_sound, width=1ex, height=1ex, transparent, activate=pageopen, deactivate=onclick, addresource=./Sounds/MITM.mp3,
flashvars={
source=./Sounds/MITM.mp3
&autoPlay=true
},]{}{APlayer.swf}
\begin{itemize}
\item<2-> 2
\item<3-> 3
\end{itemize}
\end{frame}

\begin{frame}{3}
3
\end{frame}

\begin{frame}{4}
4
\end{frame}
\end{document}

MWE für multimedia: (auch hier autostartfunktioniert die Option nicht und ich muss den Ton manuell starten und ohne die Option kommt überhaupt kein Ton inlinesound)

\documentclass{beamer}
\usepackage{multimedia}
\begin{document}
\begin{frame}{1}
1
\end{frame}

\begin{frame}{2}
\sound[autostart, inlinesound, bitspersample=16, channels=2, encoding=Signed, samplingrate=48000]{A}{./Sounds/MITM.au}
\begin{itemize}
\item<2-> 2
\item<3-> 3
\end{itemize}
\end{frame}

\begin{frame}{3}
3
\end{frame}

\begin{frame}{4}
4
\end{frame}
\end{document}

Falls das wichtig ist: Ich verwende Adobe Reader (die neuste Version) unter Windows 11.

Antwort1

Soweit mir bekannt ist, unterstützt derzeit keiner der gängigen PDF-Viewer die Wiedergabe von Ton über mehrere Folien hinweg. Es ist jedoch ziemlich einfach, einen eigenen Viewer zu erstellen, der dies unterstützt.

Speichern Sie Folgendes in einer Datei mit dem Namen viewer.html(oder, <anything>.htmlwenn Sie das bevorzugen):

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="
            width=device-width,
            initial-scale=1,
            maximum-scale=1
        ">
        <style>
            /* Make the viewer take up the full screen */
            * {
                margin: 0;
                padding: 0;
                border: 0;
                outline: 0;
            }

            #viewer-container {
                overflow: clip;
                position: absolute;
                width: 100%;
                height: 100%;
            }

            /* Center the page */
            .page {
                margin: 0 auto 0;
            }
        </style>

        <script type="module">
            import "https://cdnjs.cloudflare.com/ajax/libs/pdf.js/4.1.392/pdf.mjs"
            import "https://cdnjs.cloudflare.com/ajax/libs/pdf.js/4.1.392/pdf_viewer.mjs"
            pdfjsLib.GlobalWorkerOptions.workerSrc =
            "https://cdnjs.cloudflare.com/ajax/libs/pdf.js/4.1.392/pdf.worker.mjs"

            const bus = new pdfjsViewer.EventBus()
            const container = document.getElementById("viewer-container")

            const viewer = new pdfjsViewer.PDFSinglePageViewer({
                container,
                eventBus: bus,
                textLayerMode: 0,
                annotationMode: 0,
            })

            bus.on("pagechanging", () => {
                viewer.currentScaleValue = "page-fit"
            })

            const file_element = document.getElementById("file")

            async function open(event) {
                let file
                if (event instanceof window.DragEvent) {
                    event.dataTransfer.dropEffect = "copy"
                    file = event.dataTransfer.files[0]
                } else if (event.target instanceof window.HTMLInputElement) {
                    file = event.target.files[0]
                }
                file_element.remove()

                const doc = await pdfjsLib.getDocument(
                    await file.arrayBuffer()
                ).promise

                viewer.setDocument(doc)

                const attatchments = await doc.getAttachments()

                let audio

                async function sound() {
                    viewer.currentScaleValue = "page-fit"

                    const page = await doc.getPage(viewer.currentPageNumber)
                    const annotations = await page.getAnnotations()

                    for (const annotation of annotations) {
                        const directive = annotation?.contentsObj?.str
                        if (!directive) continue

                        const [cmd, file] = directive.split(" ")

                        if (["play", "stop"].includes(cmd) && audio) {
                            audio.pause()
                            audio = null
                        }

                        if (cmd === "play") {
                            const blob = new Blob([attatchments[file].content])
                            audio = new Audio(URL.createObjectURL(blob))
                            audio.play()
                        }
                    }
                }

                function move(direction) {
                    if (!document.fullscreenElement) {
                        document.documentElement.requestFullscreen()
                    } else {
                        if (direction === "next") {
                            viewer.nextPage()
                        } else if (direction === "previous") {
                            viewer.previousPage()
                        }
                    }
                    sound()
                }

                document.addEventListener("keydown", (event) => {
                    switch (event.key) {
                        case "ArrowRight":
                        case "ArrowDown":
                        case " ":
                        case "PageDown":
                        case "Enter":
                            event.preventDefault()
                            move("next")
                            break
                        case "ArrowLeft":
                        case "ArrowUp":
                        case "PageUp":
                        case "Backspace":
                            event.preventDefault()
                            move("previous")
                            break
                    }
                })

                document.addEventListener("click", (event) => {
                    event.preventDefault()
                    switch (event.button) {
                        case 0:
                            event.preventDefault()
                            move("next")
                            break
                        case 2:
                            event.preventDefault()
                            move("previous")
                            break
                    }
                })
            }

            file_element.addEventListener("change", open)
            document.addEventListener("drop", open)
            document.addEventListener("dragover", event => event.preventDefault())
        </script>
    </head>
    <body>
        <input type="file" id="file" accept="application/pdf">
        <div id="viewer-container">
            <div id="viewer"></div>
        </div>
    </body>
</html>

Kompilieren Sie dann das folgende Dokument entweder mit pdfLaTeX oder LuaLaTeX:

\documentclass{beamer}


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Load required packages %%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Needed to embed the audio files
\usepackage{embedfile}

%% Needed for \pdfannot to work with LuaTeX
\ifdefined\directlua
    \usepackage{luatex85}
\fi

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Variable Declarations %%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\ExplSyntaxOn
    \seq_new:N \g__example_files_seq %% Store all used files
    \tl_new:N \l__example_lastfile_tl %% The most recent file loaded
    \tl_new:N \g__example_currentfile_tl %% The currently playing file


%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Command Definitions %%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%

    %% Embeds a file if it isn't already embedded, and stores its attachment
    %% name in "\l__example_lastfile_tl".
    \cs_new_protected:Nn \__example_add_file:n {
        \tl_set:Ne \l__example_lastfile_tl { \str_mdfive_hash:n { #1 } }

        \seq_if_in:NVF \g__example_files_seq \l__example_lastfile_tl {
            \seq_gput_right:NV \g__example_files_seq \l__example_lastfile_tl
            \embedfile [ filespec = \l__example_lastfile_tl ] { #1 }
        }
    }

    %% Writes to the PDF the given command and parameter.
    \cs_new_protected:Nn \__example_pdfcmd:nn {
        \pdfannot width 0pt~ height 0pt~ depth 0pt~ {%
            /Subtype~ /Text
            /Contents~ (#1~ #2)
            /F~ 1
        }
    }
    \cs_generate_variant:Nn \__example_pdfcmd:nn { nV }

    %% Plays the given audio file.
    \cs_new_protected:Nn \__example_play:n {
        \__example_add_file:n { #1 }
        \tl_if_eq:NNF \g__example_currentfile_tl \l__example_lastfile_tl {
            \tl_gset:NV \g__example_currentfile_tl \l__example_lastfile_tl
            \__example_pdfcmd:nV { play } \l__example_lastfile_tl
        }
    }

    %% Stops the currently playing audio file.
    \cs_new_protected:Nn \__example_stop: {
        \__example_pdfcmd:nn { stop } { }
        \tl_gclear:N \g__example_currentfile_tl
    }

    %% Expose the commands publicly
    \cs_set_eq:NN \playaudio \__example_play:n
    \cs_set_eq:NN \stopaudio \__example_stop:
\ExplSyntaxOff


%%%%%%%%%%%%%%%%%%%%%
%%% Demonstration %%%
%%%%%%%%%%%%%%%%%%%%%

%% There are no audio files in $TEXMFDIST, so we need to lookup the
%% (system-dependent) path to a sample audio file.
\ExplSyntaxOn
    \sys_get_shell:nnN { kpsewhich~ --format=source~ bird.mp3 } { } \l_tmpa_tl
    \iow_term:e {
        \iow_newline:
        ``bird.mp3''~ path:~ \tl_to_str:N \l_tmpa_tl
        \iow_newline:
    }
\ExplSyntaxOff

\begin{document}
    \begin{frame}{Title One}
        Body One
    \end{frame}

    \begin{frame}{Title Two}
        %% Replace this path with the path output to the terminal earlier
        \playaudio{/usr/local/texlive/2024/texmf-dist/source/latex/media9/files/bird.mp3}

        Body Two A
        \begin{itemize}
            \item<2-> Body Two B
            \item<3-> Body Two C
        \end{itemize}
    \end{frame}

    \begin{frame}{Title Three}
        \stopaudio
        Body Three
    \end{frame}

    \begin{frame}{Title Four}
        Body Four
    \end{frame}
\end{document}

Öffnen Sie abschließend viewer.htmlin einem Webbrowser die PDF-Datei, klicken Sie auf „Durchsuchen“ und öffnen Sie sie. Wenn Sie irgendwo klicken, wird die Präsentation im Vollbildmodus angezeigt. Mit [ Left Click, , Page ↓, Space, Enter] blättern Sie eine Folie weiter und mit [ , Page ↑, Backspace] blättern Sie eine Folie zurück.

Wie es funktioniert

Auf der TeX-Seite fügen wir eine Sounddatei als PDF-„Anhang“ hinzu und platzieren dann einen Wiedergabe-/Stopp-Befehl in einer versteckten PDF-„Anmerkung“.

Auf der HTML-Seite importieren wir pdf.js, um es als Basis für unseren Viewer zu verwenden. Anschließend konfigurieren wir es so, dass die Anmerkungen auf jeder Seite analysiert werden und anhand dieser entschieden wird, wann angehängte Audiodateien abgespielt/gestoppt werden sollen.

Der Betrachter istsehrist spartanisch und nicht besonders robust, sollte aber im Allgemeinen für eine einfache Diashow ausreichen. Das Hinzufügen neuer Funktionen und die Verbesserung der Robustheit bleibt dem Leser überlassen :)

Antwort2

Dies ist keine Antwort, sondern meine Lösung für das Problem. Ich hatte im Wesentlichen das beschriebene Problem und bin nach einigem Probieren zu dem Schluss gekommen, dass es derzeit nicht wirklich mit der PDF-Anzeige zusammenhängt.

Aus diesem Grund bin ich von Beamer-basierten PDF-Präsentationen auf webbasierte Präsentationen mit Frameworks wie reveal.js umgestiegen. Dies hat den Nachteil, dass typische JS-basierte Latex-Rendering-Engines nur eine Teilmenge von Latex bieten, sodass die Produktionszeit für Folien gestiegen ist.

Der Hauptnachteil des neuen Ansatzes ist, dass er arbeitsintensiver ist. Wenn ich also bestimmte Latex-Renderings benötige, die von KaTex oder ähnlichen Latex-basierten Web-Renderern nicht unterstützt werden, render ich zunächst Standard-Latex in eine PNG-Datei und füge es bei Bedarf als Bild in die Folie ein. Der große Vorteil besteht darin, dass das Hinzufügen von Audio- und Videoinhalten einfach geworden ist und das Hinzufügen von interaktiven Inhalten (in Form von JavaScript-Programmen) sehr machbar, aber maßgeschneidert ist. Meine Arbeit umfasst häufig algorithmische Realisierungen, daher ist es schön, die Arbeit nahtlos in der Präsentation zeigen zu können (kein Alt-Tab-Ausblenden usw.). Reveal.js verfügt über Bibliotheken, die sogar Markup und Skizzen über animierten und interaktiven Inhalten ermöglichen. Ich hoffe, dass der Hauptnachteil dieses Ansatzes mit der Zeit überwunden wird und eine Option übrig bleibt, die das Beste aus allen Welten bietet.

verwandte Informationen