Example #1
0
    def _create_syncmap(self, text_map):
        """
        Create a sync map out of the provided interval map,
        and store it in the task object.

        Return a success bool flag.
        """
        self._log("Creating SyncMap")
        self._log("Number of fragments: %d" % len(text_map))
        if len(text_map) != len(self.task.text_file.fragments):
            return False
        try:
            sync_map = SyncMap()
            i = 0
            head = 0
            if self.task.configuration.is_audio_file_head_length != None:
                head = gf.safe_float(self.task.configuration.is_audio_file_head_length, 0)
            for fragment in self.task.text_file.fragments:
                start = head + text_map[i][0]
                end = head + text_map[i][1]
                sync_map_frag = SyncMapFragment(fragment, start, end)
                sync_map.append(sync_map_frag)
                i += 1
            self.task.sync_map = sync_map
            return True
        except:
            return False
Example #2
0
    def _compute_head_process_tail(self, audio_file_mfcc):
        """
        Set the audio file head or tail,
        by either reading the explicit values
        from the Task configuration,
        or using SD to determine them.

        This function returns the lengths, in seconds,
        of the (head, process, tail).

        :rtype: tuple (float, float, float)
        """
        head_length = self.task.configuration["i_a_head"]
        process_length = self.task.configuration["i_a_process"]
        tail_length = self.task.configuration["i_a_tail"]
        head_max = self.task.configuration["i_a_head_max"]
        head_min = self.task.configuration["i_a_head_min"]
        tail_max = self.task.configuration["i_a_tail_max"]
        tail_min = self.task.configuration["i_a_tail_min"]
        if (
            (head_length is not None) or
            (process_length is not None) or
            (tail_length is not None)
        ):
            self.log(u"Setting explicit head process tail")
        else:
            self.log(u"Detecting head tail...")
            sd = SD(audio_file_mfcc, self.task.text_file, rconf=self.rconf, logger=self.logger)
            head_length = TimeValue("0.000")
            process_length = None
            tail_length = TimeValue("0.000")
            if (head_min is not None) or (head_max is not None):
                self.log(u"Detecting HEAD...")
                head_length = sd.detect_head(head_min, head_max)
                self.log([u"Detected HEAD: %.3f", head_length])
                self.log(u"Detecting HEAD... done")
            if (tail_min is not None) or (tail_max is not None):
                self.log(u"Detecting TAIL...")
                tail_length = sd.detect_tail(tail_min, tail_max)
                self.log([u"Detected TAIL: %.3f", tail_length])
                self.log(u"Detecting TAIL... done")
            self.log(u"Detecting head tail... done")
        self.log([u"Head:    %s", gf.safe_float(head_length, None)])
        self.log([u"Process: %s", gf.safe_float(process_length, None)])
        self.log([u"Tail:    %s", gf.safe_float(tail_length, None)])
        return (head_length, process_length, tail_length)
Example #3
0
    def _compute_head_process_tail(self, audio_file_mfcc):
        """
        Set the audio file head or tail,
        by either reading the explicit values
        from the Task configuration,
        or using SD to determine them.

        This function returns the lengths, in seconds,
        of the (head, process, tail).

        :rtype: tuple (float, float, float)
        """
        head_length = self.task.configuration["i_a_head"]
        process_length = self.task.configuration["i_a_process"]
        tail_length = self.task.configuration["i_a_tail"]
        head_max = self.task.configuration["i_a_head_max"]
        head_min = self.task.configuration["i_a_head_min"]
        tail_max = self.task.configuration["i_a_tail_max"]
        tail_min = self.task.configuration["i_a_tail_min"]
        if ((head_length is not None) or (process_length is not None)
                or (tail_length is not None)):
            self.log(u"Setting explicit head process tail")
        else:
            self.log(u"Detecting head tail...")
            sd = SD(audio_file_mfcc,
                    self.task.text_file,
                    rconf=self.rconf,
                    logger=self.logger)
            head_length = TimeValue("0.000")
            process_length = None
            tail_length = TimeValue("0.000")
            if (head_min is not None) or (head_max is not None):
                self.log(u"Detecting HEAD...")
                head_length = sd.detect_head(head_min, head_max)
                self.log([u"Detected HEAD: %.3f", head_length])
                self.log(u"Detecting HEAD... done")
            if (tail_min is not None) or (tail_max is not None):
                self.log(u"Detecting TAIL...")
                tail_length = sd.detect_tail(tail_min, tail_max)
                self.log([u"Detected TAIL: %.3f", tail_length])
                self.log(u"Detecting TAIL... done")
            self.log(u"Detecting head tail... done")
        self.log([u"Head:    %s", gf.safe_float(head_length, None)])
        self.log([u"Process: %s", gf.safe_float(process_length, None)])
        self.log([u"Tail:    %s", gf.safe_float(tail_length, None)])
        return (head_length, process_length, tail_length)
Example #4
0
 def __str__(self):
     accumulator = ""
     accumulator += "File path:         %s\n" % self.file_path
     accumulator += "File size (bytes): %s\n" % gf.safe_int(self.file_size)
     accumulator += "Audio length (s):  %s\n" % gf.safe_float(self.audio_length)
     accumulator += "Audio format:      %s\n" % self.audio_format
     accumulator += "Audio sample rate: %s\n" % gf.safe_int(self.audio_sample_rate)
     accumulator += "Audio channels:    %s" % gf.safe_int(self.audio_channels)
     return accumulator
Example #5
0
 def __unicode__(self):
     msg = [
         u"File path:         %s" % self.file_path,
         u"File size (bytes): %s" % gf.safe_int(self.file_size),
         u"Audio length (s):  %s" % gf.safe_float(self.audio_length),
         u"Audio format:      %s" % self.audio_format,
         u"Audio sample rate: %s" % gf.safe_int(self.audio_sample_rate),
         u"Audio channels:    %s" % gf.safe_int(self.audio_channels),
         u"Samples capacity:  %s" % gf.safe_int(self.__samples_capacity),
         u"Samples length:    %s" % gf.safe_int(self.__samples_length),
     ]
     return u"\n".join(msg)
Example #6
0
 def __unicode__(self):
     msg = [
         u"File path:         %s" % self.file_path,
         u"File size (bytes): %s" % gf.safe_int(self.file_size),
         u"Audio length (s):  %s" % gf.safe_float(self.audio_length),
         u"Audio format:      %s" % self.audio_format,
         u"Audio sample rate: %s" % gf.safe_int(self.audio_sample_rate),
         u"Audio channels:    %s" % gf.safe_int(self.audio_channels),
         u"Samples capacity:  %s" % gf.safe_int(self.__samples_capacity),
         u"Samples length:    %s" % gf.safe_int(self.__samples_length),
     ]
     return u"\n".join(msg)
