def create_waveform_and_spectrogram_from_wav_file(wavFilename,
													startTime=0,
													endTime=-1,
													maxFrequency=5500.,
													onlySpectrogramColorbar=True,
													showPitch=False,
													showIntensity=False,
													showFormants=False,
													maxFormantFrequency=None,
													dynamicRange=None,
													dynamicRangeMin=None,
													showTextgridFormants=False,
													wavFormAnnotations=None,
													output_runtime=True):
	global_values.plot_counter += 1
	start_time = time.perf_counter()
	####################################################################################
	### Check and fix arguments for problems OR to set default values (if necessary)
	####################################################################################
	##----------------------------------------------------------------------##
	### No audio file specified... can't do anything
	##----------------------------------------------------------------------##
	if wavFilename is None or wavFilename == '':
		raise Exception("praatFormants.py->scatterPlotFormants: No audio filename provided")

	wav_file_path = audio_file_helper.get_full_wav_file_path(wavFilename)
	
	##----------------------------------------------------------------------##
	### Check that the specified file actually exists
	##----------------------------------------------------------------------##
	global_path_helper.verify_file_exists(wav_file_path)
	
	if wavFormAnnotations is None:
		wavFormAnnotations=[]
	
	# Close other existing plots to avoid issues
	plt.close("all")

	# Create subplots for waveform, spectrogram, and colorbar
		# get the associated figure and axes
	fig, axs = matplotlib.pyplot.subplots(ncols=1,
											nrows=2,
											figsize=(global_values.figure_width, global_values.figure_height),
											dpi=global_values.image_DPI)
	fig.subplots_adjust(right=0.95)  # making some room for cbar

	##-----------------------------------##
	### Plot waveform
	##-----------------------------------##
	waveform_start_time = time.perf_counter()
	create_waveform_from_wav_file(wav_file_path,
									startTime=startTime,
									endTime=endTime,
									isSubplot=True,
									ax=axs[0],
									annotations=wavFormAnnotations)
	waveform_runtime = time.perf_counter() - waveform_start_time

	##-----------------------------------##
	### Plot spectrogram
	##-----------------------------------##
	spectrogram_start_time = time.perf_counter()
	colorImageMap = create_spectrogram_from_wav_file(wav_file_path,
														startTime=startTime,
														endTime=endTime,
														maxFrequency=maxFrequency,
														showPitch=showPitch,
														showIntensity=showIntensity,
														showFormants=showFormants,
														maxFormantFrequency=maxFormantFrequency,
														dynamicRange=dynamicRange,
														dynamicRangeMin=dynamicRangeMin,
														showTextgridFormants=showTextgridFormants,
														isSubplot=True,
														ax=axs[1])
	spectrogram_runtime = time.perf_counter() - spectrogram_start_time

	##-----------------------------------##
	### Create/plot colorbar (for spectrogram)
	##-----------------------------------##
	# getting the lower left (x0,y0) and upper right (x1,y1) corners:
	[[x00,y00],[x01,y01]] = axs[0].get_position().get_points()
	[[x10,y10],[x11,y11]] = axs[1].get_position().get_points()
	# Set width of colorbar
	colorbarPadding = 0.045
	# Set padding between subplots and colorbar
	colorbarWidth = 0.0125
	# If colorbar should only be height of spectrogram
		# otherwise, set colorbar height to be complete figure
	if onlySpectrogramColorbar:
		cbarAxis = fig.add_axes([x11+colorbarPadding, y10, colorbarWidth, y11-y10])
	else:
		cbarAxis = fig.add_axes([x11+colorbarPadding, y10, colorbarWidth, y01-y10])
	# Create colorbar using color image map from spectrogram and colorbar axis dimensions
	cbar = create_spectrogram_colorbar("Level (dB)",
										colorImageMap,
										theFigure=fig,
										theColorbarAxis=cbarAxis,
										theParentAxis=axs[1])

	#####################
	### Put waveform axis on top so that annotations/text will be
	### visible if there is any overlapping
	#####################
	axs[0].set_zorder(50)
	axs[1].set_zorder(1)

	#####################
	### Save plot to an image
	#####################
	# You can set the format by changing the extension
	# (e.g. .pdf, .svg, .eps)
	filename = global_path_helper.get_filename_only(wav_file_path) + '_waveform'
	filename = image_file_helper.fix_filename_conflicts(filename)
	
	# The frameon kwarg to savefig and the rcParams["savefig.frameon"] rcParam.
	# To emulate frameon = False, set facecolor to fully transparent ("none", or (0, 0, 0, 0)).
	
	fig.patch.set_alpha(0.0)
	axs[0].patch.set_alpha(0.0)
	axs[1].patch.set_alpha(0.0)
	plt.savefig(global_values.python_images_dir + '{0:0>2}'.format(global_values.plot_counter) + filename,
					format=global_values.image_format,
					bbox_inches='tight',
					dpi=global_values.image_DPI,
					#frameon=False,
					aspect='normal',
					pad_inches=0.15,
					facecolor=fig.get_facecolor(),
					edgecolor='none',
					transparent=global_values.image_transparency)
	
	### Optimize the SVG file
	image_file_helper.optimize_SVG(global_values.cli_python_images_dir + '{0:0>2}'.format(global_values.plot_counter) + filename)
	
	### Display plot in GUI window, if desired
	#display_plot(fig, plt)

	# Clear axis
	plt.cla()
	# Clear figure
	plt.clf()
	# Close a figure window
	plt.close()

	#####################
	### Print plot image to LaTeX
	#####################
	if global_values.image_format == 'svg' or global_values.image_format == 'svgz':
		### Normalize SVGz to a standarized width (defined by global_values.normalized_SVGz_width)
		#image_file_helper.normalize_SVGz_width(filename)
		### Output \includegraphic command for Tex
		image_file_helper.create_TeX_include_SVG_command(filename)
	
	if output_runtime:
		runtimes_output = '------------------------------------\n'
		runtimes_output += 'Runtimes for ' + wav_file_path + '\n'
		runtimes_output += 'waveform plot time: {:.7f}\n'.format(waveform_runtime)
		runtimes_output += 'spectrogram  plot time: {:.7f}\n'.format(spectrogram_runtime)
		runtimes_output += 'waveform+spectrogram plot time: {:.7f}\n'.format(time.perf_counter() - start_time)
		runtimes_output += '\n'
		global_path_helper.append_to_file(global_values.python_runtimes_dir + 'create_waveform_and_spectrogram_from_wav_file.log', runtimes_output)
