Skip to content

The Nendo Track¤

This page provides a thorough introduction into one of the basic concepts of Nendo Core: The class NendoTrack. It is the most relevant user-facing object in Nendo Core. It represents an audio artifact (imported or generated) that can be interacted with in various ways.

Developer info

If you are a developer, you might as well just directly dive into the API Referece. This page looks at the NendoTrack from a user's perspective.

Fields

Field Type Description
id uuid The unique ID of the track.
uder_id uuid The ID of the user who "owns" the track.
signal np.ndarray The track's waveform in the form of a numpy.ndarray.
sr int The sample rate of the track's signal.
track_type str A free-form type descriptor that can be used by developers to further differentiate between different kinds of track objects. Unless explicitly set by the developer, "track" is used as a placeholder value.
visibility Visibility A special field that can be used by developers to assign different visibilities to tracks that can be used to isolate tracks between users. Possible values are public, private and deleted.
resource NendoResource The NendoResource representing the file on disk to which the NendoTrack.signal corresponds.
plugin_data list[NendoPluginData] List of NendoPluginData entries as produced by Analysis Plugins.
related_tracks list[NendoTrackTrackRelationship] List containing all relationships to other nendo tracks.
related_collections list[NendoTrackCollectionRelationship] List containing all relationships to nendo collections.
meta dict A dictionary containing a track's metadata.

Manually changing fields

It is not recommended to overwrite the fields mentioned in the above table manually! Instead, you should be using the functions described in the following paragraphs. If you end up manipulating the fields directly, be aware that things might break and that you need to manually persist any changes by saving the track.

Creating a track¤

Tracks are created by nendo everytime a file is added to the library or when a plugin is called that produces a new track as its output.

Example

Creating a track from a file on disk:

track = nendo.library.add_track("/path/to/file.mp3")

Example

Creating a track from a file on disk and creating a relationship to an existing track:

# track_1 = ...
track_2 = nendo.library.add_related_track("/path/to/file.mp3", related_track_id=track_1.id)

Example

Creating a track from a signal, in the form of a numpy.ndarray:

# signal, sr = ...
track = nendo.library.add_track_from_signal(signal=signal, sr=sr)

Example

Creating a track by running a plugin:

track = nendo.plugins.gen_musicgen("Epic guitar solo")

Saving, exporting and deleting a track¤

When you change a track by writing to any of its fields directly or delete the in-memory object by using python's del track, the changes are not persisted to the nendo library. You have to call the corresponding track functions to persist your changes:

Example

# track = ...
track.save()

A track can be exported to file in a given format using the NendoTrack.export() function. Please note that the function either accepts a full path to the target file as its file_path parameter, in which case the desired format is automatically deduced from the specified file ending. If instead a path to a directory is given as file_path, the filename will be automatically generated by nendo and the output format will be determined by the file_format parameter.

Example

track.export(file_path="/path/to/output/target_file.mp3")
# or
track.export(file_path="/path/to/output/", file_format="mp3")
# the latter will automatically generate a filename

Supported formats

When exporting a track this way, the supported formats are wav, mp3 and ogg.

Example

# track = ...
track.delete()
# Now, the track has been removed from the nendo library.
# To also delete the object from memory, call:
del track

Manipulating tracks¤

Nendo provides various functions that make it easy to manipulate tracks as typically needed in audio processing workflows.

To obtain a resampled signal from an existing track:

Example

resampled_signal = track.resample(48000) # resample to 48kHz

Warning

Resampling a track using track.resample() will never change the file in the nendo library. It only overwrites the track.signal and track.sr values in-memory and additionally returns the resampled signal in the form of a numpy ndarray.

To combine two tracks into one by overlaying them, you can use the following conveniece function:

Example

track_1 = nendo.library.add_track("/path/to/file_1.wav")
track_2 = nendo.library.add_track("/path/to/file_2.wav")
combined_track = track_1.overlay(track_2, gain_db=0.12)
# The `gain_db` is applied to the second track
# combined_track is a new track with a relationship to track_1

To slice a track to the first 10 seconds, use:

Example

track_slice = track.slice(10)
# track_slice now contains the signal of the track's first 10 seconds

To slice a track from second 5 to 10:

Example

track_slice = track.slice(start = 5, end = 10)

Warning

track.slice() returns the slice as a numpy.ndarray. The original track is not changed. To save the slice as a new signal, use Nendo.library.add_track_from_signal(signal=track_slice, sr=track.sr) or, to add the sliced track with a relationship to the original track, use track.add_related_track_from_signal(signal=track_slice, sr=track.sr, related_track_id=track.id).

Working with relationships¤

As stated above, it is possible for tracks in Nendo Core to have relationships to other tracks, as well as to collections. This feature is used to track the "history" of how a specific audio sample was obtained and to group tracks together into playlists, etc. The following examples show how to work with a track's relationships to other tracks.

To add a new track to nendo that has a relationship to an existing given track:

Example

# track_1 = ...
track_2 = track_1.add_related_track("/path/to/file.mp3")
Hey, I have seen this above!

You might have noticed that this function closely resembles the Nendo.library.add_related_track() function (minus the related_track_id parameter). As most of the functions defined on the NendoTrack, the add_related_track() is but a shortcut that calls the corresponding function that is defined as part of the nendo library.

To check whether a NendoTrack has any relationships, you can use:

