summaryrefslogtreecommitdiffstats
path: root/theremin.m
diff options
context:
space:
mode:
Diffstat (limited to 'theremin.m')
-rw-r--r--theremin.m162
1 files changed, 162 insertions, 0 deletions
diff --git a/theremin.m b/theremin.m
new file mode 100644
index 0000000..22f9991
--- /dev/null
+++ b/theremin.m
@@ -0,0 +1,162 @@
+clear;
+
+%%%%%%%%
+% "constants" %
+%%%%%%%%
+
+% how many updates per second, determines the length of audio snippets
+const_frames_per_second = 100;
+
+const_Fs = 44100; % sampling rate in Hz
+const_te = 1/const_frames_per_second; % signal duration in seconds
+const_samples_per_frame = ceil(const_Fs * const_te);
+const_sample_range = 0:const_samples_per_frame-1;
+
+% factor the last read frame is multiplied by
+const_fade_speed = 0.975;
+
+% signal "generator"
+signal = @(freq) (freq ./ const_Fs .* 2 .* pi .* const_sample_range);
+
+% dimensions to use for detecting number of hands in frame
+zero_hands = size(NaN(0,0));
+one_hand = size(NaN(1,1));
+two_hands = size(NaN(1,2));
+
+deviceWriter = audioDeviceWriter('SampleRate', const_Fs, 'SupportVariableSizeInput', true, 'BufferSize', 3 * const_samples_per_frame);
+
+%%%%%%%%%%%%%%%%%%%
+% init matleap by calling for first frame %
+%%%%%%%%%%%%%%%%%%%
+matleap_frame;
+
+% runtime variables
+P = NaN(1000000,3);
+count = 1;
+done = false;
+
+frequency_pos = 0;
+height_pos = 0;
+
+offset_one = 0;
+offset_two = 0;
+
+signal_one = sin(signal(0));
+signal_two = sin(signal(0));
+
+full_signal = signal_one + signal_two;
+complete_signal = full_signal;
+
+% time gestures, used for program termination
+gesture_count = 0;
+
+while gesture_count < const_frames_per_second
+ frame = matleap_frame;
+ handCount = size(frame.hands);
+
+ % slowly decrease volume
+ height_pos = height_pos * const_fade_speed;
+
+ if isequal(one_hand, handCount)
+ %pos = frame.hands(1).palm.position;
+ pos = frame.hands(1).palm.stabilized_position;
+
+ frequency_pos = pos(1);
+ height_pos = max(150, height_pos);
+
+ P(count, 1:3) = pos;
+ count = count + 1;
+
+ if frame.gesture > 0
+ gesture_count = gesture_count + 1;
+ else
+ gesture_count = 0;
+ end
+ elseif isequal(two_hands, handCount)
+ gesture_count = 0;
+
+ %pos = frame.hands(1).palm.position;
+ pos = frame.hands(1).palm.stabilized_position;
+
+ frequency_pos = pos(1);
+ %y_one = pos(2);
+
+ P(count, 1:3) = pos;
+ count = count + 1;
+
+ %pos = frame.hands(2).palm.position;
+ pos = frame.hands(2).palm.stabilized_position;
+
+ %x_two = pos(1);
+ height_pos = pos(2);
+ else
+ % no hands, do nothing (or more than 2 (how?! :D))
+ gesture_count = 0;
+ end
+
+ % play current sound
+ [signal_one, offset_one] = get_theremin_sound_bit(frequency_pos, height_pos, offset_one, signal);
+ %[signal_two, offset_two] = get_theremin_sound_bit(x_two, y_two, offset_two, signal);
+ full_signal = signal_one;% + signal_two;
+
+ buffer_under_flow = deviceWriter(full_signal(:));
+
+ if buffer_under_flow ~= 0
+ disp("Buffer ran empty!");
+ end
+
+ complete_signal = [complete_signal full_signal];
+end
+
+release(deviceWriter)
+
+%theremin_player = audioplayer(complete_signal, const_Fs);
+%play(theremin_player);
+
+% extract values
+x = P(:,1); % links (-) rechts (+) (LED zu uns)
+y = P(:,2); % oben unten
+z = P(:,3); % vorne (+) hinten (-) (LED zu uns)
+
+% plot
+figure("Position",[0,0, 1200, 2400]);
+t = tiledlayout(4,1);
+
+nexttile;
+plot(x);
+ylabel('left right');
+
+nexttile;
+plot(y);
+ylabel('height');
+
+nexttile;
+plot(z);
+ylabel('depth');
+
+nexttile;
+plot3(z,x,y);
+xlabel('depth');
+ylabel('left right');
+zlabel('height');
+
+function [sound,offset] = get_theremin_sound_bit(x, y, offset, generator)
+ % finding ranges for x and y:
+ %'x'
+ %min(x)
+ %max(x)
+ %'y'
+ %min(y)
+ %max(y)
+ % --> 50 < x < 270
+ % --> 90 < y < 350
+
+ volume = (y) / 1300;
+ frequency = max(0.003, (x - 50) / 220) * 1000; % https://web.physics.ucsb.edu/~lecturedemonstrations/Composer/Pages/60.17.html # How it works
+
+ base = generator(frequency) + offset;
+
+ offset = base(end);
+
+ sound = sin(base) .* volume;
+end \ No newline at end of file