changeset 5:42f189846ba8

More options parsing, can read scores in csv,json,xls as well as annotation data
author Emmanouil Thoefanis Chourdakis <e.t.chourdakis@qmul.ac.uk>
date Mon, 02 Oct 2017 15:54:26 +0100
parents 94eb0280ad4a
children f5edaa5ca167
files simscene.py
diffstat 1 files changed, 173 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/simscene.py	Fri Sep 29 20:39:54 2017 +0100
+++ b/simscene.py	Mon Oct 02 15:54:26 2017 +0100
@@ -2,8 +2,100 @@
 # -*- coding: utf-8 -*-
 # For licensing please see: LICENSE
 # Copyright (c) Emmanouil Theofanis Chourdakis <e.t.chourdakis@qmul.ac.uk>
+
 import argparse
+import logging
+import pandas as pd
+import sys
 
+from tabulate import tabulate
+
+def read_events_file(fname):
+    if fname[-3:].lower() == 'xls':
+        df = pd.read_excel(fname)
+    elif fname[-4:].lower() == 'json':
+        df = pd.read_json(fname)    
+    elif fname[-3:].lower() in ['txt', 'csv']:           
+        with open(fname) as f:
+            header = f.readline()
+        
+            s = f.readline()
+            f.seek(0,0)
+            if ',' in s:
+                sep = ','
+            elif '\t' in s:
+                sep = '\t'
+            else:
+                sep = ' '
+            if sep in header:
+                logging.warning('Probably no header or malformed .csv. Will try to parse it raw.')
+                df = pd.read_csv(f, header=None, sep=sep)                  
+            else:
+                df = pd.read_csv(f, sep=sep)
+                df = None
+            df.columns = ['label','sampleid','ebr','ebr_stddev','mean_time_between_instances','time_between_instances_stddev','start_time','end_time','fade_in_time','fade_out_time']
+
+    logging.info('Using input:\n'+tabulate(df, headers='keys', tablefmt='psql'))                   
+    return df
+
+def read_backgrounds_file(fname):
+    if fname[-3:].lower() == 'xls':
+        df = pd.read_excel(fname)
+    elif fname[-4:].lower() == 'json':
+        df = pd.read_json(fname)    
+    elif fname[-3:].lower() in ['txt', 'csv']:           
+        with open(fname) as f:
+            header = f.readline()
+        
+            s = f.readline()
+            f.seek(0,0)
+            if ',' in s:
+                sep = ','
+            elif '\t' in s:
+                sep = '\t'
+            else:
+                sep = ' '
+            if sep in header:
+                logging.warning('Probably no header or malformed .csv. Will try to parse it raw.')
+                df = pd.read_csv(f, header=None, sep=sep)                  
+            else:
+                df = pd.read_csv(f, sep=sep)
+                df = None
+            df.columns = ['label','sampleid','snr']
+
+
+    logging.info('Using input:\n'+tabulate(df, headers='keys', tablefmt='psql'))                   
+    return df
+
+def read_annotations_file(fname):
+    if fname[-3:].lower() == 'xls':
+        df = pd.read_excel(fname)
+    elif fname[-4:].lower() == 'json':
+        df = pd.read_json(fname)
+    elif fname[-3:].lower() in ['txt', 'csv']:
+                                
+        with open(fname) as f:
+            header = f.readline()
+        
+            s = f.readline()
+            f.seek(0,0)
+            if ',' in s:
+                sep = ','
+            elif '\t' in s:
+                sep = '\t'
+            else:
+                sep = ' '
+            if sep in header:
+                logging.warning('Probably no header or malformed .csv. Will try to parse it raw.')
+                df = pd.read_csv(f, header=None, sep=sep)
+                df.columns = ['start', 'stop', 'class']                        
+            else:
+                df.columns = ['start', 'stop', 'class']
+                df = pd.read_csv(f, sep=sep)
+                df = None
+
+    logging.info('Using input:\n'+tabulate(df, headers='keys', tablefmt='psql'))                   
+    return df
 
 def run_demo():
     print("TODO: Implement run_demo()")
@@ -14,7 +106,10 @@
              score_events,
              score_backgrounds,
              **kwargs):
-    print("TODO: Implement simscene()")
+    logging.info('simscene() is not yet implemented')
+
+    
+    
              
 def not_implemented():
     print("TODO: not implemented")
