def __init__(self,
              xml_dict,
              midi_in,
              midi_out,
              ignore_sysex=True,
              ignore_timing=True,
              ignore_active_sense=True):
     """
 Calls the MidiInputHandler constructor and initializes the sub class
 attributes
 Parameters:
 * xml_dict: dictionary containing the parsed values from the xml
   configuration file
 * midi_in: MIDI IN interface to use
 * midi_out: MIDI OUT interface to use
 * ignore_* parameters: see the "_ignore_messages" method
 """
     self.__log.debug("Initializing MidiProcessor")
     super().__init__(midi_in, midi_out, ignore_sysex, ignore_timing,
                      ignore_active_sense)
     self._xml_dict = xml_dict
     self._quit = False
     self._status = None
     self._panic_command = []
     self._send_bank_names = "F0 7D 00 "
     self.__log.debug("MidiProcessor Initialized:\n%s",
                      PrettyFormat(self.__dict__))
예제 #2
0
    def _send_system_exclusive(self, message):
        """
    Handles a SysEx (system exlusive) message. By default it just sends it
    back.
    Parameters:
    * message: Contains a list with the bytes of the MIDI Message, ie:
      [240, 67, 112, 247] (in Hexadecimal: F0 43 70, F7). Please note that
      if the SysEx message is too long, then this method will be called
      several times untill the end byte of the SysEx (0xF7) gets sent.
    Remarks:
    * Please note that this callback isn't supposed to be overwritten by
      a subclass. Instead of doing this, create a handler called:
      _on_system_exclusive (Here I'm assuming that _callback_preffix is
      equal to "_on_"), then leave the part receiving the SysEx equal; only
      after you have received the whole SysEx, you should add your post
      processing. You must also clear the SysEx buffer afterwards
    """
        if not self._receive_sysex(message):
            self.__log.debug("Sending SysEx message: %s", message)
            #This means that the end of the SysEx message (0xF7) was detected,
            #so, no further bytes will be received. Here the SysEx buffer will
            #be sent and afterwards cleared
            if (self._midi_out == None) or (self._console_echo):
                self.__log.info(PrettyFormat(self._sysex_buffer))

            if self._midi_out != None:
                self._midi_out.send_message(self._sysex_buffer)

            #Clears SysEx buffer
            self._sysex_buffer = []
            #Resets SysEx count to zero
            self._sysex_chunk = 0
            self.__log.debug("SysEx message was sent")
 def _get_all_ports(self):
     """
 Gets all the available MIDI IN and Out ports.
 """
     in_ports = []
     out_ports = []
     if self._open_midi():
         self.__log.debug("Getting all MIDI IN ports")
         in_ports = self._get_midi_ports(self._midi_in)
         self.__log.debug("Got:\n%s", PrettyFormat(in_ports))
         self.__log.debug("Getting all MIDI OUT ports")
         out_ports = self._get_midi_ports(self._midi_out)
         self.__log.debug("Got:\n%s", PrettyFormat(out_ports))
     self._in_ports = in_ports
     self._out_ports = out_ports
     self._free_midi()
    def _parse_panic(self):
        """
    Parses the panic mode
    """
        self.__log.debug("Parsing XML node: Panic")
        node = self._xml_dict.get("Panic")
        if node != None:
            if type(node) == str:
                node_str = node
                command_file = None
            else:
                node_str = node.get('$')
                command_file = node.get('@File')

            commad_list = []
            if node_str != None:
                commad_list = self._parse_panic_string(node_str)

            if (commad_list != []) and command_file:
                message = "The Panic node only accepts either the inline command or a" \
                          " 'File' attribute, but not both"
                self.__log.debug(message)
                raise Exception(message)

            if (commad_list == []) and command_file:
                is_valid, command_str = read_text_file(command_file)
                if not is_valid:
                    message = "Trouble accessing file: %s" % command_file
                    self.__log.debug(message)
                    raise Exception(message)
                commad_list = self._parse_panic_string(command_str)

            self._panic_command = commad_list
            self.__log.debug("Got:\n%s", PrettyFormat(self._panic_command))
        self.__log.debug("Node was parsed")
 def _open_port(self, interface_type, midi_port, is_virtual=False):
     """
 Opens the specified MIDI port for the entered midi_callback
 Parameters:
 * interface_type: which interface to open: 'input' or 'output' 
 * midi_port: MIDI port used to open the MIDI interface
 * is_virtual: whether or not the port is virtual
 Returns:
 * In case of opening a virtual port, it will return a MIDI interface
 """
     if not is_virtual:
         self.__log.debug("Opening MIDI port: %s", PrettyFormat(midi_port))
         port_name = None
         client_name = None
     else:
         self.__log.debug("Opening Virtual MIDI port")
         port_name = midi_port
         midi_port = None
         client_name = VIRTUAL_PREFFIX[:-1]
     try:
         midi_interface = open_midiport(port=midi_port,
                                        type_=interface_type,
                                        use_virtual=is_virtual,
                                        interactive=False,
                                        client_name=client_name,
                                        port_name=port_name)[0]
     except:
         error = traceback.format_exc()
         self.__log.info(error)
         self._free_midi()
         sys.exit()
     return midi_interface
