Про настройку и визуализацию музыки с помощью openFrameworks мы недавно рассказывали. К сожалению русскоязычной информации по фреймворку OpenFrameworks достаточно мало. Для заполнения этого вакуума — начинаем серию публикаций лекций, что были прочитаны в Екатеринбурге в Институте математики и механики им. Н.Н.Красовского (УрО РАН) Денисом Переваловым.
В этой лекции будут рассказаны теоретические основы цифрового звука, и показан пример создания интерактивного приложения по генерации звука на базе захвата изображения с камеры.
Что такое цифровой звук, и звук вообще?

Звук, в широком смысле — упругие волны, продольно распространяющиеся в среде и создающие в ней механические колебания;
в узком смысле — субъективное восприятие этих колебаний специальными органами чувств животных или человека. Как и любая волна, звук характеризуется амплитудой и частотой.
Представление звука в цифровом виде
Реальный звук захватывается микрофоном, затем подвергается аналого-цифровому преобразованию.
Оно характеризуется
разрешением по времени — частота дискретизации, [процедура — дискретизация]
разрешением по амплитуде — разрядность. [процедура — квантование]

Частота дискретизации
8 000 Гц — телефон, достаточно для речи.
11 025 Гц — игры, сэмплы для электронной музыки.
22 050 Гц — то же, что и 11 025 Гц.
44 100 Гц — многие синтезаторы и библиотеки сэмплов. Audio CD.
48 000 Гц — студии звукозаписи, живые инструменты, вокал. DVD.
96 000 Гц — DVD-Audio (MLP 5.1).
192 000 Гц — DVD-Audio (MLP 2.0).
Разрядность
Разрядность — число бит, используемых для представления отсчетов сигнала при квантовании (в нашем случае — при квантовании амплитуды).
8 бит сэмплы электронной музыки.
12 бит студийные звуковые эффекты.
16 бит компьютерные игры, плееры, сэмплы, Audio CD.18 бит студийные звуковые эффекты
24 бит живые звуки, вокал, DVD-Audio
32 бит представление с плавающей точкой, поэтому точность не теряется для тихих звуков, поэтому используется для внутренней обработки звука.
64 бит также с плавающей точкой, обработка звука.
Представление звука в памяти
Пример
1 секунду 16-битного звука с частотой дискретизации 44100 Гц можно представить в виде вектора
X = ( x _1, x _2, ..., ..., x _44100),
где 0 <= x _i <= 2^16-1 = 65535.
Представление звуков таких способом — с помощью вектора — называется PCM (Pulse Code Modulation).
Оно является наиболее распространенным и аналогично пиксельному представлению изображений.
Фундаментальное различие звука и изображений
С изображениями очень удобно работать на уровне пикселов. В частности:
1. два изображения мы считаем одинаковыми, если значения их пикселов близки.
2. можно изменять изображения, основываясь назначениях соседних пикселов (например, операция сглаживания).
Для звука в PCM формате обе возможности неприменимы, покажем это на примере:

