sonifyGSR.pl
# SonifyGSR
# Copyright (C) 2022 Cliff Hammett
#
# 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 3 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, see <https://www.gnu.org/licenses/>.
#################### META ###############################################################################
# Title : sonifyGSR.pl #
# Purpose : creates a really simple sonification of GSR data to accompany the #
# bio|audio maps of bat walks #
#################### PREQUISITES ########################################################################
# perl : tested on perl v5.26.1. Should run on other versions! #
# chuck : the Chuck music programming language https://chuck.cs.princeton.edu/ #
# sox : Sound eXchange http://sox.sourceforge.net/ #
# #
# This script was written for operating on linux systems. It will probably run on Macs. It may need #
# some modification to run on windows #
# #
#################### USAGE ##############################################################################
# perl sonifyGSR.pl <raw data filename> <processed data filename> <target output directory> #
# #
# raw data filename : original CSV output from location|stimulation censor #
# processed data filename : processed CSV file created by peak SS #
# target directory : directory where the files will be outputted. Top level directory does #
# not need to exist yet, rest of path does #
# #
#########################################################################################################
use strict;
use warnings;
my $dataFileIn = $ARGV[0];
my $structureFileIn = $ARGV[1];
my $dirOut = $ARGV[2];
my $freqMin = 100;
my $freqMax = 1000;
my $rah_data = &processCSV($dataFileIn);
my $rah_structure = &processCSV($structureFileIn);
my $range = &findMinMaxValue($rah_data, "sens");
&printHash($rah_structure->[0]);
system "mkdir $dirOut";
system "mkdir $dirOut/raw";
system "mkdir $dirOut/raw/compiled";
my $k = 0;
my $size = @{$rah_data};
my $soxline = "";
my %snd;
my $c=0;
my $f=0;
for (my $i=0; $i+1<$size; $i++){
my $rd = $rah_data->[$i];
# &printHash($rd);
my $rs = $rah_structure->[$k];
# &printHash($rs);
if ($rd->{millis} > $rs->{millisStart}){
my $freqFract = (1 + $rd->{sens} - $range->{min}) / $range->{max};
my $freqOut = ($freqFract * ($freqMax - $freqMin)) + $freqMin;
my $dur = $rah_data->[$i+1]->{millis} - $rd->{millis};
my $file = "$rs->{id}.mp3";
# print "emit $freqOut to $file for $dur milliseconds\n";
my $fileOut;
my $s = "$freqOut-$dur";
if ($snd{$s}){
$fileOut = $snd{$s};
}else{
$fileOut = "$dirOut/raw/$rs->{id}-$f.wav";
my $chuckcmd = "chuck ck/fm2:$dur:$freqOut:50:50 ck/rec-auto-stereo:$dur:$fileOut";
print "$chuckcmd\n";
system $chuckcmd;
$snd{"$freqOut-$dur"} = $fileOut;
}
$soxline .= "$fileOut ";
$f++;
# push @sox, $fileOut;
if ($c > 1000){
print "compiling at $f\n";
&compileFile($soxline, $rs->{id});
$soxline = "";
$c=0;
$f=0;
}
$c++;
}
if ($rd->{millis} >= $rs->{millisEnd} || $i+2 == $size){
$k++;
print "compiling at $f\n";
&compileFile($soxline, $rs->{id});
$soxline = "";
$f=0;
$c=0;
}
}
sub compileFile{
my ($soxline, $id) = @_;
print "compiling $id\n";
my $destFile = "$dirOut/$id.wav";
my $compfile = "$dirOut/raw/compiled/$id.wav";
if (-e $destFile){
system "sox $destFile $soxline $compfile";
system "cp $compfile $destFile";
}else{
system "sox $soxline $destFile";
}
}
sub findMinMaxValue{
my ($rah, $key) = @_;
my $size = @{$rah};
my $rtn->{min} = 1024;
$rtn->{max} = 0;
for(my $i=0; $i<$size; $i++){
my $val = $rah->[$i]->{$key};
if ($val > $rtn->{max}){
$rtn->{max} = $val;
}
if($val < $rtn->{min}){
$rtn->{min} = $val;
}
}
return $rtn;
}
sub processCSV{
my $file = shift;
open DATA, '<', $file or die "file didn't work?!\n";
my @data = <DATA>;
my $datasize = @data;
chomp $data[0];
my @header = split(",", $data[0]);
my $headersize = @header;
my $ra_data;
for (my $i=0; $i<$datasize; $i++){
chomp $data[$i];
# if ($csv->parse($data[$i])){
# print $data[$i] . "\n";
my @value = split(",", $data[$i]);
my $rh_line;
for (my $k=0; $k<$headersize; $k++){
# print "$k - " . $header[$k] . ": " . $value[$k] . "\n";
$rh_line->{$header[$k]} = $value[$k];
}
push @{$ra_data}, $rh_line;
# }else{print 'error in processing';}
}
close (DATA);
return $ra_data;
}
sub printHash{
my $rh = shift;
foreach my $key (keys %{$rh}) {
my $value = $rh->{$key};
print "$key : $value,";
}
print "\n";
}