Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

26 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Last commit dateγ€€View licenseγ€€View gradeγ€€

Description πŸ“

Library for managing marker/region-related objects in audio files. 🚩

Features ✨

  • Read, write, delete and rename CUE markers, regions and SMPL loops
  • Modify ACID fields such as tempo, root note, time signature and more
  • Cross-file marker copying
  • Region extraction to separate files
  • Marker to region conversion
  • Immutable API β€” every mutation returns a new main file class object

Marker types 🏷️

Constant FourCC
🟦 MarkerType.BASIC rgn
🟨 MarkerType.BEAT beat
🟩 MarkerType.DOWNBEAT dwnb
πŸŸͺ MarkerType.CD_TRACK trak
🟦 MarkerType.CD_INDEX indx
🟩 MarkerType.SINGLE_CYCLE wtsc

Roadmap 🏁

  • Bug fixing πŸ›
  • WavPack support βš™οΈ

Installation πŸ–₯️

  1. git (recommended)
git clone https://github.com/kubinka0505/wavemarks
cd wavemarks
python -m pip install -e .
  1. pip
pip install git+https://github.com/kubinka0505/wavemarks -U

Usage πŸ“

Add or remove marker/region ✏️

>>> from wavemarks import MarkerFile, MarkerType, Entry
>>> 
>>> # Open file
>>> file_obj = MarkerFile("audio.wav")
>>> 
>>> # Marker named "important" at sample 2801 with type SINGLE_CYCLE
>>> # By adding "end" argument a region is created
>>> my_marker = Entry(name = "important", start = 2801, type = MarkerType.SINGLE_CYCLE)
>>> 
>>> # Add it
>>> file_obj += my_marker
>>> 
>>> # Check if exists
>>> my_marker in file_obj
True
>>> 
>>> # Then remove it (must be equivalent!)
>>> file_obj -= my_marker
>>> 
>>> my_marker in file_obj
False
>>> 
>>> # Save to output
>>> file_obj.save("audio_modified.wav")

Rename marker πŸ”„

>>> from wavemarks import MarkerFile, Entry, Note
>>> 
>>> # Open file
>>> file_obj = MarkerFile("Loop.wav")
>>> 
>>> # Initialize variables
>>> name_old = "old"
>>> name_new = "new"
>>> 
>>> # Find target region
>>> target = [m for m in file_obj.regions if m.name == name_old][0]
>>> 
>>> # Rename
>>> file_obj = file_obj.rename_cue(target, name_new)
>>> 
>>> # Save to input file (no `path` argument), and optionally
>>> # detect tempo and write initial key to A-flat 4th octave (read help(save))
>>> file_obj.save(tempo = -2, note_root = Note.Ab4)

CUE inspection 🧐

>>> from wavemarks import MarkerFile, MarkerType
>>> 
>>> # Open file
>>> file_obj = MarkerFile("input.wav")
>>> 
>>> # File has 1 marker named "intro" at sample 0 with type RegionType.BASIC
>>> file_obj.markers
[Entry(name='intro', type='rgn ', start=0, end=None)]
>>> 
>>> # File has no regions (as end = None dictates marker)
>>> file_obj.regions
[]
>>> # ...but the file is NOT empty, since it has that 1 marker
>>> file_obj.is_empty()
False

ACID inspection πŸ”¬

>>> from wavemarks import MarkerFile, MarkerType
>>> 
>>> # Open file and pray that someone qualified managed its metadata
>>> file_obj = MarkerFile("LPS_LOOP_01.wav")
>>> 
>>> print(f"File tempo is: {file_obj.tempo:.3f} BPM")
File tempo is: 128.000 BPM
>>> 
>>> print(f"File time signature is:", "/".join(file_obj.time_signature))
File time signature is: 4/4

Search for objects πŸ”

>>> from wavemarks import MarkerFile, Entry
>>> 
>>> # Open file
>>> file_obj = MarkerFile("../snd.wav")
>>> 
>>> # Search for all markers named "abracadabra" with CD_INDEX type
>>> mks = []
>>> 
>>> for m in file_obj.markers:
>>>     if m.name == "abracadabra" and m.type == RegionType.CD_INDEX:
>>>         mks.append(m)
>>> 
>>> # Search for all regions between sample 900 and 1601
>>> rgs = []
>>> 
>>> for r in file_obj.regions:
>>>     if r.start >= 900 and r.end <= 1601:
>>>         rgs.append(r)

Advanced utilities πŸ› οΈ

>>> from wavemarks import MarkerFile, MarkerType
>>> 
>>> # Open file
>>> file_obj = MarkerFile("input.wav")
>>> 
>>> # Get all entries
>>> list(file_obj)
[Entry(name='', type='rgn ', start=22935, end=None),
 Entry(name='drop', type='rgn ', start=89408, end=None)]
>>> 
>>> # Convert consecutive point markers into regions
>>> file_obj = file_obj.markers_to_regions()
>>> file_obj.regions
[Entry(name='drop', type='rgn ', start=22935, end=89408)]
>>> 
>>> # Extract audio regions to separate wave files
>>> #
>>> # Basename is:
>>> # For named regions: `{region.name}`
>>> # Otherwise: `region_{marker.start}_{marker.end}`
>>> file_obj = MarkerFile("input.wav")
>>> 
>>> for region in file_obj.regions:
>>>     file_obj.export(region, "existing_directory/") 
>>> 
>>> # Copy all entries from one file to another
>>> MarkerFile("input.wav").copy("output.wav")

Copying and context manager πŸ“‹

>>> from wavemarks import MarkerFile, MarkerType, Entry
>>> 
>>> # Initialize variables
>>> file_path = "input.wav"
>>> 
>>> # Context manager
>>> with MarkerFile(file_path) as f:
...     # marker
...     f += Entry(start = 101, name = "example point", type = MarkerType.BASIC)
...     # region
...     f += Entry(start = 2000, end = 4000, "example region", type = MarkerType.CD_TRACK)
...     f.save("output.wav")
>>>
>>> # Reapply markers after external processing
>>> snapshot = MarkerFile(file_path).markers
>>>
>>> # normalize_input_audio(file_path)
>>> 
>>> # Reload and save
>>> MarkerFile(file_path).apply(snapshot).save()

Disclaimer ⚠️

This software:

  • Operates on WAV (RIFF/WAVE) files only.
  • Does not support BWF (bext), iXML, or other metadata chunks β€” they are preserved byte-for-byte but not parsed.
  • Round-trips all non-marker chunks without modification; however, malformed or non-standard RIFF files may not parse correctly.

About

Library for managing marker/region-related objects in audio files. 🚩

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Contributors

Languages