0

I have long .wav files that I wish to split into separate files of exactly 60 minutes each, using ffmpeg. I am using ffmpeg from the terminal in Mac OS X. If a bash or other shell script is necessary to solve my issue, that is okay.

From this question, I use the following

ffmpeg -i input.wav -f segment -segment_time 3600 output_%03d.wav

However, each file has a file creation date and time for when I execute the split segment command above. I would like the files to have creation dates and times based on creation time of the original file. From this question, I know I can use ffprobe to get the creation information. For example,

ffprobe -v quiet input.wav -show_entries stream_tags:format_tags

might show

TAG:date=2025-09-18
TAG:creation_time=22:24:54

Is it possible to include this information in the newly created split files? The first file output_000.wav should have the same creation date and time as the original file. Subsequent files (e.g., output_001, output_002, etc) should have creation times exactly 60 minutes after the previous file. From the example above, output_000 should have a creation time of 22:24:54. output_001 should have a creation_time of 23:24:54, etc. The last file may not be 60 minutes long. For example, a file that is 130 minutes long would have two files of 60 minutes each and a final file of 10 minutes.

Note that the original files typically start at night and can go past midnight to the next morning, so that would need to be accounted for.

PS: I've seen this answer but that overlays text on a video file. I can't figure out how to adapt it to my question.

2
  • 1
    I'll be very honest here: you're still adding more complexity to produce redundant data when you should instead be modifying your consuming end to just seek in one large uninterrupted audio file to extract snippets. We long have left the region where "was that effort worth not having to touch the consuming end" was ambiguous! anyways, yes, you can manually set metadata using -metadata. It can't be copied from the input, because you want to modify it. doing this correctly in a shell environment is complex, do it in the consuming end instead. Commented Sep 30 at 11:33
  • @MarcusMüller: I appreciate your candor. I have a specific need for this, which is why I asked. Commented Sep 30 at 21:13

1 Answer 1

3

You'll have to capture the original date and then apply it to the files after the fact.

#!/bin/bash

original_time=$(exiftool input.wav | grep "File Access Date" | cut -d ':' -f2- | awk '{$1=$1;print}')

# Parse timestamp into touch format (drop timezone offset)
touch_timestamp=$(date -d "${original_time//:/ } +%Y%m%d%H%M.%S" +%Y%m%d%H%M.%S 2>/dev/null)

ffmpeg -i input.wav -f segment -segment_time 3600 output_%03d.wav

for segment in output*.wav; do
    if [[ -n "$touch_timestamp" && -f "$segment" ]]; then
        touch -a -t "$touch_timestamp" "$segment"
        echo "Set timestamp on $segment to $original_time"
    fi
done

That should be close. You might need to adjust the date command to include --utc if MacOS uses UTC.


Adjusting for the duration of the file is significantly more difficult.

#!/bin/bash

original_time=$(exiftool input.wav | grep "File Access Date" | cut -d ':' -f2- | awk '{$1=$1;print}')
base_touch_timestamp=$(date -d "${original_time//:/ } +%Y%m%d%H%M.%S" +%Y%m%d%H%M.%S 2>/dev/null)

ffmpeg -i input.wav -f segment -segment_time "$max_segment_time" output_%03d.wav

segments=($(ls -1v output_*.wav 2>/dev/null || true))

if [[ -n "$base_touch_timestamp" && ${#segments[@]} -gt 0 ]]; then
    running_offset=0
    
    for segment in "${segments[@]}"; do
        # Get actual duration of this segment in seconds
        duration=$(ffprobe -v quiet -show_entries format=duration -of csv=p=0 "$segment" 2>/dev/null | awk '{printf "%.0f", $1}')
        
        if [[ -n "$duration" && "$duration" -gt 0 ]]; then
            # Calculate adjusted timestamp: base + running_offset seconds
            adjusted_touch_timestamp=$(date -d "${original_time//:/ } +${running_offset}s" +%Y%m%d%H%M.%S 2>/dev/null)
            
            if [[ -n "$adjusted_touch_timestamp" ]]; then
                touch -a -m -t "$adjusted_touch_timestamp" "$segment"
                adjusted_readable=$(date -d "${original_time//:/ } +${running_offset}s" '+%Y-%m-%d %H:%M:%S')
                echo "Set timestamp on $segment to $adjusted_readable (cumulative offset: ${running_offset}s, this duration: ${duration}s)"
            fi
            # Add this segment's duration to running offset for next
            running_offset=$((running_offset + duration))
        fi
    done
fi

Hopefully this will do the trick!

1
  • Thank you for your time, truly, but this does not work as written. Osx does not have a -d switch for date to read from the original_time strong. However, I have the gist of what your script is doing so I might be able to figure out the proper switch to use for os x's version of date. Also, exiftool is apparently not part of os x's default installation; it was a non-issue to install it. Commented Sep 30 at 21:12

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.