kitstarter 0.2.2__tar.gz → 0.3.0__tar.gz
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.
- {kitstarter-0.2.2 → kitstarter-0.3.0}/PKG-INFO +2 -2
- {kitstarter-0.2.2 → kitstarter-0.3.0}/kitstarter/__init__.py +3 -3
- {kitstarter-0.2.2 → kitstarter-0.3.0}/kitstarter/gui/main_window.py +8 -2
- {kitstarter-0.2.2 → kitstarter-0.3.0}/kitstarter/gui/samples_widget.py +27 -24
- {kitstarter-0.2.2 → kitstarter-0.3.0}/kitstarter/pindb.py +10 -2
- {kitstarter-0.2.2 → kitstarter-0.3.0}/kitstarter/starter_kits.py +7 -14
- {kitstarter-0.2.2 → kitstarter-0.3.0}/pyproject.toml +2 -2
- {kitstarter-0.2.2 → kitstarter-0.3.0}/.gitignore +0 -0
- {kitstarter-0.2.2 → kitstarter-0.3.0}/LICENSE +0 -0
- {kitstarter-0.2.2 → kitstarter-0.3.0}/README.md +0 -0
- {kitstarter-0.2.2 → kitstarter-0.3.0}/kitstarter/gui/__init__.py +0 -0
- {kitstarter-0.2.2 → kitstarter-0.3.0}/kitstarter/gui/main_window.ui +0 -0
- {kitstarter-0.2.2 → kitstarter-0.3.0}/kitstarter/res/arrow-down-disabled.svg +0 -0
- {kitstarter-0.2.2 → kitstarter-0.3.0}/kitstarter/res/arrow-down-enabled.svg +0 -0
- {kitstarter-0.2.2 → kitstarter-0.3.0}/kitstarter/res/arrow-up-disabled.svg +0 -0
- {kitstarter-0.2.2 → kitstarter-0.3.0}/kitstarter/res/arrow-up-enabled.svg +0 -0
- {kitstarter-0.2.2 → kitstarter-0.3.0}/kitstarter/res/delete.svg +0 -0
- {kitstarter-0.2.2 → kitstarter-0.3.0}/kitstarter/res/empty.sfz +0 -0
- {kitstarter-0.2.2 → kitstarter-0.3.0}/kitstarter/res/inst-complete.svg +0 -0
- {kitstarter-0.2.2 → kitstarter-0.3.0}/kitstarter/res/inst-incomplete.svg +0 -0
- {kitstarter-0.2.2 → kitstarter-0.3.0}/kitstarter/res/pin.svg +0 -0
- {kitstarter-0.2.2 → kitstarter-0.3.0}/kitstarter/res/sample-mismatch.svg +0 -0
- {kitstarter-0.2.2 → kitstarter-0.3.0}/kitstarter/res/sample-okay.svg +0 -0
- {kitstarter-0.2.2 → kitstarter-0.3.0}/tests/pindb.py +0 -0
- {kitstarter-0.2.2 → kitstarter-0.3.0}/tests/samples_widget.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: kitstarter
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: kitstarter is a program you can use to "sketch in" a drumkit SFZ file.
|
|
5
5
|
Author-email: Leon Dionne <ldionne@dridesign.sh.cn>
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -9,7 +9,7 @@ Requires-Dist: PyQt5
|
|
|
9
9
|
Requires-Dist: soso_qt_extras >= 1.6.0
|
|
10
10
|
Requires-Dist: conn_jack
|
|
11
11
|
Requires-Dist: midi_notes
|
|
12
|
-
Requires-Dist: sfzen >= 1.2.0
|
|
12
|
+
Requires-Dist: sfzen >= 1.2.0, <= 2.0.0
|
|
13
13
|
Requires-Dist: liquiphy
|
|
14
14
|
Requires-Dist: soundfile
|
|
15
15
|
Requires-Dist: jack_audio_player
|
|
@@ -15,7 +15,7 @@ from PyQt5.QtWidgets import QApplication, QWidget, QSplitter
|
|
|
15
15
|
from qt_extras import DevilBox
|
|
16
16
|
from conn_jack import JackConnectError
|
|
17
17
|
|
|
18
|
-
__version__ = "0.
|
|
18
|
+
__version__ = "0.3.0"
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
APPLICATION_NAME = "KitStarter"
|
|
@@ -77,13 +77,13 @@ def _geometry_key(widget):
|
|
|
77
77
|
"""
|
|
78
78
|
Automatic QSettings key generated from class name.
|
|
79
79
|
"""
|
|
80
|
-
return f'{
|
|
80
|
+
return f'{widget.__class__.__name__}/geometry'
|
|
81
81
|
|
|
82
82
|
def _splitter_geometry_key(widget, splitter):
|
|
83
83
|
"""
|
|
84
84
|
Automatic QSettings key generated from class name.
|
|
85
85
|
"""
|
|
86
|
-
return f'{
|
|
86
|
+
return f'{widget.__class__.__name__}/{splitter.objectName()}/geometry'
|
|
87
87
|
|
|
88
88
|
QWidget.restore_geometry = _restore_geometry
|
|
89
89
|
QWidget.save_geometry = _save_geometry
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
#
|
|
3
3
|
# Copyright 2025 Leon Dionne <ldionne@dridesign.sh.cn>
|
|
4
4
|
#
|
|
5
|
-
|
|
5
|
+
"""
|
|
6
|
+
Provides MainWindow of the kitstarter application.
|
|
7
|
+
"""
|
|
8
|
+
import os, logging, tempfile
|
|
6
9
|
from os.path import join, dirname, basename, abspath, splitext
|
|
7
10
|
from functools import lru_cache
|
|
8
11
|
from collections import namedtuple
|
|
@@ -39,6 +42,9 @@ MESSAGE_TIMEOUT = 3000
|
|
|
39
42
|
|
|
40
43
|
|
|
41
44
|
class MainWindow(QMainWindow):
|
|
45
|
+
"""
|
|
46
|
+
User interface of the kitstarter application.
|
|
47
|
+
"""
|
|
42
48
|
|
|
43
49
|
sig_ports_complete = pyqtSignal() # \
|
|
44
50
|
sig_sources_changed = pyqtSignal() # Used to decouple JackConnectionManager callbacks
|
|
@@ -369,7 +375,7 @@ class MainWindow(QMainWindow):
|
|
|
369
375
|
# file tree / sample display management
|
|
370
376
|
|
|
371
377
|
@pyqtSlot(int)
|
|
372
|
-
def slot_instrument_changed(self,
|
|
378
|
+
def slot_instrument_changed(self, _):
|
|
373
379
|
self.chk_filter_instrument.setText('Filter "{}"'.format(
|
|
374
380
|
self.lst_instruments.currentItem().text()))
|
|
375
381
|
if self.chk_filter_instrument.isChecked():
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
#
|
|
3
3
|
# Copyright 2025 Leon Dionne <ldionne@dridesign.sh.cn>
|
|
4
4
|
#
|
|
5
|
+
"""
|
|
6
|
+
Provides classes used inside a Qt dialog for editing a multi-sample instrument.
|
|
7
|
+
"""
|
|
5
8
|
import logging
|
|
6
9
|
from os.path import join, basename
|
|
7
10
|
from math import sqrt
|
|
@@ -161,23 +164,23 @@ class VelocityGraph(_Track):
|
|
|
161
164
|
if self.hover_point_index is None:
|
|
162
165
|
self.range_change_event(event)
|
|
163
166
|
else:
|
|
164
|
-
self.sample.
|
|
165
|
-
self.sample.
|
|
167
|
+
self.sample.velcurves[self.hover_point_index] = Velcurve(
|
|
168
|
+
self.sample.velcurves[self.hover_point_index].velocity \
|
|
166
169
|
if event.modifiers() & Qt.ControlModifier \
|
|
167
170
|
else self.x2v(event.x()),
|
|
168
|
-
self.sample.
|
|
171
|
+
self.sample.velcurves[self.hover_point_index].amplitude \
|
|
169
172
|
if event.modifiers() & Qt.ShiftModifier \
|
|
170
173
|
else self.y2a(event.y())
|
|
171
174
|
)
|
|
172
175
|
self.update()
|
|
173
|
-
elif event.buttons() == Qt.NoButton and self.sample.
|
|
176
|
+
elif event.buttons() == Qt.NoButton and self.sample.velcurves:
|
|
174
177
|
near_points = [ (
|
|
175
178
|
sqrt(
|
|
176
179
|
pow(abs(self.v2x(velcurve.velocity) - event.x()), 2) +
|
|
177
180
|
pow(abs(self.a2y(velcurve.amplitude) - event.y()), 2)
|
|
178
181
|
),
|
|
179
182
|
index
|
|
180
|
-
) for index, velcurve in enumerate(self.sample.
|
|
183
|
+
) for index, velcurve in enumerate(self.sample.velcurves) ]
|
|
181
184
|
near_points.sort()
|
|
182
185
|
hover_point_index = near_points[0][1] if near_points[0][0] < POLAR_SNAP_RANGE else None
|
|
183
186
|
if hover_point_index != self.hover_point_index:
|
|
@@ -192,7 +195,7 @@ class VelocityGraph(_Track):
|
|
|
192
195
|
self.hover_point_grabbed = True
|
|
193
196
|
self.update()
|
|
194
197
|
|
|
195
|
-
def mouseReleaseEvent(self,
|
|
198
|
+
def mouseReleaseEvent(self, _):
|
|
196
199
|
if self.hover_point_grabbed:
|
|
197
200
|
self.hover_point_grabbed = False
|
|
198
201
|
self.sig_value_changed.emit()
|
|
@@ -240,8 +243,8 @@ class VelocityGraph(_Track):
|
|
|
240
243
|
# line across to lovel:
|
|
241
244
|
points.append(QPointF(self.v2x(self.lovel), self.rect().bottom()))
|
|
242
245
|
|
|
243
|
-
if self.sample.
|
|
244
|
-
for velcurve in self.sample.
|
|
246
|
+
if self.sample.velcurves:
|
|
247
|
+
for velcurve in self.sample.velcurves:
|
|
245
248
|
points.append(QPointF(self.v2x(velcurve.velocity), self.a2y(velcurve.amplitude)))
|
|
246
249
|
else:
|
|
247
250
|
points.append(QPointF(self.v2x(self.lovel), self.v2y(self.lovel)))
|
|
@@ -264,7 +267,7 @@ class VelocityGraph(_Track):
|
|
|
264
267
|
painter.drawLine(start, end)
|
|
265
268
|
start = end
|
|
266
269
|
|
|
267
|
-
for index, velcurve in enumerate(self.sample.
|
|
270
|
+
for index, velcurve in enumerate(self.sample.velcurves):
|
|
268
271
|
if self.hover_point_index == index:
|
|
269
272
|
painter.setPen(self.velcurve_pen_grabbed \
|
|
270
273
|
if self.hover_point_grabbed else self.velcurve_pen_hover)
|
|
@@ -325,7 +328,7 @@ class VelocityGraph(_Track):
|
|
|
325
328
|
if lovel < hivel else None
|
|
326
329
|
|
|
327
330
|
def update_velcurves(self):
|
|
328
|
-
velcurves = []
|
|
331
|
+
self.sample.velcurves = []
|
|
329
332
|
if self.overlaps:
|
|
330
333
|
self.overlaps.sort(key = lambda overlap: overlap.lovel)
|
|
331
334
|
lo_overlap = self.overlaps.pop(0) if self.overlaps[0].lovel == self.lovel else None
|
|
@@ -338,21 +341,21 @@ class VelocityGraph(_Track):
|
|
|
338
341
|
else:
|
|
339
342
|
mid_overlaps = []
|
|
340
343
|
if lo_overlap:
|
|
341
|
-
velcurves.append(Velcurve(self.lovel, 0.0))
|
|
342
|
-
velcurves.append(Velcurve(lo_overlap.hivel, self.v2a(lo_overlap.hivel)))
|
|
344
|
+
self.sample.velcurves.append(Velcurve(self.lovel, 0.0))
|
|
345
|
+
self.sample.velcurves.append(Velcurve(lo_overlap.hivel, self.v2a(lo_overlap.hivel)))
|
|
343
346
|
else:
|
|
344
|
-
velcurves.append(Velcurve(self.lovel, self.v2a(self.lovel)))
|
|
347
|
+
self.sample.velcurves.append(Velcurve(self.lovel, self.v2a(self.lovel)))
|
|
345
348
|
for mid_overlap in mid_overlaps:
|
|
346
|
-
velcurves.append(Velcurve(mid_overlap.lovel, self.v2a(mid_overlap.lovel)))
|
|
349
|
+
self.sample.velcurves.append(Velcurve(mid_overlap.lovel, self.v2a(mid_overlap.lovel)))
|
|
347
350
|
mid_overlap_center = mid_overlap.lovel + round((mid_overlap.hivel - mid_overlap.lovel) / 2)
|
|
348
|
-
velcurves.append(Velcurve(mid_overlap_center, 0.0))
|
|
349
|
-
velcurves.append(Velcurve(mid_overlap.hivel, self.v2a(mid_overlap.hivel)))
|
|
351
|
+
self.sample.velcurves.append(Velcurve(mid_overlap_center, 0.0))
|
|
352
|
+
self.sample.velcurves.append(Velcurve(mid_overlap.hivel, self.v2a(mid_overlap.hivel)))
|
|
350
353
|
if hi_overlap:
|
|
351
|
-
velcurves.append(Velcurve(hi_overlap.lovel, self.v2a(hi_overlap.lovel)))
|
|
352
|
-
velcurves.append(Velcurve(self.hivel, 0.0))
|
|
354
|
+
self.sample.velcurves.append(Velcurve(hi_overlap.lovel, self.v2a(hi_overlap.lovel)))
|
|
355
|
+
self.sample.velcurves.append(Velcurve(self.hivel, 0.0))
|
|
353
356
|
else:
|
|
354
|
-
velcurves.append(Velcurve(self.hivel, self.v2a(self.hivel)))
|
|
355
|
-
self.sample.
|
|
357
|
+
self.sample.velcurves.append(Velcurve(self.hivel, self.v2a(self.hivel)))
|
|
358
|
+
self.sample.dirty = True
|
|
356
359
|
self.update()
|
|
357
360
|
|
|
358
361
|
|
|
@@ -686,7 +689,7 @@ class SamplesWidget(QWidget):
|
|
|
686
689
|
|
|
687
690
|
def velo_graphs(self):
|
|
688
691
|
"""
|
|
689
|
-
Returns a list of VelocityGraph
|
|
692
|
+
Returns a list of VelocityGraph objects.
|
|
690
693
|
"""
|
|
691
694
|
# Exclude first row (scale) and last row (pad)
|
|
692
695
|
return self.grid.column(COL_GRAPH)[1:-1]
|
|
@@ -729,9 +732,9 @@ class SamplesWidget(QWidget):
|
|
|
729
732
|
def slot_spread(self):
|
|
730
733
|
tracks = self.velo_graphs()
|
|
731
734
|
spread = 127 / len(tracks)
|
|
732
|
-
for i in
|
|
733
|
-
|
|
734
|
-
|
|
735
|
+
for i, track in enumerate(tracks):
|
|
736
|
+
track.lovel = round(i * spread)
|
|
737
|
+
track.hivel = round((i + 1) * spread)
|
|
735
738
|
self.find_overlaps()
|
|
736
739
|
self.slot_value_changed()
|
|
737
740
|
|
|
@@ -2,13 +2,21 @@
|
|
|
2
2
|
#
|
|
3
3
|
# Copyright 2025 Leon Dionne <ldionne@dridesign.sh.cn>
|
|
4
4
|
#
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
"""
|
|
6
|
+
Provides PinDatabase class - an abstraction over an sqlite database which keeps
|
|
7
|
+
track of samples on your hard drive and which instrument(s) thay have been
|
|
8
|
+
"pinned" to.
|
|
9
|
+
"""
|
|
10
|
+
import os, logging
|
|
7
11
|
from sqlite3 import connect
|
|
8
12
|
from appdirs import user_config_dir
|
|
9
13
|
|
|
10
14
|
|
|
11
15
|
class PinDatabase():
|
|
16
|
+
"""
|
|
17
|
+
An abstraction over an sqlite database which keeps track of samples on your
|
|
18
|
+
hard drive and which instrument(s) thay have been "pinned" to.
|
|
19
|
+
"""
|
|
12
20
|
|
|
13
21
|
instance = None # Enforce singleton
|
|
14
22
|
conn = None
|
|
@@ -6,12 +6,11 @@
|
|
|
6
6
|
"""
|
|
7
7
|
Provides Drumkit SFZ wrapper which allows import / copy operations.
|
|
8
8
|
"""
|
|
9
|
-
import logging
|
|
10
9
|
from os.path import abspath, basename
|
|
11
10
|
from collections import namedtuple
|
|
12
11
|
from midi_notes import Note, MIDI_DRUM_IDS, MIDI_DRUM_NAMES
|
|
13
12
|
from sfzen import SFZ
|
|
14
|
-
from sfzen.drumkits import
|
|
13
|
+
from sfzen.drumkits import PITCH_GROUPS, pitch_id_tuple, iter_pitch_by_group
|
|
15
14
|
|
|
16
15
|
Velcurve = namedtuple('Velcurve', ['velocity', 'amplitude'])
|
|
17
16
|
|
|
@@ -50,7 +49,7 @@ class StarterKit:
|
|
|
50
49
|
starter_sample._tune = opcodes['tune'].value
|
|
51
50
|
for code, opcode in region.opcodes.items():
|
|
52
51
|
if code.startswith('amp_velcurve'):
|
|
53
|
-
starter_sample.
|
|
52
|
+
starter_sample.velcurves.append(Velcurve(int(code[13:]), float(opcode.value)))
|
|
54
53
|
instrument.samples[starter_sample.path] = starter_sample
|
|
55
54
|
|
|
56
55
|
def samples(self):
|
|
@@ -147,6 +146,9 @@ class StarterInstrument:
|
|
|
147
146
|
|
|
148
147
|
|
|
149
148
|
class StarterSample:
|
|
149
|
+
"""
|
|
150
|
+
Contains basic sample info which is compiled into an .sfz opcode.
|
|
151
|
+
"""
|
|
150
152
|
|
|
151
153
|
def __init__(self, path, pitch):
|
|
152
154
|
self.path = abspath(path)
|
|
@@ -156,7 +158,7 @@ class StarterSample:
|
|
|
156
158
|
self._volume = 0.0
|
|
157
159
|
self._transpose = 0
|
|
158
160
|
self._tune = 0
|
|
159
|
-
self.
|
|
161
|
+
self.velcurves = []
|
|
160
162
|
self.dirty = False
|
|
161
163
|
|
|
162
164
|
def __str__(self):
|
|
@@ -207,15 +209,6 @@ class StarterSample:
|
|
|
207
209
|
self._tune = value
|
|
208
210
|
self.dirty = True
|
|
209
211
|
|
|
210
|
-
@property
|
|
211
|
-
def velcurves(self):
|
|
212
|
-
return self._velcurves
|
|
213
|
-
|
|
214
|
-
@velcurves.setter
|
|
215
|
-
def velcurves(self, value):
|
|
216
|
-
self._velcurves = value
|
|
217
|
-
self.dirty = True
|
|
218
|
-
|
|
219
212
|
def write(self, stream):
|
|
220
213
|
stream.write('<region>\n')
|
|
221
214
|
stream.write(f'sample={self.path}\n')
|
|
@@ -225,7 +218,7 @@ class StarterSample:
|
|
|
225
218
|
stream.write(f'lovel={self._lovel}\n')
|
|
226
219
|
if self._hivel < 127:
|
|
227
220
|
stream.write(f'hivel={self._hivel}\n')
|
|
228
|
-
for point in self.
|
|
221
|
+
for point in self.velcurves:
|
|
229
222
|
stream.write(f'amp_velcurve_{point.velocity}={point.amplitude:.1f}\n')
|
|
230
223
|
if self._transpose != 0:
|
|
231
224
|
stream.write(f'transpose={self._transpose}\n')
|
|
@@ -10,7 +10,7 @@ dependencies = [
|
|
|
10
10
|
"soso_qt_extras >= 1.6.0",
|
|
11
11
|
"conn_jack",
|
|
12
12
|
"midi_notes",
|
|
13
|
-
"sfzen >= 1.2.0",
|
|
13
|
+
"sfzen >= 1.2.0, <= 2.0.0",
|
|
14
14
|
"liquiphy",
|
|
15
15
|
"soundfile",
|
|
16
16
|
"jack_audio_player"
|
|
@@ -27,7 +27,7 @@ requires = ["flit_core >=3.2,<4"]
|
|
|
27
27
|
build-backend = "flit_core.buildapi"
|
|
28
28
|
|
|
29
29
|
[bumpver]
|
|
30
|
-
current_version = "0.
|
|
30
|
+
current_version = "0.3.0"
|
|
31
31
|
version_pattern = "MAJOR.MINOR.PATCH"
|
|
32
32
|
commit_message = "Bump version {old_version} -> {new_version}"
|
|
33
33
|
commit = true
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|