비머 프리젠테이션에 사운드를 포함하고 해당 사운드가 특정 슬라이드에서 시작되어 여러 오버레이 또는 여러 슬라이드에 걸쳐 지속되기를 원합니다. 나는 media9
or multimedia
(문서에 따르면 둘 다 작동해야 함)을 사용하여 그것을 얻으려고 노력했습니다. 그러나 두 경우 모두(아래 MWE 참조) 사운드는 시작해야 할 때 시작되지만 항목 2가 표시되면 중지됩니다. 두 개 이상의 슬라이드에 대해 사운드가 지속되도록 하려면 어떻게 해야 합니까(패키지 중 하나 media9
또는 multimedia
어느 것인지는 상관하지 않음)?
다음 에 대한 MWE 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 for multimedia
: (여기에서도 autostart
옵션이 작동하지 않으며 수동으로 사운드를 시작해야 하며 옵션이 없으면 사운드가 전혀 들리지 않습니다 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}
이것이 중요한 경우: Windows 11에서 Adobe Reader(최신 버전)를 사용합니다.
답변1
내가 아는 한, 현재 일반적으로 사용되는 PDF 뷰어 중 어느 것도 여러 슬라이드에서 사운드 재생을 지원하지 않습니다. 그러나 이를 지원할 수 있는 자체 뷰어를 만드는 것은 매우 간단합니다.
다음을 이름이 지정된 파일 viewer.html
(또는 <anything>.html
원하는 경우)에 저장합니다.
<!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>
그런 다음 pdfLaTeX 또는 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}
마지막으로 웹 브라우저에서 열고 viewer.html
"찾아보기"를 클릭한 다음 위에서 컴파일한 PDF 파일을 엽니다. 아무 곳이나 클릭하면 프레젠테이션이 전체 화면으로 전환되고, [ Left Click, →, Page ↓, Space, Enter]는 한 슬라이드 앞으로 이동하고, [ ←, Page ↑, Backspace]는 한 슬라이드 뒤로 이동합니다.
작동 원리
TeX 측에서는 사운드 파일을 PDF "첨부 파일"로 추가한 다음 숨겨진 PDF "주석" 내부에 재생/중지 명령을 배치합니다.
HTML 측에서는 뷰어의 기반으로 사용할 pdf.js를 가져온 다음 각 페이지의 주석을 구문 분석하고 이를 사용하여 첨부된 오디오 파일을 재생/중지할 시기를 결정합니다.
시청자는매우기본이며 그다지 견고하지는 않지만 일반적으로 기본 슬라이드 쇼에는 충분합니다. 새로운 기능을 추가하고 더욱 강력하게 만드는 것은 독자의 과제로 남겨집니다. :)
답변2
이것은 답변이 아니라 문제에 대한 나의 해결책입니다. 나는 본질적으로 설명된 문제에 직면했고 꽤 많은 노력 끝에 현재 PDF 보기 범위 내에 있지 않다는 결론을 내렸습니다.
따라서 저는 비머 기반 PDF 프리젠테이션에서reveal.js와 같은 프레임워크를 사용하는 웹 기반 프리젠테이션으로 전환했습니다. 이는 일반적인 js 기반 라텍스 렌더링 엔진이 라텍스의 하위 집합만 제공하므로 슬라이드 제작 시간이 길어진다는 단점이 있습니다.
새로운 접근 방식의 가장 큰 단점은 노동 집약적이라는 것입니다. 즉, KaTex 또는 유사한 라텍스 기반 웹 렌더러에서 지원하지 않는 특정 라텍스 렌더링이 필요한 경우 먼저 표준 라텍스를 PNG 파일로 렌더링하고 필요한 경우 슬라이드에 이미지로 포함합니다. 가장 큰 이점은 오디오 콘텐츠와 비디오 콘텐츠를 추가하는 것이 쉬워지고 대화형 콘텐츠(자바스크립트 프로그램 형식)를 추가하는 것이 매우 가능하지만 맞춤형 작업이라는 것입니다. 내 작업에는 알고리즘 구현이 포함되는 경우가 많으므로 프레젠테이션 내에서 작업을 원활하게 표시할 수 있는 것이 좋습니다(Alt-Tab을 누르지 않는 등). Reveal.js에는 애니메이션 및 대화형 콘텐츠 위에 마크업 및 스케치를 허용하는 라이브러리도 있습니다. 나는 이 접근법의 주된 단점이 시간이 지나면서 극복되어 모든 세계에서 가장 좋은 것을 제공하는 옵션을 남기기를 바라고 있습니다.