summaryrefslogtreecommitdiffstats
path: root/README.md
blob: d557938350fab6576bce819698fbcab24dbe78f5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
Overview
========

`libdjinterop` is a C++ library that allows access to database formats used to store information about DJ record libraries.

This library currently supports:

* Engine Library, as used on "Prime"-series DJ equipment.

State of Support
================

The library is currently in an early alpha stage, and not all features are implemented yet.  It currently supports only the Engine Library format.

What is supported:

* Track metadata
* Beat grids
* Hot cues
* Loops
* Waveforms (overview and high-resolution)
* Crates
* Engine library formats for the following database schema versions:
  * Schema 1.6.0 (used by firmware 1.0.0)
  * Schema 1.7.1 (used by firmware 1.0.3)

What is not supported (yet):

* Album art
* Playlists
* Play history
* Engine Library formats associated with other firmware versions
* DJ record libraries in formats other than Engine Prime

How Do I Use It?
================

The library is not ready for prime-time yet, but if you are willing to read the source code, you can get an example application up and running using code similar to the following:

```c++
#include <djinterop/djinterop.hpp>

namespace el = djinterop::enginelibrary;

int main(int argc, char **argv)
{
    auto db = el::make_database("Engine Library");

    auto tr = db.create_track("../01 - Some Artist - Some Song.mp3");

    tr.set_track_number(1);
    tr.set_bpm(120);
    tr.set_year(1970);
    tr.set_title("Some Song"s);
    tr.set_artist("Some Artist"s);
    tr.set_publisher(std::nullopt);  // std::nullopt indicates missing metadata
    tr.set_key(djinterop::musical_key::a_minor);
    tr.set_bitrate(320);
    tr.set_average_loudness(0.5);  // loudness range (0, 1]
    int64_t sample_count = 16140600;
    tr.set_sampling({44100,          // sample rate
                     sample_count}); // sample count
    std::vector<djinterop::beatgrid_marker> beatgrid{
        {-4, -83316.78},                 // 1st marker
        {812, 17470734.439}};            // 2nd marker
    tr.set_default_beatgrid(beatgrid);   // as analyzed
    tr.set_adjusted_beatgrid(beatgrid);  // manually adjusted

    // The main cue concerns the cue button
    tr.set_default_main_cue(2732);   // as analyzed
    tr.set_adjusted_main_cue(2732);  // manually adjusted

    // There are always 8 hot cues, whereby each can optionally be set
    std::array<std::optional<djinterop::hot_cue>, 8> cues;
    cues[0] =
        djinterop::hot_cue{"Cue 1", 1377924.5,  // position in number of samples
                           el::standard_pad_colors::pad_1};
    tr.set_hot_cues(cues);

    // Setting a single hot cue can also be done like this
    tr.set_hot_cue_at(3, {"Cue 4", 5508265.96, el::standard_pad_colors::pad_4});

    // The loop API works like the hot cue API
    tr.set_loop_at(
        0, {"Loop 1", 1144.012, 345339.134, el::standard_pad_colors::pad_1});

    // Set high-resolution waveform
    int64_t spe = tr.required_waveform_samples_per_entry();
    int64_t waveform_size = (sample_count + spe - 1) / spe; // Ceiling division
    std::vector<djinterop::waveform_entry> waveform;
    waveform.reserve(waveform_size);
    for (int64_t i = 0; i < waveform_size; ++i)
    {
        waveform.push_back(  // VALUE and OPACITY for each band (low/mid/high)
            {{0, 255},       // low
             {42, 255},      // mid
             {255, 255}});   // high
    }
    tr.set_waveform(std::move(waveform));

    auto cr = db.create_crate("My Example Crate");
    cr.add_track(tr);
}
```

How Do I Build It?
============================

`libdjinterop` requires the following compile-time dependencies:

* [SQLite3](https://sqlite.org)
* [zlib](http://zlib.net)
* [Boost](https://boost.org) (only required for unit tests, not the main library)

`libdjinterop` uses the [Meson build system](https://mesonbuild.com).  Assuming you have the above dependencies in place, and the build tools, you can issue the following commands:

```
$ meson build/
$ ninja -C build/
$ ninja -C build/ test                (optional, run unit tests)
# ninja -C build/ install             (as a suitably-privileged user)
```

## With Nix

When [Nix](http://nixos.org/nix) is installed, then you don't need to manually install any
dependencies.
`libdjinterop` can then simply be built with:

```
$ nix build
```

In order to drop into a development environment with dependencies available, execute:

```
$ nix-shell
```

You can then build `libdjinterop` by using Meson as described above.
This is advantageous when developing since it only recompiles sources that it needs to.

Thanks To
=========

`libdjinterop` makes use of a number of software libraries, and is extremely grateful for:

* [Boost](https://boost.org)
* [ClangFormat](https://clang.llvm.org/docs/ClangFormat.html)
* [SQLite](https://sqlite.org)
* [SQLite Modern C++ Wrapper](https://github.com/SqliteModernCpp/sqlite_modern_cpp)
* [zlib](http://zlib.net)

Interfacing with the Engine Library database format was made a lot easier with the help of MixMasterG from ATGR, who is the author of the [Denon Conversion Utility](https://sellfy.com/atgr_production_team).