Add an audio file to a slide in a presentation

Audio in PresentationML is stored as media data plus slide relationships and XML markup that binds the media to a shape. The visible slide usually contains a picture or shape; the media relationship supplies the actual audio file.

Package shape

A slide that references audio can contain markup like this:

<p:pic>
  <p:nvPicPr>
    <p:cNvPr id="7" name="audio">
      <a:hlinkClick r:id="" action="ppaction://media"/>
    </p:cNvPr>
    <p:nvPr>
      <a:audioFile r:link="rAudio1"/>
    </p:nvPr>
  </p:nvPicPr>
</p:pic>

The r:link value points to a relationship on the slide part. The package also needs a media data part, a media reference relationship, and usually an image or shape used as the clickable placeholder.

audioFile is stored under the non-visual properties of the picture or shape. The object appears on the slide like normal drawing content, but actual playback is controlled from the slide timing tree. The drawing object's non-visual ID is what ties the visible placeholder to the media timing node.

The complete shape normally includes:

  • p:cNvPr, including an a:hlinkClick action of ppaction://media,
  • p:cNvPicPr, often with picture locks,
  • p:nvPr with a:audioFile,
  • a media relationship for the audio data,
  • an image relationship and a:blip when a picture is used as the placeholder,
  • shape properties such as a:off, a:stretch, and a:fillRect.

Rust workflow

Use ooxmlsdk to open the presentation and find the target slide. The package-side media operation is tested here: create a media data part, write the audio bytes, and add slide-level audio and media reference relationships.

#![allow(unused)]
fn main() {
pub fn add_audio_media_references(
  path: &Path,
  audio_bytes: &[u8],
) -> Result<(Vec<u8>, String, String), Box<dyn std::error::Error>> {
  let mut document = PresentationDocument::new_from_file_with_settings(path, lazy_settings())?;
  let presentation_part = document.presentation_part()?;
  let Some(slide_part) = presentation_part.slide_parts(&document).next() else {
    return Err("presentation has no slide parts".into());
  };

  let media_part = document.create_media_data_part_by_type(MediaDataPartType::Wav)?;
  media_part.set_data(&mut document, audio_bytes.to_vec())?;
  let audio_relationship_id =
    slide_part.add_audio_reference_relationship(&mut document, &media_part)?;
  let media_relationship_id =
    slide_part.add_media_reference_relationship(&mut document, &media_part)?;

  let mut buffer = Cursor::new(Vec::new());
  document.save(&mut buffer)?;
  Ok((
    buffer.into_inner(),
    audio_relationship_id,
    media_relationship_id,
  ))
}
}

The listing covers the package-side media relationships. A complete PowerPoint-compatible writer must verify all of these together:

  • media data part content type and extension,
  • slide audio and media reference relationships,
  • placeholder shape or picture XML,
  • timing markup for playback,
  • package save and PowerPoint compatibility.

Use this chapter as the package map for implementing and testing audio insertion: keep relationship creation, placeholder XML, and timing XML in one save operation so the slide remains internally consistent.