Quellcode durchsuchen

Export audio handling to a library -- we will need it for RiffTrax as well as RAVE

Zachary Kotlarek vor 13 Jahren
Ursprung
Commit
cbda72867c
2 geänderte Dateien mit 378 neuen und 0 gelöschten Zeilen
  1. 270 0
      dmx/Audio.pm
  2. 108 0
      dmx/rifftrax.pl

+ 270 - 0
dmx/Audio.pm

@@ -0,0 +1,270 @@
+#!/usr/bin/perl
+use strict;
+use warnings;
+use POSIX;
+use File::Touch;
+use File::Basename;
+use Time::HiRes qw( usleep sleep time );
+use IPC::System::Simple qw( system capture );
+
+# Package name
+package Audio;
+
+# Config
+my $MEDIA_PATH = `~/bin/video/mediaPath`;
+my %FILES      = ();
+
+# Debug
+my $DEBUG = 0;
+if ($ENV{'DEBUG'}) {
+	$DEBUG = 1;
+}
+
+# Always load the SILENCE file (also serves as a sanity check)
+audioLoad('SILENCE', 'DMX/Silence.wav');
+
+sub runApplescript($) {
+	my ($script) = @_;
+	if ($DEBUG) {
+		print STDERR 'AppleScript: ' . $script . "\n";
+	}
+
+	my $retval = capture('osascript', '-e', $script);
+	if ($DEBUG) {
+		print STDERR "\tAppleScript result: " . $retval . "\n";
+	}
+
+	return $retval;
+}
+
+sub audioAdd($$) {
+	my ($name, $path) = @_;
+	if ($DEBUG) {
+		print STDERR 'AppleScript::audioAdd(): ' . $name . "\n";
+	}
+
+	# Construct an absolute file path
+	if (!($path =~ /^\//)) {
+		$path = $MEDIA_PATH . '/' . $path;
+	}
+
+	# Validate
+	if (!-r $path) {
+		die('Invalid audio path: ' . $path . "\n");
+	}
+
+	# We only support one document per name
+	audioDrop($name);
+
+	# Append the array
+	my %tmp = ('path' => $path);
+	$FILES{$name} = \%tmp;
+}
+
+sub audioDrop($) {
+	my ($name) = @_;
+	if ($DEBUG) {
+		print STDERR 'AppleScript::dropAudio(): ' . $name . "\n";
+	}
+
+	# Unload as necessary
+	if (audioLoaded($name)) {
+		audioStop($name);
+		audioUnload($name);
+	}
+
+	# Drop as necessary
+	if (audioExists($name)) {
+		delete($FILES{$name});
+	}
+}
+
+sub audioLoaded($) {
+	my ($name) = @_;
+	if ($DEBUG) {
+		print STDERR 'AppleScript::audioLoaded(): ' . $name . "\n";
+	}
+
+	if (audioExists($name) && defined($FILES{$name}->{'name'})) {
+		return 1;
+	}
+	return 0;
+}
+
+sub audioExists($) {
+	my ($name) = @_;
+	if ($DEBUG) {
+		print STDERR 'AppleScript::audioExists(): ' . $name . "\n";
+	}
+
+	if (defined($name) && defined($FILES{$name})) {
+		return 1;
+	}
+	return 0;
+}
+
+sub audioPlay($) {
+	my ($name) = @_;
+	if ($DEBUG) {
+		print STDERR 'AppleScript::playAudio(): ' . $name . "\n";
+	}
+
+	# Start playback
+	audioBackground($name);
+
+	# Wait for it to complete
+	my @cmd = ('tell application "QuickTime Player"');
+	push(@cmd, 'repeat while playing of document ' . $FILES{$name}->{'name'} . ' = true');
+	push(@cmd, 'delay 0.05');
+	push(@cmd, 'end repeat');
+	push(@cmd, 'end tell');
+	runApplescript(join("\n", @cmd));
+}
+
+sub audioBackground($) {
+	my ($name) = @_;
+	if ($DEBUG) {
+		print STDERR 'AppleScript::playAudio(): ' . $name . "\n";
+	}
+	if (!audioLoaded($name)) {
+		die('Invalid QT document: ' . $name . "\n");
+	}
+
+	# Start playback
+	my @cmd = ('tell application "QuickTime Player"');
+	push(@cmd, 'set current time of document ' . $FILES{$name}->{'name'} . ' to 0');
+	push(@cmd, 'play document ' . $FILES{$name}->{'name'});
+	push(@cmd, 'end tell');
+	runApplescript(join("\n", @cmd));
+}
+
+sub audioPlaying($) {
+	my ($name) = @_;
+	if ($DEBUG) {
+		print STDERR 'AppleScript::audioPlaying(): ' . $name . "\n";
+	}
+	if (!audioLoaded($name)) {
+		die('Invalid QT document: ' . $name . "\n");
+	}
+
+	# Ask QT if the document is playing
+	my $result = runApplescript('tell application "QuickTime Player" to get playing of document ' . $FILES{$name}->{'name'});
+	if ($result =~ /true/i) {
+		return 1;
+	}
+	return 0;
+}
+
+sub audioPosition($$) {
+	my ($name, $new) = @_;
+	if ($DEBUG) {
+		print STDERR 'AppleScript::audioPosition(): ' . $name . "\n";
+	}
+	if (!audioLoaded($name)) {
+		die('Invalid QT document: ' . $name . "\n");
+	}
+
+	# Optionally set the audio position
+	if ($new) {
+		runApplescript('tell application "QuickTime Player" to set current time of document ' . $FILES{$name}->{'name'} . ' to ' . $new);
+	}
+
+	# Get the current playback position from QT
+	return runApplescript('tell application "QuickTime Player" to get current time of document ' . $FILES{$name}->{'name'});
+}
+
+sub audioRate($$) {
+	my ($name, $new) = @_;
+	if ($DEBUG) {
+		print STDERR 'AppleScript::audioRate(): ' . $name . "\n";
+	}
+	if (!audioLoaded($name)) {
+		die('Invalid QT document: ' . $name . "\n");
+	}
+
+	# Optionally set the playback rate
+	if ($new) {
+		runApplescript('tell application "QuickTime Player" to set rate of document ' . $FILES{$name}->{'name'} . ' to ' . $new);
+	}
+
+	# Get the current playback position from QT
+	return runApplescript('tell application "QuickTime Player" to get rate of document ' . $FILES{$name}->{'name'});
+}
+
+sub audioStop($) {
+	my ($name) = @_;
+	if ($DEBUG) {
+		print STDERR "AppleScript::stopAudio()\n";
+	}
+
+	# Allow control of all documents or a specific document
+	my $doc = 'every document';
+	if (!audioLoaded($name)) {
+		$doc = 'document ' . $FILES{$name}->{'name'};
+	}
+
+	# Stop and rewind all QT documents
+	runApplescript('tell application "QuickTime Player" to stop ' . $doc);
+	runApplescript('tell application "QuickTime Player" to set current time of ' . $doc . ' to 0');
+
+	# Bring Plex back to the front
+	runApplescript('tell application "Plex" to activate');
+}
+
+sub audioUnload($) {
+	my ($name) = @_;
+	if ($DEBUG) {
+		print STDERR 'AppleScript::loadAudio(): ' . $name . "\n";
+	}
+	if (!audioExists($name)) {
+		die('Invalid QT document: ' . $name . "\n");
+	}
+
+	# Nothing to do if the file isn't loaded
+	if (!audioLoaded($name)) {
+		return;
+	}
+
+	# Close to document
+	runApplescript('tell application "QuickTime Player" to close document ' . $FILES{$name}->{'name'});
+}
+
+sub audioLoad($) {
+	my ($name) = @_;
+	if (!audioExists($name)) {
+		die('Invalid QT document: ' . $name . "\n");
+	}
+	if ($DEBUG) {
+		print STDERR 'AppleScript::loadAudio(): ' . $name . "\n";
+	}
+
+	# Unload first, if we're already loaded
+	if (audioLoaded($name)) {
+		audioUnload($name);
+	}
+
+	# Find out how many files are already open
+	my $count = runApplescript('tell application "QuickTime Player" to count items of every document');
+
+	# Open the file
+	system('open', '-a', 'QuickTime Player', $file);
+
+	# Wait for QT to load the new file
+	my @cmd = ('tell application "QuickTime Player"');
+	push(@cmd, 'repeat while (count items of every document) <= ' . $count);
+	push(@cmd, 'delay 0.05');
+	push(@cmd, 'end repeat');
+	push(@cmd, 'get document 1');
+	push(@cmd, 'end tell');
+	my $doc = runApplescript(join("\n", @cmd));
+
+	# Clean up the document name
+	$doc =~ s/^\s*document //;
+	$doc =~ s/\s+$//;
+	$doc =~ s/\"/\\\"/g;
+	$doc = '"' . $doc . '"';
+	return $doc;
+}
+
+# Always return true
+1;

+ 108 - 0
dmx/rifftrax.pl

@@ -0,0 +1,108 @@
+#!/usr/bin/perl
+use strict;
+use warnings;
+use File::Temp qw( tempfile );
+use Time::HiRes qw( usleep sleep time );
+use IPC::System::Simple qw( system capture );
+
+# Local modules
+use Cwd qw(abs_path);
+use File::Basename qw(dirname);
+use lib dirname(abs_path($0));
+use DMX;
+use Audio;
+
+# Config
+my $RIFF_PATH = `~/bin/video/mediaPath` . '/iTunes/iTunes Music/RiffTrax';
+my %RIFFS     = (
+	'Harry Potter 1' => {
+		'file'   => 'Harry Potter/Harry Potter 1_ The Sorcerer\'s Stone.mp3',
+		'part'   => '82481',
+		'offset' => 100,
+	},
+	'Harry Potter 2' => {
+		'file'   => 'Harry Potter/Harry Potter 2_ The Chamber of Secrets.mp3',
+		'part'   => '82469',
+		'offset' => 100,
+	}
+);
+
+# App config
+my $DATA_DIR     = DMX::dataDir();
+my $OUTPUT_FILE  = $DATA_DIR . 'RIFF';
+my $STATE_SOCK   = $OUTPUT_FILE . '.socket';
+my $PULL_TIMEOUT = 60;
+my $DELAY        = $PULL_TIMEOUT / 2;
+
+# Prototypes
+sub runApplescript($);
+
+# Debug
+my $DEBUG = 0;
+if ($ENV{'DEBUG'}) {
+	$DEBUG = 1;
+}
+
+# Sockets
+DMX::stateSocket($STATE_SOCK);
+DMX::stateSubscribe($STATE_SOCK);
+
+# State
+my $RIFF     = undef();
+my %exists   = ();
+my $pullLast = time();
+
+# Validate our riff files at launch
+foreach my $riff (keys(%RIFFS)) {
+	if ($RIFFS{$riff}{'file'} =~ /^\//) {
+		$RIFFS{$riff}{'path'} = $RIFFS{$riff}{'file'};
+	} else {
+		$RIFFS{$riff}{'path'} = $RIFF_PATH . '/' . $RIFFS{$riff}{'file'};
+	}
+
+	if (!-r $RIFFS{$riff}{'path'}) {
+		die('Invalid riff file: ' . $riff . ' => ' . $RIFFS{$riff}{'path'} . "\n");
+	}
+}
+
+# Loop forever
+while (1) {
+
+	# If the color profile has changed, save the state to disk
+	#if ($colorLast ne $color) {
+	#	if ($DEBUG) {
+	#		print STDERR 'New color profile: ' . $colorLast . ' => ' . $color . "\n";
+	#	}
+	#	my ($fh, $tmp) = tempfile($OUTPUT_FILE . '.XXXXXXXX', 'UNLINK' => 0);
+	#	print $fh $color . "\n";
+	#	close($fh);
+	#	rename($tmp, $OUTPUT_FILE);
+	#}
+
+	# State is calculated; use newState to gather data
+	my $newState = $state;
+
+	# Wait for state updates
+	my $cmdState = DMX::readState($DELAY, \%exists, undef(), undef());
+	if (defined($cmdState)) {
+		$newState = $cmdState;
+		$pullLast = time();
+	}
+
+	# Die if we don't see regular updates
+	if (time() - $pullLast > $PULL_TIMEOUT) {
+		die('No update on state socket in past ' . $PULL_TIMEOUT . " seconds. Exiting...\n");
+	}
+
+	# Launch riffs when the underlying media is active
+	if (!$RIFF && $cmdState eq 'PLAY') {
+		my %tmp = ();
+		$tmp{'url'} = $exists{'PLAYING_URL'};
+		$RIFF = \%tmp;
+	}
+
+	# Close riffs when the underlying media stops
+	if ($RIFF && $exists{'PLAYING_URL'} ne $RIFF->{'url'}) {
+		$RIFF = undef();
+	}
+}