示例#1
0
    def _add_meta(
        self,
        file: str,
        version: str,
        archive: str,
        checksum: str,
    ):
        r"""Add or update table file.

        Args:
            file: relative file path
            archive: archive name without extension
            checksum: checksum of file
            version: version string

        """
        format = audeer.file_extension(file).lower()

        self._df.loc[file] = [
            archive,  # archive
            0,  # bit_depth
            0,  # channels
            checksum,  # checksum
            0.0,  # duration
            format,  # format
            0,  # removed
            0,  # sampling_rate
            define.DependType.META,  # type
            version,  # version
        ]
示例#2
0
    def _add_media(
        self,
        root: str,
        file: str,
        version: str,
        archive: str = None,
        checksum: str = None,
    ):
        r"""Add or update media file.

        If you want to update only the version
        of an unaltered media file,
        don't specify ``archive`` and ``checksum``.

        Args:
            root: root directory
            file: relative file path
            archive: archive name without extension
            checksum: checksum of file
            version: version string

        """
        format = audeer.file_extension(file).lower()

        if archive is None:
            archive = self.archive(file)

        if checksum is None:
            checksum = self.checksum(file)
            bit_depth = self.bit_depth(file)
            channels = self.channels(file)
            duration = self.duration(file)
            sampling_rate = self.sampling_rate(file)
        else:
            bit_depth = channels = sampling_rate = 0
            duration = 0.0
            if format in define.FORMATS:
                path = os.path.join(root, file)
                bit_depth = audiofile.bit_depth(path)
                channels = audiofile.channels(path)
                duration = audiofile.duration(path)
                sampling_rate = audiofile.sampling_rate(path)

        self._df.loc[file] = [
            archive,
            bit_depth,
            channels,
            checksum,
            duration,
            format,
            0,  # removed
            sampling_rate,
            define.DependType.MEDIA,
            version,
        ]
示例#3
0
def test_empty_file(empty_file):
    # Reading file
    signal, sampling_rate = af.read(empty_file)
    assert len(signal) == 0
    # Metadata
    for sloppy in [True, False]:
        assert af.duration(empty_file, sloppy=sloppy) == 0.0
    assert af.channels(empty_file) == 1
    assert af.sampling_rate(empty_file) == sampling_rate
    assert af.samples(empty_file) == 0
    if audeer.file_extension(empty_file) == 'wav':
        assert af.bit_depth(empty_file) == 16
    else:
        assert af.bit_depth(empty_file) is None
示例#4
0
    def load(self, path: str):
        r"""Read dependencies from file.

        Clears existing dependencies.

        Args:
            path: path to file.
                File extension can be ``csv`` or ``pkl``.

        Raises:
            ValueError: if file extension is not ``csv`` or ``pkl``
            FileNotFoundError: if ``path`` does not exists

        """
        self._df = pd.DataFrame(columns=define.DEPEND_FIELD_NAMES.values())
        path = audeer.safe_path(path)
        extension = audeer.file_extension(path)
        if extension not in ['csv', 'pkl']:
            raise ValueError(
                f"File extension of 'path' has to be 'csv' or 'pkl' "
                f"not '{extension}'")
        if not os.path.exists(path):
            raise FileNotFoundError(
                errno.ENOENT,
                os.strerror(errno.ENOENT),
                path,
            )
        if extension == 'pkl':
            self._df = pd.read_pickle(path)
        elif extension == 'csv':
            # Data type of dependency columns
            dtype_mapping = {
                name: dtype
                for name, dtype in zip(
                    define.DEPEND_FIELD_NAMES.values(),
                    define.DEPEND_FIELD_DTYPES.values(),
                )
            }
            # Data type of index
            index = 0
            dtype_mapping[index] = str
            self._df = pd.read_csv(
                path,
                index_col=index,
                na_filter=False,
                dtype=dtype_mapping,
            )