Example

>>> # track = ...
>>> track.has_relationship()
True

And to check only for a specific relationship_type:

Example

>>> # track = ...
>>> track.has_relationship(relationship_type="stem")
False

To check whether a given track has a relationship to another given track:

Example

# track_1 = ...
# track_2 = track_1.add_related_track(...)
>>> track_1.has_related_track(track_2.id)
True

The NendoTrack.related_tracks field contains a list of NendoRelationship objects. To obtain a list of all NendoTrack objects that are connected to the current track by means of a NendoRelationship, you can use the NendoTrack.get_related_tracks() method.

Example

# track = ...
related_tracks = track.get_related_tracks()

Please refer to the API reference to see the full list of parameters for this function.

Working with a track's metadata¤

Tracks in nendo have metadata attached to them, in the form of the NendoTrack.meta dictionary. For example, upon importing track into the nendo library from a file, its parsed ID3 information is stored to NendoTrack.meta.

Note

The NendoTrack.meta field is a normal dictionary, whose fields can be accessed simply via track.meta['album'] and added via track.meta.update({"album": "Autobahn"}). However, manipulating the meta dictionary directly like that will not persist the changes to the nendo library, which is why the functions below exist and should be used: They will take care of persisting any changes to the meta dictionary into the nendo library.

Arbitrary metadata can be added to NendoTrack.meta by passing a dictionary with the desired key: value pairs:

Example

# track = ...
track.set_meta({"composer": "Sarah"})

The NendoTrack.meta field is a normal dictionary, whose fields can be accessed simply via track.meta['album']. However, there is also a convenience function that will return None if the key can not be found (instead of raising a KeyError):

Example

>>> # track = ...
>>> existing_meta_value = track.get_meta("album")
>>> print(existing_meta_value)
'Wish you were here'
>>> missing_meta_value = track.get_meta("something")
>>> print(missing_meta_value)
>>>

To check whether a given track has metadata belonging to a specific key:

Example

>>> # track = ...
>>> track.has_meta("album")
True
>>> track.has_meta("something")
False

To remove a specific field from a track's metadata:

Example

# track = ...
track.remove_meta("album")

Working with plugin data¤

Plugin data version

Plugin data in Nendo Core is versioned, in accordance with the version of the plugin that was used to produce it. That means, that if a specific plugin is used to process a given track twice, it will only overwrite the previous data if the version of the plugin has not changed since the last run. This is to ensure proper traceability of results and to make the nendo library compatible with plugin upgrades.

The NendoTrack object provides various functions for processing it with a NendoPlugin and for accessing the resulting NendoPluginData:

To run a plugin on the given NendoTrack, you can use the NendoTrack.process() function:

Example

track.process("classify_core") # this runs the "classify_core" plugin on the track

Upon processing a track, a nendo AnalysisPlugin produces NendoPluginData and "attaches" it to the track's plugin_data field. You can access that field directly or use the NendoTrack.get_plugin_data() convenience function to filter for the data of a specific plugin. The function accepts two arguments that can be either specified in isolation or together to refine the filtering of plugin_data. If you call the functions without any aruguments, all plugin data will be returned, just as if you'd directly accessed the field NendoTrack.plugin_data. The function will return a list of NendoPluginData in all cases, except when the list has only a single entry. In this case, the value of the plugin data will be returned immediately.

Example

If you specify the plugin_name, the plugin_data will be filtered and only entries whose plugin_name field matches the provided name will be returned.

>>> plugin_data = track.get_plugin_data(
...  plugin_name="nendo_plugin_classify_core",
... )
>>> for entry in plugin_data:
...    print(entry)
----------------
plugin name: nendo_plugin_classify_core
plugin version: 0.1.0
key: loudness
value: 105.32542419433594
----------------
plugin name: nendo_plugin_classify_core
plugin version: 0.1.0
key: duration
value: 121.1
# ...

If you are sure that for a specific key, only a single plugin_data entry exists, you can also use the get_plugin_value() method that will directly return the value for a given key. If the track has multiple plugin_data entries for the given key, the first one is returned (and actually, in most cases, it would make sense to use the get_plugin_data() method instead to make sure you really retrieve the value what you want).

Example

>>> plugin_data = track.get_plugin_value(
...   key = "duration",
... )
>>> print(plugin_data)
121.1

Working with collections¤

Aside from having relationships to other tracks, a NendoTrack can also have relationships to one or more collections. You can use the following functions to add or remove such assignments directly from the NendoTrack object:

To add a given track to an existing collection, you can use the track.add_to_collection() function. Use the position argument specifies the position of the track inside the collection. Set it to 0 to prepend it to the beginning, omit the parameter entirely to append it to the end.

Example

# track = ...
# collection = ...
track.add_to_collection(collection_id=collection.id, position=5)

To remove a given track from an existing collection:

Example

# track = ...
# collection = ...
track.remove_from_collection(collection.id)

Playing tracks¤

There are a few simple functions to allow for preview playpack of tracks from inside the python console.

Warning

The following functions are intended for very simple previewing. No skipping, rewinding, etc. are support. Playback can only be stopped by using the keyboard shortcut Ctrl + C.

Example

track.play() # plays the track once

Example

track.loop() # plays the track on repeat ("looped")

Success

That's all you need to know about the NendoTrack. Next up, learn more about collections in nendo.