def plotIntensity(wav_file_path,
                  startTime=0.0,
                  endTime=-1,
                  minFrequency=100,
                  maxFrequency=5000.0,
                  sampleRate=None,
                  limitToTextgrid=False,
                  ax=None):
    ####################################################################################
    ### Check and fix arguments for problems OR to set default values (if necessary)
    ####################################################################################
    ##----------------------------------------------------------------------##
    ### No audio file specified... can't do anything
    ##----------------------------------------------------------------------##
    if wav_file_path is None or wav_file_path == '':
        raise Exception(
            "praatIntensity.py->plotIntensity: No audio filename provided")

    ##----------------------------------------------------------------------##
    ### Check that the specified file actually exists
    ##----------------------------------------------------------------------##
    global_path_helper.verify_file_exists(wav_file_path)

    ### Set minimum viewing frequency for pitch to 0 Hz, if not set properly
    if minFrequency == None:
        minFrequency = 100.0
    ### Set maximum viewing frequency for pitch to 5000 Hz, if not set properly
    if maxFrequency == None:
        maxFrequency = 5000.0

    ##----------------------------------------------------------------------##
    ### Create a plot object which is either matplotlib.pyplot (if the axis argument was not specified)
    ##### or matplotlib.ax.Axis (if the axis argument was specified)
    ##----------------------------------------------------------------------##
    if ax == None or ax is None:
        the_plot = plt
    else:
        the_plot = ax

    ####################################################################################
    ### Calculate all intensities using Praat in the audio file (within start/end times)
    ##### and then break the intensities into continuous intervals
    ####################################################################################
    ##----------------------------------------------------------------------##
    ### Have Praat calculate all times and intensities for audio file
    ##----------------------------------------------------------------------##
    times, intensities = praatUtil.calculateIntensity(wav_file_path,
                                                      startTime=startTime,
                                                      endTime=endTime,
                                                      fMin=minFrequency,
                                                      timeStep=0.0,
                                                      subtractMean=True)

    ##----------------------------------------------------------------------##
    ### If the start time is not specified OR is less than the minimum one in Praat data,
    ##### then use the first time of the Praat data
    ##----------------------------------------------------------------------##
    if startTime == 0.0 or startTime < times[0]:
        startTime = times[0]
    else:
        cropped_start_time = numpy.unravel_index((times >= startTime).argmax(),
                                                 times.shape)[0]
        times = times[cropped_start_time:]
        intensities = intensities[cropped_start_time:]
    ##----------------------------------------------------------------------##
    ### If the end time is not specified OR is greater than the maximum one in Praat data,
    ##### then use the last time of the Praat data
    ##----------------------------------------------------------------------##
    if endTime == -1 or endTime > times[-1]:
        endTime = times[-1]
    else:
        cropped_end_time = numpy.unravel_index((times >= endTime).argmax(),
                                               times.shape)[0]
        times = times[:cropped_end_time]
        intensities = intensities[:cropped_end_time]

    ####################################################################################
    ### Normalize intensities onto the spectrogram
    ####################################################################################
    ### Minimum intensity of all intensities
    min_intensity = numpy.amin(intensities)
    ### Maximum intensity of all intensities
    max_intensity = numpy.amax(intensities)
    intensities -= min_intensity
    intensities /= (max_intensity - min_intensity)
    intensities *= maxFrequency

    ####################################################################################
    ### Plot intensity data points
    ####################################################################################
    the_plot.plot(
        times,
        intensities,
        c='y',
        linewidth=2,
        path_effects=[pe.Stroke(linewidth=6, foreground='k'),
                      pe.Normal()],
        zorder=2)