示例#5
0
def test_mp3(tmpdir, magnitude, sampling_rate, channels):

    # Currently we are not able to setup the Windows runner with MP3 support
    # https://github.com/audeering/audiofile/issues/51
    if sys.platform == 'win32':
        return

    signal = sine(magnitude=magnitude,
                  sampling_rate=sampling_rate,
                  channels=channels)
    # Create wav file and use sox to convert to mp3
    wav_file = str(tmpdir.join('signal.wav'))
    mp3_file = str(tmpdir.join('signal.mp3'))
    af.write(wav_file, signal, sampling_rate)
    subprocess.call(['sox', wav_file, mp3_file])
    assert audeer.file_extension(mp3_file) == 'mp3'
    sig, fs = af.read(mp3_file)
    assert_allclose(_magnitude(sig), magnitude, rtol=0, atol=tolerance(16))
    assert fs == sampling_rate
    assert _channels(sig) == channels
    if channels == 1:
        assert sig.ndim == 1
    else:
        assert sig.ndim == 2
    assert af.channels(mp3_file) == _channels(sig)
    assert af.sampling_rate(mp3_file) == sampling_rate
    assert af.samples(mp3_file) == _samples(sig)
    assert af.duration(mp3_file) == _duration(sig, sampling_rate)
    assert af.duration(mp3_file,
                       sloppy=True) == sox.file_info.duration(mp3_file)
    assert af.bit_depth(mp3_file) is None

    # Test additional arguments to read with sox
    offset = 0.1
    duration = 0.5
    sig, fs = af.read(mp3_file, offset=offset, duration=duration)
    assert _duration(sig, sampling_rate) == duration
    sig, fs = af.read(mp3_file, offset=offset)
    # Don't test for 48000 Hz and 2 channels
    # https://github.com/audeering/audiofile/issues/23
    if not (sampling_rate == 48000 and channels == 2):
        assert_allclose(
            _duration(sig, sampling_rate),
            af.duration(mp3_file) - offset,
            rtol=0,
            atol=tolerance('duration', sampling_rate),
        )
示例#6
0
    def _check_convert(
            self,
            file: str,
            bit_depth: typing.Optional[int],
            channels: typing.Optional[int],
            sampling_rate: typing.Optional[int],
    ) -> bool:
        r"""Check if file needs to be converted to flavor."""
        format = audeer.file_extension(file).lower()

        # format change
        if self.format is not None:
            if self.format != format:
                return True

        convert = False

        # precision change
        if not convert and self.bit_depth is not None:
            bit_depth = bit_depth or audiofile.bit_depth(file)
            if self.bit_depth != bit_depth:
                convert = True

        # mixdown and channel selection
        if not convert and self.mixdown or self.channels is not None:
            channels = channels or audiofile.channels(file)
            if self.mixdown and channels != 1:
                convert = True
            elif list(range(channels)) != self.channels:
                convert = True

        # sampling rate change
        if not convert and self.sampling_rate is not None:
            sampling_rate = sampling_rate or audiofile.sampling_rate(file)
            if self.sampling_rate != sampling_rate:
                convert = True

        if convert and format not in define.FORMATS:
            raise RuntimeError(
                f"You have to specify the 'format' argument "
                f"to convert '{file}' "
                f"to the specified flavor "
                f"as we cannot write to {format.upper()} files."
            )

        return convert
示例#7
0
    def destination(
            self,
            file: str,
    ) -> str:
        r"""Return converted file path.

        The file path will only change if the file is converted to a different
        format.

        Args:
            file: path to input file

        Returns:
            path to output file

        """
        if self.format is not None:
            format = audeer.file_extension(file).lower()
            if format != self.format:
                file = f'{file[:-len(format)]}{self.format}'
        return file
示例#8
0
def test_broken_file(non_audio_file):
    # Only match the beginning of error message
    # as the default soundfile message differs at the end on macOS
    error_msg = 'Error opening'
    # Reading file
    with pytest.raises(RuntimeError, match=error_msg):
        af.read(non_audio_file)
    # Metadata
    if audeer.file_extension(non_audio_file) == 'wav':
        with pytest.raises(RuntimeError, match=error_msg):
            af.bit_depth(non_audio_file)
    else:
        assert af.bit_depth(non_audio_file) is None
    with pytest.raises(RuntimeError, match=error_msg):
        af.channels(non_audio_file)
    with pytest.raises(RuntimeError, match=error_msg):
        af.duration(non_audio_file)
    with pytest.raises(RuntimeError, match=error_msg):
        af.samples(non_audio_file)
    with pytest.raises(RuntimeError, match=error_msg):
        af.sampling_rate(non_audio_file)
