Skip to content

Commit 0f3b9b8

Browse files
committed
option for velocity sensitive notes!
1 parent 3fd2bf9 commit 0f3b9b8

File tree

4 files changed

+47
-17
lines changed

4 files changed

+47
-17
lines changed

installer/build_all.ps1

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,5 @@ $fatbin_dir = 'cuda_fatbin'
2626
/Dmodel_cfg=$model_cfg `
2727
/Dfatbin_dir=$fatbin_dir `
2828
/DAPP_NAME=3d-beats `
29-
/DAPP_VERSION=2.1 `
29+
/DAPP_VERSION=2.2 `
3030
/Oinstaller

readme.md

-2
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,6 @@ Now try the full 3d-beats app.
9292

9393
# TODOs:
9494

95-
- velocity:
96-
- option to vary midi notes' velocity with velocity of tap
9795
- ignore fingertips that are not featured prominently / or have high variance from mean shift
9896
- handle when mean shift has identified a pixel which is not part of the depth image (0 depth!)
9997
- auto-tuning of plane / z threshold. should be able to determine best z threshold automatically

src/3d_bz.py

+25-7
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,10 @@ def __init__(self):
5858

5959
self.gauss_sigma = 2.5
6060
self.z_thresh_offset = 25.
61-
self.min_velocity = 5.
61+
self.min_velocity = 10.
62+
63+
self.velocity_sensitive = True
64+
self.max_velocity = 120. # anything w/ higher velocity is clipped at this point
6265

6366
self.group_min_size = 0.06
6467

@@ -313,22 +316,34 @@ def tick(self, _):
313316
# right hand on right
314317
self.hand_states[0].draw_imgui(self.z_thresh_offset, self.dpi_scale, (pos_start_x + (self.width * self.dpi_scale / 2), pos_start_y))
315318

316-
# per finger calibration
317319
imgui.new_line()
320+
pos_start_x, pos_start_y = imgui.get_cursor_pos()
321+
322+
imgui.push_item_width(200)
323+
_, self.min_velocity = imgui.slider_float('min velocity', self.min_velocity, 0., 50.)
324+
_, self.velocity_sensitive = imgui.checkbox('velocity sensitive', self.velocity_sensitive)
325+
if self.velocity_sensitive:
326+
_, self.max_velocity = imgui.slider_float('max velocity', self.max_velocity, 50., 200.)
327+
imgui.pop_item_width()
328+
329+
imgui.set_cursor_pos((pos_start_x + 400, pos_start_y))
330+
331+
# per finger calibration
318332
if imgui.button('reset fingers'):
319333
for h in self.hand_states:
320334
for f, t in zip(h.fingertips, self.DEFAULT_FINGERTIP_THRESHOLDS):
321335
f.z_thresh = t
322336

323-
imgui.same_line()
337+
imgui.set_cursor_pos((pos_start_x + 400, imgui.get_cursor_pos()[1]))
324338
self.calibrate_next_frame = imgui.button('recalibrate plane')
325339

326-
imgui.new_line()
340+
imgui.set_cursor_pos((pos_start_x + 400, imgui.get_cursor_pos()[1]))
341+
imgui.push_item_width(200)
327342
self.midi.draw_imgui()
343+
imgui.pop_item_width()
328344

329345
imgui.end()
330346

331-
332347
imgui.set_next_window_position(0, (self.DIM_Y + 220) * self.dpi_scale)
333348
imgui.set_next_window_size(400 * self.dpi_scale, 124 * self.dpi_scale)
334349
imgui.set_next_window_bg_alpha(0.3)
@@ -337,7 +352,6 @@ def tick(self, _):
337352
imgui.push_item_width(150. * self.dpi_scale)
338353
_, self.PLANE_Z_OUTLIER_THRESHOLD = imgui.slider_float('plane threshold', self.PLANE_Z_OUTLIER_THRESHOLD, 0., 100.)
339354
_, self.z_thresh_offset = imgui.slider_float('finger threshold offset', self.z_thresh_offset, 0., 100.)
340-
_, self.min_velocity = imgui.slider_float('min velocity', self.min_velocity, 0., 100.)
341355
imgui.pop_item_width()
342356

343357

@@ -491,6 +505,10 @@ def run_per_hand_pipeline(self, g_id, flip_x):
491505

492506
for i, f_idx in zip(range(len(self.fingertip_idxes)), self.fingertip_idxes):
493507

508+
hand_state.fingertips[i].velocity_sensitive = self.velocity_sensitive
509+
hand_state.fingertips[i].min_velocity = self.min_velocity
510+
hand_state.fingertips[i].max_velocity = self.max_velocity
511+
494512
px, py = label_means[f_idx-1].astype(np.int32)
495513
px *= self.LABELS_REDUCE
496514
py *= self.LABELS_REDUCE
@@ -504,7 +522,7 @@ def run_per_hand_pipeline(self, g_id, flip_x):
504522
pt.append(1.)
505523
pt = self.calibrated_plane.plane @ pt
506524
pt_z = -pt[2]
507-
hand_state.fingertips[i].next_z_pos(pt_z, self.z_thresh_offset, self.min_velocity)
525+
hand_state.fingertips[i].next_z_pos(pt_z, self.z_thresh_offset)
508526

509527
if __name__ == '__main__':
510528
run_app(App_3d_bz)

src/hand_state.py

+21-7
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,21 @@ def __init__(self, on_fn, off_fn, num_positions = 40, z_thresh = 150, midi_note
1515
self.midi_note = midi_note
1616
self.note_on = False
1717

18-
self.calibrate_alpha = 0.05
18+
self.calibrate_alpha = 0.1
19+
20+
self.min_velocity = 15.
21+
self.velocity_sensitive = True
22+
self.max_velocity = 150.
23+
24+
self.min_midi_velocity = 0.4 # out of 1
1925

2026
def reset_positions(self):
2127
# turn off note if on
2228
self.positions = [0 for _ in range(self.num_positions)]
2329
self.set_midi_state(False)
2430
pass
2531

26-
def next_z_pos(self, z_pos, z_thresh_offset, min_velocity):
32+
def next_z_pos(self, z_pos, z_thresh_offset):
2733

2834
self.positions.append(z_pos)
2935
while len(self.positions) > self.num_positions:
@@ -33,18 +39,26 @@ def next_z_pos(self, z_pos, z_thresh_offset, min_velocity):
3339
# z_pos = self.positions[-1]
3440
# must be located below 'on surface' threshold, and have downward velocity above threshold to be a note
3541
if z_pos < (self.z_thresh + z_thresh_offset):
36-
if np.all(-np.diff(self.positions)[-2:] > min_velocity):
37-
self.set_midi_state(True)
42+
last_2_velocities = -np.diff(self.positions)[-2:]
43+
if np.all(last_2_velocities > self.min_velocity):
44+
if self.velocity_sensitive:
45+
v = (np.sum(last_2_velocities) / 2) / (self.max_velocity - self.min_velocity)
46+
v = self.min_midi_velocity + (v * (1 - self.min_midi_velocity))
47+
if v > 1:
48+
v = 1
49+
else:
50+
v = 1.
51+
self.set_midi_state(True, v)
3852
else:
39-
self.set_midi_state(False)
53+
self.set_midi_state(False, 0)
4054

4155
if self.note_on:
4256
self.on_positions.append(z_pos)
4357

44-
def set_midi_state(self, s):
58+
def set_midi_state(self, s, vel):
4559
if s and not self.note_on:
4660
self.note_on = True
47-
self.on_fn(self.midi_note, 127) # todo: velocity!
61+
self.on_fn(self.midi_note, int(vel * 127))
4862
self.on_positions.clear()
4963

5064
elif not s and self.note_on:

0 commit comments

Comments
 (0)