-
All replies
-
Helpful answers
-
Apr 7, 2014 6:41 PM in response to speedyrazorby Hiroto,Hello
I took me a while to get the knack of QTKit. The AppleScript script listed below, which is indeed a simple wrapper of rubycocoa script, will extract sound tracks of specified files silently without using QuickTime Player 7.
It will let you choose movie file(s) and output directory and then extract each sound track from chosen files and save it as movie file in the specified directory. Currently it does NOT export sound track as wav file, for I'm yet to understand how to define export setting in QTKit. If you're familiar with python, you'd be able to do the same thing using pyobjc.
Please see comments in script for the current naming convention of extracted sound tracks. A log file is created in the output directory.
I'm going to try to revise the code so that it can export sound track as wav file of desired specifications.
Hope this may help somehow,
H
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) (* 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 mov file in outdir with name # source file name & '_[' & sound track index & ']_' & sound track name & '.mov'. # # E.g., given source file name = a.mov, its 3rd sound track named 'Center' will be saved in # outdir/a.mov_[3]_Center.mov # # * 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, %Q[# 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.mov' % [mov_name, i + 1, trk_name]) err = OCObject.new r = mov1.objc_send( :writeToFile, outfile, :withAttributes, { QTMovieFlatten => NSNumber.numberWithBool(true), }, :error, err) if r log(logf, %Q[Extracted track %d (%s) of %s => %s] % [trk_id, trk_name, infile, outfile]) else log(logf, %Q[# 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 -
Apr 11, 2014 10:08 PM in response to speedyrazorby Hiroto,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 -
Apr 14, 2014 11:20 PM in response to Hirotoby speedyrazor,Hi Hiroto, many thanks for taking the time to write these applescripts, although the restriction of not being able to handle multichannel is the killer for me. I have since developed a Python GUI application that takes cae of this using ffmpeg. Happy to share if anyone wants it.
Kind regards.