Module guitarsounds.interface

Expand source code
from IPython.display import display, clear_output, HTML
from IPython import get_ipython
from guitarsounds.analysis import Plot, Signal, Sound, SoundPack
import ipywidgets as widgets
import matplotlib.pyplot as plt
import io
import wave
import struct
import numpy as np

def generate_error_widget(text):
    return widgets.HTML('<p style="color:#CC4123;">' + text + '</p>')


class guitarGUI(object):
    # Output layout
    out_layout = {'border': '1px solid black'}

    # Box Layout
    box_layout = widgets.Layout(align_items='stretch', flex_flow='line', width='50%')

    # Fundamental input style
    fundamental_style = style = {'description_width': 'initial'}

    # Attribute for output layout
    output = widgets.Output(layout={'border': '1px solid black'})

    # List of plot methods
    plot_methods = [Plot.signal, Plot.envelop, Plot.log_envelop, Plot.fft, Plot.fft_hist, Plot.peaks, Plot.peak_damping,
                    Plot.time_damping, Plot.timbre, ]
    bin_ticks_methods = [Plot.fft, Plot.fft_hist, Plot.peaks, Plot.peak_damping, ]

    # Plot info dict
    plot_info_dict = {'signal': Plot.signal,
                      'envelop': Plot.envelop,
                      'log envelop': Plot.log_envelop,
                      'fft': Plot.fft,
                      'fft hist': Plot.fft_hist,
                      'peaks': Plot.peaks,
                      'peak damping': Plot.peak_damping,
                      'time damping': Plot.time_damping,
                      'timbre': Plot.timbre,
                      'integral': Plot.integral}

    # analysis drop downs
    # Single analysis drop down
    options = [('', 1),
               ('Listen Sound', Signal.listen),
               ('Listen frequency bins', Sound.listen_freq_bins),
               ('Frequency bin plot', Sound.plot_freq_bins),
               ('Frequency bin histogram', Sound.bin_hist),
               ('Signal plot', Plot.signal),
               ('Envelop plot', Plot.envelop),
               ('Log-envelop plot', Plot.log_envelop),
               ('Fourier transform plot', Plot.fft),
               ('Fourier transform histogram', Plot.fft_hist),
               ('Peaks plot', Plot.peaks),
               ('Peak damping plot', Plot.peak_damping),
               ('Time damping plot', Plot.time_damping),
               ('Timbre attributes plot', Plot.timbre),
               ('Frequency damping values', Sound.peak_damping), ]

    style = {'description_width': '150px'}
    single_drop_down = widgets.Dropdown(options=options, value=1, style=style,
                                        description='Choose an analysis : ')
    single_drop_down.rank = 'first'

    unique_plot_methods = [SoundPack.compare_peaks, SoundPack.fft_mirror, SoundPack.fft_diff, SoundPack.plot,
                           SoundPack.bin_power_hist, ]

    # Dual analysis drop down
    options = [('', 1),
               ('Compare Peaks', SoundPack.compare_peaks),
               ('Mirror FFT', SoundPack.fft_mirror),
               ('FFT difference', SoundPack.fft_diff),
               ('Bin power comparison', SoundPack.integral_compare),
               ('Stacked plot', SoundPack.plot),
               ('Compared plot', SoundPack.compare_plot),
               ('Bin power plot', SoundPack.integral_plot),
               ('Bin power table', SoundPack.bin_power_table),
               ('Bin power histogram', SoundPack.bin_power_hist),
               ('Frequency Bin plot', SoundPack.freq_bin_plot),
               ('Print Fundamentals', SoundPack.fundamentals), ]

    style = {'description_width': '150px'}
    dual_drop_down = widgets.Dropdown(options=options, value=1, style=style,
                                      description='Choose an analysis : ')
    dual_drop_down.rank = 'first'

    # Multiple analysis drop down
    options = [('', 1),
               ('Stacked plot', SoundPack.plot),
               ('Compared plot', SoundPack.compare_plot),
               ('Frequency Bin plot', SoundPack.freq_bin_plot),
               ('Combine Envelops', SoundPack.combine_envelop),
               ('Print Fundamentals', SoundPack.fundamentals),
               ('Bin power plot', SoundPack.integral_plot),
               ('Print bin powers', SoundPack.bin_power_table),
               ('Bin power histogram', SoundPack.bin_power_hist), ]

    DM_bin_choice_methods = [SoundPack.freq_bin_plot, SoundPack.integral_plot, SoundPack.integral_compare]

    style = {'description_width': '150px'}
    mult_drop_down = widgets.Dropdown(options=options, value=1, style=style,
                                      description='Choose an analysis : ')
    mult_drop_down.rank = 'first'

    # Frequency bin choice drop down
    options = [('', 1),
               ('all', 'all'),
               ('bass', 'bass'),
               ('mid', 'mid'),
               ('highmid', 'highmid'),
               ('uppermid', 'uppermid'),
               ('presence', 'presence'),
               ('brillance', 'brillance'), ]

    style = {'description_width': '150px'}
    bin_drop_down = widgets.Dropdown(options=options, value='all', style=style,
                                     description='Choose a frequency bin: ')
    bin_drop_down.rank = 'second'
    bin_drop_down.name = 'bin'

    # Plot type choice drop down
    options = [('', 1),
               ('Signal', 'signal'),
               ('Envelop', 'envelop'),
               ('Log Scale Envelop', 'log envelop'),
               ('Fourier Transform', 'fft'),
               ('Fourier Transform Histogram', 'fft hist'),
               ('Fourier Transform Peaks', 'peaks'),
               ('Peak Damping', 'peak damping'),
               ('Time Damping', 'time damping'),
               ('Timbre Attributes', 'timbre'),
               ('Cumulative integral', 'integral'), ]

    style = {'description_width': '150px'}
    plot_drop_down = widgets.Dropdown(options=options, value='signal', style=style,
                                      description='Choose a plot type: ')
    plot_drop_down.rank = 'second'
    plot_drop_down.name = 'plot'

    def __init__(self):
        """
        Here We display the three file choosing buttons matched with the
        three types of analyses when one is clicked the user is prompted
        to choose files.

        When files are chosen  the user press the 'Ok' Button and the
        Program advances to defining names see `.on_ok_button_clicked_1`.
        """

        # __ Buttons __
        # Number of sound choice buttons
        self.button1 = widgets.Button(description="Single Sound")
        self.button2 = widgets.Button(description="Dual Sounds")
        self.button3 = widgets.Button(description="Multiple Sounds")

        # Ok, Done and Go Buttons
        self.ok_button = widgets.Button(description="Ok")
        self.done_button = widgets.Button(description="Done")
        self.go_button = widgets.Button(description='Go')

        # Normalize toggle button
        self.toggle_normalize_button = widgets.Button(description='Normalize')
        # Associated attribute to normalize the Sounds for the method called
        self.normalize = False

        # Info button
        self.info_button = widgets.Button(description='Info')

        # Button box when the GUI starts
        self.button_box = widgets.Box(children=[self.button1,
                                                self.button2,
                                                self.button3,
                                                self.ok_button], layout=self.box_layout)

        # Load bar when importing sounds
        self.load_bar = widgets.IntProgress(value=5, min=0, max=10,
                                            description='Importing sound files :',
                                            style={'bar_color': '#6495ED',
                                                   'description_width': '140px'}, )

        # File selectors for uploading files into the program
        self.single_file_selector = widgets.FileUpload(accept='.wav', multiple=False)
        self.dual_file_selector_1 = widgets.FileUpload(accept='.wav', multiple=False)
        self.dual_file_selector_2 = widgets.FileUpload(accept='.wav', multiple=False)
        self.mult_file_selector = widgets.FileUpload(accept='.wav', multiple=True)

        # Dict with drop down methods to display the drop down associated to
        # the analysis
        self.first_level_drop_down = {'Single': self.single_drop_down,
                                      'Dual': self.dual_drop_down,
                                      'Multiple': self.mult_drop_down}

        # Save analysis type
        self.analysis = None

        # Define the current state of the program
        self.state = 'start'

        # Listen for clicks on the first button panel
        self.button1.on_click(self.on_single_button_clicked)
        self.button2.on_click(self.on_dual_button_clicked)
        self.button3.on_click(self.on_multiple_button_clicked)
        self.ok_button.on_click(self.on_ok_button_clicked_1)
        self.disable_file_selection(False)

        # display the buttons
        display(self.button_box)

    """
    File Choosing Interface Button Click Methods
    """

    def on_single_button_clicked(self, b):
        """
        Displays the single file selector, allowing the user to choose
        one file.
        """
        clear_output(wait=True)

        output = widgets.Output(layout={'border': '1px solid black'})
        self.disable_file_selection(True)
        with output:
            display(self.single_file_selector)

        self.analysis = 'Single'
        self.state = 'file entry'

        display(self.button_box)
        display(output)

    def on_dual_button_clicked(self, b):
        """
        Displays two single file selectors, allowing the user
        to choose two files.
        """
        clear_output(wait=True)

        output = widgets.Output(layout={'border': '1px solid black'})
        self.disable_file_selection(True)
        with output:
            display(self.dual_file_selector_1)
            display(self.dual_file_selector_2)

        self.analysis = 'Dual'
        self.state = 'file entry'

        display(self.button_box)
        display(output)

    def on_multiple_button_clicked(self, b):
        """
        Displays a multiple file selector allowing the user
        to select multiple files
        """
        clear_output(wait=True)

        output = widgets.Output(layout={'border': '1px solid black'})
        self.disable_file_selection(True)
        with output:
            display(self.mult_file_selector)

        self.analysis = 'Multiple'
        self.state = 'file entry'

        display(self.button_box)
        display(output)

    def disable_file_selection(self, state):
        self.button1.disabled = state
        self.button2.disabled = state
        self.button3.disabled = state

    def on_ok_button_clicked_1(self, b):
        """
        The user clicks this button when he is done choosing files and when
        he is done defining names
        """
        # Clear the output
        clear_output(wait=True)

        # Check if the user did good when choosing files
        file_selectors = [self.single_file_selector,
                          self.dual_file_selector_1,
                          self.dual_file_selector_2,
                          self.mult_file_selector]
        files_where_chosen = False
        for file_selector in file_selectors:
            if file_selector.value != {}:
                files_where_chosen = True

        # If the file where chosen the user is taken to the define name interface
        if files_where_chosen:
            self.define_sound_names()

        # if not we go back to file selection
        else:
            output = widgets.Output(layout={'border': '1px solid black'})
            with output:
                if self.analysis == 'Single':
                    display(self.single_file_selector)
                elif self.analysis == 'Dual':
                    display(self.dual_file_selector_1)
                    display(self.dual_file_selector_2)
                elif self.analysis == 'Multiple':
                    display(self.mult_file_selector)
                else:
                    error = generate_error_widget('Chose an analysis type')
                    display(error)

                # Display an error if a file selector was clicked but no file was chosen
                if self.analysis in ['Single', 'Dual', 'Multiple']:
                    error = generate_error_widget('No sound was chosen')
                    display(error)

            display(self.button_box)
            display(output)

    """ 
    Analysis interface button click methods
    """

    def on_ok_button_clicked_2(self, b):
        """
        Method to make the "Ok" button interact with the
        analysis method choice.

        __ when interface.state = 'method choice' __
        - The "Ok" and "Go" buttons appears after the loading bar is done
        - The drop down corresponds to the methods associated to
        the analysis

        """
        # Clear the Output
        clear_output(wait=True)
        output = widgets.Output(layout=self.out_layout)

        # Save the drop down value
        drop_down_value = self.current_drop_down.value
        
        # enable the info button when coming back from display
        if self.state != 'display':
            self.info_button.disabled = False
            self.toggle_normalize_button.disabled = False

        # Deactivate the info button if it was activated
        if self.info_button.button_style == 'info':
            self.info_button.button_style = ''

        if self.state == 'method choice':  # State when the user is choosing the analysis method

            # If we only analyse a single sound
            if self.analysis == 'Single':

                # Special case when the method is the frequency bin plot
                if drop_down_value == Sound.plot_freq_bins:
                    self.analysis_tuple = [drop_down_value]  # Store the method
                    # Change the drop down to frequency bin choice
                    self.current_drop_down = self.bin_drop_down
                    self.state = 'method choice 2'  # a second choice is needed
                    self.display = 'plot'

                # Case for the methods without plotting
                elif drop_down_value in [Sound.peak_damping, Sound.listen_freq_bins, Signal.listen]:
                    self.analysis_tuple = [drop_down_value]  # store the method
                    self.state = 'display'  # ready to display
                    self.display = 'print'

                # Signal.plot.method() methods
                elif drop_down_value in [*self.plot_methods, Sound.bin_hist]:
                    # store method and arg in a list
                    self.analysis_tuple = [drop_down_value]
                    self.state = 'display'  # ready to display
                    self.display = 'plot'

                # Error when no method is chosen
                elif drop_down_value == 1:
                    error = generate_error_widget('No analysis method was chosen')
                    with output:
                        display(error)

            # Case when two sounds or multiple sounds are being analysed
            elif self.analysis in ['Dual', 'Multiple']:

                # Special case for the frequency bin plot
                if drop_down_value in self.DM_bin_choice_methods:
                    self.analysis_tuple = [drop_down_value]  # Store the method
                    # Update the drop down to frequency bin choice
                    self.current_drop_down = self.bin_drop_down
                    self.state = 'method choice 2'  # a second choice is needed
                    self.display = 'plot'

                # Case for plot methods
                elif (drop_down_value == SoundPack.plot) or (drop_down_value == SoundPack.compare_plot):
                    self.analysis_tuple = [drop_down_value]  # Store the method
                    # Update the drop down to the plot drop down
                    self.current_drop_down = self.plot_drop_down
                    self.state = 'method choice 2'  # a second choice is needed
                    self.display = 'plot'

                # Error when no method is chosen
                elif drop_down_value == 1:
                    error = generate_error_widget('No analysis method was chosen')
                    with output:
                        display(error)

                # Case for methods with no arguments
                else:
                    if drop_down_value == SoundPack.fundamentals:
                        self.display = 'print'
                    else:
                        self.display = 'plot'
                    self.analysis_tuple = [drop_down_value]  # store the method
                    self.state = 'display'

        # Case when the method is chosen and an argument needs to be added 'method choice 2'
        elif self.state == 'method choice 2':

            # add the arg part to the analysis tuple
            self.analysis_tuple.append(self.current_drop_down.value)
            self.state = 'display'

        # if we are coming back from the display the state is redefined and we restart
        elif self.state == 'analysis displayed':
            self.state = 'method choice'

        # If the button is pressed and the method is defined, the go button is enabled
        if self.state == 'display':
            self.go_button.disabled = False
            self.ok_button.disabled = True
            self.info_button.disabled = True
            self.toggle_normalize_button.disabled = True

        # Actualize the button box and display
        children = [self.ok_button, self.go_button, self.toggle_normalize_button, self.info_button]
        self.button_box = widgets.Box(children=children, layout=self.box_layout)

        # Put the updated drop down in the output
        with output:
            display(self.current_drop_down)

        display(self.button_box, output)

    def on_info_button_clicked(self, info):
        """
        Method called when the info button is clicked
        Displays the help string associated with the current drop down method
        """
        if info.button_style == '':
            # change the style to make the button blue
            info.button_style = 'info'

            # Clear the Output
            clear_output(wait=True)
            output = widgets.Output(layout=self.out_layout)

            # Case when the user is selecting the first method
            if self.state == 'method choice':

                # if the method is a tuple with an argument
                if type(self.current_drop_down.value) == tuple:
                    with output:
                        display(help(self.current_drop_down.value[0]))

                # if no method was selected
                elif type(self.current_drop_down.value) == int:
                    error = generate_error_widget('No analysis was selected')
                    with output:
                        display(error)

                # a method not in a tuple was selected
                else:
                    with output:
                        display(help(self.current_drop_down.value))

                # display every thing
                display(self.button_box, output)

            # case when the user is doing a secondary selection
            elif self.state == 'method choice 2':

                # case for the plot type drop down
                if self.current_drop_down.name == 'plot':
                    with output:
                        display(help(self.plot_info_dict[self.current_drop_down.value]))

                # case for bin type drop down (display the previous method)
                elif self.current_drop_down.name == 'bin':
                    with output:
                        display(help(self.analysis_tuple[0]))

                display(self.button_box, output)

        elif info.button_style == 'info':
            info.button_style = ''

            # Clear the Output
            clear_output(wait=True)
            output = widgets.Output(layout=self.out_layout)
            with output:
                display(self.current_drop_down)

            # display every thing
            display(self.button_box, output)

    def on_normalize_button_clicked(self, toggle):
        """
        Method called when the normalize button is clicked
        The normalized attribute is inversed according to the current value
        """
        if toggle.button_style == '':
            toggle.button_style = 'success'
            toggle.icon = 'check'
            self.normalize = True

        elif toggle.button_style == 'success':
            toggle.button_style = ''
            toggle.icon = ''
            self.normalize = False

    def on_done_button_clicked(self, b):
        """
        When the done button is clicked after the user had
        the option to define custom names this function is executed

        A load bar is displayed while te files are loaded, when the
        load bar is done the `.on_loaded_bar()` method is called.
        """
        clear_output(wait=True)

        display(self.load_bar)
        self.load_bar.observe(self.on_loaded_bar, names="value")

        self.load_bar.value += 1
        self.import_sound_files()

    def on_go_button_clicked(self, b):
        """
        Go button to display the analysis when all choices are made

        What happens :
        ___________________________________
        1. The output is cleared
        2. A output widget to store the output is instanciated
        3. The method in `self.analysis_tuple` is called
        4. The display is added to the output
        5. The 'Ok' button is enabled and the 'Go' button is disabled
        6. The drop down is set back to its default value
        7. The buttons and output are displayed
        """
        # Always clear the output
        clear_output(wait=True)
        output = widgets.Output(layout=self.out_layout)  # Create a output

        # Change the GUI state
        self.state = 'analysis displayed'

        # Set the matplotlib display method
        get_ipython().run_line_magic('matplotlib', 'inline')

        # Case for a single sound
        if self.analysis == 'Single':

            # Case for Sound.plot_freq_bins method
            if self.analysis_tuple[0] == Sound.plot_freq_bins:
                # change interface
                get_ipython().run_line_magic('matplotlib', 'notebook')
                # create a figure
                plt.figure(figsize=(8, 6))
                # Call the method
                self.analysis_tuple[0](self.sounds, bins=[self.analysis_tuple[1]])

                # Define the title according to the chosen bin
                if self.analysis_tuple[1] == 'all':
                    plt.title('Frequency bin plot for ' + self.sounds.name)
                else:
                    plt.title(self.analysis_tuple[1] + ' bin plot for ' + self.sounds.name)

                    plt.show()

            # Case for the Sound.peak_damping method (print only)
            elif self.analysis_tuple[0] in [Sound.peak_damping, Sound.listen_freq_bins]:
                with output:
                    self.analysis_tuple[0](self.sounds)  # add print to output

            # Case for the Signal.plot method
            elif self.analysis_tuple[0] in self.plot_methods:
                # change plot interface
                get_ipython().run_line_magic('matplotlib', 'notebook')
                # create a figure
                plt.figure(figsize=(8, 6))
                # Add the fill argument if there is just one plot
                if self.analysis_tuple[0] == Plot.timbre:
                    kwargs = {'fill': True}
                else:
                    kwargs = {}
                # Call the method according to normalization
                if not self.normalize:
                    self.analysis_tuple[0](self.sounds.signal.plot, **kwargs)
                elif self.normalize:
                    self.analysis_tuple[0](self.sounds.signal.normalize().plot, **kwargs)

                if self.analysis_tuple[0] == Plot.time_damping:
                    zeta = np.around(self.sounds.signal.time_damping(), 5)
                    plt.title(self.current_drop_down.label + ' for ' + self.sounds.name + ' Zeta = ' + str(zeta))
                # Define a title from the signal.plot(kind)
                else:
                    plt.title(self.current_drop_down.label + ' for ' + self.sounds.name)

                # make the x-axis ticks the frequency bins if the axe is frequency
                if self.analysis_tuple[0] in self.bin_ticks_methods:
                    Plot.set_bin_ticks(self.sounds.signal.plot)
                # add to output
                with output:
                    plt.show()

            # Case for the Sound.bin_hist method
            elif self.analysis_tuple[0] == Sound.bin_hist:
                # change plot interface
                get_ipython().run_line_magic('matplotlib', 'notebook')
                # call the method
                self.analysis_tuple[0](self.sounds)
                # set a title
                plt.title(self.current_drop_down.label + ' for ' + self.sounds.name)
                # add to output
                with output:
                    plt.show()

            # Case for the Signal.listen method
            elif self.analysis_tuple[0] == Signal.listen:
                # add to output
                with output:
                    # Call the method according to normalization
                    if not self.normalize:
                        self.analysis_tuple[0](self.sounds.signal)
                    elif self.normalize:
                        self.analysis_tuple[0](self.sounds.signal.normalize())

        # Case for Dual and Multiple analyses
        elif self.analysis in ['Dual', 'Multiple']:

            # normalize the sound_pack if self.normalize is True
            if self.normalize:
                sound_pack = self.Pack.normalize()
            elif not self.normalize:
                sound_pack = self.Pack

            # if the analysis method is a unique plot, make matplotlib interactive
            get_ipython().run_line_magic('matplotlib', 'inline')
            if self.analysis_tuple[0] in self.unique_plot_methods:
                get_ipython().run_line_magic('matplotlib', 'notebook')

            # Call with no arguments
            if len(self.analysis_tuple) == 1:
                # Case for a print output
                if self.display == 'print':
                    with output:
                        self.analysis_tuple[0](sound_pack)  # add print to output

                # special case to have bins ticks for the fft_diff method
                elif self.analysis_tuple[0] == SoundPack.fft_diff:
                    self.analysis_tuple[0](sound_pack, ticks='bins')
                    with output:
                        plt.show()  # display plot in output

                # Case for a plot output
                elif self.display == 'plot':
                    self.analysis_tuple[0](sound_pack)
                    with output:
                        plt.show()  # display plot in output

            # Call with arguments
            elif len(self.analysis_tuple) == 2:
                self.analysis_tuple[0](sound_pack, self.analysis_tuple[1])
                with output:
                    plt.show()

        # Setup the drop down to go back to method choice
        self.current_drop_down.value = 1
        self.current_drop_down = self.first_level_drop_down[self.analysis]
        self.current_drop_down.value = 1

        # Set the Go and Ok buttons to default value
        self.go_button.disabled = True
        self.ok_button.disabled = False

        # Set the normalization button to not normalized
        self.toggle_normalize_button.button_style = ''
        self.toggle_normalize_button.icon = ''
        self.normalize = False


        # display
        display(self.button_box, output)
        # Make the window larger
        display(HTML("<style>div.output_scroll { height: 44em; }</style>"))

    """
    Observe methods
    """

    def on_loaded_bar(self, change):
        """
        This method monitors the value of the load bar used
        when loading files.

        When the load bar is complete (value = 10), the
        button box is displayed with the "Ok" and "Go" buttons
        The "Go" button is disabled
        The Drop down with the methods according to the
        current analysis is displayed.
        """
        # When the bar reaches the end
        if change["new"] >= 10:
            clear_output(wait=True)

            # disable the go_button
            self.state = 'method choice'

            # Actualize the button box and display
            children = [self.ok_button, self.go_button, self.toggle_normalize_button, self.info_button]
            self.button_box = widgets.Box(children=children, layout=self.box_layout)
            self.ok_button.on_click(self.on_ok_button_clicked_2)
            self.go_button.on_click(self.on_go_button_clicked)
            self.toggle_normalize_button.on_click(self.on_normalize_button_clicked)
            self.info_button.on_click(self.on_info_button_clicked)
            self.go_button.disabled = True
            display(self.button_box)

            # create the output
            output = widgets.Output(layout=self.out_layout)

            # display the drop down associated to the current analysis
            self.current_drop_down = self.first_level_drop_down[self.analysis]
            with output:
                display(self.current_drop_down)

            display(output)

    """
    Back end functions
    """

    def define_sound_names(self):
        """
        A method to define sound names and fundamentals
        """

        # Clear the output and define the new one
        clear_output(wait=True)
        output = widgets.Output(layout=self.out_layout)

        # Style for the text inputs
        style = {'description_width': 'initial'}

        # Small string 'Hz' to indicate units
        HZ_string = widgets.HTML('<p>' + 'Hz' + '</p>')

        # Define the button box
        self.button_box = widgets.Box(children=[self.done_button], layout=self.box_layout)

        # Define the output with the text inputs
        with output:

            # Case for a single sound analysis
            if self.analysis == 'Single':

                # get the filenames
                self.file_names = [ky for ky in self.single_file_selector.value.keys()]

                # make a sound name input widget
                sound_name_input = widgets.Text(value='',
                                                placeholder='sound name',
                                                description=self.file_names[0],
                                                layout=widgets.Layout(width='40%'),
                                                style=style,
                                                )

                # make a fundamental input widget
                fundamental_input = widgets.FloatText(value=0,
                                                      description='Fundamental :',
                                                      style=style,
                                                      layout=widgets.Layout(width='20%')
                                                      )

                # childrens that go in the name box
                childrens = [sound_name_input, fundamental_input, HZ_string]

                # define a name box widget
                name_box_layout = widgets.Layout(align_items='stretch', flex_flow='line', width='75%')
                name_box = widgets.Box(children=childrens, layout=name_box_layout)

                # display the box
                display(name_box)

                # store the input to refer them later
                self.sound_name_inputs = [sound_name_input]
                self.sound_fundamental_inputs = [fundamental_input]

            # Case for dual sound analysis
            elif self.analysis in ['Dual', 'Multiple']:

                if self.analysis == 'Dual':
                    # get the file names
                    name1 = [ky for ky in self.dual_file_selector_1.value.keys()][0]
                    name2 = [ky for ky in self.dual_file_selector_2.value.keys()][0]
                    self.file_names = [name1, name2]

                elif self.analysis == 'Multiple':
                    self.file_names = [ky for ky in self.mult_file_selector.value.keys()]

                # create empty lists for the inputs
                self.sound_name_inputs = []
                self.sound_fundamental_inputs = []

                for file in self.file_names:
                    # make a text input widget
                    sound_name_input = widgets.Text(value='',
                                                    placeholder='sound name',
                                                    description=file,
                                                    layout=widgets.Layout(width='40%'),
                                                    style=style,
                                                    )

                    # make a fundamental input widget
                    fundamental_input = widgets.FloatText(value=0,
                                                          description='Fundamental :',
                                                          layout=widgets.Layout(width='20%'),
                                                          style=style
                                                          )

                    # childrens that go in the name box
                    childrens = [sound_name_input, fundamental_input, HZ_string]

                    # define a name box widget
                    name_box_layout = widgets.Layout(align_items='stretch', flex_flow='line', width='75%')
                    name_box = widgets.Box(children=childrens, layout=name_box_layout)

                    # display the box
                    display(name_box)

                    # append the inputs
                    self.sound_name_inputs.append(sound_name_input)
                    self.sound_fundamental_inputs.append(fundamental_input)

        self.done_button.on_click(self.on_done_button_clicked)

        # display everything
        display(self.button_box, output)

    def import_sound_files(self):
        """
        Method to import the soundfile vectors into the program
        after the files and names where defined.
        *Only works with .wav files*
        """

        # Case for when only a single file is imported
        if self.analysis == 'Single':
            # Loading Bar Value = 0
            # Get the filename values from the file selector
            file_values = self.single_file_selector.value[self.file_names[0]]

            # Get the signal audio bytes
            bites = file_values['content']

            # Convert to wav audio object
            audio = wave.open(io.BytesIO(bites))

            sr = audio.getframerate()  # save the frame rate

            samples = []
            self.load_bar.value += 1  # LoadBar value = 1
            n = audio.getnframes()
            milestones = [int(i) for i in np.linspace(0, n, 5)][1:]
            for _ in range(audio.getnframes()):
                frame = audio.readframes(1)
                samples.append(struct.unpack("h", frame)[0])
                if _ in milestones:
                    self.load_bar.value += 1  # LoadBar value increases to 5 in loop

            self.load_bar.value += 1  # LoadBar value = 7
            signal = np.array(samples) / 32768
            Sound_Input = (signal, sr)
            self.load_bar.value += 1  # LoadBar value = 8

            # Get the sound name
            if self.sound_name_inputs[0].value == '':
                name = self.file_names[0].replace('.wav', '')
            else:
                name = self.sound_name_inputs[0].value

            # Get the sound fundamental
            if self.sound_fundamental_inputs[0].value == 0:
                fundamental = None
            else:
                fundamental = self.sound_fundamental_inputs[0].value

            self.load_bar.value += 1  # LoadBar value = 9
            # This takes a long time
            sound = Sound(Sound_Input, name=name, fundamental=fundamental)
            self.sounds = sound.condition(return_self=True, verbose=False)
            self.load_bar.value += 2  # Loadbar = 10

        # Case for two files from two file selectors
        elif self.analysis == 'Dual':
            # LoadBar = 0
            self.sounds = []
            file_dicts = [self.dual_file_selector_1.value, self.dual_file_selector_2.value]
            self.load_bar.value += 2  # LoadBar = 1

            # zipped iterator
            iterator = zip(self.file_names, file_dicts, self.sound_name_inputs, self.sound_fundamental_inputs)

            #  Create a sound for every file
            for file, dic, name_input, fundamental_input in iterator:

                file_values = dic[file]
                bites = file_values['content']
                audio = wave.open(io.BytesIO(bites))
                sr = audio.getframerate()
                samples = []
                self.load_bar.value += 1  # LoadBar +=2
                for _ in range(audio.getnframes()):
                    frame = audio.readframes(1)
                    samples.append(struct.unpack("h", frame)[0])
                self.load_bar.value += 1  # LoadBar +=2
                signal = np.array(samples) / 32768
                Sound_Input = (signal, sr)

                # get the name value
                if name_input.value == '':
                    name = file.replace('.wav', '')
                else:
                    name = name_input.value

                # get the fundamental value
                if fundamental_input.value == 0:
                    fundamental = None
                else:
                    fundamental = fundamental_input.value

                sound = Sound(Sound_Input, name=name, fundamental=fundamental)
                sound.condition(verbose=False)
                self.sounds.append(sound)
                self.load_bar.value += 1  # LoadBar +=2
            # Load Bar = 8
            self.Pack = SoundPack(self.sounds, names=[sound.name for sound in self.sounds])
            self.load_bar.value += 2  # Load Bar = 10

        # Case for multiple files
        elif self.analysis == 'Multiple':
            # LoadBar = 0
            self.sounds = []
            self.load_bar.value += 1  # LoadBar = 1

            # zipped iterator
            iterator = zip(self.file_names, self.sound_name_inputs, self.sound_fundamental_inputs)

            for file, name_input, fundamental_input in iterator:
                file_values = self.mult_file_selector.value[file]
                bites = file_values['content']
                audio = wave.open(io.BytesIO(bites))
                sr = audio.getframerate()
                samples = []
                for _ in range(audio.getnframes()):
                    frame = audio.readframes(1)
                    samples.append(struct.unpack("h", frame)[0])
                signal = np.array(samples) / 32768
                Sound_Input = (signal, sr)

                # get the sound names
                if name_input.value == '':
                    name = file.replace('.wav', '')
                else:
                    name = name_input.value

                # get the fundamental values
                if fundamental_input.value == 0:
                    fundamental = None
                else:
                    fundamental = fundamental_input.value

                sound = Sound(Sound_Input, name=name, fundamental=fundamental)
                sound.condition(verbose=False)
                self.sounds.append(sound)
                if self.load_bar.value < 9:
                    self.load_bar.value += 1

            self.Pack = SoundPack(self.sounds, names=[sound.name for sound in self.sounds])
            while self.load_bar.value < 10:
                self.load_bar.value += 1  # LoadBar = 10