예제 #6
0
    def __call__(self, event, data=None):
        self.__log.debug("Executing callback with: %s", PrettyFormat(event))
        message, deltatime = event
        status = message[0]
        if message[0] != SYSTEM_EXCLUSIVE:
            new_status = message[0] & 0xF0
            if new_status != SYSTEM_EXCLUSIVE:
                #This will avoid that messages like SONG_START (0XFA) get
                #wrongly classified as SysEx
                status = new_status

        midi_message = self._midi_messages.get(status, None)
        callback_name = '_send_midi_message'
        if midi_message is None:
            if len(self._sysex_buffer) != 0:
                self.__log.debug("Catched SysEx message chunk")
                #This means that a SysEx message started
                callback_name = self._callback_preffix + 'system_exclusive'
            else:
                self.__log.debug("Catched unhandled MIDI message")
        else:
            self.__log.debug("Catched message: %s", midi_message)
            callback_name = self._callback_preffix + midi_message
        callback = getattr(self, callback_name)
        callback(message)
        self.__log.debug("Callback was excecuted")
 def _get_midi_ports(self, midi_interface):
     """
 Gets the available ports for the specified MIDI interface
 Parameters:
 * midi_interface: interface used for listing the ports. It can be
   either _midi_in or _midi_out.
 """
     self.__log.debug("Getting available MIDI ports")
     ports = midi_interface.get_ports()
     self.__log.debug("Got:\n%s", PrettyFormat(ports))
     port_index = 0
     for port in ports:
         port_index_str = str(port_index)
         ports[port_index] = port
         port_index += 1
     self.__log.debug("Fixed port indexes:\n%s", PrettyFormat(ports))
     return ports
 def _get_formatted_port_list(self, port_list):
     """
 Gets the port list as follows:
   <port_index>: <port_name>
 """
     self.__log.debug("Getting formatted port list")
     port_list_tuples = []
     for port_index, port_name in enumerate(port_list):
         port_list_tuples.append(str(port_index + 1) + ": " + port_name)
     self.__log.debug("Got: %s", PrettyFormat(port_list_tuples))
     return '\n\r'.join(port_list_tuples)
 def _send_system_exclusive(self, message):
     """
 Overrides the _send_system_exclusive method from MidiInputHandler.
 """
     self.__log.debug("Sending SysEx message: %s", PrettyFormat(message))
     if not self._receive_sysex(message) and self._xml_dict["@MidiEcho"]:
         #This means that the end of the SysEx message (0xF7) was detected,
         #so, no further bytes will be received. Here the SysEx buffer will
         #be sent and afterwards cleared
         self._midi_out.send_message(self._sysex_buffer)
         #Clears SysEx buffer
         self._sysex_buffer = []
         #Resets SysEx count to zero
         self._sysex_chunk = 0
     self.__log.debug("SysEx was sent")
