Changeset 35224


Ignore:
Timestamp:
2021-07-03T11:44:55+12:00 (3 years ago)
Author:
davidb
Message:

Substantial refactoring and code development so a CSV header line is guaranteed to be present in the generated CSV file AND that any frame that is silent (causing essential features to exit with an error) to generate line of zeros in the feature file

File:
1 edited

Legend:

Unmodified
Added
Removed
  • main/trunk/model-sites-dev/mars/collect/amc-essentia/AUDIO-FRAME-TO-ESSENTIA-CSV-FEATURE-FILE.pl

    r34768 r35224  
    77use Cwd qw(cwd getcwd);
    88
    9 if (scalar(@ARGV) != 3) {
    10     print STDERR "****\n";
    11     print STDERR "* Error: incorrect usage\n";
    12     print STDERR "* Usage: $0 frame-step-in-secs frame-duration-in-secs audio_input_file\n";
    13     print STDERR "****\n";
    14     exit(1);
    15 }
    16 
    17 my $frame_step_secs = $ARGV[0];
    18 my $frame_dur_secs  = $ARGV[1];
    19 
    20 my $audio_file        = $ARGV[2];
    21 my ($full_audio_root) = ($audio_file =~ m/^(.*)\..+?$/);
    22 my ($audio_root)      = ($full_audio_root =~ m/^.*\/(.*?)$/);
    23 
    24 if ( ! -d "tmp" ) {
    25     print "Creating directory: tmp\n";
    26     mkdir("tmp");
    27 }
    28 
    29 my $tmp_json_file = "tmp/${audio_root}_essentiafeatures.json";
    30 my $tmp_csv_file  = "tmp/${audio_root}_essentiafeatures.csv";
    31 
    32 my $csv_file  = "${full_audio_root}_essentiafeatures_frames.csv";
    33 
    34 my $pwd = cwd();
    35 my $profile_template_file = "$pwd/essentia-2013-2014.profile.in";
    36 my $profile_file = "$pwd/essentia-2013-2014.profile";
    37 
     9sub get_audio_duration
     10{
     11    my ($audio_file) = @_;
     12
     13    my $audio_cmd = "./AUDIO-DURATION.sh $audio_file";
     14   
     15    open(my $fh, '-|', $audio_cmd) or die $!;
     16   
     17    # Just need to read in one line:
     18
     19    my $audio_duration = <$fh>;
     20    chomp($audio_duration);
     21
     22    # otherwise would do
     23    #
     24    #while (my $line = <$fh>) {
     25    #    # Do stuff with each $line.
     26    #}
     27
     28    close($fh);
     29
     30    return $audio_duration;
     31}
     32
     33sub generate_bespoke_profile
     34{
     35    my ($startTime, $frame_dur_secs, $profile_template_file,$profile_file) = @_;
     36
     37    #
     38    # Generate bespoke profile_file for this time-slice
     39    #
     40    open(my $ipfh, '<', $profile_template_file) or die $!;
     41    open(my $opfh, '>', $profile_file) or die $!;
     42   
     43    my $endTime   = $startTime + $frame_dur_secs;
     44   
     45    while (my $line = <$ipfh>) {
     46    chomp($line);
     47    $line =~ s/\@startTime\@/$startTime/g;
     48    $line =~ s/\@endTime\@/$endTime/g;
     49   
     50    print $opfh "$line\n";
     51    }
     52       
     53    close($opfh);
     54    close($ipfh);
     55}
     56
     57
     58#
     59# Extract features and convert to CSV
     60#
     61sub extract_audio_features_as_csv
     62{
     63    my ($audio_root,$audio_file,$profile_file,$ignore_fields, $fallback_rec) = @_;
     64
     65    my $tmp_json_file = "tmp/${audio_root}_essentiafeatures.json";
     66    my $tmp_csv_file  = "tmp/${audio_root}_essentiafeatures.csv";
     67
     68    my $extract_cmd = "essentia_streaming_extractor_music $audio_file $tmp_json_file $profile_file";
     69    my $extract_status = system($extract_cmd);
     70
     71    if ($extract_status != 0) {
     72    if (($extract_status == 256) && defined($fallback_rec)) {
     73        # effectively error status '1'
     74        print "$!\n";
     75        print "Warning: Failed to run command with exit status 1:\n";
     76        print "    $extract_cmd\n";
     77        print "\n";
     78        print "The most likely issue is that the segement of audio process was silence\n";
     79        print "=> Generating row of 0 feature values for CSV output\n";
     80
     81        my @lines = ($fallback_rec->{'headerline'}, $fallback_rec->{'zeroline'});
     82        return \@lines;
     83    }
     84    else {
     85
     86        print STDERR "$!\n";
     87        print STDERR "Error: Failed to run command:\n";
     88        print STDERR "    $extract_cmd\n";
     89        return undef;
     90    }
     91    }
     92   
     93    my $convert_cmd = "json_to_csv.py -i $tmp_json_file -o $tmp_csv_file --ignore $ignore_fields";
     94    my $convert_status = system($convert_cmd);
     95
     96    # Whatever the outcome of this command, now finished with the tmp_json_file
     97    unlink($tmp_json_file) or die "Can't delete $tmp_json_file: $!\n";
     98
     99    if ($convert_status != 0) {
     100    print STDERR "$!\n";
     101    print STDERR "Error: Failed to run command:\n";
     102    print STDERR "    $convert_cmd\n";
     103    return undef;
     104    }
     105
     106
     107    open(my $ifh, '<', $tmp_csv_file) or die $!;
     108   
     109    # read in lines to array: single line shot
     110    chomp(my @lines = <$ifh>);
     111   
     112    # my @lines = ();
     113    #
     114    # while (my $line = <$ifh>) {
     115    #     chomp($line);
     116    #     push(@lines,$line);
     117    # }
     118   
     119    close($ifh);
     120
     121    unlink($tmp_csv_file)  or die "Can't delete $tmp_csv_file: $!\n";
     122
     123    return \@lines;
     124}
     125
     126sub generate_csv_feature_header
     127{
     128    my $exemplar_inputfile="import/00/ds_20491_4100.m4a";
     129
     130    # _essentiafeatures_frames.csv
     131
     132
     133}
     134
     135
     136sub ensure_representative_fallback
     137{
     138    my ($audio_file,$profile_file,$ignore_fields) = @_;
     139
     140    my $fallback_rec = {};
     141
     142    my $representative_headerline_file = "etc/representative_header.csv";
     143    my $representative_zeroline_file   = "etc/representative_zeroline.csv";
     144
     145    if ((! -f $representative_headerline_file) || (! -f $representative_zeroline_file)) {
     146    print "Generating Representative Fallback files for CSV Header and Zero-val files:\n";
     147    print "  $representative_headerline_file and $representative_zeroline_file\n";
     148    print "\n";
     149
     150    my ($full_audio_root) = ($audio_file =~ m/^(.*)\..+?$/);
     151    my ($audio_root)      = ($full_audio_root =~ m/^.*\/(.*?)$/);
     152
     153    my $lines = extract_audio_features_as_csv($audio_root,$audio_file,$profile_file,$ignore_fields,undef);
     154
     155    # Count how many elements in the first line (which is the CSV header)
     156    my $header_line = $lines->[0];
     157    my @header_line_vals = split(",",$header_line);
     158
     159    # Build an array full of zeros to match
     160    my @zero_vals = ();
     161    for my $v (@header_line_vals) {
     162        push(@zero_vals,0);
     163    }
     164
     165    my $zero_line = join(",",@zero_vals);
     166
     167    # output headerline
     168    open(my $ofh, '>', $representative_headerline_file) or die $!;
     169    print $ofh "$header_line\n";
     170    close($ofh);
     171
     172
     173    # output zeroline
     174    open($ofh, '>', $representative_zeroline_file) or die $!;
     175    print $ofh "$zero_line\n";
     176    close($ofh);
     177
     178    $fallback_rec->{'headerline'} = $header_line;
     179    $fallback_rec->{'zeroline'}   = $zero_line;
     180    }
     181    else {
     182    # read in files
     183    print "Reading in Representative Fallback files for CSV Header and Zero-val files:\n";
     184    print "  $representative_headerline_file and $representative_zeroline_file\n";
     185    print "\n";
     186
     187    open(my $ifh, '<', $representative_headerline_file) or die $!;
     188    chomp(my @header_lines = <$ifh>);
     189    close($ifh);
     190
     191    open($ifh, '<', $representative_zeroline_file) or die $!;
     192    chomp(my @zero_lines = <$ifh>);
     193    close($ifh);
     194
     195
     196    $fallback_rec->{'headerline'} = $header_lines[0];
     197    $fallback_rec->{'zeroline'}   = $zero_lines[0];
     198
     199    }
     200
     201    return $fallback_rec;
     202}
     203
     204
     205sub main
     206{
     207    my $representative_audio_file = "import/00/ds_20491_4100.m4a";
     208
     209
     210    if (scalar(@ARGV) != 3) {
     211    print STDERR "****\n";
     212    print STDERR "* Error: incorrect usage\n";
     213    print STDERR "* Usage: $0 frame-step-in-secs frame-duration-in-secs audio_input_file\n";
     214    print STDERR "****\n";
     215    exit(1);
     216    }
     217   
     218    my $frame_step_secs = $ARGV[0];
     219    my $frame_dur_secs  = $ARGV[1];
     220   
     221    my $audio_file        = $ARGV[2];
     222    my ($full_audio_root) = ($audio_file =~ m/^(.*)\..+?$/);
     223    my ($audio_root)      = ($full_audio_root =~ m/^.*\/(.*?)$/);
     224   
     225    if ( ! -d "tmp" ) {
     226    print "Creating directory: tmp\n";
     227    mkdir("tmp");
     228    }
     229   
     230   
     231    my $csv_file  = "${full_audio_root}_essentiafeatures_frames.csv";
     232   
     233    my $pwd = cwd();
     234    my $profile_template_file = "$pwd/essentia-2013-2014.profile.in";
     235    my $profile_file = "$pwd/essentia-2013-2014.profile";
     236   
    38237# knock out any arrays in the JSON extracted features file
    39 my $ignore_fields="\
     238    my $ignore_fields="\
    40239 lowlevel.barkbands.* \
    41240 lowlevel.erbbands.* \
     
    58257 tonal.thpcp.*";
    59258
    60 $ignore_fields =~ s/\n//sg;
    61 
    62 
    63 my $audio_cmd = "./AUDIO-DURATION.sh $audio_file";
    64 
    65 open(my $fh, '-|', $audio_cmd) or die $!;
    66 
    67 # Just need to read in one line:
    68 
    69 my $audio_duration = <$fh>;
    70 chomp($audio_duration);
    71 
    72 # otherwise would do
    73 #
    74 #while (my $line = <$fh>) {
    75 #    # Do stuff with each $line.
    76 #}
    77 
    78 close($fh);
    79 
    80 
    81    
    82 if ( ! -f $csv_file ) {
     259    $ignore_fields =~ s/\n//sg;
     260
     261
     262    generate_bespoke_profile(0,$frame_dur_secs,$profile_template_file,$profile_file);
     263    my $fallback_rec = ensure_representative_fallback($representative_audio_file,$profile_file,$ignore_fields);
     264
     265    my $audio_duration = get_audio_duration($audio_file);
     266   
     267    if ( ! -f $csv_file ) {
    83268       
    84     print "******\n";
    85     print "* Running Essentia music extractor\n";
    86     print "*  on input file:     $audio_file (duration $audio_duration)\n";
    87     print "*  with profile:      $profile_file\n";
    88     print "*  generating output: $csv_file\n";
    89     print "****\n";
    90 
    91     open(my $ofh, '>', $csv_file) or die $!;
    92 
    93 
    94     for (my $t=0; $t<$audio_duration; $t+=$frame_step_secs) {
    95     ##print "," if ($t>0);
    96     print "*\n";
    97     print "*\n";
    98     print "* ### [Time step: $t]\n";
    99     print "*\n";
    100     print "*\n";
    101 
    102     #
    103     # Generate bespoke profile_file for this time-slice
    104     #
    105     open(my $ipfh, '<', $profile_template_file) or die $!;
    106     open(my $opfh, '>', $profile_file) or die $!;
    107 
    108     my $startTime = $t;
    109     my $endTime   = $t + $frame_dur_secs;
     269    print "******\n";
     270    print "* Running Essentia music extractor\n";
     271    print "*  on input file:     $audio_file (duration $audio_duration)\n";
     272    print "*  with profile:      $profile_file\n";
     273    print "*  generating output: $csv_file\n";
     274    print "****\n";
    110275   
    111     while (my $line = <$ipfh>) {
    112         chomp($line);
    113         $line =~ s/\@startTime\@/$startTime/g;
    114         $line =~ s/\@endTime\@/$endTime/g;
    115 
    116         print $opfh "$line\n";
     276    open(my $ofh, '>', $csv_file) or die $!;
     277
     278    for (my $t=0; $t<$audio_duration; $t+=$frame_step_secs) {
     279        ##print "," if ($t>0);
     280        print "*\n";
     281        print "*\n";
     282        print "* ### [Time step: $t]\n";
     283        print "*\n";
     284        print "*\n";
     285       
     286        generate_bespoke_profile($t,$frame_dur_secs,$profile_template_file,$profile_file);
     287       
     288        my $lines = extract_audio_features_as_csv($audio_root,$audio_file,$profile_file,$ignore_fields, $fallback_rec);
     289        if (!defined($lines) ) {
     290        next;
     291        }
     292       
     293        if ($t == 0) {
     294        # output first line from $tmp_csv_file to $csv_file
     295        print $ofh $lines->[0], "\n";
     296        }
     297       
     298        # append 2nd line of $tmp_json_file (i.e. data vals) to $csv_file
     299        print $ofh $lines->[1], "\n";
     300       
     301        # break out of loop if there isn't enough time left for a full $frame_dur_secs
     302        my $end_of_next_frame = $t+$frame_step_secs+$frame_dur_secs;   
     303        last if ($end_of_next_frame > $audio_duration);
    117304    }
    118 
    119     close($opfh);
    120     close($ipfh);
    121 
    122     #
    123     # Extract features and convert to CSV
    124     #
    125 
    126     my $extract_cmd = "essentia_streaming_extractor_music $audio_file $tmp_json_file $profile_file";
    127     my $extract_status = system($extract_cmd);
    128     if ($extract_status != 0) {
    129         print STDERR "$!\n";
    130         print STDERR "Error: Failed to run command:\n";
    131         print STDERR "    $extract_cmd\n";
    132         next;
    133     }
    134 
    135     my $convert_cmd = "json_to_csv.py -i $tmp_json_file -o $tmp_csv_file --ignore $ignore_fields";
    136     my $convert_status = system($convert_cmd);
    137     if ($convert_status != 0) {
    138         print STDERR "$!\n";
    139         print STDERR "Error: Failed to run command:\n";
    140         print STDERR "    $convert_cmd\n";
    141         next;
    142     }
    143 
    144     open(my $ifh, '<', $tmp_csv_file) or die $!;
    145 
    146     # read in lines to array: single line shot
    147     chomp(my @lines = <$ifh>);
    148305   
    149     # my @lines = ();
    150     #
    151     # while (my $line = <$ifh>) {
    152     #     chomp($line);
    153     #     push(@lines,$line);
    154     # }
    155 
    156     close($ifh);
     306    close($ofh);
    157307   
    158     if ($t == 0) {
    159         # output first line from $tmp_csv_file to $csv_file
    160         print $ofh $lines[0], "\n";
    161     }
    162 
    163     # append 2nd line of $tmp_json_file (i.e. data vals) to $csv_file
    164     print $ofh $lines[1], "\n";
    165        
    166     unlink($tmp_json_file) or die "Can't delete $tmp_json_file: $!\n";
    167     unlink($tmp_csv_file)  or die "Can't delete $tmp_csv_file: $!\n";
    168    
    169     # break out of loop if there isn't enough time left for a full $frame_dur_secs
    170     my $end_of_next_frame = $t+$frame_step_secs+$frame_dur_secs;   
    171     last if ($end_of_next_frame > $audio_duration);
    172     }
    173 
    174     close($ofh);
    175    
    176     print "******\n";
    177    
    178 }
    179 else {
    180     print "*  Skipping frame-by-frame audio features computation as $csv_file already exists\n";
    181 }
    182 
    183 
    184 
     308    print "******\n";
     309   
     310    }
     311    else {
     312    print "*  Skipping frame-by-frame audio features computation as $csv_file already exists\n";
     313    }
     314}
     315
     316main();
     317
     318
     319
Note: See TracChangeset for help on using the changeset viewer.