def create_pitch_and_table_from_wav_file(
        wavFilename,
        xaxisLabel="Time (s)",
        yaxisLabel="Frequency (Hz)",
        startTime=0.0,
        endTime=-1,
        timeStep=0.0,  #Praat default is 0.0 (calculated)
        minPitch=75.0,
        maxPitch=600.0,
        silenceThreshold=0.03,  #Praat default is 0.03
        voicingThreshold=0.45,  #Praat default is 0.45
        octaveCost=0.01,  #Praat default is 0.01
        octaveJumpCost=0.35,  #Praat default is 0.35
        voicedUnvoicedCost=0.14,  #Praat default is 0.14
        showIntensity=False,
        languageBreaks=[],
        language=[],
        glossBreaks=[],
        glosses=[],
        removeMaximums=[],
        output_runtime=True):
    global_values.plot_counter += 1
    start_time = time.perf_counter()
    ####################################################################################
    ### Check and fix arguments for problems OR to set default values (if necessary)
    ####################################################################################
    ##----------------------------------------------------------------------##
    ### No audio file specified... can't do anything
    ##----------------------------------------------------------------------##
    if wavFilename is None or wavFilename == '':
        raise Exception(
            "plotPitchTable.py->create_pitch_and_table_from_wav_file: No audio filename provided"
        )

    wav_file_path = audio_file_helper.get_full_wav_file_path(wavFilename)

    ##----------------------------------------------------------------------##
    ### Check that the specified file actually exists
    ##----------------------------------------------------------------------##
    global_path_helper.verify_file_exists(wav_file_path)

    if languageBreaks is None:
        languageBreaks = []
    if language is None:
        language = []
    if glossBreaks is None:
        glossBreaks = []
    if glosses is None:
        glosses = []
    if removeMaximums is None:
        removeMaximums = []

    ### Close other existing plots to avoid issues
    plt.close("all")

    ### Create subplots for waveform, spectrogram, and colorbar
    ##### get the associated figure and axes
    fig, axs = matplotlib.pyplot.subplots(
        ncols=1,
        nrows=1,
        figsize=(global_values.figure_width, global_values.figure_height),
        dpi=global_values.image_DPI)

    pitch_start_time = time.perf_counter()
    praatPitch.plotPurePitch(
        wav_file_path,
        startTime=startTime,
        endTime=endTime,
        timeStep=timeStep,  #Praat default is 0.0 (calculated)
        minPitchHz=minPitch,  #Praat default is 75
        maxPitchHz=maxPitch,  #Praat default is 500
        silenceThreshold=silenceThreshold,  #Praat default is 0.03
        voicingThreshold=voicingThreshold,  #Praat default is 0.45
        octaveCost=octaveCost,  #Praat default is 0.01
        octaveJumpCost=octaveJumpCost,  #Praat default is 0.35
        voicedUnvoicedCost=voicedUnvoicedCost,  #Praat default is 0.14
        #silenceThreshold = 0.03,#Praat default is 0.03
        #voicingThreshold = 0.45,#Praat default is 0.45
        #octaveCost = 0.01,#Praat default is 0.01
        #octaveJumpCost = 0.35,#Praat default is 0.35
        #voicedUnvoicedCost = 0.14,#Praat default is 0.14
        removeMaximums=removeMaximums,
        ax=axs)
    pitch_runtime = time.perf_counter() - pitch_start_time

    ##-----------------------------------##
    ###Show intensity (plot)
    ##-----------------------------------##
    '''if showIntensity:
		praatIntensity.plotIntensity(wav_file_path,
										startTime=startTime,
										endTime=endTime,
										maxFrequency=maxFrequency,
										sampleRate=samplerate,
										minFrequency=100,
										limitToTextgrid=False,
										ax=ax)'''

    #-----------------------------------##
    ###Create and show language/gloss table
    ##-----------------------------------##

    table_start_time = time.perf_counter()
    line_times = sorted(numpy.unique(languageBreaks + glossBreaks))

    for line_time in line_times:
        plt.plot(numpy.array([line_time, line_time]),
                 numpy.array([plt.ylim()[0], plt.ylim()[1]]),
                 c='k',
                 linewidth=0.25,
                 zorder=0)

    glosses_column_widths = []
    for index, break_point in enumerate(glossBreaks):
        if index == 0:
            glosses_column_widths.append(
                (break_point - startTime) / (endTime - startTime))
        else:
            glosses_column_widths.append(
                (break_point - glossBreaks[index - 1]) / (endTime - startTime))
    glosses_column_widths.append(
        (endTime - glossBreaks[-1]) / (endTime - startTime))

    language_column_widths = []
    for index, break_point in enumerate(languageBreaks):
        if index == 0:
            language_column_widths.append(
                (break_point - startTime) / (endTime - startTime))
        else:
            language_column_widths.append(
                (break_point - languageBreaks[index - 1]) /
                (endTime - startTime))
    language_column_widths.append(
        (endTime - languageBreaks[-1]) / (endTime - startTime))

    gloss_table = axs.table(cellText=[glosses, glosses],
                            colWidths=glosses_column_widths,
                            cellLoc='center',
                            loc='bottom')
    language_table = axs.table(cellText=[language],
                               colWidths=language_column_widths,
                               cellLoc='center',
                               loc='bottom')

    for key, cell in gloss_table.get_celld().items():
        cell.set_text_props(fontproperties=global_values.gloss_fontproperties)

    for key, cell in language_table.get_celld().items():
        cell.set_text_props(
            fontproperties=global_values.language_fontproperties)

    for cell in gloss_table._cells:
        if gloss_table._cells[cell]._text.get_text() == '':
            gloss_table._cells[cell].set_color('lightgrey')
            gloss_table._cells[cell].set_edgecolor('black')
        else:
            if '{sc}' in gloss_table._cells[cell]._text.get_text():
                gloss_table._cells[cell].set_text_props(
                    fontproperties=global_values.
                    gloss_small_caps_fontproperties)
                gloss_table._cells[cell]._text.set_text(
                    gloss_table._cells[cell]._text.get_text().replace(
                        '{sc}', '{}'))
            if '{}' in gloss_table._cells[cell]._text.get_text():
                if gloss_table._cells[cell]._text.get_text().startswith('{}'):
                    gloss_table._cells[cell]._loc = 'left'
                    gloss_table._cells[cell].PAD = 0.04
                    gloss_table._cells[cell]._text.set_horizontalalignment(
                        'left')
                elif gloss_table._cells[cell]._text.get_text().endswith('{}'):
                    gloss_table._cells[cell]._loc = 'right'
                    gloss_table._cells[cell]._text.set_horizontalalignment(
                        'right')
                    gloss_table._cells[cell].PAD = 0.03
                gloss_table._cells[cell]._text.set_text(
                    gloss_table._cells[cell]._text.get_text().replace(
                        '{}', ''))

    for cell in language_table._cells:
        if language_table._cells[cell]._text.get_text() == '':
            language_table._cells[cell].set_color('lightgrey')
            language_table._cells[cell].set_edgecolor('black')
        else:
            if '{}' in language_table._cells[cell]._text.get_text():
                if language_table._cells[cell]._text.get_text().startswith(
                        '{}'):
                    language_table._cells[cell]._loc = 'left'
                    language_table._cells[cell].PAD = 0.04
                    language_table._cells[cell]._text.set_horizontalalignment(
                        'left')
                elif language_table._cells[cell]._text.get_text().endswith(
                        '{}'):
                    language_table._cells[cell]._loc = 'right'
                    language_table._cells[cell]._text.set_horizontalalignment(
                        'right')
                    language_table._cells[cell].PAD = 0.03
                language_table._cells[cell].set_text_props(
                    fontproperties=global_values.language_fontproperties)
                language_table._cells[cell]._text.set_text(
                    language_table._cells[cell]._text.get_text().replace(
                        '{}', ''))

    #gloss_table.set_fontsize(18)
    #language_table.set_fontsize(18)
    gloss_table.set_fontsize(24)
    language_table.set_fontsize(24)
    gloss_table.scale(1, 3)
    language_table.scale(1, 3)

    table_runtime = time.perf_counter() - table_start_time

    ##-----------------------------------##
    ###Format x-axis and y-axis
    ##-----------------------------------##
    plt.tick_params(axis='both',
                    which='major',
                    labelsize=global_values.axis_tick_font_size)
    for label in axs.get_xticklabels():
        label.set_fontproperties(global_values.axis_tick_fontproperties)
    plt.ylabel(yaxisLabel,
               fontproperties=global_values.axis_label_fontproperties,
               fontsize=global_values.axis_label_font_size)
    plt.xlabel(xaxisLabel,
               fontproperties=global_values.axis_label_fontproperties,
               fontsize=global_values.axis_label_font_size,
               labelpad=-1 * int(global_values.axis_label_font_size / 3))

    axs.spines['bottom'].set_position(
        ('data', plt.ylim()[0] -
         ((plt.ylim()[1] - plt.ylim()[0]) *
          (gloss_table.properties()['child_artists'][0].get_height() * 2))))

    #####################
    ###Save plot to an image
    #####################
    #You can set the format by changing the extension
    #(e.g. .pdf, .svg, .eps)
    '''filename = global_path_helper.get_filename_only(wav_file_path) + '_pitch_plot'
	#filename_CMKY = filename_RGB + '_CMKY'
	
	#filename_RGB, filename_CMKY = image_file_helper.fix_RGB_and_CMYK_filenames(filename_RGB, filename_CMKY)
	filename = image_file_helper.fix_filename_conflicts(filename)
	
	#if global_values.image_format == 'svg' or global_values.image_format == 'svgz':
	#filename_CMKY = filename_RGB
	
	plt.savefig(global_values.python_images_dir + '{0:0>2}'.format(global_values.plot_counter) + filename,
					fontproperties=global_values.generic_fontproperties,
					format=global_values.image_format,
					bbox_inches='tight',
					dpi=global_values.image_DPI,
					#dpi=600,#global_values.image_DPI*2,
					frameon='false',
					aspect='normal',
					pad_inches=0.15,
					transparent=global_values.image_transparency)'''
    filename = global_path_helper.get_filename_only(
        wav_file_path) + '_pitch_plot'
    filename = image_file_helper.fix_filename_conflicts(filename)

    fig.patch.set_alpha(0.0)
    #axs[0].patch.set_alpha(0.0)
    #axs[1].patch.set_alpha(0.0)
    axs.patch.set_alpha(0.0)
    plt.savefig(global_values.python_images_dir +
                '{0:0>2}'.format(global_values.plot_counter) + filename,
                format=global_values.image_format,
                bbox_inches='tight',
                dpi=global_values.image_DPI,
                frameon=False,
                aspect='normal',
                pad_inches=0.15,
                facecolor=fig.get_facecolor(),
                edgecolor='none',
                transparent=global_values.image_transparency)

    ### Optimize the SVG file
    image_file_helper.optimize_SVG(
        global_values.cli_python_images_dir +
        '{0:0>2}'.format(global_values.plot_counter) + filename)

    #Clear axis
    plt.cla()
    #Clear figure
    plt.clf()
    #Close a figure window
    plt.close()

    #####################
    ###Print plot image to LaTeX
    #####################
    if global_values.image_format == 'svg' or global_values.image_format == 'svgz':
        ### Normalize SVGz to a standarized width (defined by global_values.normalized_SVGz_width)
        #image_file_helper.normalize_SVGz_width(filename)
        ### Output \includegraphic command for Tex
        image_file_helper.create_TeX_include_SVG_command(filename)

    if output_runtime:
        runtimes_output = '------------------------------------\n'
        runtimes_output += 'Runtimes for ' + wav_file_path + '\n'
        runtimes_output += 'pitch plot time: {:.7f}\n'.format(pitch_runtime)
        runtimes_output += 'table  plot time: {:.7f}\n'.format(table_runtime)
        runtimes_output += 'pitch+table plot time: {:.7f}\n'.format(
            time.perf_counter() - start_time)
        runtimes_output += '\n'
        global_path_helper.append_to_file(
            global_values.python_runtimes_dir +
            'create_pitch_and_table_from_wav_file.log', runtimes_output)
