-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Measures Downbeats Bars And Phrases
Author: Harshit Maurya (hacksdump)
Email: hmaurya999@gmail.com
Mentors: Be (Be-ing), Jan Holthuis (Holzhaus), Uwe Klotz (uklotzde)
Related Project: Downbeats-And-Phrase-Detection
During the community bonding period, I added cue menu to the scrolling waveform which was only available to the overview waveform. This served as an important precursor to my project since it requires interactions on the scrolling waveform, which were non-existent earlier.
#2783 summarises that work.
With the commencement of the coding period, we decided I should begin with some UI. So I added downbeat triangles to the scrolling waveform renderer. Initially, support for 4/4 time signature was added and every fourth beat was accented with downbeat triangles.
Thanks to the presence of downbeats, we could now zoom out even further out of the scrolling waveform by displaying only the downbeats at high zoom out levels, thus reducing clutter on display and only displaying meaningful information.
This PR also introduces the new Beat
class which encapsulates all properties a beat may store, whether it is a beat or downbeat and its index.
#2844 summarises the initial work.
Due to the added metadata, it was necessary to start working on the data format. So I continued past work which aimed at making the Beats
class non-polymorphic by removing BeatMap
and BeatGrid
. Although a polymorphic approach for containing beats of different nature sounds appropriate, BeatMap
was mostly causing trouble with DJing operations. The past work only aimed at preserving BeatMap
essentially moving its implementation to Beats
class, so this could not clearly support our use case.
So I started working in another PR #2861 starting off by fixing all broken tests, without which the core functionality of Mixxx was rendered dysfunctional. This was an important PR since we had to fixate on a Beats protocol buffer data structure with minimal base state.
An important refactor this PR introduced was the creation of a new Frame
class. Earlier, Mixxx used sample positions to reference a point in the track, but the sample position is channel dependent. So I refactored a lot of existing code to use Frame
which means the elimination of perplexing frame-sample conversions. Creating this class was a dive into building correct semantic architecture with C++. I discovered the granularity of control and what operations are considered appropriate when creating a class.
At this point, more of my work started in the direction of improving the existing code architecture and it was a great learning experience.
Using the new Beats protobuf format meant that old beat grid data could be broken when someone upgrades Mixxx to a newer version. Fortunately, BeatFactory
provided an easy interface to write the migration path.
We took a step ahead in the direction of creating a sample independent data structure by basing the markers (BPM, Time Signature) on beat or bar indices. This facilitates the deterministic regeneration of beats from the minimal beats data.
The new beats protobuf structure was still controversial so we decided that I should start implementing UI and editing operations on top of the new beats. So we decided to merge #2844 and #2861 closing them in favour of #2961. This was necessary to gain confidence in the new beats format.
I created a new widget to edit the time signature at any downbeat in the track. With this widget, we can change the time signature for any bar in the track. A similar menu was created to edit BPM at any beat in the track.
Play marker is a rendering component of the scrolling waveform and unlike other components which are modular, it was included with code where it did not belong. So I factored it out to its own renderer.
A major architectural change was the redesign of the Beats
class inheriting from QObject
. The earlier design was problematic since it allowed a QObject
to be copied and any client could create Beats
object by injecting TrackPointer
which led to cyclic dependency and is overall bad design. So the first step was to separate the hull inheriting from QObject
which takes care of locking in the multithreaded GUI environment and signal-slot connections. By allowing only the Track
to create Beats
object, the cyclic dependency could be removed and the Track
now holds direct control over Beats
.
Nearing the end of GSoC, I fixed a major bug which was caused due to ignoring the BeatGrid
nature of the Beats
and limiting ourselves to a BeatMap
. Beats needed to be created dynamically outside the boundaries of the track and cached inside the track. This fixed DJing operations like beat jumping before the track.
I favour a test-driven approach and tried my best to create new tests for the code I added and even improved old tests to increase the range of coverage.
#2961 is not merged as of now as we do not have a strong consensus on the Beats protobuf format. This decision cannot be arbitrary since it may break old user data. This PR also needs more seamless UX for beat grid editing, though it can be handled in another PR, this will be helpful to gain confidence in the new data structure.
- Fix existing tests and add more tests to check old and new behaviour.
- Create a more seamless editing UX.
- Discuss more with the community and fixate on the ideal Beats protobuf data structure.
- Find and fix any remaining architectural flaws.
- Create section overview widget under overview waveform and section renderer for scrolling waveform.
- Add phrase display to section display.
- Improve DJing operations aided by the new metadata.
It's been a great experience working with Mixxx and I am glad to be a part of an active community that combines two of my interests. I greatly enjoyed working on my pre-GSoC PR creating WTrackMenu
which added a menu to decks and samplers in addition to the track table. I personally use it quite often now.
The time and effort put in by my mentors into my project are really appreciable and I learnt a lot during the course of the project, be it design choices, C++ semantics or architecture planning. The architectural and data structure challenges throughout the course of the project kept me technically inspired.
I would like to thank the entire community as a whole in addition to my primary mentors for taking a huge interest in my project and adding valuable inputs to discussions and PR reviews.
This is the final proposal that got accepted in GSoC 2020.
The main idea of the project is to enhance and accent the beatgrid with more information about the track than just the beat markers. The project will enable more interactions with the scrolling waveforms to edit the tempo, downbeats, cues, time signature, phrases and sections. This metadata will be backed by the analysis features with the help of the sibling project and this metadata, of course, can be edited by the user at will in case the analysis data is incoherent with the track.
These beatgrid editing features will be different from any other DJing software since they are aimed at providing absolute granular control over the beatgrid at any point in the track.
This project will display and enable the user to edit meter(time signature), downbeats, phrases and sections in the beatgrid.
A new SectionOverview widget will be added under the existing WaveformOverview widget to display track section divisions.
Downbeat Markers
The downbeat markers will be more opaque than the other bar beats.
Bar Counter
Adjustment markers
Sections bands
The sections will be displayed on the waveform as tabbed strips.
Phrase markers
These markers define the start of a new phrase. The phrase markers
will be coloured differently and more accented than the downbeat
markers.
Cleaner option
Note: The start of a section implicitly indicates the start of a phrase.
Clicking on a section will seek to the section start position.
In the edit mode, the section overview and section bands on scrolling waveform can be edited to modify, add and remove sections. Since the section overview is relatively small, larger quantization units will be preferred over smaller units. Snap/Quantize behaviour will follow the order of preference: Phrase > Downbeat > Beat. (Phrase quantization will be used only if a phrase marker is close enough within a range of few bars). For scrolling waveform, it will be beat.
The base state for the sections when no sections have been assigned either manually or via the analyzer is a single section named INTRO which spans throughout the audible range of the track.
Adding a section Click the clip/scissors icon and click within an existing section to cut the section in two parts with the same properties (name, color) as the earlier section. The scissors tool can be used on the section overview and section bands on the scrolling waveform.
Removing a section The section context menu has the option to delete a section. A section can also be deleted by selecting it and pressing the delete key afterwards. The preceding section occupies the empties space. If it is the first section, the second section occupies the cleared space. Sections can also be removed by dragging the section divider from one end to the other overlapping the entire section with a neighbouring section as a result.
Modifying a section The length can be adjusted by dragging the section dividers on either the section overview widget or the section bands on the scrolling waveforms. Their names and color can be changed via the section context menus. This functionality is similar to the cue context menu.
The scrolling waveform will be powered by context-menus for every beat on the waveform. This gives the perfect granular control over the beatgrid for the tracks and allows editing for tracks with variable time signatures and tempos.
Beat context menu
Downbeat context menu
Time signature menu
Phrase context menu Inherit options from beat and downbeat menu (if applicable) Added option:
- Remove phrase marker
Adjustment markers Markers define ranges of user defined changes from the previously set marker (or the default in case no marker is set before) to the upcoming marker (or the end of the track). This will enable us to make multiple adjustments in the track. The marker encodes BPM and time signature information. (Time signature can only be changed at a downbeat)
Marker BPM context menu Adjust BPM ahead by tapping the buttons or setting a value in the input box.
Note: Quarter note = beat and no adjustments will be allowed to this convention.
A combo box next to their controls will be added to select the jump/loop unit. Selection options (in ascending order):
Downbeats will be matched. First and last sections (need not necessarily be called intro/outro since tracks without intro exist) will be used to determine transition/mixing triggers instead of older intro and outro cues.
Since the intro and outro information will now be included with the sections, explicit intro and outro cues will be removed along with the editor buttons for the same.
Earlier: To mark the intro/outro, the start marker was set at the starting position and end marker was placed at the end position of the intro/outro. The enclosed segment defined the intro/outro.
Change: Use the section editor or the drag control on the scrolling waveform to mark the first and the last sections.
As much as this project depends on the analyzer to generate the metadata for markers, the analyzer can also benefit from the metadata marked by the user. For instance, precise time signature values can aid the analyzer to find phrases and section. Thus, every edit by the user will trigger another analysis based on the new empirical data and gather the rest of the data so that the user does not have to edit the rest of it manually.
The scrolling waveform, which currently only renders the waveform and
the tracks metadata, will be made interactive. To do so, the elements
must share common interfaces such as WaveformItemClickable
,
WaveformItemDraggable
.
Since the skins can be very different in a way that a skin might not even have the buttons or panel that another one has, the scrolling waveform context menus will be used as the primary way to adjust beatgrid via the GUI.
TimeSignature
Members:
- int beatsPerBar
- int noteValue
Section
Members:
- int beatOffset
- QColor color
- QString label
- TrackPointer pTrack
GridAdjustmentMarker
Members:
- int beatOffset
- TimeSignature newTimeSignature
- Bpm newBpm
In the first phase, the groundwork for subsequent work, the base for all track metadata, that is, beats will be perfected. This will involve setting BPM, time signature and downbeats.
Week 1
- Add
Downbeat Offset
andTime Signature
as members of the Track class. Initially, this data will not be persistent. - Add mouse pointer interaction capabilities to beats on the scrolling waveform. Additionally, try to create a general interface for functionality common to scrolling waveform elements (Currently, they are only rendered and can't be interacted with).
- Add context menu to mark a beat as downbeat and set global time signature for the track.
- Create tests to check downbeats and time signature data are in sync.
Week 2
- Persist the data by adding time signature (single global value, not repeated) and downbeat offset fields to beats.proto.
- Shift from global metadata adjustment to a marker based system.
- Add an adjustment marker renderer.
- Add tests to check multiple marker data.
Week 3
- Implement changes to recalculate marker dependent values like
waveformrenderbeat
. - Add bar/beat counter near play marker.
Week 4
- Finalize PR with granular beatgrid editing features. This PR will enable us to set downbeats, and arbitrary BPM and time signature via adjustment markers.
Sections and phrases.
Week 5
- Start by creating in memory sections as a member of Track. A Track stores a list of section dividers which demarcate the start of a new section. A section always clamps to a beat.
- Add tests.
Week 6
- Create a new
SectionOverviewWidget
with draggable section boundaries and splitting capability with scissor-like pointer. - Create a PR with a section overview widget displayed under the waveform overview with editing capabilities.
Week 7
- Create a new renderer
WaveformRenderSection
to draw on scrolling waveform. - Add drag and split interactions.
Week 8
- Working on section context menu to change color and label.
- Add context menu option to downbeat/beat to mark a part of section as phrase demarcation.
- Prepare a PR with new section renderer with editing and phrase addition capabilities.
Oriented towards DJing functionality aided by the new metadata and beatgrid editing with button controls (without context menus)
Week 9
- Add loop and jump combobox selector for setting units.
- Create a PR for the same.
Week 10
- AutoDJ changes to match track downbeats during transition.
- Create a new PR for this feature.
Week 11
- Integrating results from the analyser.
- Create a separate PR for this integration.
Week 12
- Create a beatgrid editing panel with buttons mappable to controller.
- Add controls to edit beatgrid using the current play position.
- Create the final PR.
Mixxx is a free and open-source DJ software.
Manual
Hardware Compatibility
Reporting Bugs
Getting Involved
Contribution Guidelines
Coding Guidelines
Using Git
Developer Guide
Creating Skins
Contributing Mappings
Mixxx Controls
MIDI Scripting
Components JS
HID Scripting