########################################################################### # # jAudioExtractor - helper plugin that computers audio features for # music information retrieval use, using jAudio # # A component of the Greenstone digital library software # from the New Zealand Digital Library Project at the # University of Waikato, New Zealand. # # Copyright (C) 2010 New Zealand Digital Library Project # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # ########################################################################### package jAudioExtractor; use BaseMediaConverter; use Cwd; use FileUtil; use strict; no strict 'refs'; # allow filehandles to be variables and viceversa BEGIN { @jAudioExtractor::ISA = ('BaseMediaConverter'); } my $arguments = [ { 'name' => "window_size", 'desc' => "{jAudioExtractor.window_size}", 'type' => "int", 'range' => "128,", 'deft' => '512', 'reqd' => "no" }, { 'name' => "window_overlap", 'desc' => "{jAudioExtractor.window_overlap}", 'type' => "string", 'range' => "0.0", 'deft' => '0.0', 'reqd' => "no" }, { 'name' => "sample_rate", 'desc' => "{jAudioExtractor.sample_rate}", 'type' => "enum", 'list' => [{'name' => "8 kHz", 'desc' => "{jAudioExtractor.8000Hz}"}, {'name' => "11.025 kHz", 'desc' => "{jAudioExtractor.11025Hz}"}, {'name' => "16 kHz", 'desc' => "{jAudioExtractor.16000Hz}"}, {'name' => "22.05 kHz", 'desc' => "{jAudioExtractor.22050Hz}"}, {'name' => "44.1 kHz", 'desc' => "{jAudioExtractor.44100Hz}"} ], 'deft' => '16 kHz', 'reqd' => "no" }, { 'name' => "extracted_data", 'desc' => "{jAudioExtractor.extracted_data}", 'type' => "enum", 'list' => [{'name' => "Overall and Windowed", 'desc' => "{jAudioExtractor.overall_and_windowed}"}, {'name' => "Windowed only", 'desc' => "{jAudioExtractor.windowed_only}"}, {'name' => "Overall only", 'desc' => "{jAudioExtractor.overall_only}"}], 'deft' => 'Overall and Windowed', 'reqd' => "no" }, { 'name' => "output_type", 'desc' => "{jAudioExtractor.output_type}", 'type' => "enum", 'list' => [{'name' => "ACE XML", 'desc' => "{jAudioExtractor.ace_xml}"}, {'name' => "Weka ARFF", 'desc' => "{jAudioExtractor.weka_arff}"}], 'deft' => 'ACE XML', 'reqd' => "no" } ]; my $options = { 'name' => "jAudioExtractor", 'desc' => "{jAudioExtractor.desc}", 'abstract' => "yes", 'inherits' => "yes", 'args' => $arguments }; sub new { my ($class) = shift (@_); my ($pluginlist,$inputargs,$hashArgOptLists) = @_; push(@$pluginlist, $class); push(@{$hashArgOptLists->{"ArgList"}},@{$arguments}); push(@{$hashArgOptLists->{"OptList"}},$options); my $self = new BaseMediaConverter($pluginlist, $inputargs, $hashArgOptLists, 1); # Set controlling variables my $gsdl_home = $ENV{'GSDLHOME'}; my $music_ir_home = $ENV{'GEXT_MUSICIR'}; my $ace_xml_output_directory = &util::filename_cat($gsdl_home,"tmp"); # Set the directory to save the the ACE XML output files in if (!FileUtil::directoryExists($ace_xml_output_directory)) { FileUtil::makeDirectory($ace_xml_output_directory); } $self->{'ace_xml_output_directory'} = $ace_xml_output_directory; $self->{'jmir_directory'} = &util::filename_cat($music_ir_home,"lib","java"); # Set the directory holding the jMIR .jar files return bless $self, $class; } # Create and save a temporary jAudio batch file referring to a file to extract # features from sub prepareTempJAudioBatchFile { # ARG Ob1: $new_batch_file_path refers to the path of the temporary batch file to create # ARG Ob2: $model_batch_file_path refers to the path of the model batch file to base the temporary one on # ARG Ob3: $input_music_file_path refers to the file to extract features with # ARG Ob4: $feature_values_file_path refers to the path of the ACE XML Feature Values file that the jMIR component will output to # ARG Ob5: $feature_values_file_path refers to the path of the ACE XML Feature Descriptions file that the jMIR component will output to my ( $self, $new_batch_file_path, $model_batch_file_path, $input_music_file_path, $feature_values_file_path, $feature_descriptions_file_path ) = @_; # Retrieve settings for jAudioPlugin and use in batch file that is generated my $sample_rate = $self->{'sample_rate'}; # sample of how to get parameter from Greenstone/GLI # Read the contents of the model batch file my $batch_file_contents; local $/=undef; open (INPUT, "$model_batch_file_path") or die "Could not read the model jAudio batch file $model_batch_file_path"; binmode INPUT; $batch_file_contents = ; close INPUT; # Set the batch ID tag in the temporary file $batch_file_contents =~ s///; # Set the input file name in the file tag in the temporary file if ($^O eq "cygwin") { $input_music_file_path = `cygpath -m "$input_music_file_path"`; $input_music_file_path=~ s/\s+$//; } $batch_file_contents =~ s/<\/file>/$input_music_file_path<\/file>/; # Set the feature vales save path in the temporary file if ($^O eq "cygwin") { $feature_descriptions_file_path = `cygpath -m "$feature_descriptions_file_path"`; $feature_descriptions_file_path=~ s/\s+$//; } $batch_file_contents =~ s/<\/destination>/$feature_descriptions_file_path<\/destination>/; # Set the feature vales save path in the temporary file if ($^O eq "cygwin") { $feature_values_file_path = `cygpath -m "$feature_values_file_path"`; $feature_values_file_path=~ s/\s+$//; } $batch_file_contents =~ s/<\/destination>/$feature_values_file_path<\/destination>/; # Save the temporary batch file open (OUTPUT, ">$new_batch_file_path") or die "Could not create the temporary jAudio batch file $new_batch_file_path"; print OUTPUT "$batch_file_contents"; close OUTPUT; # Done return 0; } sub compute_features { my $self = shift(@_); my $source_file_path = shift(@_); my $convert_options = shift(@_) || ""; my $outhandle = $self->{'outhandle'}; my $verbosity = $self->{'verbosity'}; my $source_file_no_path = &File::Basename::basename($source_file_path); $self->init_cache_for_file($source_file_path); # Determine the full name and path of the output file my $target_file_path; my $feature_values_file_path; my $feature_descriptions_file_path; my $target_file_type; if ($self->{'output_type'} eq "ACE XML") { $target_file_type="xml"; } else { # ARFF $target_file_type="arff"; } if ($self->{'enable_cache'}) { my $cached_dir = $self->{'cached_dir'}; my $file_root = $self->{'cached_file_root'}; my $target_file = "$file_root.$target_file_type"; $target_file_path = &util::filename_cat($cached_dir,$target_file); } else { $target_file_path = &util::get_tmp_filename($target_file_type); } if ($self->{'output_type'} eq "ACE XML") { $feature_values_file_path = $target_file_path; $feature_values_file_path =~ s/\.xml$/_FV.xml/; $feature_descriptions_file_path = $target_file_path; $feature_descriptions_file_path =~ s/\.xml$/_FD.xml/; # Make target_file_path be the principle file generated by jAudio when using ACE XML $target_file_path = $feature_values_file_path; } my $ace_xml_output_directory = $self->{'ace_xml_output_directory'}; my $jmir_directory = $self->{'jmir_directory'}; my $store_cwd = cwd(); if (!-d $jmir_directory) { print STDERR "Error: Unable able to find directory '$jmir_directory'\n"; print STDERR " Cannot run jAudio\n"; } elsif (chdir($jmir_directory)) { # Run the feature extraction. # Specify the name for a temporary jAudio batch file my $template_batch_file_path = &util::filename_cat($jmir_directory,"SampleJAudioBatchFile.xml.in"); my $batch_file_path = &util::filename_cat($ace_xml_output_directory,"tempjaudiobatchfile.xml"); # Create the batch file $self->prepareTempJAudioBatchFile( $batch_file_path, $template_batch_file_path, $source_file_path, $feature_values_file_path, $feature_descriptions_file_path ); # Input and Output files to use are stored in the batch_file my $batch_file_path_os = $batch_file_path; if ($^O eq "cygwin") { $batch_file_path_os = `cygpath -w "$batch_file_path"`; $batch_file_path_os =~ s/\s+$//; } my $jaudio_cmd = "java -Xmx1024M -jar jAudio.jar $convert_options -b \"$batch_file_path_os\""; # Test the execution path # print("EXECUTION CMD: $jaudio_cmd\n"); my $print_info = { 'message_prefix' => "jAudio", 'message' => "Extracting audio features from $source_file_no_path" }; my ($regenerated,$result,$had_error) = $self->autorun_general_cmd($jaudio_cmd,$source_file_path,$target_file_path,$print_info); # Delete the jAudio batch file unlink($batch_file_path); } else { print STDERR "Error: failed to change directory to '$jmir_directory'\n"; print STDERR " Cannot run jAudio\n"; } chdir($store_cwd); return ($target_file_path); } 1;