Esempio n. 4
0
def plotSpectrogramPitch(wav_file_path,
							startTime=0.0,
							endTime=-1,
							minViewFrequency=0.0,
							maxViewFrequency=5000.0,
							timeStep=0.0,#Praat default is 0.0
							minPitchHz=75,#Praat default is 75
							maxPitchHz=600.0,#Praat default is 600
							veryAccurate = False,#Praat default is False
							silenceThreshold = 0.0295,#Praat default is 0.03
							voicingThreshold = 0.45,#Praat default is 0.45
							octaveCost = 0.01,#Praat default is 0.01
							octaveJumpCost = 0.25,#Praat default is 0.35
							voicedUnvoicedCost = 0.26,#Praat default is 0.14
							normalizePitch = False,#Need to normalize pitch for spectrograms, but not pitch plots
							ax=None):
	####################################################################################
	### Check and fix arguments for problems OR to set default values (if necessary)
	####################################################################################
	##----------------------------------------------------------------------##
	### No audio file specified... can't do anything
	##----------------------------------------------------------------------##
	if wav_file_path is None or wav_file_path == '':
		raise Exception("praatPitch.py->plotSpectrogramPitch: No audio filename provided")
	
	##----------------------------------------------------------------------##
	### Check that the specified file actually exists
	##----------------------------------------------------------------------##
	global_path_helper.verify_file_exists(wav_file_path)
	
	### Set minimum viewing frequency for pitch to 0 Hz, if not set properly
	if minViewFrequency == None:
		minViewFrequency = 0.0
	### Set maximum viewing frequency for pitch to 5000 Hz, if not set properly
	if maxViewFrequency == None:
		maxViewFrequency = 5000.0
	### Set minimum pitch to 75 Hz, if not set properly
	if minPitchHz == None:
		minPitchHz = 75.0
	### Set maximum pitch to 75 Hz, if not set properly
	if maxPitchHz == None:
		maxPitchHz = 600.0
	
	##----------------------------------------------------------------------##
	### Create a plot object which is either matplotlib.pyplot (if the axis argument was not specified)
	##### or matplotlib.ax.Axis (if the axis argument was specified)
	##----------------------------------------------------------------------##
	if ax == None or ax is None:
		the_plot = plt
	else:
		the_plot = ax
	
	####################################################################################
	### Calculate all pitches using Praat in the audio file (within start/end times)
	##### and then break the pitches into continuous intervals
	####################################################################################
	##----------------------------------------------------------------------##
	### Have Praat calculate all times and pitches (F0) for audio file
	##----------------------------------------------------------------------##
	times, pitches = praatUtil.calculatePitch(wav_file_path,
												startTime=startTime,
												endTime=endTime,
												timeStep=timeStep,
												fMin=minPitchHz,
												fMax=maxPitchHz,
												veryAccurate = veryAccurate,
												silenceThreshold = silenceThreshold,
												voicingThreshold = voicingThreshold,
												octaveCost = octaveCost,
												octaveJumpCost = octaveJumpCost,
												voicedUnvoicedCost = voicedUnvoicedCost)
	
	##----------------------------------------------------------------------##
	### If the start time is not specified OR is less than the minimum one in Praat data, 
	##### then use the first time of the Praat data
	##----------------------------------------------------------------------##
	if startTime is None or startTime == 0.0 or startTime < times[0]:
		startTime = times[0]
	##----------------------------------------------------------------------##
	### If the end time is not specified OR is greater than the maximum one in Praat data, 
	##### then use the last time of the Praat data
	##----------------------------------------------------------------------##
	if endTime is None or endTime == -1 or endTime > times[-1]:
		endTime = times[-1]
	##----------------------------------------------------------------------##
	### If the timeStep is not specified OR is 0.0, 
	##### then use the time step determined by the Praat data
	##### Ignore the first time, because may be junk (due to start/end time being specified)
	##----------------------------------------------------------------------##
	if timeStep is None or int(timeStep) == 0:
		timeStep = times[2]-times[1]
	
	####################################################################################
	### Separate continuous data into different lists, and store each list
	##### in a single large list
	####################################################################################
	### List of each continuous time list
	croppedTimes = []
	### List of each continuous pitch list
	croppedPitches = []
	### (Temporary) list of continuous time
	nobreakTimes = []
	### (Temporary) list of continuous time
	nobreakPitches = []
	##----------------------------------------------------------------------##
	### Loop through pitch data (which was calculated by Praat)
	##----------------------------------------------------------------------##
	for index, pitch in enumerate(pitches):
		##----------------------------------------------------------------------##
		### Only use data within the specified start/end time range
		##----------------------------------------------------------------------##
		if times[index] >= startTime and times[index] <= endTime:
			##----------------------------------------------------------------------##
			### If the difference of time between last an current timestamp
			##### is approximately the time step used by Praat to calculate data
			##### then we assume it is continuous.
			##### Therefore, continue adding to the (temporary) continuous data list
			##----------------------------------------------------------------------##
			if index > 0 and (times[index]-times[index-1]) <= (timeStep*3):
				nobreakTimes.append(times[index])
				nobreakPitches.append(pitch)
			##----------------------------------------------------------------------##
			### Otherwise, it appears we reached a new set of continuous data.
			##### Therefore, store the previous continuous data list into the overall
			##### list of times and pitches.
			##----------------------------------------------------------------------##
			#elif nobreakTimes is not [] and nobreakPitches is not []:
			else:
				croppedTimes.append(nobreakTimes)
				croppedPitches.append(nobreakPitches)
				nobreakTimes = []
				nobreakPitches = []
	##----------------------------------------------------------------------##
	### Make sure that if the final list of continuous data is stored,
	##### in case it wasn't inside the above FOR loop.
	##----------------------------------------------------------------------##
	if len(nobreakTimes) > 0:
		croppedTimes.append(nobreakTimes)
		croppedPitches.append(nobreakPitches)
		nobreakTimes = []
		nobreakPitches = []
	
	####################################################################################
	### Calculate the normalized pitch frequencies so that they fit properly on
	##### a spectrogram.
	####################################################################################
	normalizedPitches = []
	normalizedNobreakPitches = []
	##----------------------------------------------------------------------##
	### Only normalize if instructed to do so
	##----------------------------------------------------------------------##
	if normalizePitch:
		##----------------------------------------------------------------------##
		### Need to iterate through each of the continuous data lists
		##----------------------------------------------------------------------##
		for eachPitchList in croppedPitches:
			##----------------------------------------------------------------------##
			### Iterate through continuous pitch data
			##----------------------------------------------------------------------##
			for pitch in eachPitchList:
				##----------------------------------------------------------------------##
				#### Calculate the normalized pitch frequency
				##### and store in temporary, continuous pitch data list
				##----------------------------------------------------------------------##
				normalizedNobreakPitches.append(((pitch-minPitchHz)/(maxPitchHz-minPitchHz)) * (maxViewFrequency-minViewFrequency))
			### Append continuous data list to list of all continuous pitches
			normalizedPitches.append(normalizedNobreakPitches)
			### Reset the temporary normalized continuous pitch data list
			normalizedNobreakPitches = []
	##----------------------------------------------------------------------##
	### We were instructed not to normalize
	##----------------------------------------------------------------------##
	else:
		normalizedPitches = croppedPitches
	
	####################################################################################
	### Loop through each set of continuous data points (for pitch)
	##### and plot each continuous line.
	####################################################################################
	for index, timeArray in enumerate(croppedTimes):
		the_plot.plot(numpy.array(timeArray),
						numpy.array(normalizedPitches[index]),
						c='b',
						linewidth=2,
						path_effects=[pe.Stroke(linewidth=4, foreground='w'), pe.Normal()],
						zorder=2)
	##----------------------------------------------------------------------##
	### Setup the y-axis
	##----------------------------------------------------------------------##
	# Set minimum and maximum x-axis values
	the_plot.set_ylim(minViewFrequency, maxViewFrequency)