Последние два звука звучат одинаково. А их функции амплитуды — существенно различные. Таким образом, человеческое ухо воспринимает спектр звука, то есть состав его частот, а не амплитудное представление звука.
Что легко/трудно делать «прямо» со звуком в формате PCM
Легко:
Изменение и перестановка отдельных отсчетов, без учета соседей
— переставлять кусочки,
— менять громкость кусочков,
— делать реверс — переворот звука от конца к началу,
— смешивать несколько звуков,
— смешивать и менять стерео-каналы,
— делать простейшую компрессию,
— добавлять простейшее эхо.
Сэмплеры, портастудии и студийные программы делают это виртуозно.
Трудно:
Учет соседних отсчетов
— сравнивать два звука на похожесть,
— подавлять низкие или высокие частоты,
— добавлять реверберацию.
Это обычно делается не прямо в PCM, а через спектральное представление звука (оконное преобразование Фурье).
Форматы хранения звука
WAV
wav = заголовок + байты PCMХранит звук без потери качества
(аналог в изображениях — bmp)
MP3
Данные с потерями, хорошо подходит для хранения музыки.
(аналог в изображениях — jpg)
AMR
Данные с потерями, предназначен для хранения речи.Используется в мобильной телефонии (2011).
(аналог в изображениях — png)
Способы генерации цифрового звука
Есть следующие способы построения PCM-представления некоторого звука или музыки:
1. Сэмплирование
Используется для производства всей музыки. Устройства — сэмплеры
2. (Субтрактивный) Синтез
Используется преимущественно для современной электронной музыки. Устройства — синтезаторы.
3. FM-синтез
4. Аддитивный синтез
5. Гранулярный синтез
6. S&S — Sample & Synthesis — сэмплирование, анализ, последующий синтез — сегодня одна из наиболее лучших технологий воспроизведения«живых» инструментов.
Рассмотри три способа генерации звука: сэмплирование, субтрактивный и аддитивный синтез.
Сэмплирование
Запись: «Живой звук» — микрофон — АЦП — PCM-формат.
Воспроизведение: PCM-формат — ЦАП — динамик.
Дополнительные возможности: можно менять скорость воспроизведения, тогда повысится тон и скорость сэмпла.
Современные алгоритмы также позволяют менять тон сэмпла без изменения его скорости, и наоборот.
Сэмплер Akai MPC1000:

Субтрактивный Синтез
В докомпьютерное время:
несколько простых волн (прямоугольная, синусоидальная, треугольная) обрабатывались набором фильтров (НЧ, ВЧ, вырезание нужной частоты). Полученный звук шел на динамики.
Сейчас:
делается в цифровом виде.
Есть трудности — нужно аккуратно учитывать известные проблемы, связанные с цифровым представлением звука («алиасинг»).
Синтезатор Minimoog:

Аддитивный синтез
Аддитивный синтез основан на построении звука с помощью суммирования множества гармоник (т.е. синусоид разной частоты) с изменяющейся громкостью.
Любой звук можно представить с произвольной точностью как сумму большого числа гармоник с меняющейся громкостью. Но на практике работа с большим числом гармоник требует больших вычислительных ресурсов. Хотя, в настоящее время существует несколько аппаратных и программных аддитивных синтезаторов.
Примеры проектов на базе openFrameworks
Про установку, настройку фреймворка, и IDE для сборки проектов, можно прочитать здесь.
Воспроизведение сэмплов в openFrameworks — проект «звуковой ландшафт»
Суть проекта: пользователь тыкает мышью в разные части экрана и начинает доноситься некоторый звук

