"""RWC Popular Dataset Loader
.. admonition:: Dataset Info
:class: dropdown
The Popular Music Database consists of 100 songs — 20 songs with English lyrics
performed in the style of popular music typical of songs on the American hit
charts in the 1980s, and 80 songs with Japanese lyrics performed in the style of
modern Japanese popular music typical of songs on the Japanese hit charts in
the 1990s.
For more details, please visit: https://zenodo.org/records/18656623
"""
import csv
import os
from typing import BinaryIO, Optional, TextIO, Tuple
import librosa
import numpy as np
import pretty_midi
from smart_open import open
from mirdata import annotations, core, download_utils, io
BIBTEX = """@inproceedings{goto2002rwc,
title={RWC Music Database: Popular, Classical and Jazz Music Databases.},
author={Goto, Masataka and Hashiguchi, Hiroki and Nishimura, Takuichi and Oka, Ryuichi},
booktitle={3rd International Society for Music Information Retrieval Conference},
year={2002},
series={ISMIR},
}
@inproceedings{GotoHNO03_RWCGenre_ISMIR,
author = {Masataka Goto and Hiroki Hashiguchi and Takuichi Nishimura and Ryuichi Oka},
title = {{RWC} Music Database: Music genre database and musical instrument sound database},
booktitle = {Proceedings of the International Society for Music Information Retrieval Conference ({ISMIR})},
address = {Baltimore, Maryland, USA},
year = {2003},
pages = {229--230},
}
@inproceedings{Goto06_AnnotationsAIST_ISMIR,
author = {Masataka Goto},
title = {{AIST} Annotation for the {RWC} Music Database},
booktitle = {Proceedings of the International Society for Music Information Retrieval Conference ({ISMIR})},
year = {2006},
pages = {359--360},
}
@inproceedings{mauch2011timbre,
title={Timbre and Melody Features for the Recognition of Vocal Activity and Instrumental Solos in Polyphonic Music.},
author={Mauch, Matthias and Fujihara, Hiromasa and Yoshii, Kazuyoshi and Goto, Masataka},
booktitle={ISMIR},
year={2011},
series={ISMIR},
note={Cite this if using vocal-instrumental activity annotations},
}
@Article{BalkeZAMTGM26_RWCRevisited_TISMIR,
author = {Stefan Balke and Johannes Zeitler and Vlora Arifi-Müller and Brian McFee and Tomoyasu Nakano and Masataka Goto and Meinard M{"u}ller},
title = {{RWC} Revisited: {T}owards a Community-Driven {MIR} Corpus},
journal = {Transactions of the International Society for Music Information Retrieval},
volume = {9},
issue = {1},
pages = {21--35},
year = {2026},
doi = {10.5334/tismir.326}
}
"""
INDEXES = {
"default": "2.0",
"test": "sample",
"2.0": core.Index(
filename="rwc_popular_index_2.0.json",
url="https://zenodo.org/records/20369966/files/rwc_popular_index_2.0.json?download=1",
checksum="c7f5f853768815885cea70d6c246895d",
),
"sample": core.Index(filename="rwc_popular_index_2.0_sample.json"),
}
REMOTES = {
"audio": download_utils.RemoteFileMetadata(
filename="RWC-P.zip",
url="https://zenodo.org/records/18656623/files/RWC-P.zip?download=1",
checksum="960a11a2d7fb603ad0dae8428f53d4f0",
),
"annotation": download_utils.RemoteFileMetadata(
filename="rwc-annotations-main.zip",
url="https://github.com/rwc-music/rwc-annotations/archive/2b84581b0c4c80514aadf7e9025a309c91e02cc2.zip",
checksum="40f17835554467f66de60602f72f5686",
),
}
DOWNLOAD_INFO = """
This dataset is RWC Music Database version 2.0 (released 2026).
If you need the previous version 1.0 with its loader and annotations, install an older mirdata version:
pip install "mirdata<=1.0.0"
"""
LICENSE_INFO = """
Creative Commons Attribution Non Commercial 4.0 International
"""
[docs]
class Track(core.Track):
"""rwc_popular Track class
Args:
track_id (str): track id of the track
Attributes:
audio_path (str): path of the audio file
beats_path (str): path of the beat annotation file
chords_path (str): path of the chord annotation file
f0_path (str): path of the melody (F0) annotation file
midi_path (str): path of the aligned MIDI file
piece_number (str): Piece number
cd_number (str): CD number
track_number (str): track number on the CD
title (str): title of the track
artist (str): artist name
singer_information (str): singer information (male, female, or vocal group)
singing_language (str): singing language
tempo (str): tempo of the track in BPM
variation (str): variation information
live_instrument (str): live instrument information
drum_information (str): drum information (e.g., 'Drum sequences', 'Live drums', 'Drum loops')
composer (str): composer name
composition_type (str): type of composition
main_genre (str): main genre of the track
sub_genre (str): sub genre of the track
audio_start (str): audio start time
audio_end (str): audio end time
duration (str): duration of the track in seconds
Cached Properties:
beats (BeatData): human-labeled beat annotation
chords (ChordData): human-labeled chord annotation
melody (F0Data): human-labeled melody (F0) annotation
midi (pretty_midi.PrettyMIDI): aligned MIDI annotations
"""
def __init__(self, track_id, data_home, dataset_name, index, metadata):
super().__init__(track_id, data_home, dataset_name, index, metadata)
self.audio_path = self.get_path("audio")
self.beats_path = self.get_path("beats")
self.chords_path = self.get_path("chords")
self.f0_path = self.get_path("melody")
self.midi_path = self.get_path("midi")
@property
def piece_number(self):
return self._track_metadata.get("piece_number")
@property
def cd_number(self):
return self._track_metadata.get("cd_number")
@property
def track_number(self):
return self._track_metadata.get("track_number")
@property
def title(self):
return self._track_metadata.get("title")
@property
def artist(self):
return self._track_metadata.get("artist")
@property
def singer_information(self):
return self._track_metadata.get("singer_information")
@property
def singing_language(self):
return self._track_metadata.get("singing_language")
@property
def tempo(self):
return self._track_metadata.get("tempo")
@property
def variation(self):
return self._track_metadata.get("variation")
@property
def live_instrument(self):
return self._track_metadata.get("live_instrument")
@property
def drum_information(self):
return self._track_metadata.get("drum_information")
@property
def composer(self):
return self._track_metadata.get("composer")
@property
def composition_type(self):
return self._track_metadata.get("composition_type")
@property
def main_genre(self):
return self._track_metadata.get("main_genre")
@property
def sub_genre(self):
return self._track_metadata.get("sub_genre")
@property
def audio_start(self):
return self._track_metadata.get("audio_start")
@property
def audio_end(self):
return self._track_metadata.get("audio_end")
@property
def duration(self):
return self._track_metadata.get("duration")
@core.cached_property
def beats(self) -> Optional[annotations.BeatData]:
return load_beats(self.beats_path)
@core.cached_property
def chords(self) -> Optional[annotations.ChordData]:
return load_chords(self.chords_path)
@core.cached_property
def melody(self) -> Optional[annotations.F0Data]:
return load_melody(self.f0_path)
@core.cached_property
def midi(self) -> Optional[pretty_midi.PrettyMIDI]:
return io.load_midi(self.midi_path)
@property
def audio(self) -> Optional[Tuple[np.ndarray, float]]:
"""The track's audio
Returns:
* np.ndarray - audio signal
* float - sample rate
"""
return load_audio(self.audio_path)
[docs]
@io.coerce_to_bytes_io
def load_audio(fhandle: BinaryIO) -> Tuple[np.ndarray, float]:
"""Load a RWC audio file.
Args:
fhandle (str or file-like): File-like object or path to audio file
Returns:
* np.ndarray - the mono audio signal
* float - The sample rate of the audio file
"""
return librosa.load(fhandle, sr=None, mono=True)
[docs]
@io.coerce_to_string_io
def load_chords(fhandle: TextIO) -> annotations.ChordData:
"""Load rwc chord data from a file
Args:
fhandle (str or file-like): File-like object or path to chord annotation file
Returns:
ChordData: chord data
"""
begs = [] # timestamps of chord beginnings
ends = [] # timestamps of chord endings
chords = [] # chord labels
reader = csv.reader(fhandle, delimiter=";")
next(reader, None) # skip header
for row in reader:
begs.append(float(row[0]))
ends.append(float(row[1]))
chords.append(row[2])
return annotations.ChordData(np.array([begs, ends]).T, "s", chords, "open")
[docs]
@io.coerce_to_string_io
def load_beats(fhandle: TextIO) -> annotations.BeatData:
"""Load rwc beat data from a file
Args:
fhandle (str or file-like): File-like object or path to beats annotation file
Returns:
BeatData: beat data
"""
beat_times = [] # timestamps of beat interval beginnings
beat_positions = [] # beat position inside the bar
reader = csv.reader(fhandle, delimiter=";")
next(reader, None)
for row in reader:
beat_times.append(float(row[0]))
beat_positions.append(int(row[1]))
return annotations.BeatData(
np.array(beat_times), "s", np.array(beat_positions).astype(int), "bar_index"
)
[docs]
@io.coerce_to_string_io
def load_melody(fhandle: TextIO) -> Optional[annotations.F0Data]:
"""Load rwc_popular f0 annotations
Args:
fhandle (str or file-like): path or file-like object pointing
to melody annotation file
Returns:
F0Data: predominant melody
"""
times = []
freqs = []
voicing = []
reader = csv.reader(fhandle, delimiter=";")
next(reader, None) # skip header
for row in reader:
times.append(float(row[0]))
freq = float(row[1])
freqs.append(freq)
voicing.append(float(freq > 0))
return annotations.F0Data(
np.array(times), "s", np.array(freqs), "hz", np.array(voicing), "binary"
)
[docs]
@core.docstring_inherit(core.Dataset)
class Dataset(core.Dataset):
"""
The rwc_popular dataset
"""
def __init__(self, data_home=None, version="default"):
super().__init__(
data_home,
version,
name="rwc_popular",
track_class=Track,
bibtex=BIBTEX,
indexes=INDEXES,
remotes=REMOTES,
download_info=DOWNLOAD_INFO,
license_info=LICENSE_INFO,
)
@core.cached_property
def _metadata(self):
metadata_path = os.path.join(
self.data_home,
"rwc-annotations-2b84581b0c4c80514aadf7e9025a309c91e02cc2",
"metadata.csv",
)
try:
with open(metadata_path, "r", encoding="utf-8") as fhandle:
reader = csv.DictReader(fhandle, delimiter=";")
raw_data = []
for row in reader:
raw_data.append(row)
except FileNotFoundError:
raise FileNotFoundError("Metadata not found. Did you run .download()?")
metadata_index = {}
for line in raw_data:
if line["CollID"] == "P":
track_id = line["RWCID"]
metadata_index[track_id] = {
"piece_number": line["PieceNo"],
"cd_number": line["CDNo"],
"track_number": line["TrackNo"],
"title": line["Title"],
"artist": line["Artist"],
"singer_information": line["SingerInformation"],
"singing_language": line["SingingLanguage"],
"tempo": line["Tempo"],
"variation": line["Variation"],
"live_instrument": line["LiveInstruments"],
"drum_information": line["DrumInformation"],
"composer": line["Composer"],
"composition_type": line["CompositionType"],
"main_genre": line["GenreMain"],
"sub_genre": line["GenreSub"],
"audio_start": line["audio_start"],
"audio_end": line["audio_end"],
"duration": line["duration"],
}
else:
pass
return metadata_index