Esempio n. 5
0
def plotPurePitch(wav_file_path,
					startTime=0.0,
					endTime=-1,
					timeStep=0.0,#Praat default is 0.0
					minPitchHz=75,#Praat default is False
					maxPitchHz=600.0,#Praat default is 600
					silenceThreshold = 0.0295,#Praat default is 0.03
					voicingThreshold = 0.45,#Praat default is 0.45
					octaveCost = 0.01,#Praat default is 0.01
					octaveJumpCost = 0.25,#Praat default is 0.35
					voicedUnvoicedCost = 0.26,#Praat default is 0.14
					removeMaximums = [],
					ax=None):
	####################################################################################
	### Check and fix arguments for problems OR to set default values (if necessary)
	####################################################################################
	##----------------------------------------------------------------------##
	### No audio file specified... can't do anything
	##----------------------------------------------------------------------##
	if wav_file_path is None or wav_file_path == '':
		raise Exception("praatPitch.py->plotPurePitch: No audio filename provided")
	
	##----------------------------------------------------------------------##
	### Check that the specified file actually exists
	##----------------------------------------------------------------------##
	global_path_helper.verify_file_exists(wav_file_path)
	
	### Set minimum pitch to 75 Hz, if note set properly
	if minPitchHz == None:
		minPitchHz = 75.0
	### Set maximum pitch to 75 Hz, if note set properly
	if maxPitchHz == None:
		maxPitchHz = 600.0
	
	##----------------------------------------------------------------------##
	### Create a plot object which is either matplotlib.pyplot (if the axis argument was not specified)
	##### or matplotlib.ax.Axis (if the axis argument was specified)
	##----------------------------------------------------------------------##
	if ax == None or ax is None:
		the_plot = plt
	else:
		the_plot = ax
	
	####################################################################################
	### Calculate all pitches using Praat in the audio file (within start/end times)
	##### and then break the pitches into continuous intervals
	####################################################################################
	##----------------------------------------------------------------------##
	### Have Praat calculate all times and pitches (F0) for audio file
	##----------------------------------------------------------------------##
	times, pitches = praatUtil.calculatePitch(wav_file_path,
												startTime=startTime,
												endTime=endTime,
												timeStep=timeStep,
												fMin=minPitchHz,
												fMax=maxPitchHz,
												veryAccurate = True,
												silenceThreshold = silenceThreshold,
												voicingThreshold = voicingThreshold,
												octaveCost = octaveCost,
												octaveJumpCost = octaveJumpCost,
												voicedUnvoicedCost = voicedUnvoicedCost)
	##----------------------------------------------------------------------##
	### If the start time is not specified, then use the first time of the Praat data
	##### OTHERWISE, fix the list of times to start at the specified start time
	##----------------------------------------------------------------------##
	'''if startTime == 0.0:
		startTime = times[0]
	else:
		cropped_start_time = numpy.unravel_index((times>=startTime).argmax(), times.shape)[0]
		times = times[cropped_start_time:]
		pitches = pitches[cropped_start_time:]
	##----------------------------------------------------------------------##
	### Use the last time of the Praat data, if not specified
	##----------------------------------------------------------------------##
	if endTime == -1:
		endTime = times[-1]
	else:
		cropped_end_time = numpy.unravel_index((times>=endTime).argmax(), times.shape)[0]
		times = times[:cropped_end_time]
		pitches = pitches[:cropped_end_time]'''
	
	##----------------------------------------------------------------------##
	### If the start time is not specified OR is less than the minimum one in Praat data, 
	##### then use the first time of the Praat data
	##----------------------------------------------------------------------##
	if startTime is None or startTime == 0.0 or startTime < times[0]:
		startTime = times[0]
	##----------------------------------------------------------------------##
	### If the end time is not specified OR is greater than the maximum one in Praat data, 
	##### then use the last time of the Praat data
	##----------------------------------------------------------------------##
	if endTime is None or endTime == -1 or endTime > times[-1]:
		endTime = times[-1]
	##----------------------------------------------------------------------##
	### If the timeStep is not specified OR is 0.0, 
	##### then use the time step determined by the Praat data
	##### Ignore the first time, because may be junk (due to start/end time being specified)
	##----------------------------------------------------------------------##
	if timeStep is None or timeStep == 0.0:
		timeStep = times[2]-times[1]
	
	####################################################################################
	### Separate continuous data into different lists, and store each list
	##### in a single large list
	####################################################################################
	### List of each continuous time list
	croppedTimes = []
	### List of each continuous pitch list
	croppedPitches = []
	### (Temporary) list of continuous time
	nobreakTimes = []
	### (Temporary) list of continuous time
	nobreakPitches = []
	##----------------------------------------------------------------------##
	### Loop through pitch data (which was calculated by Praat
	##----------------------------------------------------------------------##
	for index, pitch in enumerate(pitches):
		##----------------------------------------------------------------------##
		### Only use data within the specified start/end time range
		##----------------------------------------------------------------------##
		if times[index] >= startTime and times[index] <= endTime:
			##----------------------------------------------------------------------##
			### If the difference of time between last an current timestamp
			##### is approximately the time step used by Praat to calculate data
			##### then we assume it is continuous.
			##### Therefore, continue adding to the (temporary) continuous data list
			##----------------------------------------------------------------------##
			if index > 0 and (times[index]-times[index-1]) <= (timeStep*3):
				nobreakTimes.append(times[index])
				nobreakPitches.append(pitch)
			##----------------------------------------------------------------------##
			### Otherwise, it appears we reached a new set of continuous data.
			##### Therefore, store the previous continuous data list into the overall
			##### list of times and pitches.
			##----------------------------------------------------------------------##
			#elif nobreakTimes is not [] and nobreakPitches is not []:
			else:
				croppedTimes.append(nobreakTimes)
				croppedPitches.append(nobreakPitches)
				nobreakTimes = []
				nobreakPitches = []
	##----------------------------------------------------------------------##
	### Make sure that if the final list of continuous data is stored,
	##### in case it wasn't inside the above FOR loop.
	##----------------------------------------------------------------------##
	if len(nobreakTimes) > 0:
		croppedTimes.append(nobreakTimes)
		croppedPitches.append(nobreakPitches)
		nobreakTimes = []
		nobreakPitches = []
	
	####################################################################################
	### Calculate minimum and maximum y-axis frequencies
	##### so that interval is always 10, 20, 30, etc.
	##### depending on how large the maximum and minimum are
	####################################################################################
	### Temporarily set minimum y-axis frequency to maximum Praat pitch frequency
	minViewFrequency = maxPitchHz
	### Temporarily set maximum y-axis frequency to minimum Praat pitch frequency
	maxViewFrequency = minPitchHz
	##----------------------------------------------------------------------##
	### Determine minimum/maximum pitch frequencies from the pitches calculated by Praat 
	##----------------------------------------------------------------------##
	for pitchList in croppedPitches:
		### Ignore, unless the list of continuous pitches contains data
		if pitchList is not None and pitchList != []:
			minViewFrequency = min(pitchList) if min(pitchList) < minViewFrequency else minViewFrequency
			maxViewFrequency = max(pitchList) if max(pitchList) > maxViewFrequency else maxViewFrequency
	
	##----------------------------------------------------------------------##
	### Using minimum and maximum pitches from Praat,
	##### - calculate the interval between y-axis ticks
	##### - calculate the minimum y-axis tick value
	##### - calculate the maximum y-axis tick value
	##----------------------------------------------------------------------##
	yaxisInterval = round(math.ceil((maxViewFrequency-minViewFrequency) / 100) * 100) / 10
	minViewFrequency = int(math.floor(minViewFrequency / yaxisInterval)) * yaxisInterval
	maxViewFrequency = int(math.ceil(maxViewFrequency / yaxisInterval)) * yaxisInterval
	
	####################################################################################
	### Loop through each set of continuous data points (for pitch)
	##### and plot each continuous line.
	##### Also plot the (local) maximum pitches for each continuous line
	##### with a red line and red text indicating the pitch maximum.
	####################################################################################
	# A counter for how many maximum pitches have been plotted
	maximum_counter = 0
	# Loop through the list of lists.  Outer list contains each list of continuous times
	for index, timeArray in enumerate(croppedTimes):
		# Get the corresponding list of continous pitches
		pitchArray = croppedPitches[index]
		# Ignore any empty lists
		if timeArray is not None and timeArray != [] \
			and pitchArray is not None and pitchArray != []:			
			##----------------------------------------------------------------------##
			### Plot a continuous list of pitches, with a blue line
			##----------------------------------------------------------------------##
			the_plot.plot(numpy.array(timeArray),
						numpy.array(pitchArray),
						c='b',
						linewidth=2,
						path_effects=[pe.Stroke(linewidth=4, foreground='w'), pe.Normal()],
						zorder=2)
			
			####################################################################################
			### Plot a line and the numerical value for a (local) maximum pitch
			##### near the point in the plot of pitches
			####################################################################################
			#(Added an extra element at the beginning and end of the pitch list)
			#(->this is to help with finding local maximum pitches at the beginning/end of a line)
			prev_index_of_max = -1
			for index_of_max in scipy.signal.argrelextrema(numpy.array([0]+pitchArray+[0]), numpy.greater)[0]:
				index_of_max -= 1# Because we added an extra element, need to access the previous element in time and pitch lists
				# Make sure we only added maximums that are not excluded
				if maximum_counter not in removeMaximums:
					##----------------------------------------------------------------------##
					### Plot a small horizontal at the pitch maximum 
					##----------------------------------------------------------------------##
					red_line_xvalues = [timeArray[index_of_max],
										timeArray[index_of_max] + ((endTime-startTime) * 0.02),
										timeArray[index_of_max] + ((endTime-startTime) * 0.11)]
					
					red_line_height_percent = 0.05
					if prev_index_of_max > -1 and (timeArray[index_of_max] - timeArray[prev_index_of_max] < ((endTime-startTime) * 0.11)):
						red_line_height_percent = 0.025
					upper_red_line_yvalue = pitchArray[index_of_max]+(maxViewFrequency-minViewFrequency)*red_line_height_percent
					
					red_line_yvalues = [pitchArray[index_of_max],
										upper_red_line_yvalue,
										upper_red_line_yvalue]
					the_plot.plot(numpy.array(red_line_xvalues),
								numpy.array(red_line_yvalues),
								c='r',
								linewidth=0.85,
								clip_on=False,
								zorder=2)
					##----------------------------------------------------------------------##
					### Add text (on the line) indicating the pitch maximum 
					##----------------------------------------------------------------------##
					the_plot.text(timeArray[index_of_max]+((endTime-startTime) * 0.065),
									upper_red_line_yvalue + (maxViewFrequency-minViewFrequency)*0.005,
									"{:.2f} Hz".format(pitchArray[index_of_max]),
									fontproperties=global_values.text_fontproperties,
									size=global_values.text_font_size,
									color='red',
									horizontalalignment='center',
									path_effects=[pe.Stroke(linewidth=0.45, foreground='k'), pe.Normal()],
									zorder=2)
					
					prev_index_of_max = index_of_max
				# Make sure we keep track of which maximum pitch has been plotted
				maximum_counter += 1
	
	##----------------------------------------------------------------------##
	### Setup the x-axis
	##----------------------------------------------------------------------##
	# Set minimum and maximum x-axis values
	the_plot.set_xlim([float("{:.4f}".format(startTime)), float("{:.4f}".format(endTime))])
	# Only allow x-axis ticks for the start and end times
	plt.xticks([float("{:.4f}".format(startTime)), float("{:.4f}".format(endTime))], [float("{:.4f}".format(startTime)), float("{:.4f}".format(endTime))])
	
	##----------------------------------------------------------------------##
	### Setup the y-axis
	##----------------------------------------------------------------------##
	# Set minimum and maximum x-axis values
	the_plot.set_ylim(minViewFrequency, maxViewFrequency)
	# Set up y-axis ticks to always display maximum and minimum values
	### and also have an a calculated interval (i.e. yaxisInterval)
	plt.yticks(numpy.arange(minViewFrequency, maxViewFrequency+yaxisInterval, yaxisInterval))