Functions

def generate_error_widget(text)
Expand source code
def generate_error_widget(text):
    return widgets.HTML('<p style="color:#CC4123;">' + text + '</p>')

Classes

class guitarGUI

Here We display the three file choosing buttons matched with the three types of analyses when one is clicked the user is prompted to choose files.

When files are chosen the user press the 'Ok' Button and the Program advances to defining names see .on_ok_button_clicked_1.

Expand source code
class guitarGUI(object):
    # Output layout
    out_layout = {'border': '1px solid black'}

    # Box Layout
    box_layout = widgets.Layout(align_items='stretch', flex_flow='line', width='50%')

    # Fundamental input style
    fundamental_style = style = {'description_width': 'initial'}

    # Attribute for output layout
    output = widgets.Output(layout={'border': '1px solid black'})

    # List of plot methods
    plot_methods = [Plot.signal, Plot.envelop, Plot.log_envelop, Plot.fft, Plot.fft_hist, Plot.peaks, Plot.peak_damping,
                    Plot.time_damping, Plot.timbre, ]
    bin_ticks_methods = [Plot.fft, Plot.fft_hist, Plot.peaks, Plot.peak_damping, ]

    # Plot info dict
    plot_info_dict = {'signal': Plot.signal,
                      'envelop': Plot.envelop,
                      'log envelop': Plot.log_envelop,
                      'fft': Plot.fft,
                      'fft hist': Plot.fft_hist,
                      'peaks': Plot.peaks,
                      'peak damping': Plot.peak_damping,
                      'time damping': Plot.time_damping,
                      'timbre': Plot.timbre,
                      'integral': Plot.integral}

    # analysis drop downs
    # Single analysis drop down
    options = [('', 1),
               ('Listen Sound', Signal.listen),
               ('Listen frequency bins', Sound.listen_freq_bins),
               ('Frequency bin plot', Sound.plot_freq_bins),
               ('Frequency bin histogram', Sound.bin_hist),
               ('Signal plot', Plot.signal),
               ('Envelop plot', Plot.envelop),
               ('Log-envelop plot', Plot.log_envelop),
               ('Fourier transform plot', Plot.fft),
               ('Fourier transform histogram', Plot.fft_hist),
               ('Peaks plot', Plot.peaks),
               ('Peak damping plot', Plot.peak_damping),
               ('Time damping plot', Plot.time_damping),
               ('Timbre attributes plot', Plot.timbre),
               ('Frequency damping values', Sound.peak_damping), ]

    style = {'description_width': '150px'}
    single_drop_down = widgets.Dropdown(options=options, value=1, style=style,
                                        description='Choose an analysis : ')
    single_drop_down.rank = 'first'

    unique_plot_methods = [SoundPack.compare_peaks, SoundPack.fft_mirror, SoundPack.fft_diff, SoundPack.plot,
                           SoundPack.bin_power_hist, ]

    # Dual analysis drop down
    options = [('', 1),
               ('Compare Peaks', SoundPack.compare_peaks),
               ('Mirror FFT', SoundPack.fft_mirror),
               ('FFT difference', SoundPack.fft_diff),
               ('Bin power comparison', SoundPack.integral_compare),
               ('Stacked plot', SoundPack.plot),
               ('Compared plot', SoundPack.compare_plot),
               ('Bin power plot', SoundPack.integral_plot),
               ('Bin power table', SoundPack.bin_power_table),
               ('Bin power histogram', SoundPack.bin_power_hist),
               ('Frequency Bin plot', SoundPack.freq_bin_plot),
               ('Print Fundamentals', SoundPack.fundamentals), ]

    style = {'description_width': '150px'}
    dual_drop_down = widgets.Dropdown(options=options, value=1, style=style,
                                      description='Choose an analysis : ')
    dual_drop_down.rank = 'first'

    # Multiple analysis drop down
    options = [('', 1),
               ('Stacked plot', SoundPack.plot),
               ('Compared plot', SoundPack.compare_plot),
               ('Frequency Bin plot', SoundPack.freq_bin_plot),
               ('Combine Envelops', SoundPack.combine_envelop),
               ('Print Fundamentals', SoundPack.fundamentals),
               ('Bin power plot', SoundPack.integral_plot),
               ('Print bin powers', SoundPack.bin_power_table),
               ('Bin power histogram', SoundPack.bin_power_hist), ]

    DM_bin_choice_methods = [SoundPack.freq_bin_plot, SoundPack.integral_plot, SoundPack.integral_compare]

    style = {'description_width': '150px'}
    mult_drop_down = widgets.Dropdown(options=options, value=1, style=style,
                                      description='Choose an analysis : ')
    mult_drop_down.rank = 'first'

    # Frequency bin choice drop down
    options = [('', 1),
               ('all', 'all'),
               ('bass', 'bass'),
               ('mid', 'mid'),
               ('highmid', 'highmid'),
               ('uppermid', 'uppermid'),
               ('presence', 'presence'),
               ('brillance', 'brillance'), ]

    style = {'description_width': '150px'}
    bin_drop_down = widgets.Dropdown(options=options, value='all', style=style,
                                     description='Choose a frequency bin: ')
    bin_drop_down.rank = 'second'
    bin_drop_down.name = 'bin'

    # Plot type choice drop down
    options = [('', 1),
               ('Signal', 'signal'),
               ('Envelop', 'envelop'),
               ('Log Scale Envelop', 'log envelop'),
               ('Fourier Transform', 'fft'),
               ('Fourier Transform Histogram', 'fft hist'),
               ('Fourier Transform Peaks', 'peaks'),
               ('Peak Damping', 'peak damping'),
               ('Time Damping', 'time damping'),
               ('Timbre Attributes', 'timbre'),
               ('Cumulative integral', 'integral'), ]

    style = {'description_width': '150px'}
    plot_drop_down = widgets.Dropdown(options=options, value='signal', style=style,
                                      description='Choose a plot type: ')
    plot_drop_down.rank = 'second'
    plot_drop_down.name = 'plot'

    def __init__(self):
        """
        Here We display the three file choosing buttons matched with the
        three types of analyses when one is clicked the user is prompted
        to choose files.

        When files are chosen  the user press the 'Ok' Button and the
        Program advances to defining names see `.on_ok_button_clicked_1`.
        """

        # __ Buttons __
        # Number of sound choice buttons
        self.button1 = widgets.Button(description="Single Sound")
        self.button2 = widgets.Button(description="Dual Sounds")
        self.button3 = widgets.Button(description="Multiple Sounds")

        # Ok, Done and Go Buttons
        self.ok_button = widgets.Button(description="Ok")
        self.done_button = widgets.Button(description="Done")
        self.go_button = widgets.Button(description='Go')

        # Normalize toggle button
        self.toggle_normalize_button = widgets.Button(description='Normalize')
        # Associated attribute to normalize the Sounds for the method called
        self.normalize = False

        # Info button
        self.info_button = widgets.Button(description='Info')

        # Button box when the GUI starts
        self.button_box = widgets.Box(children=[self.button1,
                                                self.button2,
                                                self.button3,
                                                self.ok_button], layout=self.box_layout)

        # Load bar when importing sounds
        self.load_bar = widgets.IntProgress(value=5, min=0, max=10,
                                            description='Importing sound files :',
                                            style={'bar_color': '#6495ED',
                                                   'description_width': '140px'}, )

        # File selectors for uploading files into the program
        self.single_file_selector = widgets.FileUpload(accept='.wav', multiple=False)
        self.dual_file_selector_1 = widgets.FileUpload(accept='.wav', multiple=False)
        self.dual_file_selector_2 = widgets.FileUpload(accept='.wav', multiple=False)
        self.mult_file_selector = widgets.FileUpload(accept='.wav', multiple=True)

        # Dict with drop down methods to display the drop down associated to
        # the analysis
        self.first_level_drop_down = {'Single': self.single_drop_down,
                                      'Dual': self.dual_drop_down,
                                      'Multiple': self.mult_drop_down}

        # Save analysis type
        self.analysis = None

        # Define the current state of the program
        self.state = 'start'

        # Listen for clicks on the first button panel
        self.button1.on_click(self.on_single_button_clicked)
        self.button2.on_click(self.on_dual_button_clicked)
        self.button3.on_click(self.on_multiple_button_clicked)
        self.ok_button.on_click(self.on_ok_button_clicked_1)
        self.disable_file_selection(False)

        # display the buttons
        display(self.button_box)

    """
    File Choosing Interface Button Click Methods
    """

    def on_single_button_clicked(self, b):
        """
        Displays the single file selector, allowing the user to choose
        one file.
        """
        clear_output(wait=True)

        output = widgets.Output(layout={'border': '1px solid black'})
        self.disable_file_selection(True)
        with output:
            display(self.single_file_selector)

        self.analysis = 'Single'
        self.state = 'file entry'

        display(self.button_box)
        display(output)

    def on_dual_button_clicked(self, b):
        """
        Displays two single file selectors, allowing the user
        to choose two files.
        """
        clear_output(wait=True)

        output = widgets.Output(layout={'border': '1px solid black'})
        self.disable_file_selection(True)
        with output:
            display(self.dual_file_selector_1)
            display(self.dual_file_selector_2)

        self.analysis = 'Dual'
        self.state = 'file entry'

        display(self.button_box)
        display(output)

    def on_multiple_button_clicked(self, b):
        """
        Displays a multiple file selector allowing the user
        to select multiple files
        """
        clear_output(wait=True)

        output = widgets.Output(layout={'border': '1px solid black'})
        self.disable_file_selection(True)
        with output:
            display(self.mult_file_selector)

        self.analysis = 'Multiple'
        self.state = 'file entry'

        display(self.button_box)
        display(output)

    def disable_file_selection(self, state):
        self.button1.disabled = state
        self.button2.disabled = state
        self.button3.disabled = state

    def on_ok_button_clicked_1(self, b):
        """
        The user clicks this button when he is done choosing files and when
        he is done defining names
        """
        # Clear the output
        clear_output(wait=True)

        # Check if the user did good when choosing files
        file_selectors = [self.single_file_selector,
                          self.dual_file_selector_1,
                          self.dual_file_selector_2,
                          self.mult_file_selector]
        files_where_chosen = False
        for file_selector in file_selectors:
            if file_selector.value != {}:
                files_where_chosen = True

        # If the file where chosen the user is taken to the define name interface
        if files_where_chosen:
            self.define_sound_names()

        # if not we go back to file selection
        else:
            output = widgets.Output(layout={'border': '1px solid black'})
            with output:
                if self.analysis == 'Single':
                    display(self.single_file_selector)
                elif self.analysis == 'Dual':
                    display(self.dual_file_selector_1)
                    display(self.dual_file_selector_2)
                elif self.analysis == 'Multiple':
                    display(self.mult_file_selector)
                else:
                    error = generate_error_widget('Chose an analysis type')
                    display(error)

                # Display an error if a file selector was clicked but no file was chosen
                if self.analysis in ['Single', 'Dual', 'Multiple']:
                    error = generate_error_widget('No sound was chosen')
                    display(error)

            display(self.button_box)
            display(output)

    """ 
    Analysis interface button click methods
    """

    def on_ok_button_clicked_2(self, b):
        """
        Method to make the "Ok" button interact with the
        analysis method choice.

        __ when interface.state = 'method choice' __
        - The "Ok" and "Go" buttons appears after the loading bar is done
        - The drop down corresponds to the methods associated to
        the analysis

        """
        # Clear the Output
        clear_output(wait=True)
        output = widgets.Output(layout=self.out_layout)

        # Save the drop down value
        drop_down_value = self.current_drop_down.value
        
        # enable the info button when coming back from display
        if self.state != 'display':
            self.info_button.disabled = False
            self.toggle_normalize_button.disabled = False

        # Deactivate the info button if it was activated
        if self.info_button.button_style == 'info':
            self.info_button.button_style = ''

        if self.state == 'method choice':  # State when the user is choosing the analysis method

            # If we only analyse a single sound
            if self.analysis == 'Single':

                # Special case when the method is the frequency bin plot
                if drop_down_value == Sound.plot_freq_bins:
                    self.analysis_tuple = [drop_down_value]  # Store the method
                    # Change the drop down to frequency bin choice
                    self.current_drop_down = self.bin_drop_down
                    self.state = 'method choice 2'  # a second choice is needed
                    self.display = 'plot'

                # Case for the methods without plotting
                elif drop_down_value in [Sound.peak_damping, Sound.listen_freq_bins, Signal.listen]:
                    self.analysis_tuple = [drop_down_value]  # store the method
                    self.state = 'display'  # ready to display
                    self.display = 'print'

                # Signal.plot.method() methods
                elif drop_down_value in [*self.plot_methods, Sound.bin_hist]:
                    # store method and arg in a list
                    self.analysis_tuple = [drop_down_value]
                    self.state = 'display'  # ready to display
                    self.display = 'plot'

                # Error when no method is chosen
                elif drop_down_value == 1:
                    error = generate_error_widget('No analysis method was chosen')
                    with output:
                        display(error)

            # Case when two sounds or multiple sounds are being analysed
            elif self.analysis in ['Dual', 'Multiple']:

                # Special case for the frequency bin plot
                if drop_down_value in self.DM_bin_choice_methods:
                    self.analysis_tuple = [drop_down_value]  # Store the method
                    # Update the drop down to frequency bin choice
                    self.current_drop_down = self.bin_drop_down
                    self.state = 'method choice 2'  # a second choice is needed
                    self.display = 'plot'

                # Case for plot methods
                elif (drop_down_value == SoundPack.plot) or (drop_down_value == SoundPack.compare_plot):
                    self.analysis_tuple = [drop_down_value]  # Store the method
                    # Update the drop down to the plot drop down
                    self.current_drop_down = self.plot_drop_down
                    self.state = 'method choice 2'  # a second choice is needed
                    self.display = 'plot'

                # Error when no method is chosen
                elif drop_down_value == 1:
                    error = generate_error_widget('No analysis method was chosen')
                    with output:
                        display(error)

                # Case for methods with no arguments
                else:
                    if drop_down_value == SoundPack.fundamentals:
                        self.display = 'print'
                    else:
                        self.display = 'plot'
                    self.analysis_tuple = [drop_down_value]  # store the method
                    self.state = 'display'

        # Case when the method is chosen and an argument needs to be added 'method choice 2'
        elif self.state == 'method choice 2':

            # add the arg part to the analysis tuple
            self.analysis_tuple.append(self.current_drop_down.value)
            self.state = 'display'

        # if we are coming back from the display the state is redefined and we restart
        elif self.state == 'analysis displayed':
            self.state = 'method choice'

        # If the button is pressed and the method is defined, the go button is enabled
        if self.state == 'display':
            self.go_button.disabled = False
            self.ok_button.disabled = True
            self.info_button.disabled = True
            self.toggle_normalize_button.disabled = True

        # Actualize the button box and display
        children = [self.ok_button, self.go_button, self.toggle_normalize_button, self.info_button]
        self.button_box = widgets.Box(children=children, layout=self.box_layout)

        # Put the updated drop down in the output
        with output:
            display(self.current_drop_down)

        display(self.button_box, output)

    def on_info_button_clicked(self, info):
        """
        Method called when the info button is clicked
        Displays the help string associated with the current drop down method
        """
        if info.button_style == '':
            # change the style to make the button blue
            info.button_style = 'info'

            # Clear the Output
            clear_output(wait=True)
            output = widgets.Output(layout=self.out_layout)

            # Case when the user is selecting the first method
            if self.state == 'method choice':

                # if the method is a tuple with an argument
                if type(self.current_drop_down.value) == tuple:
                    with output:
                        display(help(self.current_drop_down.value[0]))

                # if no method was selected
                elif type(self.current_drop_down.value) == int:
                    error = generate_error_widget('No analysis was selected')
                    with output:
                        display(error)

                # a method not in a tuple was selected
                else:
                    with output:
                        display(help(self.current_drop_down.value))

                # display every thing
                display(self.button_box, output)

            # case when the user is doing a secondary selection
            elif self.state == 'method choice 2':

                # case for the plot type drop down
                if self.current_drop_down.name == 'plot':
                    with output:
                        display(help(self.plot_info_dict[self.current_drop_down.value]))

                # case for bin type drop down (display the previous method)
                elif self.current_drop_down.name == 'bin':
                    with output:
                        display(help(self.analysis_tuple[0]))

                display(self.button_box, output)

        elif info.button_style == 'info':
            info.button_style = ''

            # Clear the Output
            clear_output(wait=True)
            output = widgets.Output(layout=self.out_layout)
            with output:
                display(self.current_drop_down)

            # display every thing
            display(self.button_box, output)

    def on_normalize_button_clicked(self, toggle):
        """
        Method called when the normalize button is clicked
        The normalized attribute is inversed according to the current value
        """
        if toggle.button_style == '':
            toggle.button_style = 'success'
            toggle.icon = 'check'
            self.normalize = True

        elif toggle.button_style == 'success':
            toggle.button_style = ''
            toggle.icon = ''
            self.normalize = False

    def on_done_button_clicked(self, b):
        """
        When the done button is clicked after the user had
        the option to define custom names this function is executed

        A load bar is displayed while te files are loaded, when the
        load bar is done the `.on_loaded_bar()` method is called.
        """
        clear_output(wait=True)

        display(self.load_bar)
        self.load_bar.observe(self.on_loaded_bar, names="value")

        self.load_bar.value += 1
        self.import_sound_files()

    def on_go_button_clicked(self, b):
        """
        Go button to display the analysis when all choices are made

        What happens :
        ___________________________________
        1. The output is cleared
        2. A output widget to store the output is instanciated
        3. The method in `self.analysis_tuple` is called
        4. The display is added to the output
        5. The 'Ok' button is enabled and the 'Go' button is disabled
        6. The drop down is set back to its default value
        7. The buttons and output are displayed
        """
        # Always clear the output
        clear_output(wait=True)
        output = widgets.Output(layout=self.out_layout)  # Create a output

        # Change the GUI state
        self.state = 'analysis displayed'

        # Set the matplotlib display method
        get_ipython().run_line_magic('matplotlib', 'inline')

        # Case for a single sound
        if self.analysis == 'Single':

            # Case for Sound.plot_freq_bins method
            if self.analysis_tuple[0] == Sound.plot_freq_bins:
                # change interface
                get_ipython().run_line_magic('matplotlib', 'notebook')
                # create a figure
                plt.figure(figsize=(8, 6))
                # Call the method
                self.analysis_tuple[0](self.sounds, bins=[self.analysis_tuple[1]])

                # Define the title according to the chosen bin
                if self.analysis_tuple[1] == 'all':
                    plt.title('Frequency bin plot for ' + self.sounds.name)
                else:
                    plt.title(self.analysis_tuple[1] + ' bin plot for ' + self.sounds.name)

                    plt.show()

            # Case for the Sound.peak_damping method (print only)
            elif self.analysis_tuple[0] in [Sound.peak_damping, Sound.listen_freq_bins]:
                with output:
                    self.analysis_tuple[0](self.sounds)  # add print to output

            # Case for the Signal.plot method
            elif self.analysis_tuple[0] in self.plot_methods:
                # change plot interface
                get_ipython().run_line_magic('matplotlib', 'notebook')
                # create a figure
                plt.figure(figsize=(8, 6))
                # Add the fill argument if there is just one plot
                if self.analysis_tuple[0] == Plot.timbre:
                    kwargs = {'fill': True}
                else:
                    kwargs = {}
                # Call the method according to normalization
                if not self.normalize:
                    self.analysis_tuple[0](self.sounds.signal.plot, **kwargs)
                elif self.normalize:
                    self.analysis_tuple[0](self.sounds.signal.normalize().plot, **kwargs)

                if self.analysis_tuple[0] == Plot.time_damping:
                    zeta = np.around(self.sounds.signal.time_damping(), 5)
                    plt.title(self.current_drop_down.label + ' for ' + self.sounds.name + ' Zeta = ' + str(zeta))
                # Define a title from the signal.plot(kind)
                else:
                    plt.title(self.current_drop_down.label + ' for ' + self.sounds.name)

                # make the x-axis ticks the frequency bins if the axe is frequency
                if self.analysis_tuple[0] in self.bin_ticks_methods:
                    Plot.set_bin_ticks(self.sounds.signal.plot)
                # add to output
                with output:
                    plt.show()

            # Case for the Sound.bin_hist method
            elif self.analysis_tuple[0] == Sound.bin_hist:
                # change plot interface
                get_ipython().run_line_magic('matplotlib', 'notebook')
                # call the method
                self.analysis_tuple[0](self.sounds)
                # set a title
                plt.title(self.current_drop_down.label + ' for ' + self.sounds.name)
                # add to output
                with output:
                    plt.show()

            # Case for the Signal.listen method
            elif self.analysis_tuple[0] == Signal.listen:
                # add to output
                with output:
                    # Call the method according to normalization
                    if not self.normalize:
                        self.analysis_tuple[0](self.sounds.signal)
                    elif self.normalize:
                        self.analysis_tuple[0](self.sounds.signal.normalize())

        # Case for Dual and Multiple analyses
        elif self.analysis in ['Dual', 'Multiple']:

            # normalize the sound_pack if self.normalize is True
            if self.normalize:
                sound_pack = self.Pack.normalize()
            elif not self.normalize:
                sound_pack = self.Pack

            # if the analysis method is a unique plot, make matplotlib interactive
            get_ipython().run_line_magic('matplotlib', 'inline')
            if self.analysis_tuple[0] in self.unique_plot_methods:
                get_ipython().run_line_magic('matplotlib', 'notebook')

            # Call with no arguments
            if len(self.analysis_tuple) == 1:
                # Case for a print output
                if self.display == 'print':
                    with output:
                        self.analysis_tuple[0](sound_pack)  # add print to output

                # special case to have bins ticks for the fft_diff method
                elif self.analysis_tuple[0] == SoundPack.fft_diff:
                    self.analysis_tuple[0](sound_pack, ticks='bins')
                    with output:
                        plt.show()  # display plot in output

                # Case for a plot output
                elif self.display == 'plot':
                    self.analysis_tuple[0](sound_pack)
                    with output:
                        plt.show()  # display plot in output

            # Call with arguments
            elif len(self.analysis_tuple) == 2:
                self.analysis_tuple[0](sound_pack, self.analysis_tuple[1])
                with output:
                    plt.show()

        # Setup the drop down to go back to method choice
        self.current_drop_down.value = 1
        self.current_drop_down = self.first_level_drop_down[self.analysis]
        self.current_drop_down.value = 1

        # Set the Go and Ok buttons to default value
        self.go_button.disabled = True
        self.ok_button.disabled = False

        # Set the normalization button to not normalized
        self.toggle_normalize_button.button_style = ''
        self.toggle_normalize_button.icon = ''
        self.normalize = False


        # display
        display(self.button_box, output)
        # Make the window larger
        display(HTML("<style>div.output_scroll { height: 44em; }</style>"))

    """
    Observe methods
    """

    def on_loaded_bar(self, change):
        """
        This method monitors the value of the load bar used
        when loading files.

        When the load bar is complete (value = 10), the
        button box is displayed with the "Ok" and "Go" buttons
        The "Go" button is disabled
        The Drop down with the methods according to the
        current analysis is displayed.
        """
        # When the bar reaches the end
        if change["new"] >= 10:
            clear_output(wait=True)

            # disable the go_button
            self.state = 'method choice'

            # Actualize the button box and display
            children = [self.ok_button, self.go_button, self.toggle_normalize_button, self.info_button]
            self.button_box = widgets.Box(children=children, layout=self.box_layout)
            self.ok_button.on_click(self.on_ok_button_clicked_2)
            self.go_button.on_click(self.on_go_button_clicked)
            self.toggle_normalize_button.on_click(self.on_normalize_button_clicked)
            self.info_button.on_click(self.on_info_button_clicked)
            self.go_button.disabled = True
            display(self.button_box)

            # create the output
            output = widgets.Output(layout=self.out_layout)

            # display the drop down associated to the current analysis
            self.current_drop_down = self.first_level_drop_down[self.analysis]
            with output:
                display(self.current_drop_down)

            display(output)

    """
    Back end functions
    """

    def define_sound_names(self):
        """
        A method to define sound names and fundamentals
        """

        # Clear the output and define the new one
        clear_output(wait=True)
        output = widgets.Output(layout=self.out_layout)

        # Style for the text inputs
        style = {'description_width': 'initial'}

        # Small string 'Hz' to indicate units
        HZ_string = widgets.HTML('<p>' + 'Hz' + '</p>')

        # Define the button box
        self.button_box = widgets.Box(children=[self.done_button], layout=self.box_layout)

        # Define the output with the text inputs
        with output:

            # Case for a single sound analysis
            if self.analysis == 'Single':

                # get the filenames
                self.file_names = [ky for ky in self.single_file_selector.value.keys()]

                # make a sound name input widget
                sound_name_input = widgets.Text(value='',
                                                placeholder='sound name',
                                                description=self.file_names[0],
                                                layout=widgets.Layout(width='40%'),
                                                style=style,
                                                )

                # make a fundamental input widget
                fundamental_input = widgets.FloatText(value=0,
                                                      description='Fundamental :',
                                                      style=style,
                                                      layout=widgets.Layout(width='20%')
                                                      )

                # childrens that go in the name box
                childrens = [sound_name_input, fundamental_input, HZ_string]

                # define a name box widget
                name_box_layout = widgets.Layout(align_items='stretch', flex_flow='line', width='75%')
                name_box = widgets.Box(children=childrens, layout=name_box_layout)

                # display the box
                display(name_box)

                # store the input to refer them later
                self.sound_name_inputs = [sound_name_input]
                self.sound_fundamental_inputs = [fundamental_input]

            # Case for dual sound analysis
            elif self.analysis in ['Dual', 'Multiple']:

                if self.analysis == 'Dual':
                    # get the file names
                    name1 = [ky for ky in self.dual_file_selector_1.value.keys()][0]
                    name2 = [ky for ky in self.dual_file_selector_2.value.keys()][0]
                    self.file_names = [name1, name2]

                elif self.analysis == 'Multiple':
                    self.file_names = [ky for ky in self.mult_file_selector.value.keys()]

                # create empty lists for the inputs
                self.sound_name_inputs = []
                self.sound_fundamental_inputs = []

                for file in self.file_names:
                    # make a text input widget
                    sound_name_input = widgets.Text(value='',
                                                    placeholder='sound name',
                                                    description=file,
                                                    layout=widgets.Layout(width='40%'),
                                                    style=style,
                                                    )

                    # make a fundamental input widget
                    fundamental_input = widgets.FloatText(value=0,
                                                          description='Fundamental :',
                                                          layout=widgets.Layout(width='20%'),
                                                          style=style
                                                          )

                    # childrens that go in the name box
                    childrens = [sound_name_input, fundamental_input, HZ_string]

                    # define a name box widget
                    name_box_layout = widgets.Layout(align_items='stretch', flex_flow='line', width='75%')
                    name_box = widgets.Box(children=childrens, layout=name_box_layout)

                    # display the box
                    display(name_box)

                    # append the inputs
                    self.sound_name_inputs.append(sound_name_input)
                    self.sound_fundamental_inputs.append(fundamental_input)

        self.done_button.on_click(self.on_done_button_clicked)

        # display everything
        display(self.button_box, output)

    def import_sound_files(self):
        """
        Method to import the soundfile vectors into the program
        after the files and names where defined.
        *Only works with .wav files*
        """

        # Case for when only a single file is imported
        if self.analysis == 'Single':
            # Loading Bar Value = 0
            # Get the filename values from the file selector
            file_values = self.single_file_selector.value[self.file_names[0]]

            # Get the signal audio bytes
            bites = file_values['content']

            # Convert to wav audio object
            audio = wave.open(io.BytesIO(bites))

            sr = audio.getframerate()  # save the frame rate

            samples = []
            self.load_bar.value += 1  # LoadBar value = 1
            n = audio.getnframes()
            milestones = [int(i) for i in np.linspace(0, n, 5)][1:]
            for _ in range(audio.getnframes()):
                frame = audio.readframes(1)
                samples.append(struct.unpack("h", frame)[0])
                if _ in milestones:
                    self.load_bar.value += 1  # LoadBar value increases to 5 in loop

            self.load_bar.value += 1  # LoadBar value = 7
            signal = np.array(samples) / 32768
            Sound_Input = (signal, sr)
            self.load_bar.value += 1  # LoadBar value = 8

            # Get the sound name
            if self.sound_name_inputs[0].value == '':
                name = self.file_names[0].replace('.wav', '')
            else:
                name = self.sound_name_inputs[0].value

            # Get the sound fundamental
            if self.sound_fundamental_inputs[0].value == 0:
                fundamental = None
            else:
                fundamental = self.sound_fundamental_inputs[0].value

            self.load_bar.value += 1  # LoadBar value = 9
            # This takes a long time
            sound = Sound(Sound_Input, name=name, fundamental=fundamental)
            self.sounds = sound.condition(return_self=True, verbose=False)
            self.load_bar.value += 2  # Loadbar = 10

        # Case for two files from two file selectors
        elif self.analysis == 'Dual':
            # LoadBar = 0
            self.sounds = []
            file_dicts = [self.dual_file_selector_1.value, self.dual_file_selector_2.value]
            self.load_bar.value += 2  # LoadBar = 1

            # zipped iterator
            iterator = zip(self.file_names, file_dicts, self.sound_name_inputs, self.sound_fundamental_inputs)

            #  Create a sound for every file
            for file, dic, name_input, fundamental_input in iterator:

                file_values = dic[file]
                bites = file_values['content']
                audio = wave.open(io.BytesIO(bites))
                sr = audio.getframerate()
                samples = []
                self.load_bar.value += 1  # LoadBar +=2
                for _ in range(audio.getnframes()):
                    frame = audio.readframes(1)
                    samples.append(struct.unpack("h", frame)[0])
                self.load_bar.value += 1  # LoadBar +=2
                signal = np.array(samples) / 32768
                Sound_Input = (signal, sr)

                # get the name value
                if name_input.value == '':
                    name = file.replace('.wav', '')
                else:
                    name = name_input.value

                # get the fundamental value
                if fundamental_input.value == 0:
                    fundamental = None
                else:
                    fundamental = fundamental_input.value

                sound = Sound(Sound_Input, name=name, fundamental=fundamental)
                sound.condition(verbose=False)
                self.sounds.append(sound)
                self.load_bar.value += 1  # LoadBar +=2
            # Load Bar = 8
            self.Pack = SoundPack(self.sounds, names=[sound.name for sound in self.sounds])
            self.load_bar.value += 2  # Load Bar = 10

        # Case for multiple files
        elif self.analysis == 'Multiple':
            # LoadBar = 0
            self.sounds = []
            self.load_bar.value += 1  # LoadBar = 1

            # zipped iterator
            iterator = zip(self.file_names, self.sound_name_inputs, self.sound_fundamental_inputs)

            for file, name_input, fundamental_input in iterator:
                file_values = self.mult_file_selector.value[file]
                bites = file_values['content']
                audio = wave.open(io.BytesIO(bites))
                sr = audio.getframerate()
                samples = []
                for _ in range(audio.getnframes()):
                    frame = audio.readframes(1)
                    samples.append(struct.unpack("h", frame)[0])
                signal = np.array(samples) / 32768
                Sound_Input = (signal, sr)

                # get the sound names
                if name_input.value == '':
                    name = file.replace('.wav', '')
                else:
                    name = name_input.value

                # get the fundamental values
                if fundamental_input.value == 0:
                    fundamental = None
                else:
                    fundamental = fundamental_input.value

                sound = Sound(Sound_Input, name=name, fundamental=fundamental)
                sound.condition(verbose=False)
                self.sounds.append(sound)
                if self.load_bar.value < 9:
                    self.load_bar.value += 1

            self.Pack = SoundPack(self.sounds, names=[sound.name for sound in self.sounds])
            while self.load_bar.value < 10:
                self.load_bar.value += 1  # LoadBar = 10