Example #7
0
 def __str__(self):
     accumulator = ""
     accumulator += "File path:         %s\n" % self.file_path
     accumulator += "File size (bytes): %s\n" % gf.safe_int(self.file_size)
     accumulator += "Audio length (s):  %s\n" % gf.safe_float(
         self.audio_length)
     accumulator += "Audio format:      %s\n" % self.audio_format
     accumulator += "Audio sample rate: %s\n" % gf.safe_int(
         self.audio_sample_rate)
     accumulator += "Audio channels:    %s" % gf.safe_int(
         self.audio_channels)
     return accumulator
Example #8
0
 def test_safe_float(self):
     tests = [
         ["3.14", 1.23, 3.14],
         [" 3.14", 1.23, 3.14],
         [" 3.14 ", 1.23, 3.14],
         ["3.14f", 1.23, 1.23],
         ["0x3.14", 1.23, 1.23],
         ["", 1.23, 1.23],
         ["foo", 1.23, 1.23],
         [None, 1.23, 1.23],
     ]
     for test in tests:
         self.assertEqual(gf.safe_float(test[0], test[1]), test[2])
 def test_safe_float(self):
     tests = [
         ("3.14", 1.23, 3.14),
         (" 3.14", 1.23, 3.14),
         (" 3.14 ", 1.23, 3.14),
         ("3.14f", 1.23, 1.23),
         ("0x3.14", 1.23, 1.23),
         ("", 1.23, 1.23),
         ("foo", 1.23, 1.23),
         (None, 1.23, 1.23),
     ]
     for test in tests:
         self.assertEqual(gf.safe_float(test[0], test[1]), test[2])
Example #10
0
 def test_safe_float(self):
     tests = [
         ("3.14", 1.23, 3.14),
         (" 3.14", 1.23, 3.14),
         (" 3.14 ", 1.23, 3.14),
         ("3.14f", 1.23, 1.23),
         ("0x3.14", 1.23, 1.23),
         ("", 1.23, 1.23),
         ("foo", 1.23, 1.23),
         (None, 1.23, 1.23),
     ]
     for test in tests:
         self.assertEqual(gf.safe_float(test[0], test[1]), test[2])
Example #11
0
 def test_safe_float(self):
     tests = [
         ["3.14", 1.23, 3.14],
         [" 3.14", 1.23, 3.14],
         [" 3.14 ", 1.23, 3.14],
         ["3.14f", 1.23, 1.23],
         ["0x3.14", 1.23, 1.23],
         ["", 1.23, 1.23],
         ["foo", 1.23, 1.23],
         [None, 1.23, 1.23],
     ]
     for test in tests:
         self.assertEqual(gf.safe_float(test[0], test[1]), test[2])
Example #12
0
 def _translate_text_map(self, text_map, real_full_wave_length):
     """
     Translate the text_map by adding head and tail dummy fragments
     """
     if len(text_map) == 0:
         self._log("No fragments in the text_map", Logger.CRITICAL)
         return (False, None)
     translated = []
     head = gf.safe_float(self.task.configuration.is_audio_file_head_length, 0)
     translated.append([0, head, None, None])
     end = 0
     for element in text_map:
         start, end, fragment_id, fragment_text = element
         start += head
         end += head
         translated.append([start, end, fragment_id, fragment_text])
     translated.append([end, real_full_wave_length, None, None])
     return (True, translated)
Example #13
0
    def read_properties(self):
        """
        Populate this object by reading
        the audio properties of the file at the given path.

        Currently this function uses
        :class:`aeneas.ffprobewrapper.FFPROBEWrapper`
        to get the audio file properties.
        """

        self._log("Reading properties")

        # check the file can be read
        if self.file_path is None:
            raise AttributeError("File path is None")
        if not os.path.isfile(self.file_path):
            self._log(["File '%s' cannot be read", self.file_path],
                      Logger.CRITICAL)
            raise OSError("File cannot be read")

        # get the file size
        self._log(["Getting file size for '%s'", self.file_path])
        self.file_size = os.path.getsize(self.file_path)
        self._log(
            ["File size for '%s' is '%d'", self.file_path, self.file_size])

        # get the audio properties
        self._log("Reading properties with FFPROBEWrapper...")
        prober = FFPROBEWrapper(logger=self.logger)
        properties = prober.read_properties(self.file_path)
        self._log("Reading properties with FFPROBEWrapper... done")

        # save relevant properties in results inside the audiofile object
        self.audio_length = gf.safe_float(
            properties[FFPROBEWrapper.STDOUT_DURATION])
        self._log(["Stored audio_length: '%s'", self.audio_length])
        self.audio_format = properties[FFPROBEWrapper.STDOUT_CODEC_NAME]
        self._log(["Stored audio_format: '%s'", self.audio_format])
        self.audio_sample_rate = gf.safe_int(
            properties[FFPROBEWrapper.STDOUT_SAMPLE_RATE])
        self._log(["Stored audio_sample_rate: '%s'", self.audio_sample_rate])
        self.audio_channels = gf.safe_int(
            properties[FFPROBEWrapper.STDOUT_CHANNELS])
        self._log(["Stored audio_channels: '%s'", self.audio_channels])
Example #14
0
 def _translate_text_map(self, text_map, real_full_wave_length):
     """
     Translate the text_map by adding head and tail dummy fragments
     """
     if len(text_map) == 0:
         self._log("No fragments in the text_map", Logger.CRITICAL)
         return (False, None)
     translated = []
     head = gf.safe_float(self.task.configuration.is_audio_file_head_length,
                          0)
     translated.append([0, head, None, None])
     end = 0
     for element in text_map:
         start, end, fragment_id, fragment_text = element
         start += head
         end += head
         translated.append([start, end, fragment_id, fragment_text])
     translated.append([end, real_full_wave_length, None, None])
     return (True, translated)
