Source code for jams.nsconvert

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# CREATED:2016-02-16 15:40:04 by Brian McFee <brian.mcfee@nyu.edu>
r"""
Namespace conversion
--------------------

.. autosummary::
    :toctree: generated

    convert
"""

import numpy as np

from copy import deepcopy
from collections import defaultdict

from .exceptions import NamespaceError


# The structure that handles all conversion mappings
__CONVERSION__ = defaultdict(defaultdict)

__all__ = ["convert", "can_convert"]


def _conversion(target, source):
    """Decorate a function to register namespace conversions.

    Examples
    --------
    >>> @conversion('tag_open', 'tag_.*')
    ... def tag_to_open(annotation):
    ...     annotation.namespace = 'tag_open'
    ...     return annotation
    """

    def register(func):
        """Register func as mapping source to target"""
        __CONVERSION__[target][source] = func
        return func

    return register


[docs] def convert(annotation, target_namespace): """Convert a given annotation to the target namespace. Parameters ---------- annotation : jams.Annotation An annotation object target_namespace : str The target namespace Returns ------- mapped_annotation : jams.Annotation if `annotation` already belongs to `target_namespace`, then it is returned directly. otherwise, `annotation` is copied and automatically converted to the target namespace. Raises ------ SchemaError if the input annotation fails to validate NamespaceError if no conversion is possible Examples -------- Convert frequency measurements in Hz to MIDI >>> ann_midi = jams.convert(ann_hz, 'note_midi') And back to Hz >>> ann_hz2 = jams.convert(ann_midi, 'note_hz') """ # First, validate the input. If this fails, we can't auto-convert. annotation.validate(strict=True) # If we're already in the target namespace, do nothing if annotation.namespace == target_namespace: return annotation if target_namespace in __CONVERSION__: # Otherwise, make a copy to mangle annotation = deepcopy(annotation) # Look for a way to map this namespace to the target for source in __CONVERSION__[target_namespace]: if annotation.search(namespace=source): return __CONVERSION__[target_namespace][source](annotation) # No conversion possible raise NamespaceError( "Unable to convert annotation from namespace=" '"{0}" to "{1}"'.format(annotation.namespace, target_namespace) )
def can_convert(annotation, target_namespace): """Test if an annotation can be mapped to a target namespace Parameters ---------- annotation : jams.Annotation An annotation object target_namespace : str The target namespace Returns ------- True if `annotation` can be automatically converted to `target_namespace` False otherwise """ # If we're already in the target namespace, do nothing if annotation.namespace == target_namespace: return True if target_namespace in __CONVERSION__: # Look for a way to map this namespace to the target for source in __CONVERSION__[target_namespace]: if annotation.search(namespace=source): return True return False @_conversion("pitch_contour", "pitch_hz") def pitch_hz_to_contour(annotation): """Convert a pitch_hz annotation to a contour""" annotation.namespace = "pitch_contour" data = annotation.pop_data() for obs in data: annotation.append( time=obs.time, duration=obs.duration, confidence=obs.confidence, value=dict(index=0, frequency=np.abs(obs.value), voiced=obs.value > 0), ) return annotation @_conversion("pitch_contour", "pitch_midi") def pitch_midi_to_contour(annotation): """Convert a pitch_hz annotation to a contour""" annotation = pitch_midi_to_hz(annotation) return pitch_hz_to_contour(annotation) @_conversion("note_hz", "note_midi") def note_midi_to_hz(annotation): """Convert a pitch_midi annotation to pitch_hz""" annotation.namespace = "note_hz" data = annotation.pop_data() for obs in data: annotation.append( time=obs.time, duration=obs.duration, confidence=obs.confidence, value=440 * (2.0 ** ((obs.value - 69.0) / 12.0)), ) return annotation @_conversion("note_midi", "note_hz") def note_hz_to_midi(annotation): """Convert a pitch_hz annotation to pitch_midi""" annotation.namespace = "note_midi" data = annotation.pop_data() for obs in data: annotation.append( time=obs.time, duration=obs.duration, confidence=obs.confidence, value=12 * (np.log2(obs.value) - np.log2(440.0)) + 69, ) return annotation @_conversion("pitch_hz", "pitch_midi") def pitch_midi_to_hz(annotation): """Convert a pitch_midi annotation to pitch_hz""" annotation.namespace = "pitch_hz" data = annotation.pop_data() for obs in data: annotation.append( time=obs.time, duration=obs.duration, confidence=obs.confidence, value=440 * (2.0 ** ((obs.value - 69.0) / 12.0)), ) return annotation @_conversion("pitch_midi", "pitch_hz") def pitch_hz_to_midi(annotation): """Convert a pitch_hz annotation to pitch_midi""" annotation.namespace = "pitch_midi" data = annotation.pop_data() for obs in data: annotation.append( time=obs.time, duration=obs.duration, confidence=obs.confidence, value=12 * (np.log2(obs.value) - np.log2(440.0)) + 69, ) return annotation @_conversion("segment_open", "segment_.*") def segment_to_open(annotation): """Convert any segmentation to open label space""" annotation.namespace = "segment_open" return annotation @_conversion("tag_open", "tag_.*") def tag_to_open(annotation): """Convert any tag annotation to open label space""" annotation.namespace = "tag_open" return annotation @_conversion("tag_open", "scaper") def scaper_to_tag(annotation): """Convert scaper annotations to tag_open""" annotation.namespace = "tag_open" data = annotation.pop_data() for obs in data: annotation.append( time=obs.time, duration=obs.duration, confidence=obs.confidence, value=obs.value["label"], ) return annotation @_conversion("beat", "beat_position") def beat_position(annotation): """Convert beat_position to beat""" annotation.namespace = "beat" data = annotation.pop_data() for obs in data: annotation.append( time=obs.time, duration=obs.duration, confidence=obs.confidence, value=obs.value["position"], ) return annotation @_conversion("chord", "chord_harte") def chordh_to_chord(annotation): """Convert Harte annotation to chord""" annotation.namespace = "chord" return annotation