Class variables

var DM_bin_choice_methods
var bin_drop_down
var bin_ticks_methods
var box_layout
var dual_drop_down
var fundamental_style
var mult_drop_down
var options
var out_layout
var output
var plot_drop_down
var plot_info_dict
var plot_methods
var single_drop_down
var style
var unique_plot_methods

Methods

def define_sound_names(self)

A method to define sound names and fundamentals

Expand source code
def define_sound_names(self):
    """
    A method to define sound names and fundamentals
    """

    # Clear the output and define the new one
    clear_output(wait=True)
    output = widgets.Output(layout=self.out_layout)

    # Style for the text inputs
    style = {'description_width': 'initial'}

    # Small string 'Hz' to indicate units
    HZ_string = widgets.HTML('<p>' + 'Hz' + '</p>')

    # Define the button box
    self.button_box = widgets.Box(children=[self.done_button], layout=self.box_layout)

    # Define the output with the text inputs
    with output:

        # Case for a single sound analysis
        if self.analysis == 'Single':

            # get the filenames
            self.file_names = [ky for ky in self.single_file_selector.value.keys()]

            # make a sound name input widget
            sound_name_input = widgets.Text(value='',
                                            placeholder='sound name',
                                            description=self.file_names[0],
                                            layout=widgets.Layout(width='40%'),
                                            style=style,
                                            )

            # make a fundamental input widget
            fundamental_input = widgets.FloatText(value=0,
                                                  description='Fundamental :',
                                                  style=style,
                                                  layout=widgets.Layout(width='20%')
                                                  )

            # childrens that go in the name box
            childrens = [sound_name_input, fundamental_input, HZ_string]

            # define a name box widget
            name_box_layout = widgets.Layout(align_items='stretch', flex_flow='line', width='75%')
            name_box = widgets.Box(children=childrens, layout=name_box_layout)

            # display the box
            display(name_box)

            # store the input to refer them later
            self.sound_name_inputs = [sound_name_input]
            self.sound_fundamental_inputs = [fundamental_input]

        # Case for dual sound analysis
        elif self.analysis in ['Dual', 'Multiple']:

            if self.analysis == 'Dual':
                # get the file names
                name1 = [ky for ky in self.dual_file_selector_1.value.keys()][0]
                name2 = [ky for ky in self.dual_file_selector_2.value.keys()][0]
                self.file_names = [name1, name2]

            elif self.analysis == 'Multiple':
                self.file_names = [ky for ky in self.mult_file_selector.value.keys()]

            # create empty lists for the inputs
            self.sound_name_inputs = []
            self.sound_fundamental_inputs = []

            for file in self.file_names:
                # make a text input widget
                sound_name_input = widgets.Text(value='',
                                                placeholder='sound name',
                                                description=file,
                                                layout=widgets.Layout(width='40%'),
                                                style=style,
                                                )

                # make a fundamental input widget
                fundamental_input = widgets.FloatText(value=0,
                                                      description='Fundamental :',
                                                      layout=widgets.Layout(width='20%'),
                                                      style=style
                                                      )

                # childrens that go in the name box
                childrens = [sound_name_input, fundamental_input, HZ_string]

                # define a name box widget
                name_box_layout = widgets.Layout(align_items='stretch', flex_flow='line', width='75%')
                name_box = widgets.Box(children=childrens, layout=name_box_layout)

                # display the box
                display(name_box)

                # append the inputs
                self.sound_name_inputs.append(sound_name_input)
                self.sound_fundamental_inputs.append(fundamental_input)

    self.done_button.on_click(self.on_done_button_clicked)

    # display everything
    display(self.button_box, output)
