Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gtzan_genre.Track.to_jams #212

Merged
merged 11 commits into from
Apr 8, 2020
6 changes: 5 additions & 1 deletion mirdata/beatles.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,11 @@ def to_jams(self):
section_data=[(self.sections, None)],
chord_data=[(self.chords, None)],
key_data=[(self.key, None)],
metadata={'artist': 'The Beatles', 'title': self.title},
metadata={
'artist': 'The Beatles',
'title': self.title,
'duration': librosa.get_duration(self.audio[0], self.audio[1]),
},
)


Expand Down
16 changes: 12 additions & 4 deletions mirdata/gtzan_genre.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
destination_dir="gtzan_genre",
)


DATA = utils.LargeData("gtzan_genre_index.json")


Expand Down Expand Up @@ -70,12 +69,21 @@ def audio(self):
return load_audio(self.audio_path, sample_rate=22050)

def to_jams(self):
"""(Not Implemented) Jams: the track's data in jams format"""
raise NotImplementedError
"""Jams: the track's data in jams format"""
return jams_utils.jams_converter(
tags_gtzan_data=[(self.genre, 'gtzan-genre')],
metadata={
'title': "Unknown track",
'artist': "Unknown artist",
'release': "Unknown album",
'duration': 30.0,
'curator': 'George Tzanetakis',
},
)


def load_audio(audio_path, sample_rate=22050):
"""Load a GTzan audio file.
"""Load a GTZAN audio file.

Args:
audio_path (str): path to audio file
Expand Down
1 change: 1 addition & 0 deletions mirdata/ikala.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ def to_jams(self):
'singer_id': self.singer_id,
'track_id': self.track_id,
'song_id': self.song_id,
'duration': librosa.get_duration(self.mix_audio[0], self.mix_audio[1]),
},
)

Expand Down
103 changes: 77 additions & 26 deletions mirdata/jams_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ def jams_converter(
multi_section_data=None,
key_data=None,
lyrics_data=None,
tags_gtzan_data=None,
metadata=None,
):