Example #15
0
    def read_properties(self):
        """
        Populate this object by reading
        the audio properties of the file at the given path.

        Currently this function uses
        :class:`aeneas.ffprobewrapper.FFPROBEWrapper`
        to get the audio file properties.
        """

        self._log("Reading properties")

        # check the file can be read
        if self.file_path is None:
            raise AttributeError("File path is None")
        if not os.path.isfile(self.file_path):
            self._log(["File '%s' cannot be read", self.file_path], Logger.CRITICAL)
            raise OSError("File cannot be read")

        # get the file size
        self._log(["Getting file size for '%s'", self.file_path])
        self.file_size = os.path.getsize(self.file_path)
        self._log(["File size for '%s' is '%d'", self.file_path, self.file_size])

        # get the audio properties
        self._log("Reading properties with FFPROBEWrapper...")
        prober = FFPROBEWrapper(logger=self.logger)
        properties = prober.read_properties(self.file_path)
        self._log("Reading properties with FFPROBEWrapper... done")

        # save relevant properties in results inside the audiofile object
        self.audio_length = gf.safe_float(properties[FFPROBEWrapper.STDOUT_DURATION])
        self._log(["Stored audio_length: '%s'", self.audio_length])
        self.audio_format = properties[FFPROBEWrapper.STDOUT_CODEC_NAME]
        self._log(["Stored audio_format: '%s'", self.audio_format])
        self.audio_sample_rate = gf.safe_int(properties[FFPROBEWrapper.STDOUT_SAMPLE_RATE])
        self._log(["Stored audio_sample_rate: '%s'", self.audio_sample_rate])
        self.audio_channels = gf.safe_int(properties[FFPROBEWrapper.STDOUT_CHANNELS])
        self._log(["Stored audio_channels: '%s'", self.audio_channels])
Example #16
0
    def _cut_head_tail(self, audio_file_path):
        """
        Set the audio file head or tail,
        suitably cutting the audio file on disk,
        and setting the corresponding parameters in the task configuration.

        Return a success bool flag
        """
        self._log("Setting head and/or tail")
        try:
            configuration = self.task.configuration
            head_length = configuration.is_audio_file_head_length
            process_length = configuration.is_audio_file_process_length
            detect_head_min = configuration.is_audio_file_detect_head_min
            detect_head_max = configuration.is_audio_file_detect_head_max
            detect_tail_min = configuration.is_audio_file_detect_tail_min
            detect_tail_max = configuration.is_audio_file_detect_tail_max

            # explicit head or process?
            explicit = (head_length is not None) or (process_length
                                                     is not None)

            # at least one detect parameter?
            detect = ((detect_head_min is not None)
                      or (detect_head_max is not None)
                      or (detect_tail_min is not None)
                      or (detect_tail_max is not None))

            if explicit or detect:
                # we need to load the audio data
                audio_file = AudioFile(audio_file_path, logger=self.logger)
                audio_file.load_data()

                if explicit:
                    self._log("Explicit head or process")
                else:
                    self._log(
                        "No explicit head or process => detecting head/tail")

                    head = 0.0
                    if (detect_head_min is not None) or (detect_head_max
                                                         is not None):
                        self._log("Detecting head...")
                        detect_head_min = gf.safe_float(
                            detect_head_min, gc.SD_MIN_HEAD_LENGTH)
                        detect_head_max = gf.safe_float(
                            detect_head_max, gc.SD_MAX_HEAD_LENGTH)
                        self._log(["detect_head_min is %.3f", detect_head_min])
                        self._log(["detect_head_max is %.3f", detect_head_max])
                        sd = SD(audio_file,
                                self.task.text_file,
                                logger=self.logger)
                        head = sd.detect_head(detect_head_min, detect_head_max)
                        self._log(["Detected head: %.3f", head])

                    tail = 0.0
                    if (detect_tail_min is not None) or (detect_tail_max
                                                         is not None):
                        self._log("Detecting tail...")
                        detect_tail_max = gf.safe_float(
                            detect_tail_max, gc.SD_MAX_TAIL_LENGTH)
                        detect_tail_min = gf.safe_float(
                            detect_tail_min, gc.SD_MIN_TAIL_LENGTH)
                        self._log(["detect_tail_min is %.3f", detect_tail_min])
                        self._log(["detect_tail_max is %.3f", detect_tail_max])
                        sd = SD(audio_file,
                                self.task.text_file,
                                logger=self.logger)
                        tail = sd.detect_tail(detect_tail_min, detect_tail_max)
                        self._log(["Detected tail: %.3f", tail])

                    # sanity check
                    head_length = max(0, head)
                    process_length = max(0,
                                         audio_file.audio_length - tail - head)

                    # we need to set these values
                    # in the config object for later use
                    self.task.configuration.is_audio_file_head_length = head_length
                    self.task.configuration.is_audio_file_process_length = process_length
                    self._log(["Set head_length:    %.3f", head_length])
                    self._log(["Set process_length: %.3f", process_length])

                if head_length is not None:
                    # in case we are reading from config object
                    head_length = float(head_length)
                if process_length is not None:
                    # in case we are reading from config object
                    process_length = float(process_length)
                # note that str() is necessary, as one might be None
                self._log(
                    ["is_audio_file_head_length is %s",
                     str(head_length)])
                self._log([
                    "is_audio_file_process_length is %s",
                    str(process_length)
                ])
                self._log("Trimming audio data...")
                audio_file.trim(head_length, process_length)
                self._log("Trimming audio data... done")
                self._log("Writing audio file...")
                audio_file.write(audio_file_path)
                self._log("Writing audio file... done")
                audio_file.clear_data()
            else:
                # nothing to do
                self._log("No explicit head/process or detect head/tail")

            self._log("Setting head and/or tail: succeeded")
            return True
        except Exception as e:
            self._log("Setting head and/or tail: failed")
            self._log(["Message: %s", str(e)])
            return False