def disable_file_selection(self, state)
Expand source code
def disable_file_selection(self, state):
    self.button1.disabled = state
    self.button2.disabled = state
    self.button3.disabled = state
def import_sound_files(self)

Method to import the soundfile vectors into the program after the files and names where defined. Only works with .wav files

Expand source code
def import_sound_files(self):
    """
    Method to import the soundfile vectors into the program
    after the files and names where defined.
    *Only works with .wav files*
    """

    # Case for when only a single file is imported
    if self.analysis == 'Single':
        # Loading Bar Value = 0
        # Get the filename values from the file selector
        file_values = self.single_file_selector.value[self.file_names[0]]

        # Get the signal audio bytes
        bites = file_values['content']

        # Convert to wav audio object
        audio = wave.open(io.BytesIO(bites))

        sr = audio.getframerate()  # save the frame rate

        samples = []
        self.load_bar.value += 1  # LoadBar value = 1
        n = audio.getnframes()
        milestones = [int(i) for i in np.linspace(0, n, 5)][1:]
        for _ in range(audio.getnframes()):
            frame = audio.readframes(1)
            samples.append(struct.unpack("h", frame)[0])
            if _ in milestones:
                self.load_bar.value += 1  # LoadBar value increases to 5 in loop

        self.load_bar.value += 1  # LoadBar value = 7
        signal = np.array(samples) / 32768
        Sound_Input = (signal, sr)
        self.load_bar.value += 1  # LoadBar value = 8

        # Get the sound name
        if self.sound_name_inputs[0].value == '':
            name = self.file_names[0].replace('.wav', '')
        else:
            name = self.sound_name_inputs[0].value

        # Get the sound fundamental
        if self.sound_fundamental_inputs[0].value == 0:
            fundamental = None
        else:
            fundamental = self.sound_fundamental_inputs[0].value

        self.load_bar.value += 1  # LoadBar value = 9
        # This takes a long time
        sound = Sound(Sound_Input, name=name, fundamental=fundamental)
        self.sounds = sound.condition(return_self=True, verbose=False)
        self.load_bar.value += 2  # Loadbar = 10

    # Case for two files from two file selectors
    elif self.analysis == 'Dual':
        # LoadBar = 0
        self.sounds = []
        file_dicts = [self.dual_file_selector_1.value, self.dual_file_selector_2.value]
        self.load_bar.value += 2  # LoadBar = 1

        # zipped iterator
        iterator = zip(self.file_names, file_dicts, self.sound_name_inputs, self.sound_fundamental_inputs)

        #  Create a sound for every file
        for file, dic, name_input, fundamental_input in iterator:

            file_values = dic[file]
            bites = file_values['content']
            audio = wave.open(io.BytesIO(bites))
            sr = audio.getframerate()
            samples = []
            self.load_bar.value += 1  # LoadBar +=2
            for _ in range(audio.getnframes()):
                frame = audio.readframes(1)
                samples.append(struct.unpack("h", frame)[0])
            self.load_bar.value += 1  # LoadBar +=2
            signal = np.array(samples) / 32768
            Sound_Input = (signal, sr)

            # get the name value
            if name_input.value == '':
                name = file.replace('.wav', '')
            else:
                name = name_input.value

            # get the fundamental value
            if fundamental_input.value == 0:
                fundamental = None
            else:
                fundamental = fundamental_input.value

            sound = Sound(Sound_Input, name=name, fundamental=fundamental)
            sound.condition(verbose=False)
            self.sounds.append(sound)
            self.load_bar.value += 1  # LoadBar +=2
        # Load Bar = 8
        self.Pack = SoundPack(self.sounds, names=[sound.name for sound in self.sounds])
        self.load_bar.value += 2  # Load Bar = 10

    # Case for multiple files
    elif self.analysis == 'Multiple':
        # LoadBar = 0
        self.sounds = []
        self.load_bar.value += 1  # LoadBar = 1

        # zipped iterator
        iterator = zip(self.file_names, self.sound_name_inputs, self.sound_fundamental_inputs)

        for file, name_input, fundamental_input in iterator:
            file_values = self.mult_file_selector.value[file]
            bites = file_values['content']
            audio = wave.open(io.BytesIO(bites))
            sr = audio.getframerate()
            samples = []
            for _ in range(audio.getnframes()):
                frame = audio.readframes(1)
                samples.append(struct.unpack("h", frame)[0])
            signal = np.array(samples) / 32768
            Sound_Input = (signal, sr)

            # get the sound names
            if name_input.value == '':
                name = file.replace('.wav', '')
            else:
                name = name_input.value

            # get the fundamental values
            if fundamental_input.value == 0:
                fundamental = None
            else:
                fundamental = fundamental_input.value

            sound = Sound(Sound_Input, name=name, fundamental=fundamental)
            sound.condition(verbose=False)
            self.sounds.append(sound)
            if self.load_bar.value < 9:
                self.load_bar.value += 1

        self.Pack = SoundPack(self.sounds, names=[sound.name for sound in self.sounds])
        while self.load_bar.value < 10:
            self.load_bar.value += 1  # LoadBar = 10