//Объявление переменных ofSoundPlayer sample; //проигрыватель сэмпла ofPoint p; //точка и радиус - для рисования кружка float rad; void testApp::setup(){ sample.loadSound("sound.wav"); //Загрузка сэмпла из папки bin/data sample.setVolume(0.5f); //громкость, [0, 1] sample.setMultiPlay(true); //разрешаем запускать несколько сэмплов ofSetFrameRate( 60 ); //скорость рисования кадров ofSetBackgroundAuto( false ); //выключаем стирание фона ofBackground(255,255,255); } void testApp::update(){ ofSoundUpdate(); //обновляем состояние звуковой системы } void testApp::draw(){ //если звук играет, рисовать прозрачный кружок ofEnableAlphaBlending(); if (sample.getIsPlaying()) { //случайный цвет ofSetColor(ofRandom(0, 255), ofRandom(0, 255), ofRandom(0, 255), 20); ofCircle( p.x, p.y, rad ); } ofDisableAlphaBlending(); } //нажата мышь void testApp::mousePressed(int x, int y, int button){ float h = ofGetHeight(); //высота экрана //вычисляем желаемую скорость воспроизведения сэмпла, //при этом 1.0 - это оригинальная скорость сэмпла float speed = (h - y) / h * 3.0; if ( speed > 0 ) { sample.play(); //запуск нового сэмпла sample.setSpeed( speed ); //установка скорости воспроизведения //запоминаем точку и радиус кружка для рисования p = ofPoint( x, y ); rad = (3 - speed); rad = 20 * rad * rad; } }
Сценарий проекта«Аддитивный синтезатор»
Суть проекта: пользователь на белом фоне машет руками перед камерой. Имеется n гармоник. Экран разделен на n
вертикальных полосок, в каждой считается число пикселов, яркость которых меньше некоторого порога. Это число определяет громкость соответствующих гармоник.
Используем n = 20 синусоидальных гармоник, с частотами
100 Гц,
200 Гц,
…
2000 Гц
Гармоники играются с помощью зацикленных сэмплов, у которых просто меняется громкость.
Исходный код проекта:
//Объявляем переменные //видео-граббер для "захвата" видеокадров ofVideoGrabber grabber; int w; //ширина кадра int h; //высота кадра const int n = 20; //число гармоник ofSoundPlayer sample[ n ]; //сэмплы гармоник float volume[ n ]; //громкость гармоник int N[ n ]; //число пикселов, играющих в этой гармонике ofSoundPlayer sampleLoop; //сэмпл барабанной петли //Инициализация void testApp::setup(){ w = 320; h = 240; grabber.initGrabber(w, h); //подключение камеры //загрузка сэмплов гармоник for (int i=0; i<n; i++) { int freq = (i+1) * 100; sample[ i ].loadSound( ofToString(freq) + ".wav"); //файлы называются 100.wav,... sample[ i ].setVolume(0.0); //громкость sample[ i ].setLoop(true); //зацикливаем звук sample[ i ].play(); //запуск звука } } //Обновление состояния void testApp::update(){ grabber.grabFrame(); //захват кадра if (grabber.isFrameNew()){ //если пришел новый кадр for (int i=0; i<n; i++) { volume[i] = 0; N[i] = 0; } //сбрасываем гармоникиun signed char * input = grabber.getPixels(); //пикселы входного изображения for (int y=0; y<h; y++) {for (int x=0; x<w; x++) { //входной пиксел (x, y): int r = input[ 3 * (x + w * y) + 0 ]; int g = input[ 3 * (x + w * y) + 1 ]; int b = input[ 3 * (x + w * y) + 2 ]; int result = (r + g + b > 400 ) ? 0 : 1; //пороговая обработка int i = (x * n / w);//в какую гармонику писать результат volume[ i ] += result; N[ i ]++; } } //устанавливаем новые громкости гармоник for (int i=0; i<n; i++) { if ( N[ i ] > 0 ) { volume[ i ] /= N[ i ]; } //нормируем громкости в [0, 1] sample[ i ].setVolume( volume[ i ] / n ); //громкость. //Делим на n, иначе будет искажение выходного звука } } ofSoundUpdate();//обновляем состояние звуковой системы} //Рисование void testApp::draw() { ofBackground(255,255,255); //задаем цвет фона float w = ofGetWidth(); //высота и ширина экрана float h = ofGetHeight(); ofSetColor( 255, 255, 255 ); //иначе картинка кадра нарисуется неверно grabber.draw(0, 0, w, h); //вывод кадра //рисование гармоник ofEnableAlphaBlending(); //включение прозрачности ofSetColor( 0, 0, 255, 80 ); //синий цвет с непрозрачностью 80 for (int i=0; i<n; i++) { float harmH = volume[i] * h;//высота столбика гармоники i ofRect( i * w / n, h - harmH, w / n, harmH ); } ofDisableAlphaBlending(); //выключение прозрачности }
Вот что получилось:
Если вам интересна тематика интерактивных систем, и openFrameworks в частности — то приглашаем в русскоязычную группу по openFrameworks.
