wavesync 1.0.0.alpha2 → 1.0.0.alpha4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +95 -59
- data/config/devices.yml +20 -0
- data/lib/wavesync/acid_chunk.rb +45 -4
- data/lib/wavesync/analyzer.rb +17 -4
- data/lib/wavesync/audio.rb +153 -90
- data/lib/wavesync/audio_format.rb +9 -2
- data/lib/wavesync/bpm_detector.rb +14 -7
- data/lib/wavesync/cli.rb +3 -0
- data/lib/wavesync/commands/analyze.rb +2 -0
- data/lib/wavesync/commands/clear_cache.rb +75 -0
- data/lib/wavesync/commands/command.rb +4 -1
- data/lib/wavesync/commands/help.rb +6 -0
- data/lib/wavesync/commands/pull.rb +43 -0
- data/lib/wavesync/commands/setlist.rb +66 -0
- data/lib/wavesync/commands/sync.rb +19 -26
- data/lib/wavesync/commands.rb +52 -12
- data/lib/wavesync/config.rb +43 -3
- data/lib/wavesync/cue_chunk.rb +203 -0
- data/lib/wavesync/device.rb +32 -7
- data/lib/wavesync/essentia_bpm_detector.rb +38 -0
- data/lib/wavesync/ffmpeg/probe.rb +74 -0
- data/lib/wavesync/ffmpeg.rb +144 -0
- data/lib/wavesync/file_converter.rb +7 -2
- data/lib/wavesync/libmtp.rb +333 -0
- data/lib/wavesync/logger.rb +84 -0
- data/lib/wavesync/path_resolver.rb +32 -6
- data/lib/wavesync/percival_bpm_detector.rb +31 -0
- data/lib/wavesync/python_venv.rb +25 -0
- data/lib/wavesync/scanner.rb +143 -27
- data/lib/wavesync/{set.rb → setlist.rb} +28 -12
- data/lib/wavesync/setlist_editor.rb +556 -0
- data/lib/wavesync/track_padding.rb +15 -4
- data/lib/wavesync/transport/filesystem.rb +36 -0
- data/lib/wavesync/transport/mtp.rb +285 -0
- data/lib/wavesync/transport.rb +21 -0
- data/lib/wavesync/ui.rb +67 -12
- data/lib/wavesync/version.rb +2 -1
- data/lib/wavesync.rb +7 -2
- metadata +17 -32
- data/lib/wavesync/commands/set.rb +0 -63
- data/lib/wavesync/set_editor.rb +0 -245
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3b3c24cfdb7c1e1e6fe97157437f690995f59a9981b0771f42f1669af101a31a
|
|
4
|
+
data.tar.gz: 03c846e8a0c9e19d6a6b9f8a6cb72edc426a1db17dcd4cfdbcaa556c643a1eb1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7f3ce9462f43a8c1fd453df2eb1919fdf6c21ef5f2efa95aec8effdb737309801f1c94b3ec31353a766e448f7c14148208628f4f0f80c86e0b19079fef6f6ac0
|
|
7
|
+
data.tar.gz: a51decb6dc314e43c134fa11b5f0332eddbde5616b2051bafa645861b386dc965f88f0d0725fc9a435d5d3206f1e8af8ad38d26b204eb49072b5e2a98daaa763
|
data/README.md
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
# Wavesync
|
|
2
2
|
|
|
3
|
-
Wavesync is a Ruby-based CLI tool that scans your music library and automatically converts audio files to match the specifications of
|
|
3
|
+
Wavesync is a Ruby-based CLI tool that scans your music library and automatically converts audio files to match the specifications of the [teenage engineering TP-7](https://teenage.engineering/products/tp-7), the [Elektron Octatrack MKII](https://www.elektron.se/explore/octatrack-mkii), and the [Playdate](https://kicooya.com/), adjusting sample rate, bit depth and file format as needed while preserving your original library structure and only converting files that don't already meet the device requirements.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
It can also analyse the BPM of the tracks in your library and store the information as metadata in your files, as well as convert the metadata so that the target device can read the BPM. When syncing to the Octatrack, you can choose to add padding to each track, so that it's dead-simple to auto-slice your tracks to a multiple of full bars which allows precise looping, and seamlessly jumping to different parts of a track with Octatrack's sequencer.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- Elektron Octatrack MKII
|
|
7
|
+

|
|
9
8
|
|
|
10
9
|
## Supported file types
|
|
11
10
|
|
|
@@ -20,23 +19,43 @@ Unsupported file types will be ignored when syncing.
|
|
|
20
19
|
|
|
21
20
|
## Installation
|
|
22
21
|
|
|
23
|
-
1. Install
|
|
22
|
+
1. Install Ruby
|
|
23
|
+
|
|
24
|
+
https://www.ruby-lang.org/en/documentation/installation/
|
|
25
|
+
|
|
26
|
+
2. Install dependencies
|
|
24
27
|
|
|
25
28
|
```bash
|
|
26
|
-
brew install ffmpeg
|
|
27
|
-
brew install taglib
|
|
28
|
-
brew install bpm-tools # required for analyze command
|
|
29
|
+
brew install ffmpeg
|
|
30
|
+
brew install taglib
|
|
29
31
|
```
|
|
30
32
|
|
|
31
|
-
|
|
33
|
+
3. Install Wavesync
|
|
32
34
|
|
|
33
35
|
```bash
|
|
34
36
|
gem install wavesync --pre
|
|
35
37
|
```
|
|
36
38
|
|
|
37
|
-
|
|
39
|
+
4. Set up the Python environment for BPM analysis
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
brew install python@3.11
|
|
43
|
+
python3.11 -m venv ~/.wavesync-venv
|
|
44
|
+
~/.wavesync-venv/bin/pip install essentia
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
5. Install libmtp (only required for syncing to TP-7)
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
brew install libmtp
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Recent versions of field kit no longer expose the TP-7 as a filesystem path, so wavesync talks to the device over MTP directly.
|
|
38
54
|
|
|
39
|
-
|
|
55
|
+
Before syncing the TP-7:
|
|
56
|
+
|
|
57
|
+
- Quit field kit and any other app that may claim the TP-7. They cannot run at the same time as wavesync — only one process can hold the MTP session.
|
|
58
|
+
- Put the TP-7 into MTP mode: hold down the Stop button while turning the TP-7 on, and keep holding until MTP mode is engaged.
|
|
40
59
|
|
|
41
60
|
## Configuration
|
|
42
61
|
|
|
@@ -49,102 +68,119 @@ library: ~/Music/Library
|
|
|
49
68
|
devices:
|
|
50
69
|
- name: TP-7
|
|
51
70
|
model: TP-7
|
|
52
|
-
|
|
71
|
+
transport: mtp
|
|
72
|
+
path: library
|
|
53
73
|
- name: Octatrack
|
|
54
74
|
model: Octatrack
|
|
55
75
|
path: /Volumes/OCTATRACK/LIBRARY/AUDIO
|
|
76
|
+
- name: Playdate
|
|
77
|
+
model: Playdate
|
|
78
|
+
path: /Volumes/PLAYDATE/Data/com.abenokobo.kicooya/media/music
|
|
56
79
|
```
|
|
57
80
|
|
|
58
81
|
- `library`: path to your source music library
|
|
59
82
|
- `devices`: list of devices to sync to, each with:
|
|
60
83
|
- `name`: a label for this device, used with the `-d` command-line option
|
|
61
|
-
- `model`: device model (`TP-7` or `
|
|
62
|
-
- `path`:
|
|
84
|
+
- `model`: device model (`TP-7`, `Octatrack`, or `Playdate`)
|
|
85
|
+
- `path`: where to write files on the device
|
|
86
|
+
- For `transport: filesystem` (default): a path on your local filesystem (e.g. a mounted USB volume).
|
|
87
|
+
- For `transport: mtp`: a folder path inside the device (e.g. `library` for the TP-7).
|
|
88
|
+
- `transport` (optional): `filesystem` (default) or `mtp`. Use `mtp` for the TP-7.
|
|
89
|
+
- `mp3_bitrate` (optional): bitrate in kbps to use when encoding MP3 files for this device. Accepted values: `96`, `128`, `160`, `192`, `256`, `320`. Defaults to `192`. Source files that are already MP3 are copied as-is regardless of this setting.
|
|
90
|
+
|
|
91
|
+
When syncing over MTP, wavesync caches converted files in `~/.cache/wavesync/<device-name>/` so subsequent syncs only push files that aren't already on the device.
|
|
63
92
|
|
|
64
|
-
|
|
93
|
+
## Usage
|
|
65
94
|
|
|
66
|
-
|
|
95
|
+
### Help
|
|
67
96
|
|
|
68
97
|
```bash
|
|
98
|
+
# List all available commands
|
|
69
99
|
wavesync help
|
|
70
100
|
```
|
|
71
101
|
|
|
72
|
-
|
|
102
|
+
### Analyze
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
# Analyze library files for BPM and write results to file metadata
|
|
106
|
+
# Files that already have BPM set are skipped
|
|
107
|
+
wavesync analyze
|
|
108
|
+
|
|
109
|
+
# Overwrite existing BPM values
|
|
110
|
+
wavesync analyze -f
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Sync
|
|
73
114
|
|
|
74
115
|
```bash
|
|
75
|
-
# Sync library
|
|
116
|
+
# Sync library
|
|
76
117
|
# If multiple devices are configured, you will be prompted to select one
|
|
77
118
|
wavesync sync
|
|
78
119
|
|
|
79
|
-
#
|
|
80
|
-
wavesync sync -c /path/to/wavesync.yml
|
|
81
|
-
|
|
82
|
-
# Sync to a specific device only (by name as defined in config)
|
|
120
|
+
# Sync to a specific device (by name as defined in config)
|
|
83
121
|
wavesync sync -d Octatrack
|
|
84
122
|
|
|
85
123
|
# Pad each track with silence so its total length aligns to a multiple of 64 bars
|
|
86
124
|
# (Octatrack only — requires BPM metadata on each track)
|
|
87
125
|
wavesync sync -p
|
|
126
|
+
```
|
|
88
127
|
|
|
89
|
-
|
|
90
|
-
# Files that already have BPM set are skipped
|
|
91
|
-
wavesync analyze
|
|
128
|
+
When a source file's sample rate isn't supported by the target device, Wavesync selects the closest supported rate. Example: If a 96kHz file is synced to an Octatrack (which only supports 44.1kHz), it will be downsampled to 44.1kHz.
|
|
92
129
|
|
|
93
|
-
|
|
94
|
-
wavesync analyze --force
|
|
130
|
+
### Pull
|
|
95
131
|
|
|
96
|
-
|
|
97
|
-
|
|
132
|
+
```bash
|
|
133
|
+
# Read cue points from the device's WAV files and write them back into the
|
|
134
|
+
# matching source files in your library. Useful when you've added or edited
|
|
135
|
+
# cue points on the device and want them reflected in the source library.
|
|
136
|
+
# If multiple devices are configured, you will be prompted to select one.
|
|
137
|
+
wavesync pull
|
|
138
|
+
|
|
139
|
+
# Pull from a specific device (by name as defined in config)
|
|
140
|
+
wavesync pull -d TP-7
|
|
98
141
|
```
|
|
99
142
|
|
|
100
|
-
|
|
143
|
+
Only WAV source files with matching WAV target files on the device are updated. If the source already has the same cue points as the device, it is left untouched.
|
|
144
|
+
|
|
145
|
+
### Setlists (experimental)
|
|
101
146
|
|
|
102
|
-
A
|
|
147
|
+
A setlist is a named, ordered selection of tracks from your library. Setlists are stored as YAML files inside a `.setlists` folder within the library directory. Syncing setlists to devices is not yet implemented.
|
|
103
148
|
|
|
104
149
|
```bash
|
|
105
|
-
# Create a new
|
|
106
|
-
wavesync
|
|
150
|
+
# Create a new setlist and open the interactive editor
|
|
151
|
+
wavesync setlist create NAME
|
|
107
152
|
|
|
108
|
-
# Edit an existing
|
|
109
|
-
wavesync
|
|
153
|
+
# Edit an existing setlist
|
|
154
|
+
wavesync setlist edit NAME
|
|
110
155
|
|
|
111
|
-
# List all
|
|
112
|
-
wavesync
|
|
156
|
+
# List all setlists
|
|
157
|
+
wavesync setlist list
|
|
113
158
|
```
|
|
114
159
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
| Key | Action |
|
|
118
|
-
|-----|--------|
|
|
119
|
-
| `↑` / `↓` | Navigate tracks |
|
|
120
|
-
| `space` | Play / pause selected track |
|
|
121
|
-
| `a` | Add track after selection |
|
|
122
|
-
| `u` | Move selected track up |
|
|
123
|
-
| `d` | Move selected track down |
|
|
124
|
-
| `r` | Remove selected track |
|
|
125
|
-
| `s` | Save and exit |
|
|
126
|
-
| `c` | Cancel without saving |
|
|
127
|
-
|
|
128
|
-
The playing track is indicated by `▶` (playing) or `⏸` (paused) before the track number. When a track finishes, the next track plays automatically. Changes are only written to disk when you press `s`.
|
|
160
|
+
## Development
|
|
129
161
|
|
|
130
|
-
|
|
162
|
+
### Running Tests, RuboCop, and Type Checks
|
|
131
163
|
|
|
132
|
-
|
|
164
|
+
```bash
|
|
165
|
+
rake
|
|
166
|
+
```
|
|
133
167
|
|
|
134
|
-
|
|
168
|
+
This runs RuboCop (with auto-fix), Steep type checks, and the test suite in sequence.
|
|
135
169
|
|
|
136
|
-
|
|
170
|
+
### Running Integration Tests
|
|
137
171
|
|
|
138
|
-
|
|
172
|
+
Integration tests sync against real connected devices and are not run as part of the default `rake` task.
|
|
139
173
|
|
|
140
174
|
```bash
|
|
141
|
-
rake test
|
|
175
|
+
rake test:integration
|
|
142
176
|
```
|
|
143
177
|
|
|
144
|
-
###
|
|
178
|
+
### Regenerating Test Fixtures
|
|
179
|
+
|
|
180
|
+
Test fixtures are pre-generated audio files committed to the repository. To regenerate them:
|
|
145
181
|
|
|
146
182
|
```bash
|
|
147
|
-
|
|
183
|
+
rake fixtures:generate
|
|
148
184
|
```
|
|
149
185
|
|
|
150
186
|
### Releasing
|
data/config/devices.yml
CHANGED
|
@@ -1,22 +1,29 @@
|
|
|
1
1
|
devices:
|
|
2
2
|
- name: TP-7
|
|
3
3
|
sample_rates:
|
|
4
|
+
- 22050
|
|
4
5
|
- 44100
|
|
5
6
|
- 48000
|
|
6
7
|
- 88200
|
|
7
8
|
- 96000
|
|
8
9
|
bit_depths:
|
|
10
|
+
- 8
|
|
9
11
|
- 16
|
|
10
12
|
- 24
|
|
11
13
|
file_types:
|
|
12
14
|
- wav
|
|
13
15
|
- mp3
|
|
14
16
|
bpm_source: :acid_chunk
|
|
17
|
+
unsupported_characters:
|
|
18
|
+
- "™"
|
|
19
|
+
- '"'
|
|
20
|
+
uppercase_paths: true
|
|
15
21
|
|
|
16
22
|
- name: Octatrack
|
|
17
23
|
sample_rates:
|
|
18
24
|
- 44100
|
|
19
25
|
bit_depths:
|
|
26
|
+
- 8
|
|
20
27
|
- 16
|
|
21
28
|
- 24
|
|
22
29
|
file_types:
|
|
@@ -25,3 +32,16 @@ devices:
|
|
|
25
32
|
- aif
|
|
26
33
|
bpm_source: :filename
|
|
27
34
|
bar_multiple: 64
|
|
35
|
+
unsupported_characters:
|
|
36
|
+
- "™"
|
|
37
|
+
- '"'
|
|
38
|
+
- "’"
|
|
39
|
+
- ":"
|
|
40
|
+
- "?"
|
|
41
|
+
|
|
42
|
+
- name: Playdate
|
|
43
|
+
sample_rates:
|
|
44
|
+
- 44100
|
|
45
|
+
file_types:
|
|
46
|
+
- mp3
|
|
47
|
+
transliterate_metadata: true
|
data/lib/wavesync/acid_chunk.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
# rbs_inline: enabled
|
|
2
3
|
|
|
3
4
|
module Wavesync
|
|
4
5
|
class AcidChunk
|
|
@@ -27,6 +28,7 @@ module Wavesync
|
|
|
27
28
|
UINT32_LITTLE_ENDIAN = 'V'
|
|
28
29
|
FLOAT32_LITTLE_ENDIAN = 'e'
|
|
29
30
|
|
|
31
|
+
#: (String filepath) -> Float?
|
|
30
32
|
def self.read_bpm(filepath)
|
|
31
33
|
File.open(filepath, 'rb') do |file|
|
|
32
34
|
file.seek(RIFF_HEADER_SIZE)
|
|
@@ -34,10 +36,10 @@ module Wavesync
|
|
|
34
36
|
chunk_id = file.read(CHUNK_ID_SIZE)
|
|
35
37
|
break if chunk_id.nil? || chunk_id.length < CHUNK_ID_SIZE
|
|
36
38
|
|
|
37
|
-
chunk_size = file.read(CHUNK_SIZE_FIELD_SIZE).unpack1(UINT32_LITTLE_ENDIAN)
|
|
39
|
+
chunk_size = ((file.read(CHUNK_SIZE_FIELD_SIZE) || '').unpack1(UINT32_LITTLE_ENDIAN) || 0).to_i
|
|
38
40
|
if chunk_id == ACID_CHUNK_ID
|
|
39
41
|
file.seek(ACID_TEMPO_OFFSET, IO::SEEK_CUR)
|
|
40
|
-
return file.read(ACID_TEMPO_SIZE).unpack1(FLOAT32_LITTLE_ENDIAN)
|
|
42
|
+
return ((file.read(ACID_TEMPO_SIZE) || '').unpack1(FLOAT32_LITTLE_ENDIAN) || 0).to_f
|
|
41
43
|
else
|
|
42
44
|
padding = chunk_size.odd? ? 1 : 0
|
|
43
45
|
file.seek(chunk_size + padding, IO::SEEK_CUR)
|
|
@@ -47,6 +49,43 @@ module Wavesync
|
|
|
47
49
|
nil
|
|
48
50
|
end
|
|
49
51
|
|
|
52
|
+
#: (String filepath, Integer | Float | String new_bpm) -> void
|
|
53
|
+
def self.write_bpm_in_place(filepath, new_bpm)
|
|
54
|
+
bpm_bytes = [new_bpm.to_f].pack(FLOAT32_LITTLE_ENDIAN)
|
|
55
|
+
|
|
56
|
+
File.open(filepath, 'r+b') do |file|
|
|
57
|
+
file.seek(RIFF_HEADER_SIZE)
|
|
58
|
+
|
|
59
|
+
acid_chunk_found = false
|
|
60
|
+
|
|
61
|
+
until file.eof?
|
|
62
|
+
chunk_id = file.read(CHUNK_ID_SIZE)
|
|
63
|
+
break if chunk_id.nil? || chunk_id.length < CHUNK_ID_SIZE
|
|
64
|
+
|
|
65
|
+
chunk_size_bytes = file.read(CHUNK_SIZE_FIELD_SIZE) || ''
|
|
66
|
+
chunk_size = (chunk_size_bytes.unpack1(UINT32_LITTLE_ENDIAN) || 0).to_i
|
|
67
|
+
|
|
68
|
+
if chunk_id == ACID_CHUNK_ID
|
|
69
|
+
file.seek(ACID_TEMPO_OFFSET, IO::SEEK_CUR)
|
|
70
|
+
file.write(bpm_bytes)
|
|
71
|
+
acid_chunk_found = true
|
|
72
|
+
break
|
|
73
|
+
else
|
|
74
|
+
padding = chunk_size.odd? ? 1 : 0
|
|
75
|
+
file.seek(chunk_size + padding, IO::SEEK_CUR)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
unless acid_chunk_found
|
|
80
|
+
file.seek(0, IO::SEEK_END)
|
|
81
|
+
create_acid_chunk(file, new_bpm.to_f)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
update_riff_size(filepath)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
#: (String source_filepath, String output_filepath, Integer | Float | String new_bpm) -> void
|
|
50
89
|
def self.write_bpm(source_filepath, output_filepath, new_bpm)
|
|
51
90
|
bpm_bytes = [new_bpm.to_f].pack(FLOAT32_LITTLE_ENDIAN)
|
|
52
91
|
acid_chunk_found = false
|
|
@@ -60,8 +99,8 @@ module Wavesync
|
|
|
60
99
|
chunk_id = input.read(CHUNK_ID_SIZE)
|
|
61
100
|
break if chunk_id.nil? || chunk_id.length < CHUNK_ID_SIZE
|
|
62
101
|
|
|
63
|
-
chunk_size_bytes = input.read(CHUNK_SIZE_FIELD_SIZE)
|
|
64
|
-
chunk_size = chunk_size_bytes.unpack1(UINT32_LITTLE_ENDIAN)
|
|
102
|
+
chunk_size_bytes = input.read(CHUNK_SIZE_FIELD_SIZE) || ''
|
|
103
|
+
chunk_size = (chunk_size_bytes.unpack1(UINT32_LITTLE_ENDIAN) || 0).to_i
|
|
65
104
|
|
|
66
105
|
output.write(chunk_id)
|
|
67
106
|
output.write(chunk_size_bytes)
|
|
@@ -94,6 +133,7 @@ module Wavesync
|
|
|
94
133
|
update_riff_size(output_filepath)
|
|
95
134
|
end
|
|
96
135
|
|
|
136
|
+
#: (untyped output, Float bpm) -> void
|
|
97
137
|
def self.create_acid_chunk(output, bpm)
|
|
98
138
|
# Write ACID chunk ID
|
|
99
139
|
output.write(ACID_CHUNK_ID)
|
|
@@ -125,6 +165,7 @@ module Wavesync
|
|
|
125
165
|
output.write([bpm].pack(FLOAT32_LITTLE_ENDIAN))
|
|
126
166
|
end
|
|
127
167
|
|
|
168
|
+
#: (String filepath) -> void
|
|
128
169
|
def self.update_riff_size(filepath)
|
|
129
170
|
File.open(filepath, 'r+b') do |file|
|
|
130
171
|
file.seek(0, IO::SEEK_END)
|
data/lib/wavesync/analyzer.rb
CHANGED
|
@@ -1,23 +1,32 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
# rbs_inline: enabled
|
|
3
|
+
|
|
4
|
+
require_relative 'logger'
|
|
2
5
|
|
|
3
6
|
module Wavesync
|
|
4
7
|
class Analyzer
|
|
5
8
|
CONFIRM_MESSAGE = 'wavesync analyze will add bpm meta data to files in library. Continue? [y/N] '
|
|
9
|
+
SETUP_INSTRUCTIONS = 'brew install python@3.11 && python3.11 -m venv ~/.wavesync-venv && ~/.wavesync-venv/bin/pip install essentia'
|
|
6
10
|
|
|
11
|
+
#: (String library_path) -> void
|
|
7
12
|
def initialize(library_path)
|
|
8
|
-
@library_path = File.expand_path(library_path)
|
|
9
|
-
@
|
|
10
|
-
@
|
|
13
|
+
@library_path = File.expand_path(library_path) #: String
|
|
14
|
+
Logger.configure(@library_path)
|
|
15
|
+
@audio_files = find_audio_files #: Array[String]
|
|
16
|
+
@ui = UI.new #: UI
|
|
11
17
|
end
|
|
12
18
|
|
|
19
|
+
#: (?overwrite: bool) -> void
|
|
13
20
|
def analyze(overwrite: false)
|
|
14
21
|
unless BpmDetector.available?
|
|
15
|
-
puts
|
|
22
|
+
puts "Error: essentia is not installed. Set up the Python venv with:\n #{SETUP_INSTRUCTIONS}"
|
|
16
23
|
exit 1
|
|
17
24
|
end
|
|
18
25
|
|
|
19
26
|
return unless @ui.confirm(CONFIRM_MESSAGE)
|
|
20
27
|
|
|
28
|
+
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
29
|
+
|
|
21
30
|
@audio_files.each_with_index do |file, index|
|
|
22
31
|
audio = Audio.new(file)
|
|
23
32
|
|
|
@@ -32,10 +41,14 @@ module Wavesync
|
|
|
32
41
|
@ui.analyze_progress(index, @audio_files.size)
|
|
33
42
|
@ui.analyze_result(file, bpm)
|
|
34
43
|
end
|
|
44
|
+
|
|
45
|
+
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time
|
|
46
|
+
Logger.log_run_time(elapsed)
|
|
35
47
|
end
|
|
36
48
|
|
|
37
49
|
private
|
|
38
50
|
|
|
51
|
+
#: () -> Array[String]
|
|
39
52
|
def find_audio_files
|
|
40
53
|
Audio.find_all(@library_path).sort
|
|
41
54
|
end
|