def on_done_button_clicked(self, b)

When the done button is clicked after the user had the option to define custom names this function is executed

A load bar is displayed while te files are loaded, when the load bar is done the .on_loaded_bar() method is called.

Expand source code
def on_done_button_clicked(self, b):
    """
    When the done button is clicked after the user had
    the option to define custom names this function is executed

    A load bar is displayed while te files are loaded, when the
    load bar is done the `.on_loaded_bar()` method is called.
    """
    clear_output(wait=True)

    display(self.load_bar)
    self.load_bar.observe(self.on_loaded_bar, names="value")

    self.load_bar.value += 1
    self.import_sound_files()
def on_dual_button_clicked(self, b)

Displays two single file selectors, allowing the user to choose two files.

Expand source code
def on_dual_button_clicked(self, b):
    """
    Displays two single file selectors, allowing the user
    to choose two files.
    """
    clear_output(wait=True)

    output = widgets.Output(layout={'border': '1px solid black'})
    self.disable_file_selection(True)
    with output:
        display(self.dual_file_selector_1)
        display(self.dual_file_selector_2)

    self.analysis = 'Dual'
    self.state = 'file entry'

    display(self.button_box)
    display(output)
def on_go_button_clicked(self, b)

Go button to display the analysis when all choices are made

What happens :


  1. The output is cleared
  2. A output widget to store the output is instanciated
  3. The method in self.analysis_tuple is called
  4. The display is added to the output
  5. The 'Ok' button is enabled and the 'Go' button is disabled
  6. The drop down is set back to its default value
  7. The buttons and output are displayed