Example #17
0
    def perform_command(self):
        """
        Perform command and return the appropriate exit code.

        :rtype: int
        """
        if len(self.actual_arguments) < 4:
            return self.print_help()
        text_format = gf.safe_unicode(self.actual_arguments[0])
        if text_format == u"list":
            text = gf.safe_unicode(self.actual_arguments[1])
        elif text_format in TextFileFormat.ALLOWED_VALUES:
            text = self.actual_arguments[1]
            if not self.check_input_file(text):
                return self.ERROR_EXIT_CODE
        else:
            return self.print_help()

        l1_id_regex = self.has_option_with_value(u"--l1-id-regex")
        l2_id_regex = self.has_option_with_value(u"--l2-id-regex")
        l3_id_regex = self.has_option_with_value(u"--l3-id-regex")
        id_regex = self.has_option_with_value(u"--id-regex")
        class_regex = self.has_option_with_value(u"--class-regex")
        sort = self.has_option_with_value(u"--sort")
        backwards = self.has_option([u"-b", u"--backwards"])
        quit_after = gf.safe_float(self.has_option_with_value(u"--quit-after"), None)
        start_fragment = gf.safe_int(self.has_option_with_value(u"--start"), None)
        end_fragment = gf.safe_int(self.has_option_with_value(u"--end"), None)
        parameters = {
            gc.PPN_TASK_IS_TEXT_MUNPARSED_L1_ID_REGEX: l1_id_regex,
            gc.PPN_TASK_IS_TEXT_MUNPARSED_L2_ID_REGEX: l2_id_regex,
            gc.PPN_TASK_IS_TEXT_MUNPARSED_L3_ID_REGEX: l3_id_regex,
            gc.PPN_TASK_IS_TEXT_UNPARSED_CLASS_REGEX: class_regex,
            gc.PPN_TASK_IS_TEXT_UNPARSED_ID_REGEX: id_regex,
            gc.PPN_TASK_IS_TEXT_UNPARSED_ID_SORT: sort,
        }
        if (text_format == TextFileFormat.MUNPARSED) and ((l1_id_regex is None) or (l2_id_regex is None) or (l3_id_regex is None)):
            self.print_error(u"You must specify --l1-id-regex and --l2-id-regex and --l3-id-regex for munparsed format")
            return self.ERROR_EXIT_CODE
        if (text_format == TextFileFormat.UNPARSED) and (id_regex is None) and (class_regex is None):
            self.print_error(u"You must specify --id-regex and/or --class-regex for unparsed format")
            return self.ERROR_EXIT_CODE

        language = gf.safe_unicode(self.actual_arguments[2])

        output_file_path = self.actual_arguments[3]
        if not self.check_output_file(output_file_path):
            return self.ERROR_EXIT_CODE

        text_file = self.get_text_file(text_format, text, parameters)
        if text_file is None:
            self.print_error(u"Unable to build a TextFile from the given parameters")
            return self.ERROR_EXIT_CODE
        elif len(text_file) == 0:
            self.print_error(u"No text fragments found")
            return self.ERROR_EXIT_CODE
        text_file.set_language(language)
        self.print_info(u"Read input text with %d fragments" % (len(text_file)))
        if start_fragment is not None:
            self.print_info(u"Slicing from index %d" % (start_fragment))
        if end_fragment is not None:
            self.print_info(u"Slicing to index %d" % (end_fragment))
        text_slice = text_file.get_slice(start_fragment, end_fragment)
        self.print_info(u"Synthesizing %d fragments" % (len(text_slice)))

        if quit_after is not None:
            self.print_info(u"Stop synthesizing upon reaching %.3f seconds" % (quit_after))

        try:
            synt = Synthesizer(rconf=self.rconf, logger=self.logger)
            synt.synthesize(
                text_slice,
                output_file_path,
                quit_after=quit_after,
                backwards=backwards
            )
            self.print_success(u"Created file '%s'" % output_file_path)
            synt.clear_cache()
            return self.NO_ERROR_EXIT_CODE
        except ImportError as exc:
            tts = self.rconf[RuntimeConfiguration.TTS]
            if tts == Synthesizer.AWS:
                self.print_error(u"You need to install Python module boto3 to use the AWS Polly TTS API wrapper. Run:")
                self.print_error(u"$ pip install boto3")
                self.print_error(u"or, to install for all users:")
                self.print_error(u"$ sudo pip install boto3")
            elif tts == Synthesizer.NUANCE:
                self.print_error(u"You need to install Python module requests to use the Nuance TTS API wrapper. Run:")
                self.print_error(u"$ pip install requests")
                self.print_error(u"or, to install for all users:")
                self.print_error(u"$ sudo pip install requests")
            else:
                self.print_error(u"An unexpected error occurred while synthesizing text:")
                self.print_error(u"%s" % exc)
        except Exception as exc:
            self.print_error(u"An unexpected error occurred while synthesizing text:")
            self.print_error(u"%s" % exc)

        return self.ERROR_EXIT_CODE
Example #18
0
    def perform_command(self):
        """
        Perform command and return the appropriate exit code.

        :rtype: int
        """
        if len(self.actual_arguments) < 4:
            return self.print_help()
        text_format = gf.safe_unicode(self.actual_arguments[0])
        if text_format == u"list":
            text = gf.safe_unicode(self.actual_arguments[1])
        elif text_format in TextFileFormat.ALLOWED_VALUES:
            text = self.actual_arguments[1]
            if not self.check_input_file(text):
                return self.ERROR_EXIT_CODE
        else:
            return self.print_help()

        l1_id_regex = self.has_option_with_value(u"--l1-id-regex")
        l2_id_regex = self.has_option_with_value(u"--l2-id-regex")
        l3_id_regex = self.has_option_with_value(u"--l3-id-regex")
        id_regex = self.has_option_with_value(u"--id-regex")
        class_regex = self.has_option_with_value(u"--class-regex")
        sort = self.has_option_with_value(u"--sort")
        backwards = self.has_option([u"-b", u"--backwards"])
        quit_after = gf.safe_float(self.has_option_with_value(u"--quit-after"),
                                   None)
        start_fragment = gf.safe_int(self.has_option_with_value(u"--start"),
                                     None)
        end_fragment = gf.safe_int(self.has_option_with_value(u"--end"), None)
        parameters = {
            gc.PPN_TASK_IS_TEXT_MUNPARSED_L1_ID_REGEX: l1_id_regex,
            gc.PPN_TASK_IS_TEXT_MUNPARSED_L2_ID_REGEX: l2_id_regex,
            gc.PPN_TASK_IS_TEXT_MUNPARSED_L3_ID_REGEX: l3_id_regex,
            gc.PPN_TASK_IS_TEXT_UNPARSED_CLASS_REGEX: class_regex,
            gc.PPN_TASK_IS_TEXT_UNPARSED_ID_REGEX: id_regex,
            gc.PPN_TASK_IS_TEXT_UNPARSED_ID_SORT: sort,
        }
        if (text_format == TextFileFormat.MUNPARSED) and (
            (l1_id_regex is None) or (l2_id_regex is None) or
            (l3_id_regex is None)):
            self.print_error(
                u"You must specify --l1-id-regex and --l2-id-regex and --l3-id-regex for munparsed format"
            )
            return self.ERROR_EXIT_CODE
        if (text_format == TextFileFormat.UNPARSED) and (
                id_regex is None) and (class_regex is None):
            self.print_error(
                u"You must specify --id-regex and/or --class-regex for unparsed format"
            )
            return self.ERROR_EXIT_CODE

        language = gf.safe_unicode(self.actual_arguments[2])

        output_file_path = self.actual_arguments[3]
        if not self.check_output_file(output_file_path):
            return self.ERROR_EXIT_CODE

        text_file = self.get_text_file(text_format, text, parameters)
        if text_file is None:
            self.print_error(
                u"Unable to build a TextFile from the given parameters")
            return self.ERROR_EXIT_CODE
        elif len(text_file) == 0:
            self.print_error(u"No text fragments found")
            return self.ERROR_EXIT_CODE
        text_file.set_language(language)
        self.print_info(u"Read input text with %d fragments" %
                        (len(text_file)))
        if start_fragment is not None:
            self.print_info(u"Slicing from index %d" % (start_fragment))
        if end_fragment is not None:
            self.print_info(u"Slicing to index %d" % (end_fragment))
        text_slice = text_file.get_slice(start_fragment, end_fragment)
        self.print_info(u"Synthesizing %d fragments" % (len(text_slice)))

        if quit_after is not None:
            self.print_info(u"Stop synthesizing upon reaching %.3f seconds" %
                            (quit_after))

        try:
            synt = Synthesizer(rconf=self.rconf, logger=self.logger)
            synt.synthesize(text_slice,
                            output_file_path,
                            quit_after=quit_after,
                            backwards=backwards)
            self.print_success(u"Created file '%s'" % output_file_path)
            synt.clear_cache()
            return self.NO_ERROR_EXIT_CODE
        except ImportError as exc:
            tts = self.rconf[RuntimeConfiguration.TTS]
            if tts == Synthesizer.AWS:
                self.print_error(
                    u"You need to install Python module boto3 to use the AWS Polly TTS API wrapper. Run:"
                )
                self.print_error(u"$ pip install boto3")
                self.print_error(u"or, to install for all users:")
                self.print_error(u"$ sudo pip install boto3")
            elif tts == Synthesizer.NUANCE:
                self.print_error(
                    u"You need to install Python module requests to use the Nuance TTS API wrapper. Run:"
                )
                self.print_error(u"$ pip install requests")
                self.print_error(u"or, to install for all users:")
                self.print_error(u"$ sudo pip install requests")
            else:
                self.print_error(
                    u"An unexpected error occurred while synthesizing text:")
                self.print_error(u"%s" % exc)
        except Exception as exc:
            self.print_error(
                u"An unexpected error occurred while synthesizing text:")
            self.print_error(u"%s" % exc)

        return self.ERROR_EXIT_CODE
