Incluir som na apresentação do beamer para vários slides

Incluir som na apresentação do beamer para vários slides

Quero incluir som em uma apresentação do beamer e quero que esse som comece em um determinado slide e dure várias sobreposições ou vários slides. Tentei conseguir isso usando media9ou multimedia(ambos deveriam, de acordo com a documentação, funcionar). Mas em ambos os casos (veja MWEs abaixo) o som começa quando deveria começar, mas para quando o item 2 é mostrado. Como posso fazer o som durar mais de um slide (para um dos pacotes media9ou multimedia, não me importo com qual)?

MWE para 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 para multimedia: (aqui também a autostartopção não funciona e tenho que iniciar o som manualmente e não há som algum sem a opção 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}

Se isso for importante: eu uso o Adobe Reader (a versão mais recente) no Windows 11.

Responder1

Até onde sei, nenhum dos visualizadores de PDF comumente usados ​​atualmente suporta a reprodução de som em vários slides. No entanto, é bastante simples criar nosso próprio visualizador que possa suportar isso.

Salve o seguinte em um arquivo chamado viewer.html(ou <anything>.htmlse preferir):

<!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>

Em seguida, compile o seguinte documento usando pdfLaTeX ou 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}

Por fim, abra viewer.htmlem um navegador web, clique em “Navegar” e abra o arquivo PDF que você compilou acima. Clicar em qualquer lugar fará com que a apresentação seja exibida em tela inteira, qualquer um de [ Left Click, , Page ↓, Space, Enter] avançará um slide e qualquer um de [ , Page ↑, Backspace] retrocederá um slide.

Como funciona

No lado do TeX, adicionamos um arquivo de som como um “anexo” de PDF e, em seguida, colocamos um comando play/stop dentro de uma “anotação” de PDF oculta.

No lado HTML, importamos o pdf.js para usar como base para o nosso visualizador, depois o configuramos para analisar as anotações em cada página e usá-las para decidir quando reproduzir/parar qualquer arquivo de áudio anexado.

O espectador émuitobásico e não muito robusto, mas geralmente deve ser suficiente para uma apresentação de slides básica. Adicionar novos recursos e torná-lo mais robusto fica como exercício para o leitor :)

Responder2

Esta não é uma resposta, mas minha resolução para o problema. Eu enfrentei essencialmente o problema descrito e depois de algumas tentativas concluí que atualmente não está dentro do escopo da visualização de PDF.

Portanto, mudei de apresentações em PDF baseadas em beamer para apresentações baseadas na web usando estruturas como revelado.js. Isso tem desvantagens, pois os mecanismos típicos de renderização de látex baseados em js oferecem apenas um subconjunto de látex, portanto, o tempo de produção de slides aumentou.

A principal desvantagem da nova abordagem é que ela exige mais mão-de-obra. Ou seja, se eu precisar de certas renderizações de látex não suportadas pelo KaTex ou renderizadores web semelhantes baseados em látex, primeiro renderizo o látex padrão em um arquivo PNG e incorporo como imagem no slide quando necessário. O grande ganho é que adicionar conteúdo de áudio e vídeo se tornou fácil, e adicionar conteúdo interativo (na forma de programas javascript) é muito viável, mas um trabalho personalizado. Meu trabalho geralmente inclui realizações algorítmicas, portanto, é bom poder mostrar o trabalho perfeitamente na apresentação (sem alt-tab, etc.). Reveal.js possui bibliotecas que permitem até mesmo marcação e esboço sobre conteúdo animado e interativo. Espero que a principal desvantagem desta abordagem seja superada com o tempo, deixando uma opção que oferece o melhor de todos os mundos.

informação relacionada