Expand source code
def on_go_button_clicked(self, b):
    """
    Go button to display the analysis when all choices are made

    What happens :
    ___________________________________
    1. The output is cleared
    2. A output widget to store the output is instanciated
    3. The method in `self.analysis_tuple` is called
    4. The display is added to the output
    5. The 'Ok' button is enabled and the 'Go' button is disabled
    6. The drop down is set back to its default value
    7. The buttons and output are displayed
    """
    # Always clear the output
    clear_output(wait=True)
    output = widgets.Output(layout=self.out_layout)  # Create a output

    # Change the GUI state
    self.state = 'analysis displayed'

    # Set the matplotlib display method
    get_ipython().run_line_magic('matplotlib', 'inline')

    # Case for a single sound
    if self.analysis == 'Single':

        # Case for Sound.plot_freq_bins method
        if self.analysis_tuple[0] == Sound.plot_freq_bins:
            # change interface
            get_ipython().run_line_magic('matplotlib', 'notebook')
            # create a figure
            plt.figure(figsize=(8, 6))
            # Call the method
            self.analysis_tuple[0](self.sounds, bins=[self.analysis_tuple[1]])

            # Define the title according to the chosen bin
            if self.analysis_tuple[1] == 'all':
                plt.title('Frequency bin plot for ' + self.sounds.name)
            else:
                plt.title(self.analysis_tuple[1] + ' bin plot for ' + self.sounds.name)

                plt.show()

        # Case for the Sound.peak_damping method (print only)
        elif self.analysis_tuple[0] in [Sound.peak_damping, Sound.listen_freq_bins]:
            with output:
                self.analysis_tuple[0](self.sounds)  # add print to output

        # Case for the Signal.plot method
        elif self.analysis_tuple[0] in self.plot_methods:
            # change plot interface
            get_ipython().run_line_magic('matplotlib', 'notebook')
            # create a figure
            plt.figure(figsize=(8, 6))
            # Add the fill argument if there is just one plot
            if self.analysis_tuple[0] == Plot.timbre:
                kwargs = {'fill': True}
            else:
                kwargs = {}
            # Call the method according to normalization
            if not self.normalize:
                self.analysis_tuple[0](self.sounds.signal.plot, **kwargs)
            elif self.normalize:
                self.analysis_tuple[0](self.sounds.signal.normalize().plot, **kwargs)

            if self.analysis_tuple[0] == Plot.time_damping:
                zeta = np.around(self.sounds.signal.time_damping(), 5)
                plt.title(self.current_drop_down.label + ' for ' + self.sounds.name + ' Zeta = ' + str(zeta))
            # Define a title from the signal.plot(kind)
            else:
                plt.title(self.current_drop_down.label + ' for ' + self.sounds.name)

            # make the x-axis ticks the frequency bins if the axe is frequency
            if self.analysis_tuple[0] in self.bin_ticks_methods:
                Plot.set_bin_ticks(self.sounds.signal.plot)
            # add to output
            with output:
                plt.show()

        # Case for the Sound.bin_hist method
        elif self.analysis_tuple[0] == Sound.bin_hist:
            # change plot interface
            get_ipython().run_line_magic('matplotlib', 'notebook')
            # call the method
            self.analysis_tuple[0](self.sounds)
            # set a title
            plt.title(self.current_drop_down.label + ' for ' + self.sounds.name)
            # add to output
            with output:
                plt.show()

        # Case for the Signal.listen method
        elif self.analysis_tuple[0] == Signal.listen:
            # add to output
            with output:
                # Call the method according to normalization
                if not self.normalize:
                    self.analysis_tuple[0](self.sounds.signal)
                elif self.normalize:
                    self.analysis_tuple[0](self.sounds.signal.normalize())

    # Case for Dual and Multiple analyses
    elif self.analysis in ['Dual', 'Multiple']:

        # normalize the sound_pack if self.normalize is True
        if self.normalize:
            sound_pack = self.Pack.normalize()
        elif not self.normalize:
            sound_pack = self.Pack

        # if the analysis method is a unique plot, make matplotlib interactive
        get_ipython().run_line_magic('matplotlib', 'inline')
        if self.analysis_tuple[0] in self.unique_plot_methods:
            get_ipython().run_line_magic('matplotlib', 'notebook')

        # Call with no arguments
        if len(self.analysis_tuple) == 1:
            # Case for a print output
            if self.display == 'print':
                with output:
                    self.analysis_tuple[0](sound_pack)  # add print to output

            # special case to have bins ticks for the fft_diff method
            elif self.analysis_tuple[0] == SoundPack.fft_diff:
                self.analysis_tuple[0](sound_pack, ticks='bins')
                with output:
                    plt.show()  # display plot in output

            # Case for a plot output
            elif self.display == 'plot':
                self.analysis_tuple[0](sound_pack)
                with output:
                    plt.show()  # display plot in output

        # Call with arguments
        elif len(self.analysis_tuple) == 2:
            self.analysis_tuple[0](sound_pack, self.analysis_tuple[1])
            with output:
                plt.show()

    # Setup the drop down to go back to method choice
    self.current_drop_down.value = 1
    self.current_drop_down = self.first_level_drop_down[self.analysis]
    self.current_drop_down.value = 1

    # Set the Go and Ok buttons to default value
    self.go_button.disabled = True
    self.ok_button.disabled = False

    # Set the normalization button to not normalized
    self.toggle_normalize_button.button_style = ''
    self.toggle_normalize_button.icon = ''
    self.normalize = False


    # display
    display(self.button_box, output)
    # Make the window larger
    display(HTML("<style>div.output_scroll { height: 44em; }</style>"))