예제 #10
0
    def __init__(self,
                 midi_in,
                 midi_out,
                 console_echo=False,
                 ignore_sysex=True,
                 ignore_timing=True,
                 ignore_active_sense=True):
        """
    Initializes the class attributes
    Parameters:
    * midi_in: MIDI IN interface to use
    * midi_out: MIDI OUT interface to use. If None, then the messages will be
      printed into the console
    * console_echo: if used together with midi_out, then the message will be
      first printed into the console, then it will be sent
    * ignore_* parameters: see the "_ignore_messages" method
    """
        self.__log.debug("Initializing MidiInputHandler")
        self._midi_in = midi_in
        self._midi_out = midi_out
        self._console_echo = console_echo
        self._sysex_buffer = []
        self._sysex_chunk = 0

        self._ignore_messages(ignore_sysex, ignore_timing, ignore_active_sense)

        #Sets the main MIDI callback where all preprocessing will be done
        self._midi_in.set_callback(self)

        #Creates the built-in callbacks, which will only echo the MIDI message
        base_callback = getattr(self, '_send_midi_message')
        self.__log.debug("Setting callbacks")
        for midi_message in self._midi_messages.values():
            callback = base_callback
            if midi_message == 'system_exclusive':
                callback = getattr(self, '_send_system_exclusive')

            message_callback_name = self._callback_preffix + midi_message
            if not hasattr(self, message_callback_name):
                #This means that the callback hasn't defined by a subclass, so
                #it will define here at the superclass
                setattr(self, message_callback_name, callback)
        self.__log.debug("Callbacks were set")
        self.__log.debug("MidiInputHandler was initialized:\n%s",
                         PrettyFormat(self.__dict__))
 def __init__(self, args, xsd_schema='conf/MidiBassPedalController.xsd'):
     """
 Initializes the MidiConnector class
 Parameters:
 * args: command-line arguments
 * xsd_schema: path to the xsd schema
 """
     self.__log.debug("Initializing MidiConnector")
     self._args = args
     self._xsd_schema = xsd_schema
     self._midi_in = None
     self._midi_out = None
     self._in_ports = []
     self._in_port = 0
     self._use_virtual_in = False
     self._out_ports = []
     self._out_port = 0
     self._use_virtual_out = False
     self._xml_dict = {}
     self.__log.debug("MidiConnector was initialized:\n%s",
                      PrettyFormat(self.__dict__))
    def _parse_xml_config(self):
        """
    Parses the specified xml configuration file
    """
        self.__log.info("Parsing XML config: %s", self._xsd_schema)
        exit = False
        self.__log.debug("Calling XMLSchema11 api")
        try:
            xsd_schema = xmlschema.XMLSchema11(self._xsd_schema)
        except:
            exit = True
            error = traceback.format_exc()
            self.__log.info("Error while parsing xsd file:\n%s\n\n%s",
                            self._xsd_schema, error)

        if not exit:
            self.__log.debug("Converting XML schema to dict")
            try:
                xml_dict = xsd_schema.to_dict(self._args.config)
                #A last manual validation must be done here: the InitialBank value must
                #be less or equal than the total number of banks
                if xml_dict['@InitialBank'] > len(xml_dict['Bank']):
                    raise Exception("InitialBank is higher than the possible number of "
                                    "banks / maximum: " + str(len(xml_dict['Bank'])) + \
                                    ", given value: " + str(xml_dict['@InitialBank']))
                self.__log.debug("Got: \n%s", PrettyFormat(xml_dict))
            except:
                exit = True
                error = traceback.format_exc()
                message = "Error while parsing xml file:\n%s\n\n%s" % (
                    self._args.config, error)
                self.__log.info(message)
        if exit:
            self.__log.debug("Unexpected error occured, aborting...")
            self._free_midi()
            sys.exit()

        self._xml_dict = xml_dict
    def parse_xml(self):
        """Parses the xml dict"""
        self.__log.debug("Parsing xml file")
        self._current_bank = self._xml_dict['@InitialBank'] - 1
        self._previous_pedals = {}
        self.__log.info("Current Bank: %s", self._xml_dict['@InitialBank'])
        self._parse_out_channels('BassPedal', self._xml_dict)
        self._parse_out_channels('Chord', self._xml_dict)
        self._parse_velocity_transpose("BassPedal", "Velocity", self._xml_dict)
        self._parse_velocity_transpose("Chord", "Velocity", self._xml_dict)
        self._parse_velocity_transpose("BassPedal", "Transpose",
                                       self._xml_dict)
        self._parse_velocity_transpose("Chord", "Transpose", self._xml_dict)
        self._parse_octave(self._xml_dict)
        if self._xml_dict["@Octave"] == None:
            self._xml_dict["@Octave"] = 0

        #Internally midi channels begin with zero
        self._xml_dict['@InChannel'] -= 1
        self._parse_panic()
        self._parse_banks()
        self._parse_start_stop("Start")
        self._parse_start_stop("Stop")
        self.__log.debug("Got:\n%s", PrettyFormat(self._xml_dict))
    def _send_midi_message(self, message):
        """
    Overrides the _send_midi_message method from MidiInputHandler.
    """
        self.__log.debug("Processing MIDI message: %s", PrettyFormat(message))
        messages = []
        status = message[0] & 0xF0
        channel = message[0] & 0x0F
        is_controller_message = True
        note_messages = []
        bank_select_messages = []
        midi_and_sysex_messages = []
        panic_message = []
        bank_select = None
        current_velocity = None
        if (self._xml_dict['@InChannel'] == channel):
            current_bank = self._xml_dict['Bank'][self._current_bank]
            process_bank_select = False
            current_pedal = None
            if status in [NOTE_ON, NOTE_OFF]:
                self.__log.debug(
                    "NOTE message was sent to controller, checking if "
                    "there is a pedal")
                current_note = message[1]
                current_pedal = current_bank["@PedalList"].get(current_note)
                if current_pedal != None:
                    self.__log.debug(
                        "Registered NOTE message was found, processing "
                        "actions")
                    swapped_note_message = False
                    current_velocity = message[2]
                    if (current_velocity
                            == 0) and self._xml_dict["@MinVelocityNoteOff"]:
                        self.__log.debug(
                            "Swapping NOTE ON message with a zero velocity "
                            "to a NOTE OFF message")
                        status = NOTE_OFF
                        swapped_note_message = True

                    note_messages, midi_and_sysex_messages, panic_message, bank_select = \
                      self._process_note_message(status, current_velocity,
                                                 swapped_note_message, current_pedal,
                                                 current_note)
                    process_bank_select = (bank_select != None)
                else:
                    is_controller_message = False
                    self.__log.debug(
                        "Unregistered NOTE message, going to check MIDI "
                        "echo")
            elif status == CONTROL_CHANGE:
                controller = message[1]
                if controller == self._xml_dict["@BankSelectController"]:
                    process_bank_select = True
                else:
                    is_controller_message = False
                    self.__log.debug(
                        "CONTROL CHANGE message detected, going to check "
                        "MIDI echo")
            if process_bank_select:
                self.__log.debug("SelectBank message was detected, processing "
                                 "actions")
                bank_select_messages = self._process_bank_select(
                    message, bank_select)

            messages.extend(midi_and_sysex_messages + note_messages +
                            bank_select_messages + panic_message)
        else:
            self.__log.debug(
                "Non controller message was catched, going to check "
                "MIDI echo")
            is_controller_message = False

        if not is_controller_message and self._xml_dict["@MidiEcho"]:
            self.__log.debug("Midi echo was enabled")
            messages = [message]
        elif not self._xml_dict["@MidiEcho"]:
            self.__log.debug("Midi echo is disabled. Message won't be sent")

        for message in messages:
            self.__log.debug("Sending MIDI message: %s", PrettyFormat(message))
            self._midi_out.send_message(message)
    def _process_note_message(self, status, current_velocity,
                              swapped_note_message, current_pedal,
                              current_note):
        """
    Proceses the note messages for the current pedal
    Parameters:
    * status: current MIDI status; either NOTE ON or NOTE OFF
    * current_velocity: current velocity of the catched note
    * swapped_note_message: inidicated if this was a NOTE ON message with a
      velocity of zero, which was changed to NOTE OFF. On this case, the
      velocity won't be adjusted
    * current_pedal: current catched pedal
    * current_note: current MIDI note
    Returns a tuple with the following elements:
    * Note messages to send: bass pedal and chord notes.
    * Other MIDI messages to send: it contains first the General MIDI and
      SysEx messages, and the the bass and chord notes of other pedals.
    * Panic message if SendPanic is True.
    * The last element is the BankSelect message to excecute; it can be:
      - None: no BankSelect was found or it is not a NOTE OFF message
      - 'Next': select the next bank
      - 'Previous': select previous bank
      - 'Last': select the last bank
      - 'Quit': quit the controller software
      - 'Reload': restart the controller; here the whole xml
      - 'Reboot': reboots the computer or microcontroller running the software
      - 'Shutdown': shutdowns the system
      - 'List': sends a SysEx back with the bank names.
      Those messages are always processed during NOTE OFF and after all other
      messages.
    """
        note_messages = self._set_note_velocity(current_pedal, status,
                                                current_velocity,
                                                swapped_note_message)
        messages = []
        midi_and_sysex = current_pedal.get("@MessageList")
        if (midi_and_sysex != None) and status in midi_and_sysex:
            messages = midi_and_sysex[status]

        if (len(note_messages) > 0):
            if (status == NOTE_ON):
                if self._xml_dict["@PedalMonophony"]:
                    self.__log.debug(
                        "Only one pedal is allowed at the time, so, notes for "
                        "previous pedals will be muted")
                    self.__log.debug(
                        "Sending NOTE OFF for previous pedals and then "
                        "remove them:\n%s",
                        PrettyFormat(self._previous_pedals))
                    pedal_list = list(self._previous_pedals.keys())
                    for pedal_index in pedal_list:
                        pedal = self._previous_pedals[pedal_index]['pedal']
                        messages.extend(
                            self._set_note_velocity(pedal, NOTE_OFF,
                                                    current_velocity, False))
                        self._previous_pedals.pop(pedal_index)
                self.__log.debug("Adding pedal to previous pedal list")
                self._previous_pedals[current_note] = {
                    'pedal': current_pedal,
                    'velocity': current_velocity
                }
            else:
                if current_note in self._previous_pedals:
                    self.__log.debug("Removing pedal from previous pedal list")
                    self._previous_pedals.pop(current_note)

        panic_message = []
        if status == NOTE_OFF:
            panic_message = current_pedal.get("@PanicMessage", [])
            if panic_message != []:
                self.__log.debug(
                    "Panic message will be sent after processing messages")

        bank_select = None
        if status == NOTE_OFF:
            #The BANK SELECT messages will be processed only on NOTE OFF
            bank_select = current_pedal.get("@BankSelect")

        self.__log.debug("Previous pedals after processing:\n%s",
                         PrettyFormat(self._previous_pedals))

        return note_messages, messages, panic_message, bank_select
    def _process_bank_select(self, message, bank_select):
        """
      Process the BankSelect message for the current pedal
      Parameters:
      * message: last message catched by the controller
      * bank_select: BankSelect operation:
        - None: no BankSelect was found or it is not a NOTE OFF message
        - 'Next': select the next bank
        - 'Previous': select previous bank
        - 'Last': select the last bank
        - 'Quit': quit the controller software
        - 'Reload': restart the controller; here the whole xml
        - 'Reboot': reboots the computer or microcontroller running the software
        - 'Shutdown': shutdowns the system
        - 'List': sends a SysEx back with the bank names.
      Returns the resulting messages after processing the BankSelect message
    """
        self.__log.debug("Previous pedals before processing Bank Select:\n%s",
                         PrettyFormat(self._previous_pedals))
        messages = []
        previous_bank = self._current_bank
        if (bank_select != None):
            if bank_select not in [
                    "Quit", "Reload", "Reboot", "Shutdown", "List"
            ]:
                self._current_bank = bank_select
                self.__log.info("Bank changed to: %d", bank_select + 1)
            elif bank_select == "List":
                messages = [self._xml_dict["@BanksSysEx"]]
            else:
                self._quit = True
                self._status = bank_select
        else:
            select_value = message[2]
            if select_value < 119:
                if select_value >= len(self._xml_dict["Bank"]):
                    select_value = len(self._xml_dict["Bank"]) - 1
                self._current_bank = select_value
                self.__log.info("Bank changed to: %d", self._current_bank + 1)
            else:
                send_panic = False
                send_bank_list = False
                num_banks = len(self._xml_dict["Bank"])
                if select_value == 119:
                    messages = [self._xml_dict["@BanksSysEx"]]
                    send_bank_list = True
                elif select_value == 120:
                    self._current_bank -= 1
                elif select_value == 121:
                    self._current_bank += 1
                elif select_value == 122:
                    self._current_bank = num_banks - 1
                elif select_value == 123:
                    send_panic = True
                    messages = self._panic_command
                    self.__log.debug("Sending software Panic:\n%s", \
                                     PrettyFormat(self._panic_command))
                else:
                    self._quit = True
                    self._status = BANK_SELECT_FUNCTIONS[select_value]
                if not self._quit and not send_bank_list:
                    if self._current_bank < 0:
                        self._current_bank = num_banks - 1
                    elif self._current_bank >= num_banks:
                        self._current_bank = 0
                    self.__log.info("Bank changed to: %d",
                                    self._current_bank + 1)

        if previous_bank != self._current_bank:
            on_bank_change = self._xml_dict.get("@OnBankChange")
            if on_bank_change != "ContinuePlayback":
                current_bank = self._xml_dict['Bank'][self._current_bank]
                pedal_list = current_bank["@PedalList"]
                self.__log.debug("Processing previous pedals for OnBankChange = %s" % \
                                 on_bank_change)
                pedal_operation = "Stopping previous pedals playback"
                if on_bank_change == "QuickChange":
                    pedal_operation = "Replacing previous pedals according to current" + \
                                      " bank"
                self.__log.debug(pedal_operation)
                previous_pedals = list(self._previous_pedals.keys())
                for pedal_index in previous_pedals:
                    note_messages = []
                    pedal = self._previous_pedals[pedal_index]['pedal']
                    current_velocity = self._previous_pedals[pedal_index][
                        'velocity']
                    remove_pedal = False
                    if on_bank_change == "StopPlayback":
                        remove_pedal = True
                    else:
                        #QuickChange
                        new_pedal = pedal_list.get(pedal_index)
                        if (new_pedal == None):
                            remove_pedal = True
                        else:
                            #Replace this pedal
                            self._previous_pedals[pedal_index][
                                'pedal'] = new_pedal
                            self._previous_pedals[pedal_index][
                                'velocity'] = current_velocity
                            #First NOTE_OFF messages for previous pedal will be sent
                            note_messages = self._set_note_velocity(
                                pedal, NOTE_OFF, current_velocity, False)
                            #Then the NOTE_ON messages for the new pedal will be sent
                            note_messages.extend(
                                self._set_note_velocity(
                                    new_pedal, NOTE_ON, current_velocity,
                                    False))
                    if remove_pedal:
                        self._previous_pedals.pop(pedal_index)
                        note_messages = self._set_note_velocity(
                            pedal, NOTE_OFF, current_velocity, False)
                    messages.extend(note_messages)
            self.__log.debug(
                "Previous pedals after processing Bank Select:\n%s",
                PrettyFormat(self._previous_pedals))
        return messages
    def _parse_port(self, port_list, arg_name):
        """
    Gets the specified port from command line
    Parameters:
    * port_list: List of available MIDI ports
    * arg_name: name of the argument to get. It can be: InPort or OutPort
    Returns:
    * A tupple containing:
      - either a port index or a virtual port string name
      - either if using a virtual or a real port
    """
        self.__log.debug("Getting: %s from:\n%s", arg_name,
                         PrettyFormat(port_list))
        use_virtual = False
        num_ports = len(port_list)
        port_value = self._xml_dict.get('@' + arg_name, num_ports)
        self.__log.debug("Port value: %s", port_value)
        if (type(port_value) == str) and port_value.isdigit():
            port_value = int(port_value)
        elif type(port_value) == str:
            is_windows = (platform.system() == "Windows")
            if port_value.startswith(VIRTUAL_PREFFIX):
                if not is_windows:
                    #Virtual port only work unser MACOS and Linux. Windows doesn't
                    #supports this. On the last operating system, the Virtual part will be
                    #removed and it will be threatened as a normal port. You can assure
                    #compatibilty between Windows and other OS by creating first the ports
                    #with loopMIDI
                    use_virtual = True
                elif port_value[-1] != '*':
                    port_value += '*'
                port_value = port_value[len(VIRTUAL_PREFFIX):]
            if not use_virtual:
                self.__log.debug("Searching port")
                #On this case, a string with part of the name was given, so, it
                #will be searched in the available ports
                port_index = 0
                port_found = False
                for port_name in port_list:
                    filtered = fnmatch.filter([port_name], port_value)
                    if filtered != []:
                        port_found = True
                        break
                    port_index += 1
                if not port_found:
                    self.__log.info("The %s: %s wasn't found.", arg_name,
                                    port_value)
                    self._free_midi()
                    self.__log.debug("Port wasn't found, exiting")
                    sys.exit()
                port_value = port_index + 1
                self.__log.debug("Port was found, index: %d", port_value)
            else:
                self.__log.debug("Virutal Port will be used")

        if not use_virtual:
            #Internally, port numbers start from 0 because they are in an array
            port_value -= 1
            if port_value >= num_ports:
                self.__log.info("Invalid port number was supplied")
                self._free_midi()
                self.__log.debug("Exiting after getting invalid port")
                sys.exit()

        return port_value, use_virtual