diff options
Diffstat (limited to 'theremin.m')
-rw-r--r-- | theremin.m | 162 |
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 |