Example #19
0
 def __unicode__(self):
     msg = [
         u"File path:        %s" % self.file_path,
         u"Audio length (s): %s" % gf.safe_float(self.audio_length),
     ]
     return u"\n".join(msg)
Example #20
0
    def perform_command(self):
        """
        Perform command and return the appropriate exit code.

        :rtype: int
        """
        if len(self.actual_arguments) < 4:
            return self.print_help()
        text_format = gf.safe_unicode(self.actual_arguments[0])
        if text_format == u"list":
            text = gf.safe_unicode(self.actual_arguments[1])
        elif text_format in TextFileFormat.ALLOWED_VALUES:
            text = self.actual_arguments[1]
            if not self.check_input_file(text):
                return self.ERROR_EXIT_CODE
        else:
            return self.print_help()

        l1_id_regex = self.has_option_with_value(u"--l1-id-regex")
        l2_id_regex = self.has_option_with_value(u"--l2-id-regex")
        l3_id_regex = self.has_option_with_value(u"--l3-id-regex")
        id_regex = self.has_option_with_value(u"--id-regex")
        class_regex = self.has_option_with_value(u"--class-regex")
        sort = self.has_option_with_value(u"--sort")
        parameters = {
            gc.PPN_TASK_IS_TEXT_MUNPARSED_L1_ID_REGEX : l1_id_regex,
            gc.PPN_TASK_IS_TEXT_MUNPARSED_L2_ID_REGEX : l2_id_regex,
            gc.PPN_TASK_IS_TEXT_MUNPARSED_L3_ID_REGEX : l3_id_regex,
            gc.PPN_JOB_IS_TEXT_UNPARSED_ID_REGEX : id_regex,
            gc.PPN_JOB_IS_TEXT_UNPARSED_CLASS_REGEX : class_regex,
            gc.PPN_JOB_IS_TEXT_UNPARSED_ID_SORT : sort
        }
        if (text_format == TextFileFormat.MUNPARSED) and ((l1_id_regex is None) or (l2_id_regex is None) or (l3_id_regex is None)):
            self.print_error(u"You must specify --l1-id-regex and --l2-id-regex and --l3-id-regex for munparsed format")
            return self.ERROR_EXIT_CODE
        if (text_format == TextFileFormat.UNPARSED) and (id_regex is None) and (class_regex is None):
            self.print_error(u"You must specify --id-regex and/or --class-regex for unparsed format")
            return self.ERROR_EXIT_CODE

        language = gf.safe_unicode(self.actual_arguments[2])

        audio_file_path = self.actual_arguments[3]
        if not self.check_input_file(audio_file_path):
            return self.ERROR_EXIT_CODE

        text_file = self.get_text_file(text_format, text, parameters)
        if text_file is None:
            self.print_error(u"Unable to build a TextFile from the given parameters")
            return self.ERROR_EXIT_CODE
        elif len(text_file) == 0:
            self.print_error(u"No text fragments found")
            return self.ERROR_EXIT_CODE
        text_file.set_language(language)
        self.print_info(u"Read input text with %d fragments" % (len(text_file)))

        self.print_info(u"Reading audio...")
        try:
            audio_file_mfcc = AudioFileMFCC(audio_file_path, rconf=self.rconf, logger=self.logger)
        except AudioFileConverterError:
            self.print_error(u"Unable to call the ffmpeg executable '%s'" % (self.rconf[RuntimeConfiguration.FFMPEG_PATH]))
            self.print_error(u"Make sure the path to ffmpeg is correct")
            return self.ERROR_EXIT_CODE
        except (AudioFileUnsupportedFormatError, AudioFileNotInitializedError):
            self.print_error(u"Cannot read file '%s'" % (audio_file_path))
            self.print_error(u"Check that its format is supported by ffmpeg")
            return self.ERROR_EXIT_CODE
        except Exception as exc:
            self.print_error(u"An unexpected error occurred while reading the audio file:")
            self.print_error(u"%s" % exc)
            return self.ERROR_EXIT_CODE
        self.print_info(u"Reading audio... done")

        self.print_info(u"Running VAD...")
        audio_file_mfcc.run_vad()
        self.print_info(u"Running VAD... done")

        min_head = gf.safe_float(self.has_option_with_value(u"--min-head"), None)
        max_head = gf.safe_float(self.has_option_with_value(u"--max-head"), None)
        min_tail = gf.safe_float(self.has_option_with_value(u"--min-tail"), None)
        max_tail = gf.safe_float(self.has_option_with_value(u"--max-tail"), None)

        self.print_info(u"Detecting audio interval...")
        start_detector = SD(audio_file_mfcc, text_file, rconf=self.rconf, logger=self.logger)
        start, end = start_detector.detect_interval(min_head, max_head, min_tail, max_tail)
        self.print_info(u"Detecting audio interval... done")

        self.print_result(audio_file_mfcc.audio_length, start, end)
        return self.NO_ERROR_EXIT_CODE