Esempio n. 6
0
def scatterPlotFormants(
        wav_file_path,
        maxFormantFrequency=5500,  #Praat default is 5500 Hz
        windowLength=0.025,  #Praat default is 0.025
        dynamicRange=30,  #Praat default is 30 dB
        showTextgridFormants=False,
        ax=None):
    ####################################################################################
    ### Check and fix arguments for problems OR to set default values (if necessary)
    ####################################################################################
    ##----------------------------------------------------------------------##
    ### No audio file specified... can't do anything
    ##----------------------------------------------------------------------##
    if wav_file_path is None or wav_file_path == "":
        raise Exception(
            "praatFormants.py->scatterPlotFormants: No audio filename provided"
        )

    ##----------------------------------------------------------------------##
    ### Check that the specified file actually exists
    ##----------------------------------------------------------------------##
    global_path_helper.verify_file_exists(wav_file_path)

    # assemble a Praat script to analyze the formants of the file. Make sure that
    # you add a backslach in front of every single quote of your Praat script (i.e.,
    # every ' turns into /' - this does not apply here, since the Praat script below
    # does not contain any single quotes). Also, add a new line (backslash n) at the
    # end of every line in the script
    #
    # In particular, we'll create the script below. Note how the path and file names
    # are being replaced by variables, so that you can easily change them.
    #
    # do ("Read from file...", "/Users/ch/data/programming/python/lib/demo/AEIOU_vocalFry.wav")
    # do ("To Formant (burg)...", 0, 5, 5000, 0.025, 50)
    # do ("Save as short text file...", "/Users/ch/data/programming/python/lib/demo/AEIOU_vocalFry.Formant")

    if maxFormantFrequency == None:
        maxFormantFrequency = 5500
    if windowLength == None:
        windowLength = 0.025
    if dynamicRange == None:
        dynamicRange = 12.5

    wav_dir, filename_only, _ = global_path_helper.split_path_filename_extension(
        wav_file_path)
    formant_file_path = wav_dir + filename_only + '.Formant'
    textgrid_file_path = wav_dir + filename_only + '.TextGrid'

    script = ''
    script += 'do ("Read from file...", "' + wav_file_path + '")\n'
    #five arguments:
    ##### the time step,
    ##### the maximum number of formants,
    ##### the maximum hertz,
    ##### the window length,
    ##### Pre-emphasis from (Hz)
    script += 'do ("To Formant (burg)...", 0, 5, ' + '{:.2f},'.format(
        maxFormantFrequency) + ' 0.025, 50)\n'
    script += 'do ("Save as short text file...", "' + str(
        formant_file_path) + '")\n'
    scriptFileName = 'tmp_formants.praat'
    praatUtil.runPraatScript(script, scriptFileName)

    # read the generated Praat formants file
    formants = praatUtil.PraatFormants()
    formants.readFile(formant_file_path)

    if showTextgridFormants:  #First, verify .TextGrid file exists
        # read the accompanying Praat text grid (see the Praat TextGrid example for an
        # extended documentation). We expect a TextGrid that contains one IntervalTier
        # lableled 'vowels'. Within this IntervalTier, the occurring vowels are indicated
        textGrid = praatTextGrid.PraatTextGrid(0, 0)
        textGridFile = pathlib.Path(textgrid_file_path)

        if not textGridFile.exists():
            showTextgridFormants = False
            warnings.warn("File '" + str(textgrid_file_path) +
                          "' does not exist.\n")
            warnings.warn(
                "Continuing to show ALL formants (despite flag for .TextGrid file being true)\n"
            )
    if showTextgridFormants:
        arrTiers = textGrid.readFromFile(textgrid_file_path)
        numTiers = len(arrTiers)
        if numTiers != 1:
            raise Exception("we expect exactly one Tier in this file")
        tier = arrTiers[0]
        if tier.getName() != 'vowels':
            raise Exception("unexpected tier")

        # parse the TextGrid: create a dictionary that stores a list of start and end
        # times of all intervals where that particular vowel occurs (that way we'll
        # cater for multiple occurrances of the same vowel in a file, should that ever
        # happen)
        arrVowels = {}
        for i in range(tier.getSize()):
            if tier.getLabel(i) != '':
                interval = tier.get(i)
                vowel = interval[2]
                if not vowel in arrVowels:
                    arrVowels[vowel] = []
                tStart, tEnd = interval[0], interval[1]
                arrVowels[vowel].append([tStart, tEnd])

    n = formants.getNumFrames()
    arrFormants = {}
    arrGraphData = {}
    xtimes = []
    yfrequency = []

    intensities = []
    for formantFrames in formants.getAllFormants():
        for formant_dict in formantFrames:
            intensities.append(formant_dict.get('intensity_dB'))
    max_intensity = numpy.amax(intensities)
    intensities = None

    for i in range(n):
        t, formantData = formants.get(i)
        if showTextgridFormants:  #SHOW ONLY TEXTGRID FORMANTS
            # loop over all vowels and all intervals for each vowel
            for vowel in arrVowels:
                for tStart, tEnd in arrVowels[vowel]:
                    if t >= tStart and t <= tEnd:
                        for formant in formantData:
                            xtimes.append(t)
                            yfrequency.append(formant['frequency'])
        else:  #SHOW ALL FORMANTS
            for index, formant in enumerate(formantData):
                if index > 0 and formant[
                        'intensity_dB'] > max_intensity - dynamicRange / 2:
                    xtimes.append(t)
                    yfrequency.append(formant['frequency'])

    if ax == None:
        plt.scatter(numpy.array(xtimes),
                    numpy.array(yfrequency),
                    marker="D",
                    s=12,
                    c='w',
                    edgecolor='b',
                    linewidth=0.65,
                    zorder=3)
    else:
        ax.scatter(numpy.array(xtimes),
                   numpy.array(yfrequency),
                   marker="D",
                   s=12,
                   c='w',
                   edgecolor='b',
                   linewidth=0.65,
                   zorder=3)