def on_info_button_clicked(self, info)

Method called when the info button is clicked Displays the help string associated with the current drop down method

Expand source code
def on_info_button_clicked(self, info):
    """
    Method called when the info button is clicked
    Displays the help string associated with the current drop down method
    """
    if info.button_style == '':
        # change the style to make the button blue
        info.button_style = 'info'

        # Clear the Output
        clear_output(wait=True)
        output = widgets.Output(layout=self.out_layout)

        # Case when the user is selecting the first method
        if self.state == 'method choice':

            # if the method is a tuple with an argument
            if type(self.current_drop_down.value) == tuple:
                with output:
                    display(help(self.current_drop_down.value[0]))

            # if no method was selected
            elif type(self.current_drop_down.value) == int:
                error = generate_error_widget('No analysis was selected')
                with output:
                    display(error)

            # a method not in a tuple was selected
            else:
                with output:
                    display(help(self.current_drop_down.value))

            # display every thing
            display(self.button_box, output)

        # case when the user is doing a secondary selection
        elif self.state == 'method choice 2':

            # case for the plot type drop down
            if self.current_drop_down.name == 'plot':
                with output:
                    display(help(self.plot_info_dict[self.current_drop_down.value]))

            # case for bin type drop down (display the previous method)
            elif self.current_drop_down.name == 'bin':
                with output:
                    display(help(self.analysis_tuple[0]))

            display(self.button_box, output)

    elif info.button_style == 'info':
        info.button_style = ''

        # Clear the Output
        clear_output(wait=True)
        output = widgets.Output(layout=self.out_layout)
        with output:
            display(self.current_drop_down)

        # display every thing
        display(self.button_box, output)
