Pull to refresh

Еще один способ разложения сигнала в спектр

Reading time 3 min
Views 12K
Привет всем, здесь я хочу рассказать про алгоритм анализа звукового сигнала, позволяющий разобрать сигнал на отдельные волны, конечно 100% точности он не дает, но тем не менее результат на мой взгляд довольно неплох.

image

Лучше всего видна работа на какой-нибудь музыке:


И ссылки на другие примеры разных жанров. Metaldeth-Tornado of souls:


Out of Touch:


Итак для разложения нужно сделать следующие шаги:

— Из исходного сигнала нужно получить 8 промежуточных сигналов;
— Из этих промежуточных сигналов и исходного сигнала нужно получить 8 сигналов — слоев, которые можно будет разобрать на отдельные волны;
— Посчитать сколько в каждом слое волн и какая у них амплитуда.

Теперь подробнее о каждом этапе: для того чтобы получить промежуточный сигнал нужно взять производную от исходного сигнала. По сути это производная дискретной функции. Чтобы найти ее для каждого момента исходного сигнала нужно задать 1 параметр: период за который эта производная находится. Значение производной это коэффициент наклона в заданном интервале, можно найти например методом наименьших квадратов.

Требуется вычислить 8 промежуточных сигналов с 8 разными периодами. Самый простой набор периодов: 4, 8, 16, 32, 64, 128, 256, 512. Когда период задан, для каждого отсчета сигнала вычисляется производная по формуле наименьших квадратов. Это как скользящее среднее, только здесь не скользящее среднее а скользящая производная текущего интервала.

Таким образом получается 8 производных сигналов и 1 исходный. Теперь каждый из 8 производных сигналов нужно проинтегрировать. В данном случае это значит что каждый следующий семпл равен сумме всех предыдущих семплов. После этого получается 8 промежуточных слоев.

Следующий шаг — получение слоев, которые можно будет разобрать на отдельные волны. Итак теперь нужно получить 8 слоев. Слои вычисляются так:

слой0=промежуточный0-исх сигнал
слой1=промежуточный1-промежуточный0
слой2=промежуточный2-промежуточный1
слой3=промежуточный3-промежуточный2
слой4=промежуточный4-промежуточный3
слой5=промежуточный5-промежуточный4
слой6=промежуточный6-промежуточный5
слой7=промежуточный7-промежуточный6
слой8=промежуточный7

Последний слой является не разницей а просто равен последнему промежуточному сигналу.

Можно попробовать и по другому, а именно вычислять последующие промежуточные сигналы из предыдущих промежуточных. Но в текущей программе используется 1 вариант.

Теперь чтобы разобрать слои на отдельные волны нужно просто посчитать участки где значения увеличиваются и где они уменьшаются. Собственно длительность участков и есть их длины волн. Участки сигнала где значения сигнала постоянны нужно просто пропустить. Чтобы найти амплитуду сигнала в спектре в некотором интервале, нужно сложить все амплитуды волн умноженные на их длину.

image

Код который вычисляет промежуточный сигнал выглядит вот так:

здесь wavesize – число семплов
signal[] – массив с исходным сигналом
SY=0,SX=0,SXX=0,SXY=0,Ky=0 — переменные типа float
Step2=STEP/2 где STEP это период (4,8,16,32,64,128,256,512)

for(int i=Step2;i<wavesize-Step2;i++){
	SY=0,SX=0,SXX=0,SXY=0,Ky=0;

	for(int j=i-Step2,fromZ=0;j<i+Step2;j++,fromZ++){
		SX+=fromZ;
		SY+=signal[j];
		SXX+=fromZ*fromZ;
		SXY+=fromZ*signal[j];
	}
	Ky=float((STEP)*SXY-SX*SY)/float((STEP)*SXX-SX*SX);
		
	OutSignal[i]=OutSignal[i-1]+Ky;	
}

Чтобы вычесть один сигнал из другого достаточно просто вычесть каждый семпл из каждого.
Например для 0 слоя:

for(int i=0;i<wavesize-1;i++)		
 layer0[i]=OutSignal0[i]-Signal[i];

Если сложить все слои, и взять последний с обратным знаком то получится исходный сигнал, таким образом умножая какой-нибудь слой, возможно сделать фильтр частот. Далее вопрос заключается в том как считать амплитуды конкретных гармоник. Дело в том что в постоянном интервале, например = 4000 семплов, может быть очень много коротких волн и сравнительно мало длинных.

Можно конечно найти средние амплитуды для каждого слоя и сложить. Но этот способ не очень хорош так как длинных волн очень мало, а их амплитуда обычно очень большая, и получается сильная неравномерность амплитуды в сторону низких частот.

В программе, которая отображает цветомузыку по ссылкам, амплитуда каждой гармоники подсчитывается как: амплитуда волны*ее длину. Все равно возникает неравномерность, но не такая сильная как в случае с усреднением.

Вообще, не думаю что человек воспринимает звук как разложение в спектр, скорее звук состоит из звуковых образов, которые состоят из волн разной длинны. Соответственно громкость какого либо звука это скорее средняя громкость всех волн из которых звук состоит. Но пока что не понятно какие параметры составляют звуковой образ, возможно средняя частота, стандартное отклонение или что то еще.
Tags:
Hubs:
+9
Comments 42
Comments Comments 42

Articles