- PVSM.RU - https://www.pvsm.ru -

Цифровой терменвокс на базе Leap Motion (powered by .NET)

С момента появления сенсора Kinect for Windows я питал тихую страсть [1] к программированию такого рода устройств – ведь это волшебное чувство, когда написанная тобой программа отзывается на движения человека на расстоянии! Сегодня я расскажу вам про программирование ещё одного похожего устройства – Leap Motion [2].

Цифровой терменвокс на базе Leap Motion (powered by .NET) Цифровой терменвокс на базе Leap Motion (powered by .NET)

По сути дела, Leap Motion – это небольшой Kinect, который может распознавать положение кистей рук. По замыслу создателей (хорошо отраженных в этом видео [3]), его можно использовать для управления компьютером во множестве различных сценариев. В этом посте мы рассмотрим, как программируется Leap Motion (на платформе .NET), и как можно на его основе создать простой музыкальный инструмент – терменвокс [4]. Вот такой:


Leap Motion для программиста

С точки зрения программиста, Leap Motion предоставляет набор удобных API (документированных тут [5]), которые позволяют:

  • С высокой частотой (более, чем 60-100 кадров в секунду) получать модель пространства, включающую в себя координаты и скорости пальцев рук, нормали ладоней, положения и скорости различных “инструментов” (Leap Motion хорошо распознает карандаши и авторучки)
  • Распознавать основные жесты: окружность, swipe, нажатие
  • Эмулировать touch surface, т.е. касания некоторой виртуальной поверхности перед экраном
  • Распознавать основные движения рук и пальцев (motions), транслируя их в преобразования поворота, перемещения или масштабирования.

В отличие от Kinect, Leap Motion не предоставляет доступа к 3D-данным, полученным с камеры.

Программировать Leap Motion можно на различных языках и платформах, в том числе .NET. В этой статье мы рассмотрим использование первой возможности – доступа к модели руки.

Отслеживаем простейшие движения

Для наших экспериментов в Visual Studio [6] создадим пустой WPF-проект, и добавим на основной экран цветную окружность. Нашим первым шагом будет научиться перемещать эту окружность по экрану движениями пальца. Итак, разметка основной странички (XAML) будет выглядеть так:

WPF-разметка страницы MainPage.xaml

<Window x:Class="LeapVoxSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="768" Width="1024" Background="Black">
    <Canvas>
        <Ellipse x:Name="ptr" Fill="Yellow" Height="30" Width="30"/>
    </Canvas>
</Window>

Для использования Leap Motion необходимо скачать Leap Motion SDK [7] и подключить к проекту ссылку на LeapCSharp.NET4.0.dll. Однако это еще не всё – Leap SDK требует также библиотек Leap.dll и LeapCSharp.dll, которые должны находится в одной директории с выполняемым файлом проекта. Поэтому найдите эти файлы в директории LeapSDKlibx86 (в той директории, где установлен Leap SDK), перетащите их в проект Visual Studio и установите свойства “действия при компиляции = контент, копировать в выходную директорию = копировать, если новее”.

Цифровой терменвокс на базе Leap Motion (powered by .NET) Цифровой терменвокс на базе Leap Motion (powered by .NET)

Все общение с контроллером Leap ведется через объект Controller. Создадим такой объект в нашем основном файле MainWindows.xaml.cs:

Controller Leap = new Controller();

Теперь Leap Motion готов предоставлять нам кадры с данными о положении рук. Получать кадры можно двумя способами:

  • Передать в объект Controller экземпляр класса Listener, в котором будет перегружен метод OnFrame. Этот метод будет автоматически вызываться Leap Motion SDK с максимально возможной частотой кадров.
  • Создать свой метод, который будет периодически опрашивать класс Controller и получать с него текущий кадр. Так следует поступать, есть в программе уже есть естественный цикл, или если мы хотим выполнять код в UI-потоке выполнения.

В нашем случае для создания цикла опроса конроллера используем DispatcherTimer, который будет срабатывать 30 раз в секунду:

Организация цикла опроса контроллера

DispatcherTimer dt = new DispatcherTimer() 
    { 
        Interval = TimeSpan.FromSeconds(1 / 30)
    };

public MainWindow()
{
    InitializeComponent();
    dt.Tick += dt_Tick;
    dt.Start();
}

В методе dt_Tick будет сосредоточена основная логика отслеживания движений. Мы получаем текущий кадр (Frame), и если в этом кадре видны пальцы – берем первый попавшийся их них и используем его координаты, чтобы изменить положение окружности:

void dt_Tick(object sender, EventArgs e)
{
    var fr = Leap.Frame();
    if (fr!=null && fr.Fingers.Count>0)
    {
        var f = fr.Fingers[0];
        Canvas.SetLeft(ptr, 512 + f.TipPosition.x);
        Canvas.SetTop(ptr, 768-f.TipPosition.y);
    }
}