def on_loaded_bar(self, change)

This method monitors the value of the load bar used when loading files.

When the load bar is complete (value = 10), the button box is displayed with the "Ok" and "Go" buttons The "Go" button is disabled The Drop down with the methods according to the current analysis is displayed.

Expand source code
def on_loaded_bar(self, change):
    """
    This method monitors the value of the load bar used
    when loading files.

    When the load bar is complete (value = 10), the
    button box is displayed with the "Ok" and "Go" buttons
    The "Go" button is disabled
    The Drop down with the methods according to the
    current analysis is displayed.
    """
    # When the bar reaches the end
    if change["new"] >= 10:
        clear_output(wait=True)

        # disable the go_button
        self.state = 'method choice'

        # Actualize the button box and display
        children = [self.ok_button, self.go_button, self.toggle_normalize_button, self.info_button]
        self.button_box = widgets.Box(children=children, layout=self.box_layout)
        self.ok_button.on_click(self.on_ok_button_clicked_2)
        self.go_button.on_click(self.on_go_button_clicked)
        self.toggle_normalize_button.on_click(self.on_normalize_button_clicked)
        self.info_button.on_click(self.on_info_button_clicked)
        self.go_button.disabled = True
        display(self.button_box)

        # create the output
        output = widgets.Output(layout=self.out_layout)

        # display the drop down associated to the current analysis
        self.current_drop_down = self.first_level_drop_down[self.analysis]
        with output:
            display(self.current_drop_down)

        display(output)
def on_multiple_button_clicked(self, b)

Displays a multiple file selector allowing the user to select multiple files

Expand source code
def on_multiple_button_clicked(self, b):
    """
    Displays a multiple file selector allowing the user
    to select multiple files
    """
    clear_output(wait=True)

    output = widgets.Output(layout={'border': '1px solid black'})
    self.disable_file_selection(True)
    with output:
        display(self.mult_file_selector)

    self.analysis = 'Multiple'
    self.state = 'file entry'

    display(self.button_box)
    display(output)
def on_normalize_button_clicked(self, toggle)

Method called when the normalize button is clicked The normalized attribute is inversed according to the current value

Expand source code
def on_normalize_button_clicked(self, toggle):
    """
    Method called when the normalize button is clicked
    The normalized attribute is inversed according to the current value
    """
    if toggle.button_style == '':
        toggle.button_style = 'success'
        toggle.icon = 'check'
        self.normalize = True

    elif toggle.button_style == 'success':
        toggle.button_style = ''
        toggle.icon = ''
        self.normalize = False
def on_ok_button_clicked_1(self, b)

The user clicks this button when he is done choosing files and when he is done defining names

Expand source code
def on_ok_button_clicked_1(self, b):
    """
    The user clicks this button when he is done choosing files and when
    he is done defining names
    """
    # Clear the output
    clear_output(wait=True)

    # Check if the user did good when choosing files
    file_selectors = [self.single_file_selector,
                      self.dual_file_selector_1,
                      self.dual_file_selector_2,
                      self.mult_file_selector]
    files_where_chosen = False
    for file_selector in file_selectors:
        if file_selector.value != {}:
            files_where_chosen = True

    # If the file where chosen the user is taken to the define name interface
    if files_where_chosen:
        self.define_sound_names()

    # if not we go back to file selection
    else:
        output = widgets.Output(layout={'border': '1px solid black'})
        with output:
            if self.analysis == 'Single':
                display(self.single_file_selector)
            elif self.analysis == 'Dual':
                display(self.dual_file_selector_1)
                display(self.dual_file_selector_2)
            elif self.analysis == 'Multiple':
                display(self.mult_file_selector)
            else:
                error = generate_error_widget('Chose an analysis type')
                display(error)

            # Display an error if a file selector was clicked but no file was chosen
            if self.analysis in ['Single', 'Dual', 'Multiple']:
                error = generate_error_widget('No sound was chosen')
                display(error)

        display(self.button_box)
        display(output)
def on_ok_button_clicked_2(self, b)

Method to make the "Ok" button interact with the analysis method choice.

when interface.state = 'method choice' - The "Ok" and "Go" buttons appears after the loading bar is done - The drop down corresponds to the methods associated to the analysis

Expand source code
def on_ok_button_clicked_2(self, b):
    """
    Method to make the "Ok" button interact with the
    analysis method choice.

    __ when interface.state = 'method choice' __
    - The "Ok" and "Go" buttons appears after the loading bar is done
    - The drop down corresponds to the methods associated to
    the analysis

    """
    # Clear the Output
    clear_output(wait=True)
    output = widgets.Output(layout=self.out_layout)

    # Save the drop down value
    drop_down_value = self.current_drop_down.value
    
    # enable the info button when coming back from display
    if self.state != 'display':
        self.info_button.disabled = False
        self.toggle_normalize_button.disabled = False

    # Deactivate the info button if it was activated
    if self.info_button.button_style == 'info':
        self.info_button.button_style = ''

    if self.state == 'method choice':  # State when the user is choosing the analysis method

        # If we only analyse a single sound
        if self.analysis == 'Single':

            # Special case when the method is the frequency bin plot
            if drop_down_value == Sound.plot_freq_bins:
                self.analysis_tuple = [drop_down_value]  # Store the method
                # Change the drop down to frequency bin choice
                self.current_drop_down = self.bin_drop_down
                self.state = 'method choice 2'  # a second choice is needed
                self.display = 'plot'

            # Case for the methods without plotting
            elif drop_down_value in [Sound.peak_damping, Sound.listen_freq_bins, Signal.listen]:
                self.analysis_tuple = [drop_down_value]  # store the method
                self.state = 'display'  # ready to display
                self.display = 'print'

            # Signal.plot.method() methods
            elif drop_down_value in [*self.plot_methods, Sound.bin_hist]:
                # store method and arg in a list
                self.analysis_tuple = [drop_down_value]
                self.state = 'display'  # ready to display
                self.display = 'plot'

            # Error when no method is chosen
            elif drop_down_value == 1:
                error = generate_error_widget('No analysis method was chosen')
                with output:
                    display(error)

        # Case when two sounds or multiple sounds are being analysed
        elif self.analysis in ['Dual', 'Multiple']:

            # Special case for the frequency bin plot
            if drop_down_value in self.DM_bin_choice_methods:
                self.analysis_tuple = [drop_down_value]  # Store the method
                # Update the drop down to frequency bin choice
                self.current_drop_down = self.bin_drop_down
                self.state = 'method choice 2'  # a second choice is needed
                self.display = 'plot'

            # Case for plot methods
            elif (drop_down_value == SoundPack.plot) or (drop_down_value == SoundPack.compare_plot):
                self.analysis_tuple = [drop_down_value]  # Store the method
                # Update the drop down to the plot drop down
                self.current_drop_down = self.plot_drop_down
                self.state = 'method choice 2'  # a second choice is needed
                self.display = 'plot'

            # Error when no method is chosen
            elif drop_down_value == 1:
                error = generate_error_widget('No analysis method was chosen')
                with output:
                    display(error)

            # Case for methods with no arguments
            else:
                if drop_down_value == SoundPack.fundamentals:
                    self.display = 'print'
                else:
                    self.display = 'plot'
                self.analysis_tuple = [drop_down_value]  # store the method
                self.state = 'display'

    # Case when the method is chosen and an argument needs to be added 'method choice 2'
    elif self.state == 'method choice 2':

        # add the arg part to the analysis tuple
        self.analysis_tuple.append(self.current_drop_down.value)
        self.state = 'display'

    # if we are coming back from the display the state is redefined and we restart
    elif self.state == 'analysis displayed':
        self.state = 'method choice'

    # If the button is pressed and the method is defined, the go button is enabled
    if self.state == 'display':
        self.go_button.disabled = False
        self.ok_button.disabled = True
        self.info_button.disabled = True
        self.toggle_normalize_button.disabled = True

    # Actualize the button box and display
    children = [self.ok_button, self.go_button, self.toggle_normalize_button, self.info_button]
    self.button_box = widgets.Box(children=children, layout=self.box_layout)

    # Put the updated drop down in the output
    with output:
        display(self.current_drop_down)

    display(self.button_box, output)
def on_single_button_clicked(self, b)

Displays the single file selector, allowing the user to choose one file.

Expand source code
def on_single_button_clicked(self, b):
    """
    Displays the single file selector, allowing the user to choose
    one file.
    """
    clear_output(wait=True)

    output = widgets.Output(layout={'border': '1px solid black'})
    self.disable_file_selection(True)
    with output:
        display(self.single_file_selector)

    self.analysis = 'Single'
    self.state = 'file entry'

    display(self.button_box)
    display(output)