示例#9
0
def test_file_type(tmpdir, file_type, magnitude, sampling_rate, channels):
    use_sox = True
    file = str(tmpdir.join('signal.' + file_type))
    signal = sine(magnitude=magnitude,
                  sampling_rate=sampling_rate,
                  channels=channels)
    # Skip unallowed combination
    if file_type == 'flac' and channels > 8:
        return 0
    # Windows runners sox does not support flac
    if sys.platform == 'win32' and file_type in ['flac', 'ogg']:
        use_sox = False
    # Allowed combinations
    bit_depth = 16
    sig, fs = write_and_read(file, signal, sampling_rate, bit_depth=bit_depth)
    # Test file type
    assert audeer.file_extension(file) == file_type
    # Test magnitude
    assert_allclose(_magnitude(sig), magnitude, rtol=0, atol=tolerance(16))
    # Test sampling rate
    assert fs == sampling_rate
    if use_sox:
        assert sox.file_info.sample_rate(file) == sampling_rate
    # Test channels
    assert _channels(sig) == channels
    if use_sox:
        assert sox.file_info.channels(file) == channels
    # Test samples
    assert _samples(sig) == _samples(signal)
    if use_sox:
        assert sox.file_info.num_samples(file) == _samples(signal)
    # Test bit depth
    if use_sox:
        bit_depth = sox.file_info.bitdepth(file)
    elif file_type == 'ogg':
        bit_depth = None
    assert af.bit_depth(file) == bit_depth
示例#10
0
def file_extension(path):
    """Lower case file extension."""
    return audeer.file_extension(path).lower()
示例#11
0
    def __call__(
            self,
            src_path: str,
            dst_path: str,
            *,
            src_bit_depth: int = None,
            src_channels: int = None,
            src_sampling_rate: int = None,
    ):
        r"""Convert file to flavor.

        If ``bit_depth``, ``channels`` or ``sampling_rate``
        of source signal are known, they can be provided.
        Otherwise, they will be computed using :mod:`audiofile`.

        Args:
            src_path: path to input file
            dst_path: path to output file
            src_bit_depth: bit depth
            src_channels: number of channels
            src_sampling_rate: sampling rate in Hz

        Raises:
            ValueError: if extension of output file does not match the
                format of the flavor
            RuntimeError: if a conversion is requested,
                but no output format is specified,
                and the input format is not WAV or FLAC

        """
        src_path = audeer.safe_path(src_path)
        dst_path = audeer.safe_path(dst_path)

        # verify that extension matches the output format
        src_ext = audeer.file_extension(src_path).lower()
        dst_ext = audeer.file_extension(dst_path).lower()
        expected_ext = self.format or src_ext
        if expected_ext != dst_ext:
            raise ValueError(
                f"Extension of output file is '{dst_ext}', "
                f"but should be '{expected_ext}' "
                "to match the format of the converted file."
            )

        if not self._check_convert(
                src_path, src_bit_depth, src_channels, src_sampling_rate
        ):

            # file already satisfies flavor
            if src_path != dst_path:
                shutil.copy(src_path, dst_path)

        else:

            # convert file to flavor
            signal, sampling_rate = audiofile.read(src_path, always_2d=True)
            signal = self._remix(signal)
            signal, sampling_rate = self._resample(signal, sampling_rate)
            bit_depth = (
                self.bit_depth
                or src_bit_depth
                or audiofile.bit_depth(src_path)
                or 16
            )
            audiofile.write(
                dst_path,
                signal,
                sampling_rate,
                bit_depth=bit_depth,
            )
示例#12
0
def test_file_extension(path, extension):
    ext = audeer.file_extension(path)
    assert ext == extension
    assert type(ext) is str