Мы добились того, что можем перемещать кружок по экрану, передвигая палец над контроллером Leap Motion. Пример кода на данном этапе можно посмотреть на GitHub [8].

Программная генерация звука различной высоты

Для программной генерации звука различной высоты мы используем библиотеку NAudio. Простейший способ добавить поддержку NAudio в проект – использовать Nuget, набрав в консоли диспетчера пакетов:

install-package NAudio

Процесс генерации звука переменной высоты подробно описан в статье Чарльза Петцольда на MSDN [9]. Если коротко – мы создаем класс, который генерирует синусоидальную волну звука, и передаем её на вход методу NAudio, который эту волну воспроизводит. При этом есть ряд тонкостей: чтобы звук менялся более плавно, необходимо плавно менять частоту звука, и при этом учитывать задержку буферизации аудио. Все эти тонкости учтены в классе PortamentoSineWaveOscillator.cs [10], который необходимо поместить в проект.

Для генерации звука мы создаем объект типа WaveOut и инициализируем его, передавая экземпляр созданного нами осциллятора:

WaveOut WaveGen = new WaveOut();
PortamentoSineWaveOscillator Osc = new PortamentoSineWaveOscillator(44100,120);

Инициалиацию мы проводим в конструкторе страницы:

WaveGen.Init(Osc);
WaveGen.Play();

Теперь нам достаточно изменять свойство Osc.Pitch, чтобы соответствующим образом менялась высота звука, и Osc.Amplitude для изменения громкости. Пускай горизонтальные движения пальцев отвечают за громкость, а вертикальные – за высоту звука. Коэффициенты подбираются опытным путем для достижения кофортного звучания:

void dt_Tick(object sender, EventArgs e)
{
    var fr = Leap.Frame();
    if (fr!=null && fr.Fingers.Count>0)
    {
        var f = fr.Fingers[0];
        ...
        // Меняем высоту звука
        var p = Math.Abs(f.TipPosition.y / 2);
        var a = 255 - Math.Abs(f.TipPosition.x);
        if (p >= 0 && p <= 150) Osc.Pitch = p;
        if (a >= 0 && a <= 255) Osc.Amplitude = (short)a;
    }
}

Версию проекта на этой стадии можно получить тут [11].

Последние штрихи

Чтобы сделать проект чуть более красивым, вместо обычного круга, перемещаемого по экрану, можно добавить спец.эффекты на основе системы частиц (Particle Systems). Я взял за основу вот эту статью на CodeProject [12], где приводится исходный код WPF-контрола, представляющего собой источник частиц. Заменив им окружность, добавленную на первом шаге, мы получим финальный проект, исходный код которого доступен на GitHub [13].

Домашнее задание

Если вы захотите продолжить эксперименты, то простор для творчества практически неограничен! В частности, можно:

  • Добавить поддержку многоголосия и управления сразу несколькими пальцами
  • Экспериментировать с другими способами управления высотой звука, например, брать за основу не положение пальца, а его скорость
  • Добавить поддержку жестов для извлечения специальных звуков (сэмплов)

Если вас будет интерес к этой теме (пишите в комментариях), то я продолжу серию статей, добавляя различные дополнительные интересности, связанные с Leap Motion, Kinect и неожиданным использованием мобильных устройств. О чем вам бы хотелось услышать? Например, в этом видео показано, что ещё можно сделать с помощью Leap Motion за пару часов:

Автор: shwars

Источник [14]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/net/56534

Ссылки в тексте:

[1] питал тихую страсть: http://www.techdays.ru/videos/7330.html

[2] Leap Motion: https://www.leapmotion.com/

[3] этом видео: http://www.youtube.com/watch?v=_d6KuiuteIA

[4] терменвокс: http://ru.wikipedia.org/wiki/%D0%A2%D0%B5%D1%80%D0%BC%D0%B5%D0%BD%D0%B2%D0%BE%D0%BA%D1%81

[5] документированных тут: https://developer.leapmotion.com/documentation/csharp/index.html

[6] Visual Studio: http://www.visualstudio.com/downloads/download-visual-studio-vs

[7] скачать Leap Motion SDK: https://developer.leapmotion.com/downloads

[8] посмотреть на GitHub: https://github.com/shwars/LeapVoxSample/tree/Step2

[9] описан в статье Чарльза Петцольда на MSDN: http://msdn.microsoft.com/ru-ru/magazine/ee309883.aspx

[10] PortamentoSineWaveOscillator.cs: https://github.com/shwars/LeapVoxSample/blob/Step3/LeapVoxSample/PortamentoSineWaveOscillator.cs

[11] получить тут: https://github.com/shwars/LeapVoxSample/tree/Step3

[12] вот эту статью на CodeProject: http://www.codeproject.com/Articles/21010/Particle-Effects-in-WPF

[13] доступен на GitHub: https://github.com/shwars/LeapVox/tree/v-1.0

[14] Источник: http://habrahabr.ru/post/214907/