Mercurial > hg > webaudioevaluationtool
diff scripts/timeline_view_movement.py @ 1316:279930a008ca
All interfaces support comment boxes. Comment box identification matches presented tag (for instance, AB will be Comment on fragment A, rather than 1). Tighter buffer loading protocol, audioObjects register with the buffer rather than checking for buffer existence (which can be buggy depending on the buffer state). Buffers now have a state to ensure exact location in loading chain (downloading, decoding, LUFS, ready).
author | Nicholas Jillings <nickjillings@users.noreply.github.com> |
---|---|
date | Fri, 29 Jan 2016 11:11:57 +0000 |
parents | |
children | 235594325b84 b5bf2f57187c |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/timeline_view_movement.py Fri Jan 29 11:11:57 2016 +0000 @@ -0,0 +1,311 @@ +#!/usr/bin/python + +import xml.etree.ElementTree as ET +import os # list files in directory +import sys # command line arguments +import matplotlib.pyplot as plt # plots +import matplotlib.patches as patches # rectangles + + +# COMMAND LINE ARGUMENTS + +assert len(sys.argv)<3, "timeline_view_movement takes at most 1 command line argument\n"+\ + "Use: python timeline_view_movement.py [XML_files_location]" + +# XML results files location +if len(sys.argv) == 1: + folder_name = "../saves" # Looks in 'saves/' folder from 'scripts/' folder + print "Use: python timeline_view_movement.py [XML_files_location]" + print "Using default path: " + folder_name +elif len(sys.argv) == 2: + folder_name = sys.argv[1] # First command line argument is folder + +# check if folder_name exists +if not os.path.exists(folder_name): + #the file is not there + print "Folder '"+folder_name+"' does not exist." + sys.exit() # terminate script execution +elif not os.access(os.path.dirname(folder_name), os.W_OK): + #the file does exist but write privileges are not given + print "No write privileges in folder '"+folder_name+"'." + + +# CONFIGURATION + +# Folder where to store timelines +timeline_folder = folder_name + '/timelines_movement/' # Stores in 'saves/timelines_movement/' by default + +# Font settings +font = {'weight' : 'bold', + 'size' : 16} +plt.rc('font', **font) + +# Colormap for to cycle through +colormap = ['b', 'g', 'c', 'm', 'y', 'k'] + +# figure size +fig_width = 25 +fig_height = 10 + + +# CODE + +# create timeline_folder if not yet created +if not os.path.exists(timeline_folder): + os.makedirs(timeline_folder) + +# get every XML file in folder +for file in os.listdir(folder_name): + if file.endswith(".xml"): + tree = ET.parse(folder_name + '/' + file) + root = tree.getroot() + subject_id = file[:-4] # drop '.xml' + + previous_audioholder_time = 0 # time spent before current audioholder + time_offset = 0 # test starts at zero + + # ONE TIMELINE PER PAGE - make new plot per page + + # get list of all page names + for audioholder in root.findall("./audioholder"): # iterate over pages + page_name = audioholder.get('id') # get page name + plot_empty = True # check if any data is plotted + + if page_name is None: # ignore 'empty' audio_holders + print "Skipping empty audioholder name from "+subject_id+"." + break + + # subtract total audioholder length from subsequent audioholder event times + audioholder_time_temp = audioholder.find("./metric/metricresult/[@id='testTime']") + if audioholder_time_temp is not None: + audioholder_time = float(audioholder_time_temp.text) + else: + print "Skipping audioholder without total time specified from "+subject_id+"." + break + + # get audioelements + audioelements = audioholder.findall("./audioelement") + + # sort alphabetically + data = [] + for elem in audioelements: # from http://effbot.org/zone/element-sort.htm + key = elem.get("id") + data.append((key, elem)) + data.sort() + + N_audioelements = len(audioelements) # number of audio elements for this page + increment = 0 # increased for every new audioelement + + # get axes handle + fig = plt.figure(figsize=(fig_width, fig_height)) + ax = fig.add_subplot(111) + + # for page [page_name], print comments related to fragment [id] + #for tuple in data: + # audioelement = tuple[1] + for tuple in data: + audioelement = tuple[1] + if audioelement is not None: # Check it exists + audio_id = str(audioelement.get('id')) + + # break if no initial position or move events registered + initial_position_temp = audioelement.find("./metric/metricresult/[@name='elementInitialPosition']") + if initial_position_temp is None: + print "Skipping "+page_name+" from "+subject_id+": does not have initial positions specified." + break + + # get move events, initial and eventual position + initial_position = float(initial_position_temp.text) + move_events = audioelement.findall("./metric/metricresult/[@name='elementTrackerFull']/timepos") + final_position = float(audioelement.find("./value").text) + + # get listen events + start_times_global = [] + stop_times_global = [] + listen_events = audioelement.findall("./metric/metricresult/[@name='elementListenTracker']/event") + for event in listen_events: + # get testtime: start and stop + start_times_global.append(float(event.find('testtime').get('start'))-time_offset) + stop_times_global.append(float(event.find('testtime').get('stop'))-time_offset) + + # display fragment name at start + plt.text(0,initial_position+0.02,audio_id,color=colormap[increment%len(colormap)]) #,rotation=45 + + # previous position and time + previous_position = initial_position + previous_time = 0 + + # assume not playing at start + currently_playing = False # keep track of whether fragment is playing during move event + + # draw all segments except final one + for event in move_events: + # mark this plot as not empty + plot_empty = False + + # get time and final position of move event + new_time = float(event.find("./time").text)-time_offset + new_position = float(event.find("./position").text) + + # get play/stop events since last move until current move event + stop_times = [] + start_times = [] + # is there a play and/or stop event between previous_time and new_time? + for time in start_times_global: + if time>previous_time and time<new_time: + start_times.append(time) + for time in stop_times_global: + if time>previous_time and time<new_time: + stop_times.append(time) + # if no play/stop events between move events, find out whether playing + + segment_start = previous_time # first segment starts at previous move event + + # draw segments (horizontal line) + while len(start_times)+len(stop_times)>0: # while still play/stop events left + if len(stop_times)<1: # upcoming event is 'play' + # draw non-playing segment from segment_start to 'play' + currently_playing = False + segment_stop = start_times.pop(0) # remove and return first item + elif len(start_times)<1: # upcoming event is 'stop' + # draw playing segment (red) from segment_start to 'stop' + currently_playing = True + segment_stop = stop_times.pop(0) # remove and return first item + elif start_times[0]<stop_times[0]: # upcoming event is 'play' + # draw non-playing segment from segment_start to 'play' + currently_playing = False + segment_stop = start_times.pop(0) # remove and return first item + else: # stop_times[0]<start_times[0]: upcoming event is 'stop' + # draw playing segment (red) from segment_start to 'stop' + currently_playing = True + segment_stop = stop_times.pop(0) # remove and return first item + + # draw segment + plt.plot([segment_start, segment_stop], # x-values + [previous_position, previous_position], # y-values + color='r' if currently_playing else colormap[increment%len(colormap)], + linewidth=3 + ) + segment_start = segment_stop # move on to next segment + currently_playing = not currently_playing # toggle to draw final segment correctly + + # draw final segment (horizontal line) from last 'segment_start' to current move event time + plt.plot([segment_start, new_time], # x-values + [previous_position, previous_position], # y-values + # color depends on playing during move event or not: + color='r' if currently_playing else colormap[increment%len(colormap)], + linewidth=3 + ) + + # vertical line from previous to current position + plt.plot([new_time, new_time], # x-values + [previous_position, new_position], # y-values + # color depends on playing during move event or not: + color='r' if currently_playing else colormap[increment%len(colormap)], + linewidth=3 + ) + + # update previous_position value + previous_position = new_position + previous_time = new_time + + + + # draw final horizontal segment (or only segment if audioelement not moved) + # horizontal line from previous time to end of audioholder + + # get play/stop events since last move until current move event + stop_times = [] + start_times = [] + # is there a play and/or stop event between previous_time and new_time? + for time in start_times_global: + if time>previous_time and time<audioholder_time-time_offset: + start_times.append(time) + for time in stop_times_global: + if time>previous_time and time<audioholder_time-time_offset: + stop_times.append(time) + # if no play/stop events between move events, find out whether playing + + segment_start = previous_time # first segment starts at previous move event + + # draw segments (horizontal line) + while len(start_times)+len(stop_times)>0: # while still play/stop events left + # mark this plot as not empty + plot_empty = False + if len(stop_times)<1: # upcoming event is 'play' + # draw non-playing segment from segment_start to 'play' + currently_playing = False + segment_stop = start_times.pop(0) # remove and return first item + elif len(start_times)<1: # upcoming event is 'stop' + # draw playing segment (red) from segment_start to 'stop' + currently_playing = True + segment_stop = stop_times.pop(0) # remove and return first item + elif start_times[0]<stop_times[0]: # upcoming event is 'play' + # draw non-playing segment from segment_start to 'play' + currently_playing = False + segment_stop = start_times.pop(0) # remove and return first item + else: # stop_times[0]<start_times[0]: upcoming event is 'stop' + # draw playing segment (red) from segment_start to 'stop' + currently_playing = True + segment_stop = stop_times.pop(0) # remove and return first item + + # draw segment + plt.plot([segment_start, segment_stop], # x-values + [previous_position, previous_position], # y-values + color='r' if currently_playing else colormap[increment%len(colormap)], + linewidth=3 + ) + segment_start = segment_stop # move on to next segment + currently_playing = not currently_playing # toggle to draw final segment correctly + + # draw final segment (horizontal line) from last 'segment_start' to current move event time + plt.plot([segment_start, audioholder_time-time_offset], # x-values + [previous_position, previous_position], # y-values + # color depends on playing during move event or not: + color='r' if currently_playing else colormap[increment%len(colormap)], + linewidth=3 + ) + +# plt.plot([previous_time, audioholder_time-time_offset], # x-values +# [previous_position, previous_position], # y-values +# color=colormap[increment%len(colormap)], +# linewidth=3 +# ) + + # display fragment name at end + plt.text(audioholder_time-time_offset,previous_position,\ + audio_id,color=colormap[increment%len(colormap)]) #,rotation=45 + + increment+=1 # to next audioelement + + last_audioholder_duration = audioholder_time-time_offset + time_offset = audioholder_time + + if not plot_empty: # if plot is not empty, show or store + # set plot parameters + plt.title('Timeline ' + file + ": "+page_name) + plt.xlabel('Time [seconds]') + plt.xlim(0, last_audioholder_duration) + plt.ylabel('Rating') # default + plt.ylim(0, 1) # rating between 0 and 1 + + #y-ticks: labels on rating axis + label_positions = [] + label_text = [] + scale_tags = root.findall("./BrowserEvalProjectDocument/audioHolder/interface/scale") + scale_title = root.find("./BrowserEvalProjectDocument/audioHolder/interface/title") + for tag in scale_tags: + label_positions.append(float(tag.get('position'))/100) # on a scale from 0 to 100 + label_text.append(tag.text) + if len(label_positions) > 0: # if any labels available + plt.yticks(label_positions, label_text) # show rating axis labels + # set label Y-axis + if scale_title is not None: + plt.ylabel(scale_title.text) + + #plt.show() # uncomment to show plot; comment when just saving + #exit() + + plt.savefig(timeline_folder+subject_id+"-"+page_name+".pdf", bbox_inches='tight') + plt.close() + \ No newline at end of file