Example #21
0
    def read_properties(self, audio_file_path):
        """
        Read the properties of an audio file
        and return them as a dictionary.

        Example: ::

            d["index"]=0
            d["codec_name"]=mp3
            d["codec_long_name"]=MP3 (MPEG audio layer 3)
            d["profile"]=unknown
            d["codec_type"]=audio
            d["codec_time_base"]=1/44100
            d["codec_tag_string"]=[0][0][0][0]
            d["codec_tag"]=0x0000
            d["sample_fmt"]=s16p
            d["sample_rate"]=44100
            d["channels"]=1
            d["channel_layout"]=mono
            d["bits_per_sample"]=0
            d["id"]=N/A
            d["r_frame_rate"]=0/0
            d["avg_frame_rate"]=0/0
            d["time_base"]=1/14112000
            d["start_pts"]=0
            d["start_time"]=0.000000
            d["duration_ts"]=1545083190
            d["duration"]=109.487188
            d["bit_rate"]=128000
            d["max_bit_rate"]=N/A
            d["bits_per_raw_sample"]=N/A
            d["nb_frames"]=N/A
            d["nb_read_frames"]=N/A
            d["nb_read_packets"]=N/A
            d["DISPOSITION:default"]=0
            d["DISPOSITION:dub"]=0
            d["DISPOSITION:original"]=0
            d["DISPOSITION:comment"]=0
            d["DISPOSITION:lyrics"]=0
            d["DISPOSITION:karaoke"]=0
            d["DISPOSITION:forced"]=0
            d["DISPOSITION:hearing_impaired"]=0
            d["DISPOSITION:visual_impaired"]=0
            d["DISPOSITION:clean_effects"]=0
            d["DISPOSITION:attached_pic"]=0

        :param audio_file_path: the path of the audio file to analyze
        :type  audio_file_path: string (path)
        :rtype: dict
        """

        # test if we can read the file at audio_file_path
        if not os.path.isfile(audio_file_path):
            self._log(["Input file '%s' cannot be read", audio_file_path], Logger.CRITICAL)
            raise OSError("Input file cannot be read")

        # call ffprobe
        arguments = []
        arguments += [gc.FFPROBE_PATH]
        arguments += self.FFPROBE_PARAMETERS
        arguments += [audio_file_path]
        self._log(["Calling with arguments '%s'", arguments])
        proc = subprocess.Popen(arguments, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
        (stdoutdata, stderrdata) = proc.communicate()
        proc.stdout.close()
        proc.stdin.close()
        proc.stderr.close()
        self._log("Call completed")

        # if no output, raise error
        if (stdoutdata is None) or (len(stderrdata) == 0):
            msg = "No output for '%s'" % audio_file_path
            self._log(msg, Logger.CRITICAL)
            raise Exception(msg)

        # dictionary for the results
        results = dict()
        results[self.STDOUT_CHANNELS] = None
        results[self.STDOUT_CODEC_NAME] = None
        results[self.STDOUT_DURATION] = None
        results[self.STDOUT_SAMPLE_RATE] = None

        # scan the first audio stream the ffprobe stdout output
        # TODO more robust parsing
        # TODO deal with multiple audio streams
        for line in stdoutdata.splitlines():
            if line == self.STDOUT_END_STREAM:
                self._log("Reached end of the stream")
                break
            elif len(line.split("=")) == 2:
                key, value = line.split("=")
                results[key] = value
                self._log(["Found property '%s'='%s'", key, value])

        # convert duration to float
        if self.STDOUT_DURATION in results:
            self._log(["Found duration: '%s'", results[self.STDOUT_DURATION]])
            results[self.STDOUT_DURATION] = gf.safe_float(results[self.STDOUT_DURATION], None)
        else:
            self._log("No duration found in stdout")

        # if audio_length is still None, try scanning ffprobe stderr output
        try:
            if results[self.STDOUT_DURATION] is None:
                pattern = re.compile(self.STDERR_DURATION_REGEX)
                for line in stderrdata.splitlines():
                    match = pattern.search(line)
                    if match is not None:
                        self._log(["Found matching line '%s'", line])
                        v_h = int(match.group(1))
                        v_m = int(match.group(2))
                        v_s = int(match.group(3))
                        v_f = float("0." + match.group(4))
                        v_length = v_h * 3600 + v_m * 60 + v_s + v_f
                        results[self.STDOUT_DURATION] = v_length
                        self._log(["Extracted duration '%f'", v_length])
                        break
        except ValueError:
            self._log("ValueError exception while parsing stderr")
        except TypeError:
            self._log("TypeError exception while parsing stderr")

        if results[self.STDOUT_DURATION] is None:
            self._log("No duration found in stdout or stderr", Logger.CRITICAL)
            raise ValueError("Cannot determine duration of the input file")

        # return dictionary
        self._log("Returning dict")
        return results
Example #22
0
    def _cut_head_tail(self, audio_file_path):
        """
        Set the audio file head or tail,
        suitably cutting the audio file on disk,
        and setting the corresponding parameters in the task configuration.

        Return a success bool flag
        """
        self._log("Setting head and/or tail")
        try:
            configuration = self.task.configuration
            head_length = configuration.is_audio_file_head_length
            process_length = configuration.is_audio_file_process_length
            detect_head_min = configuration.is_audio_file_detect_head_min
            detect_head_max = configuration.is_audio_file_detect_head_max
            detect_tail_min = configuration.is_audio_file_detect_tail_min
            detect_tail_max = configuration.is_audio_file_detect_tail_max

            # explicit head or process?
            explicit = (head_length is not None) or (process_length is not None)

            # at least one detect parameter?
            detect = (
                (detect_head_min is not None) or
                (detect_head_max is not None) or
                (detect_tail_min is not None) or
                (detect_tail_max is not None)
            )

            if explicit or detect:
                # we need to load the audio data
                audio_file = AudioFile(audio_file_path, logger=self.logger)
                audio_file.load_data()

                if explicit:
                    self._log("Explicit head or process")
                else:
                    self._log("No explicit head or process => detecting head/tail")

                    head = 0.0
                    if (detect_head_min is not None) or (detect_head_max is not None):
                        self._log("Detecting head...")
                        detect_head_min = gf.safe_float(detect_head_min, gc.SD_MIN_HEAD_LENGTH)
                        detect_head_max = gf.safe_float(detect_head_max, gc.SD_MAX_HEAD_LENGTH)
                        self._log(["detect_head_min is %.3f", detect_head_min])
                        self._log(["detect_head_max is %.3f", detect_head_max])
                        sd = SD(audio_file, self.task.text_file, logger=self.logger)
                        head = sd.detect_head(detect_head_min, detect_head_max)
                        self._log(["Detected head: %.3f", head])

                    tail = 0.0
                    if (detect_tail_min is not None) or (detect_tail_max is not None):
                        self._log("Detecting tail...")
                        detect_tail_max = gf.safe_float(detect_tail_max, gc.SD_MAX_TAIL_LENGTH)
                        detect_tail_min = gf.safe_float(detect_tail_min, gc.SD_MIN_TAIL_LENGTH)
                        self._log(["detect_tail_min is %.3f", detect_tail_min])
                        self._log(["detect_tail_max is %.3f", detect_tail_max])
                        sd = SD(audio_file, self.task.text_file, logger=self.logger)
                        tail = sd.detect_tail(detect_tail_min, detect_tail_max)
                        self._log(["Detected tail: %.3f", tail])

                    # sanity check
                    head_length = max(0, head)
                    process_length = max(0, audio_file.audio_length - tail - head)

                    # we need to set these values
                    # in the config object for later use
                    self.task.configuration.is_audio_file_head_length = head_length
                    self.task.configuration.is_audio_file_process_length = process_length
                    self._log(["Set head_length:    %.3f", head_length])
                    self._log(["Set process_length: %.3f", process_length])

                if head_length is not None:
                    # in case we are reading from config object
                    head_length = float(head_length)
                if process_length is not None:
                    # in case we are reading from config object
                    process_length = float(process_length)
                # note that str() is necessary, as one might be None
                self._log(["is_audio_file_head_length is %s", str(head_length)])
                self._log(["is_audio_file_process_length is %s", str(process_length)])
                self._log("Trimming audio data...")
                audio_file.trim(head_length, process_length)
                self._log("Trimming audio data... done")
                self._log("Writing audio file...")
                audio_file.write(audio_file_path)
                self._log("Writing audio file... done")
                audio_file.clear_data()
            else:
                # nothing to do
                self._log("No explicit head/process or detect head/tail")

            self._log("Setting head and/or tail: succeeded")
            return True
        except Exception as e:
            self._log("Setting head and/or tail: failed")
            self._log(["Message: %s", str(e)])
            return False
Example #23
0
 def __unicode__(self):
     msg = [
         u"File path:        %s" % self.file_path,
         u"Audio length (s): %s" % gf.safe_float(self.audio_length),
     ]
     return u"\n".join(msg)
Example #24
0
    def perform_command(self):
        """
        Perform command and return the appropriate exit code.

        :rtype: int
        """
        if len(self.actual_arguments) < 4:
            return self.print_help()
        text_format = gf.safe_unicode(self.actual_arguments[0])
        if text_format == u"list":
            text = gf.safe_unicode(self.actual_arguments[1])
        elif text_format in TextFileFormat.ALLOWED_VALUES:
            text = self.actual_arguments[1]
            if not self.check_input_file(text):
                return self.ERROR_EXIT_CODE
        else:
            return self.print_help()

        l1_id_regex = self.has_option_with_value(u"--l1-id-regex")
        l2_id_regex = self.has_option_with_value(u"--l2-id-regex")
        l3_id_regex = self.has_option_with_value(u"--l3-id-regex")
        id_regex = self.has_option_with_value(u"--id-regex")
        class_regex = self.has_option_with_value(u"--class-regex")
        sort = self.has_option_with_value(u"--sort")
        parameters = {
            gc.PPN_TASK_IS_TEXT_MUNPARSED_L1_ID_REGEX: l1_id_regex,
            gc.PPN_TASK_IS_TEXT_MUNPARSED_L2_ID_REGEX: l2_id_regex,
            gc.PPN_TASK_IS_TEXT_MUNPARSED_L3_ID_REGEX: l3_id_regex,
            gc.PPN_TASK_IS_TEXT_UNPARSED_CLASS_REGEX: class_regex,
            gc.PPN_TASK_IS_TEXT_UNPARSED_ID_REGEX: id_regex,
            gc.PPN_TASK_IS_TEXT_UNPARSED_ID_SORT: sort,
        }
        if (text_format == TextFileFormat.MUNPARSED) and (
            (l1_id_regex is None) or (l2_id_regex is None) or
            (l3_id_regex is None)):
            self.print_error(
                u"You must specify --l1-id-regex and --l2-id-regex and --l3-id-regex for munparsed format"
            )
            return self.ERROR_EXIT_CODE
        if (text_format == TextFileFormat.UNPARSED) and (
                id_regex is None) and (class_regex is None):
            self.print_error(
                u"You must specify --id-regex and/or --class-regex for unparsed format"
            )
            return self.ERROR_EXIT_CODE

        language = gf.safe_unicode(self.actual_arguments[2])

        audio_file_path = self.actual_arguments[3]
        if not self.check_input_file(audio_file_path):
            return self.ERROR_EXIT_CODE

        text_file = self.get_text_file(text_format, text, parameters)
        if text_file is None:
            self.print_error(
                u"Unable to build a TextFile from the given parameters")
            return self.ERROR_EXIT_CODE
        elif len(text_file) == 0:
            self.print_error(u"No text fragments found")
            return self.ERROR_EXIT_CODE
        text_file.set_language(language)
        self.print_info(u"Read input text with %d fragments" %
                        (len(text_file)))

        self.print_info(u"Reading audio...")
        try:
            audio_file_mfcc = AudioFileMFCC(audio_file_path,
                                            rconf=self.rconf,
                                            logger=self.logger)
        except AudioFileConverterError:
            self.print_error(u"Unable to call the ffmpeg executable '%s'" %
                             (self.rconf[RuntimeConfiguration.FFMPEG_PATH]))
            self.print_error(u"Make sure the path to ffmpeg is correct")
            return self.ERROR_EXIT_CODE
        except (AudioFileUnsupportedFormatError, AudioFileNotInitializedError):
            self.print_error(u"Cannot read file '%s'" % (audio_file_path))
            self.print_error(u"Check that its format is supported by ffmpeg")
            return self.ERROR_EXIT_CODE
        except Exception as exc:
            self.print_error(
                u"An unexpected error occurred while reading the audio file:")
            self.print_error(u"%s" % exc)
            return self.ERROR_EXIT_CODE
        self.print_info(u"Reading audio... done")

        self.print_info(u"Running VAD...")
        audio_file_mfcc.run_vad()
        self.print_info(u"Running VAD... done")

        min_head = gf.safe_float(self.has_option_with_value(u"--min-head"),
                                 None)
        max_head = gf.safe_float(self.has_option_with_value(u"--max-head"),
                                 None)
        min_tail = gf.safe_float(self.has_option_with_value(u"--min-tail"),
                                 None)
        max_tail = gf.safe_float(self.has_option_with_value(u"--max-tail"),
                                 None)

        self.print_info(u"Detecting audio interval...")
        start_detector = SD(audio_file_mfcc,
                            text_file,
                            rconf=self.rconf,
                            logger=self.logger)
        start, end = start_detector.detect_interval(min_head, max_head,
                                                    min_tail, max_tail)
        self.print_info(u"Detecting audio interval... done")

        self.print_result(audio_file_mfcc.audio_length, start, end)
        return self.NO_ERROR_EXIT_CODE
Example #25
0
    def read_properties(self, audio_file_path):
        """
        Read the properties of an audio file
        and return them as a dictionary.

        Example: ::

            d["index"]=0
            d["codec_name"]=mp3
            d["codec_long_name"]=MP3 (MPEG audio layer 3)
            d["profile"]=unknown
            d["codec_type"]=audio
            d["codec_time_base"]=1/44100
            d["codec_tag_string"]=[0][0][0][0]
            d["codec_tag"]=0x0000
            d["sample_fmt"]=s16p
            d["sample_rate"]=44100
            d["channels"]=1
            d["channel_layout"]=mono
            d["bits_per_sample"]=0
            d["id"]=N/A
            d["r_frame_rate"]=0/0
            d["avg_frame_rate"]=0/0
            d["time_base"]=1/14112000
            d["start_pts"]=0
            d["start_time"]=0.000000
            d["duration_ts"]=1545083190
            d["duration"]=109.487188
            d["bit_rate"]=128000
            d["max_bit_rate"]=N/A
            d["bits_per_raw_sample"]=N/A
            d["nb_frames"]=N/A
            d["nb_read_frames"]=N/A
            d["nb_read_packets"]=N/A
            d["DISPOSITION:default"]=0
            d["DISPOSITION:dub"]=0
            d["DISPOSITION:original"]=0
            d["DISPOSITION:comment"]=0
            d["DISPOSITION:lyrics"]=0
            d["DISPOSITION:karaoke"]=0
            d["DISPOSITION:forced"]=0
            d["DISPOSITION:hearing_impaired"]=0
            d["DISPOSITION:visual_impaired"]=0
            d["DISPOSITION:clean_effects"]=0
            d["DISPOSITION:attached_pic"]=0

        :param audio_file_path: the path of the audio file to analyze
        :type  audio_file_path: string (path)
        :rtype: dict
        """

        # test if we can read the file at audio_file_path
        if not os.path.isfile(audio_file_path):
            self._log(["Input file '%s' cannot be read", audio_file_path], Logger.CRITICAL)
            raise OSError("Input file cannot be read")

        # call ffprobe
        arguments = []
        arguments += [gc.FFPROBE_PATH]
        arguments += self.FFPROBE_PARAMETERS
        arguments += [audio_file_path]
        self._log(["Calling with arguments '%s'", arguments])
        proc = subprocess.Popen(
            arguments,
            stdout=subprocess.PIPE,
            stdin=subprocess.PIPE,
            stderr=subprocess.PIPE)
        (stdoutdata, stderrdata) = proc.communicate()
        proc.stdout.close()
        proc.stdin.close()
        proc.stderr.close()
        self._log("Call completed")

        # if no output, raise error
        if (stdoutdata is None) or (len(stderrdata) == 0):
            msg = "No output for '%s'" % audio_file_path
            self._log(msg, Logger.CRITICAL)
            raise Exception(msg)

        # dictionary for the results
        results = dict()
        results[self.STDOUT_CHANNELS] = None
        results[self.STDOUT_CODEC_NAME] = None
        results[self.STDOUT_DURATION] = None
        results[self.STDOUT_SAMPLE_RATE] = None

        # scan the first audio stream the ffprobe stdout output
        # TODO more robust parsing
        # TODO deal with multiple audio streams
        for line in stdoutdata.splitlines():
            if line == self.STDOUT_END_STREAM:
                self._log("Reached end of the stream")
                break
            elif len(line.split("=")) == 2:
                key, value = line.split("=")
                results[key] = value
                self._log(["Found property '%s'='%s'", key, value])

        # convert duration to float
        if self.STDOUT_DURATION in results:
            self._log(["Found duration: '%s'", results[self.STDOUT_DURATION]])
            results[self.STDOUT_DURATION] = gf.safe_float(
                results[self.STDOUT_DURATION],
                None
            )
        else:
            self._log("No duration found in stdout")

        # if audio_length is still None, try scanning ffprobe stderr output
        try:
            if results[self.STDOUT_DURATION] is None:
                pattern = re.compile(self.STDERR_DURATION_REGEX)
                for line in stderrdata.splitlines():
                    match = pattern.search(line)
                    if match is not None:
                        self._log(["Found matching line '%s'", line])
                        v_h = int(match.group(1))
                        v_m = int(match.group(2))
                        v_s = int(match.group(3))
                        v_f = float("0." + match.group(4))
                        v_length = v_h * 3600 + v_m * 60 + v_s + v_f
                        results[self.STDOUT_DURATION] = v_length
                        self._log(["Extracted duration '%f'", v_length])
                        break
        except ValueError:
            self._log("ValueError exception while parsing stderr")
        except TypeError:
            self._log("TypeError exception while parsing stderr")

        if results[self.STDOUT_DURATION] is None:
            self._log("No duration found in stdout or stderr", Logger.CRITICAL)
            raise ValueError("Cannot determine duration of the input file")

        # return dictionary
        self._log("Returning dict")
        return results
Example #26
0
 def test_safe_float_07(self):
     default = 1.23
     expected = 1.23
     value = "0x3.14"
     result = safe_float(value, default)
     self.assertEqual(result, expected)