@@ -26,7 +121,7 @@
     simscene.
     """
     argparser = argparse.ArgumentParser(
-            description="SimScene.py audio scene generator",
+            description="SimScene.py acoustic scene generator",
     )
     argparser.add_argument(
         'input_path',
@@ -34,61 +129,128 @@
         help="Path of a directory containing wave files for sound backgrounds (in the `background' sub-directory) or events (in `event')"
     )
     argparser.add_argument(
-        '-T', '--scene-duration',
+        'output_path',
+        type=str,
+        help="The directory the generated scenes and annotations will reside."
+    )    
+    argparser.add_argument(
+        'scene_duration',
         type=float,
         help="Duration of scene in seconds",
     )
+    scene_duration = None
+    
     argparser.add_argument(
         '-e', '--score-events',
         type=str,
         help="Score events file as a comma-separated text file (.csv, .txt), JSON (.json), or Excel (.xls) file"
     )
+    score_events = None
+    
     argparser.add_argument(
         '-b', '--score-backgrounds',
         type=str,
         help="Score backgrounds file as a comma-separated text file (.csv, .txt), JSON (.json), or Excel (.xls) file"
     )
+    score_backgrounds = None
+    
     argparser.add_argument(
         '-t', '--time-mode',
         type=str,
         help="Mode of spacing between events. `generate': values must be set for each track in the score files. `abstract': values are computed from an abstract representation of an existing acoustic scene. `replicate': values are replicated from an existing acousting scene.",
         choices=['generate', 'abstract', 'replicate']
     )
+    time_mode = 'generate'
+    
     argparser.add_argument(
         '-R', '--ebr-mode',
         type=str,
         help="Mode for Event to Background power level ratio. `generate': values must be set for each track in the score files. `abstract': values are computed from an abstract representation of an existing acoustic scene. `replicate': values are replicated from an existing acousting scene.",
         choices=['generate', 'abstract', 'replicate']
     )
+    ebr_mode = 'generate'
+    
     argparser.add_argument(
-        '--annotation-file',
+        '-A', '--annotation-file',
         type=float,
         help="If -R or -m are selected, this provides the source for sourcing the times or EBRs from ANNOTATION_FILE. ANNOTATION_FILE must be comma-separated text file (.csv, .txt), JSON (.json), or Excel (.xls)."
     )
+    annotation_file = None
+    
     argparser.add_argument(
-        '--audio-file',
+        '-a', '--audio-file',
         type=float,
         help="If -R or -m are selected, this provides the source for sourcing the times or EBRs from AUDIO_FILE. AUDIO_FILE must be a 44100Hz .wav file."
     )
+    audio_file = None
+    
     argparser.add_argument(
-        '-f', '--figure', action='count',
+        '-v', '--figure-verbosity', action='count',
         help="Increase figure verbosity. (Default) 0 - Don't save or display figures, 1 - Save pictures but do not display them, 2 - Save and display figures"
     )
+    figure_verbosity = None
+    
     argparser.add_argument(
-        '-C', '--channel',
-        type=int,
-        help="number of audio channels contained in file. (Default) 0 - 1 channel (mono), 1 - As many channels as sound classes (events+textures), 2 - Same as 1, each channel is saved in a separate .wav file."
+        '-C', '--channel-mode',
+        type=str,
+        help="number of audio channels contained in file. (Default) 'mono' - 1 channel (mono), 'classes' - As many channels as sound classes (events+textures), 'separate' - Same as 'classes', each channel is saved in a separate .wav file.",
+        choices=['mono', 'classes', 'separate']
     )
+    channel_mode = None
+    
     argparser.add_argument(
         '-m', '--min-space',
         type=float,
         help="Minimum space allowed between successive events (seconds). If -1, then allow overlapping between events."
     )
+    min_space = None
+    
     argparser.add_argument(
         '-c', '--end-cut',
         action='store_true',
         help="If the last sample ends after the scene ends then: if enabled, cut the sample to duration, else remove the sample."
     )
+    end_cut = None
+    
+    logging.basicConfig(level=logging.DEBUG)
+    
     args = argparser.parse_args()
-    not_implemented()
-    
+    if args.input_path:
+        input_path = args.input_path
+        logging.debug("Using `{}' as input path".format(input_path))
+    if args.output_path:
+        output_path = args.output_path
+        logging.debug("Saving to `{}'".format(output_path))
+    if args.scene_duration:
+        if not (args.score_backgrounds or args.score_events):
+            print("You must provide one of -e or -b")
+        else:
+            if args.ebr_mode:
+                ebr_mode = args.ebr_mode
+                if ebr_mode not in ['generate']:
+                    logging.warning("`{}' not yet implemented for EBR_MODE, using default.".format(ebr_mode))
+                    ebr_mode = 'generate'
+            if args.time_mode:
+                time_mode = args.time_mode
+                if time_mode not in ['generate']:
+                    logging.warning("`{}' not yet implemented for TIME_MODE, using default.".format(time_mode))
+                    time_mode = 'generate'
+            if args.annotation_file:
+                annotations = read_annotations_file(args.annotation_file)
+                
+            scene_duration = float(args.scene_duration)
+            
+            if args.score_backgrounds:
+                score_backgrounds = read_backgrounds_file(args.score_backgrounds)
+            if args.score_events:
+                score_events = read_events_file(args.score_events)
+
+            simscene(input_path, output_path, scene_duration, score_events, score_backgrounds,
+                     time_mode=time_mode,
+                     ebr_mode=ebr_mode,
+                     channel_mode=channel_mode,
+                     annotation_file=annotation_file,
+                     audio_file=audio_file,
+                     figure_verbosity=figure_verbosity,
+                     min_space=min_space,
+                     end_cut=end_cut)