Hello again.
It turned out to be more involved than I thought to define export settings programmatically. So, for now, here's a very simple version to export to wav file using default settings that QuickTime sees fit. In my brief experiments, I found limitations as follows:
a) It does not export to multi-channel wave file other than mono and stereo (2.0). If original sound track is of multi-channel > 2.0, audio channels will be mixed down to stereo 2.0.
b) If original sound track does not use lossless codec, such as AAC, AC3 etc, resulting LPCM sample sise (bit depth) will be 16-bit whereas sample rate (frequency) will (or may) be preserved as the original. If original sound track uses lossless codec, such as ALAC, resulting LPCM will preserve the original sample size and sample rate.
If you're fine with these limitations, the following script might help.
Tested with QuickTime 7.6.6 (1800) under 10.6.8.
Regards,
H
(*
Export each sound track of movie file to wav file
*)
on run
open (choose file with prompt ("Choose movie file(s)") with multiple selections allowed)
end run
on open aa
set outdir to (choose folder with prompt "Choose output folder")'s POSIX path
repeat with a in aa
set a's contents to a's POSIX path
end repeat
extract_sound_tracks(aa, outdir)
end open
on extract_sound_tracks(infiles, outdir) -- export as wav
(*
list infiles : list of POSIX path of input movie files
string outdir : POSIX path of output directory
*)
set args to ""
repeat with a in {outdir} & infiles
set args to args & a's quoted form & space
end repeat
do shell script "/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby <<'EOF' - " & args & "
require 'FileUtils'
require 'osx/cocoa'
OSX.require_framework 'QTKit'
include OSX
def log(logf, s)
File.open(logf, 'a') do |a|
a.puts '%-26s%s' % [Time.now.strftime('%F %T%z'), s]
end
end
def extract_sound_tracks(infile, outdir)
#
# string infile : POSIX path of input movie file
# string outdir : POSIX path of output directory where extracted sound files are saved
#
# * this method extracts each sound track of infile and saves it as wav file in outdir with name
# source file name & '_[' & sound track index & ']_' & sound track name & '.wav'.
#
# E.g., given source file name = a.mov, its 3rd sound track named 'Center' will be saved in
# outdir/a.mov_[3]_Center.wav
#
# * outdir is created if not present
#
outdir = File.readlink(outdir) if File.symlink?(outdir)
FileUtils.mkdir_p outdir unless File.exists?(outdir)
raise RuntimeError, %Q[#{outdir}: Not a directory.] unless File.directory?(outdir)
logf = %Q[#{outdir}/#{Time.now.strftime('%F_extract_sound_tracks_log.txt')}]
err = OCObject.new
mov = QTMovie.objc_send(
:movieWithAttributes, {
QTMovieFileNameAttribute => infile,
QTMovieOpenAsyncOKAttribute => NSNumber.numberWithBool(false),
},
:error, err)
unless mov
log(logf, '# Failed to load movie: %s' % [infile])
log(logf, err ? err.to_s : 'err = nil')
return
end
mov_name = mov.attributeForKey(QTMovieDisplayNameAttribute)
mov.tracksOfMediaType(QTMediaTypeSound).each_with_index do |trk, i|
trk_id = trk.attributeForKey(QTTrackIDAttribute)
trk_name = trk.attributeForKey(QTTrackDisplayNameAttribute)
trk_range = trk.attributeForKey(QTTrackRangeAttribute)
mov1 = QTMovie.movie # new movie
mov1.objc_send(:setAttribute, NSNumber.numberWithBool(true), :forKey, QTMovieEditableAttribute)
mov1.objc_send(
:insertSegmentOfTrack, trk,
:fromRange, trk_range.QTTimeRangeValue,
:scaledToRange, trk_range.QTTimeRangeValue)
outfile = outdir + '/' + ('%s_[%d]_%s.wav' % [mov_name, i + 1, trk_name])
err = OCObject.new
r = mov1.objc_send(
:writeToFile, outfile,
:withAttributes, {
QTMovieExport => NSNumber.numberWithBool(true),
QTMovieExportType => NSNumber.numberWithLong(KQTFileTypeWave),
},
:error, err)
if r
log(logf, 'Extracted track %d (%s) of %s => %s' % [trk_id, trk_name, infile, outfile])
else
log(logf, '# Failed to save track %d (%s) of %s in %s' % [trk_id, trk_name, infile, outfile])
log(logf, err ? err.to_s : 'err = nil')
end
end
end
raise ArgumentError, %Q[Usage: #{File.basename($0)} outdir infile [infile ...]] unless ARGV.length > 1
outdir = ARGV.shift
ARGV.each { |f| extract_sound_tracks(f, outdir) }
EOF"
end extract_sound_tracks