Skip to content
/ remus Public

A JSON-based format for representing musical data.

License

Notifications You must be signed in to change notification settings

doremir/remus

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

71 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Remus

Remus is a JSON-based format for representing musical data. To work with remus data, the remus JavaScript library is provided.

Basic structure

Remus is a hierarchical structure of musical objects where the root object is the Song. Conceptually, a song consists of

  • a timeline
  • objects relating to the timeline
  • objects that do not relate to the timeline, and therefore have no timing (a resource pool)

Examples of the latter can be links, audio files not [yet] used in the actual song, imported lyrics, MIDI-files, etc.

Technically, objects on the timeline are stored under one of two JS keys: events and metas. This is similar (but not identical) to how MIDI files use “events” and “meta events”. The event list contains the objects that actually “sounds”, notes, chords and audio file references, while the meta list contains information such as tempo, time and key signature.

The event list may also contain other containers to created nested objects of arbitrary depth. (Note however that there are restrictions for where certain objects can be stored)

Items

All objects in remus document are referred to as items and inherit from the Item class. All items have a type, a string denoting the class name. When loading remus using the remus library, items are instantiated to the JS class named by their specified type.

Here follows an overview over the various item classes. Detailed documentation is available in the source files of the respective item class.

Abstract classes

  • Item Ancestor of all items
  • Event Ancestor of all items that have a position on the timeline
  • Meta Meta events
  • EventContainer Events containing other events

Basic types

  • Duration Durations in various units
  • Pitch Pitch representation
  • Interval Pitch intervals
  • Harmony Chords (e.g. dominant or minor seventh)
  • Mode Modes and scales (e.g. major, dorian, minor penta)

Events

  • Note A single note (essentially a Pitch placed on the timeline)
  • NoteChord A “chord” of simultanous notes
  • Chord A chord (essentially a Harmony placed on the timeline)
  • Rest

Meta-events

  • Time A time signature such as 4/4 or 6/8
  • Key Key signature
  • Tempo Tempo indication or tempo change
  • Clef Clef (for sheet music generation)

Coercing and type inference

Most of the Item classes has a coerce method that simplifies creation of instances.

For example, this is an easy way to create a Pitch object:

Pitch.coerce("Ab4")
// equivalent to
new Pitch({coord: [40, 68]})

(For information of supported formats, see the documentation of the respective Item class)

In places where a specific Item class is expected, its coerce method is called automatically. Therefore it is often not needed to specify a complete JS object.

For example, the Key class has two members called root and mode which are of type Pitch and Mode respectively. The following code works as expected:

new Key({pitch: "Bb", mode: "minor"})

This is because under the hood, the Key constructor calls Pitch.coerce on the string "Bb" and Mode.coerce on the string "minor", resulting in a Pitch and a Mode object.

As an another example, this is a valid representation of a NoteChord:

{
  "type": "NoteChord",
  "duration": "3/16 wn",
  "events": [
    {"pitch": "D5"},
    {"pitch": "Bb4"},
    {"pitch": "F4"}
  ]
}

Notice that

  • the duration is coerced to a Duration object
  • the default event type of NoteChord is Note, so the three elements in the event list doesn't need type: "Note"
  • the duration of the NoteChord is inherited by the notes
  • the pitches of the individual notes are coerced into Pitch objects

This is usually preferable to specifying every object in full:

{
  "type": "NoteChord",
  "duration": {
    "type": "Duration",
    "value": "3/16",
    "unit": "wn"
  },
  "events": [
    {
      "type": "Note",
      "pitch": {
        "type": "Pitch",
        "coord": [43, 74]
      }
    },
    {
      "type": "Note",
      "pitch": {
        "type": "Pitch",
        "coord": [41, 70]
      }
    },
    {
      "type": "Note",
      "pitch": {
        "type": "Pitch",
        "coord": [38, 65]
      }
    }
  ]
}

Positioning

Events are generally positioned in relation to their parent container. For example, notes specify their position in relation to their voice.

Each event has an anchor point, which is normally the “start” of the event. It may however be changed so that e.g. voices with pickups to sync to other events on their first downbeat, rather than their first note.

Resolving

Remus is designed with flexibility and extendability as high priorities. The flip side is that parsing remus may be a complex process since the code needs knowledge about the various concepts and representations supported by remus.

To facilitate things, the remus library can resolve a remus object. This means that it walks through its structure, expands inherited properties, converts various units etc. The result is then cached in each remus object.

let remus = loadSomeComplexRemusFile();
remus.resolve();
let note = remus.findEvent('Note');
note.cache.absWn
  => the absolute position of note in wn (whole notes)

For detailed documentation of the timeline-related cache keys, see Event.

About

A JSON-based format for representing musical data.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Contributors 4

  •  
  •  
  •  
  •