Expand All @@ -41,6 +42,9 @@ def jams_converter(
A list of tuples of (KeyData, str), where str describes the annotation.
lyrics_data (list or None):
A list of tuples of (LyricData, str), where str describes the annotation.
tags_gtzan_data (list or None):
A list of tuples of (str, str), where the first srt is the tag and the second
is a descriptor of the annotation.
metadata (dict or None):
A dictionary containing the track metadata.

Expand All @@ -59,12 +63,13 @@ def jams_converter(
setattr(jam.file_metadata, key, metadata[key])
else:
setattr(jam.sandbox, key, metadata[key])

# beats
if beat_data is not None:
if type(beat_data) != list:
if not isinstance(beat_data, list):
raise TypeError('beat_data should be a list of tuples')
for beats in beat_data:
if type(beats) != tuple:
if not isinstance(beats, tuple):
raise TypeError(
'beat_data should be a list of tuples, '
+ 'but contains a {} element'.format(type(beats))
Expand All @@ -73,10 +78,10 @@ def jams_converter(

# sections
if section_data is not None:
if type(section_data) != list:
if not isinstance(section_data, list):
raise TypeError('section_data should be a list of tuples')
for sections in section_data:
if type(sections) != tuple:
if not isinstance(sections, tuple):
raise TypeError(
'section_data should be a list of tuples, '
+ 'but contains a {} element'.format(type(sections))
Expand All @@ -85,15 +90,17 @@ def jams_converter(

# multi-sections (sections with multiple levels)
if multi_section_data is not None:
if type(multi_section_data) != list:
if not isinstance(multi_section_data, list):
raise TypeError('multi_section_data should be a list of tuples')
for sections in multi_section_data:
if type(sections) != tuple:
if not isinstance(sections, tuple):
raise TypeError(
'multi_section_data should be a list of tuples, '
+ 'but contains a {} element'.format(type(sections))
)
if (type(sections[0]) != list) or (type(sections[0][0]) != tuple):
if not (
isinstance(sections[0], list) and isinstance(sections[0][0], tuple)
):
raise TypeError(
'tuples in multi_section_data should contain a '
+ 'list of tuples, indicating annotations in the different '
Expand All @@ -104,10 +111,10 @@ def jams_converter(

# chords
if chord_data is not None:
if type(chord_data) != list:
if not isinstance(chord_data, list):
raise TypeError('chord_data should be a list of tuples')
for chords in chord_data:
if type(chords) != tuple:
if not isinstance(chords, tuple):
raise TypeError(
'chord_data should be a list of tuples, '
+ 'but contains a {} element'.format(type(chords))
Expand All @@ -116,10 +123,10 @@ def jams_converter(

# notes
if note_data is not None:
if type(note_data) != list:
if not isinstance(note_data, list):
raise TypeError('note_data should be a list of tuples')
for notes in note_data:
if type(notes) != tuple:
if not isinstance(notes, tuple):
raise TypeError(
'note_data should be a list of tuples, '
+ 'but contains a {} element'.format(type(notes))
Expand All @@ -128,10 +135,10 @@ def jams_converter(

# keys
if key_data is not None:
if type(key_data) != list:
if not isinstance(key_data, list):
raise TypeError('key_data should be a list of tuples')
for keys in key_data:
if type(keys) != tuple:
if not isinstance(keys, tuple):
raise TypeError(
'key_data should be a list of tuples, '
+ 'but contains a {} element'.format(type(keys))
Expand All @@ -140,10 +147,10 @@ def jams_converter(

# f0
if f0_data is not None:
if type(f0_data) != list:
if not isinstance(f0_data, list):
raise TypeError('f0_data should be a list of tuples')
for f0s in f0_data:
if type(f0s) != tuple:
if not isinstance(f0s, tuple):
raise TypeError(
'f0_data should be a list of tuples, '
+ 'but contains a {} element'.format(type(f0s))
Expand All @@ -152,16 +159,28 @@ def jams_converter(

# lyrics
if lyrics_data is not None:
if type(lyrics_data) != list:
if not isinstance(lyrics_data, list):
raise TypeError('lyrics_data should be a list of tuples')
for lyrics in lyrics_data:
if type(lyrics) != tuple:
if not isinstance(lyrics, tuple):
raise TypeError(
'lyrics_data should be a list of tuples, '
+ 'but contains a {} element'.format(type(lyrics))
)
jam.annotations.append(lyrics_to_jams(lyrics))

# tags
if tags_gtzan_data is not None:
if not isinstance(tags_gtzan_data, list):
raise TypeError('tags_gtzan_data should be a list of tuples')
for tag in tags_gtzan_data:
if not isinstance(tag, tuple):
raise TypeError(
'tags_gtzan_data should be a list of tuples, '
+ 'but contains a {} element'.format(type(tag))
)
jam.annotations.append(tag_gtzan_to_jams(tag))
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tag_gtzan_to_jams returns an Annotation in the gtzan namespace. This Annotation will be JAMS-compliant if and only if the tag is a GTZAN genre: country, jazz, rock, etc.

This tag converter will not work for other datasets than GTZAN (e.g. MSD, which requires tag_open)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes - this is why I called the method "tag_gtzan_to_jams"

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see though - the value is called "tags_data" so it's confusing. I'll update.


return jam


Expand All @@ -183,7 +202,7 @@ def beats_to_jams(beats):
jannot_beat = jams.Annotation(namespace='beat')
jannot_beat.annotation_metadata = jams.AnnotationMetadata(data_source='mirdata')
if beats[0] is not None:
if type(beats[0]) != utils.BeatData:
if not isinstance(beats[0], utils.BeatData):
raise TypeError('Type should be BeatData.')
for t, p in zip(beats[0].beat_times, beats[0].beat_positions):
jannot_beat.append(time=t, duration=0.0, value=p)
Expand All @@ -209,7 +228,7 @@ def sections_to_jams(sections):
jannot_seg = jams.Annotation(namespace='segment_open')
jannot_seg.annotation_metadata = jams.AnnotationMetadata(data_source='mirdata')
if sections[0] is not None:
if type(sections[0]) != utils.SectionData:
if not isinstance(sections[0], utils.SectionData):
raise TypeError('Type should be SectionData.')
for inter, seg in zip(sections[0].intervals, sections[0].labels):
jannot_seg.append(time=inter[0], duration=inter[1] - inter[0], value=seg)
Expand All @@ -235,7 +254,7 @@ def chords_to_jams(chords):
jannot_chord = jams.Annotation(namespace='chord')
jannot_chord.annotation_metadata = jams.AnnotationMetadata(data_source='mirdata')
if chords[0] is not None:
if type(chords[0]) != utils.ChordData:
if not isinstance(chords[0], utils.ChordData):
raise TypeError('Type should be ChordData.')
for beg, end, ch in zip(
chords[0].intervals[:, 0], chords[0].intervals[:, 1], chords[0].labels
Expand Down Expand Up @@ -263,7 +282,7 @@ def notes_to_jams(notes):
jannot_note = jams.Annotation(namespace='note_hz')
jannot_note.annotation_metadata = jams.AnnotationMetadata(data_source='mirdata')
if notes[0] is not None:
if type(notes[0]) != utils.NoteData:
if not isinstance(notes[0], utils.NoteData):
raise TypeError('Type should be NoteData.')
for beg, end, n in zip(
notes[0].intervals[:, 0], notes[0].intervals[:, 1], notes[0].notes
Expand Down Expand Up @@ -291,7 +310,7 @@ def keys_to_jams(keys):
jannot_key = jams.Annotation(namespace='key_mode')
jannot_key.annotation_metadata = jams.AnnotationMetadata(data_source='mirdata')
if keys[0] is not None:
if type(keys[0]) != utils.KeyData:
if not isinstance(keys[0], utils.KeyData):
raise TypeError('Type should be KeyData.')
for beg, end, key in zip(keys[0].start_times, keys[0].end_times, keys[0].keys):
jannot_key.append(time=beg, duration=end - beg, value=key)
Expand Down Expand Up @@ -323,7 +342,7 @@ def multi_sections_to_jams(multi_sections):
)
for sections in multi_sections[0]:
if sections[0] is not None:
if type(sections[0]) != utils.SectionData:
if not isinstance(sections[0], utils.SectionData):
raise TypeError('Type should be SectionData.')
for inter, seg in zip(sections[0].intervals, sections[0].labels):
jannot_multi.append(
Expand Down Expand Up @@ -351,10 +370,15 @@ def f0s_to_jams(f0s):
jannot_f0 = jams.Annotation(namespace='pitch_contour')
jannot_f0.annotation_metadata = jams.AnnotationMetadata(data_source='mirdata')
if f0s[0] is not None:
if type(f0s[0]) != utils.F0Data:
if not isinstance(f0s[0], utils.F0Data):
raise TypeError('Type should be F0Data.')
for t, f, c in zip(f0s[0].times, f0s[0].frequencies, f0s[0].confidence):
jannot_f0.append(time=t, duration=0.0, value=f, confidence=c)
jannot_f0.append(
time=t,
duration=0.0,
value={'index': 0, 'frequency': f, 'voiced': f > 0},
confidence=c,
)
if f0s[1] is not None:
jannot_f0.sandbox = jams.Sandbox(name=f0s[1])
return jannot_f0
Expand All @@ -377,7 +401,7 @@ def lyrics_to_jams(lyrics):
jannot_lyric = jams.Annotation(namespace='lyrics')
jannot_lyric.annotation_metadata = jams.AnnotationMetadata(data_source='mirdata')
if lyrics[0] is not None:
if type(lyrics[0]) != utils.LyricData:
if not isinstance(lyrics[0], utils.LyricData):
raise TypeError('Type should be LyricData.')
for beg, end, lyric in zip(
lyrics[0].start_times, lyrics[0].end_times, lyrics[0].lyrics
Expand All @@ -386,3 +410,30 @@ def lyrics_to_jams(lyrics):
if lyrics[1] is not None:
jannot_lyric.sandbox = jams.Sandbox(name=lyrics[1])
return jannot_lyric


def tag_gtzan_to_jams(tags):
'''
Convert tag-gtzan annotations into jams format.

Parameters
----------
tags: tuple
A tuple in the format (str, str), where the first str is the tag
and the second describes the annotation.

Returns
-------
jannot_tag_gtzan: JAM tag_gtzan annotation object.
'''
jannot_tag_gtzan = jams.Annotation(namespace='tag_gtzan')
jannot_tag_gtzan.annotation_metadata = jams.AnnotationMetadata(
data_source='mirdata'
)
if tags[0] is not None:
if not isinstance(tags[0], str):
raise TypeError('Type should be str.')
jannot_tag_gtzan.append(time=0.0, duration=0.0, value=tags[0])
if tags[1] is not None:
jannot_tag_gtzan.sandbox = jams.Sandbox(name=tags[1])
return jannot_tag_gtzan
4 changes: 3 additions & 1 deletion mirdata/medley_solos_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,9 @@ def audio(self):

def to_jams(self):
"""Jams: the track's data in jams format"""
return jams_utils.jams_converter(metadata=self._track_metadata)
metadata = {'duration': librosa.get_duration(self.audio[0], self.audio[1])}
metadata.update(self._track_metadata)
return jams_utils.jams_converter(metadata=metadata)


def load_audio(audio_path):
Expand Down
4 changes: 3 additions & 1 deletion mirdata/medleydb_melody.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,11 @@ def audio(self):
def to_jams(self):
"""Jams: the track's data in jams format"""
# jams does not support multipitch, so we skip melody3
metadata = {k: v for k, v in self._track_metadata.items() if v is not None}
metadata['duration'] = librosa.get_duration(self.audio[0], self.audio[1])
return jams_utils.jams_converter(
f0_data=[(self.melody1, 'melody1'), (self.melody2, 'melody2')],
metadata=self._track_metadata,
metadata=metadata,
)


Expand Down
4 changes: 3 additions & 1 deletion mirdata/medleydb_pitch.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,10 @@ def audio(self):

def to_jams(self):
"""Jams: the track's data in jams format"""
metadata = {k: v for k, v in self._track_metadata.items() if v is not None}
metadata['duration'] = librosa.get_duration(self.audio[0], self.audio[1])
return jams_utils.jams_converter(
f0_data=[(self.pitch, None)], metadata=self._track_metadata
f0_data=[(self.pitch, 'annotated pitch')], metadata=metadata
)


Expand Down
6 changes: 5 additions & 1 deletion mirdata/orchset.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,12 @@ def audio_stereo(self):

def to_jams(self):
"""Jams: the track's data in jams format"""
metadata = {k: v for k, v in self._track_metadata.items() if v is not None}
metadata['duration'] = librosa.get_duration(
self.audio_mono[0], self.audio_mono[1]
)
return jams_utils.jams_converter(
f0_data=[(self.melody, None)], metadata=self._track_metadata
f0_data=[(self.melody, 'annotated melody')], metadata=metadata
)


Expand Down
4 changes: 3 additions & 1 deletion mirdata/rwc_classical.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,12 @@ def audio(self):

def to_jams(self):
"""Jams: the track's data in jams format"""
metadata = {k: v for k, v in self._track_metadata.items() if v is not None}
metadata['duration'] = librosa.get_duration(self.audio[0], self.audio[1])
return jams_utils.jams_converter(
beat_data=[(self.beats, None)],
section_data=[(self.sections, None)],
metadata=self._track_metadata,
metadata=metadata,
)


Expand Down
Loading