Попробуем заняться довольно бессмысленным занятием, а именно получением звука с определенной спектрограммой. Вдруг ряды любителей ЭГФ сократятся, не в обиду фанатам фильма «Белый шум» 🙂
Итак, начнем с моделирования на Matlab. Хотим алгоритм, позволяющий переводить звук в картинку-спектрограмму и обратно. Логично будет начать с вычисления спектрограммы, по которой будет вычисляться изображение.
FFTLEN = 2048;%длина преобразования Фурье FFTLEN2 = FFTLEN/2 + 1; FRAME_LEN = 256;%длина кадра SHIFT = FRAME_LEN/2;%смещение кадров
function spectrogram = spectrogrambywave(wavname, FRAME_LEN, SHIFT,FFTLEN, FFTLEN2) [wav, Fs, nbits] = wavread(wavname); %% frames = enframe(wav,FRAME_LEN, SHIFT); frame_count = size(frames,1); spectrogram = zeros(frame_count, FFTLEN2); win = hann(FRAME_LEN); for index=1:frame_count x = frames(index,:); x = x.*win'; s = fft(x,FFTLEN); s = abs(s); s = s(1:FFTLEN2); spectrogram(index,: ) = s'; end
Чтобы не делать того, что уже давно сделано за нас другими, здесь используется функция enframe из библиотеки voicebox,предназначенной для решения разнообразных задач при распознавании речи. Функция enframe как раз и позволяет нарезать сигнал на кадры с заданным шагом. Длину кадра мы берем мы берем равным длине преобразования Фурье(FFTLEN= 256), а шаг — половина этого значения(128). Все в лучших традициях 🙂 На каждый кадр накладывается окно Хэннинга для устранения боковых лепестков при вычислении ДПФ. Так как кадры идут с половинным перекрытием, то при наложении окна, сумма весов под перекрывающимися участками равна 1 и мы легко можем восстановить сигнал в половине кадра по двум соседним кадрам.
Для фразы «Почему не стоит пользоваться аутсорсингом в странах с дешевой рабочей силой» получилась следующая спектрограмма:
Исходный звук был такой (с онлайн-плеером для .wav файлов пока не разобрался)
Естественно, что однозначно мы восстановить сигнал не сможем. При получении изображения каждый отсчета спектрограммы берется модулем комплексного значения — теряется информация о фазе. Поэтому, если просто собрать восстановленные половинки, то будут две проблемы:
- Будут четко видны границы кадров(так как все гармоники будут с одинаковой фазой в каждом кадре
- Составляющие с нечетным числом периодов синусоиды не склеятся гладко
Первая проблема пока остается, для решения второй я пытался подкручивать фазу при сборке полукадров — через один полукадр. Делалось это примерно так:
frame_count = size(spectrogram, 1); wavlen = frame_count*SHIFT; %% восстанавливаем index = 1; datarecov = zeros(wavlen, 1); oldx = zeros(1, FRAME_LEN); win1 = linspace(1.0, 0,SHIFT); win2 = linspace(0, 1.0, SHIFT); for frame_index=1:frame_count scolumn = spectrogram(frame_index,: ); scolumn(1) = 0; %% %четные индексы соответствуют нечетному числу периодов for j=2:2:length(scolumn) if ( mod(frame_index, 2)==1) p = FRAME_LEN / (j-1) * pi*4; val = scolumn(j) * exp(1i*p); scolumn(j) = val; end end scolumn = [scolumn conj((scolumn((end-1):-1:2)))]; %#ok<agrow> %% %% x = real(ifft(scolumn, FFTLEN)); x = x(1:FRAME_LEN); % складываем конец предыдущего с началом следующего subplot(3,1,1), plot(oldx); subplot(3,1,2), plot(x); halfpart = oldx(SHIFT+1:end).*win1 ; halfpart = halfpart + x(1:SHIFT).*win2; subplot(3,1,3), plot(halfpart); datarecov(index:index+ SHIFT-1) = halfpart; index = index + SHIFT; oldx = x; end
После восстановления, получилось вот что
И теперь осталось только написать небольшую функцию получения спектрограммы из картинки. Изображение превращаем в черно-белое, а затем масштабируем до нужного размера по высоте.
function spectrogram = spectrogrambyimage(imname, FFTLEN2) image = rgb2gray(imread(imname)); [height width] = size(image); newwidth = ceil(double( width )/ height*FFTLEN2); image = imresize(image, [FFTLEN2 newwidth]); % imshow(image); image= double(image); image = image'; image = image(:,end:-1:1); image = image./max(max(image)); image = 1-image; spectrogram = image;
Немножко сжульничал, строчкой «image = 1-image;» сделав негативное изображение, чтобы наш мишка выглядел яркими пятнами на темном, а не наоборот. Иначе — было бы слишком много шума.
Полученный звук, естественно, для сохранения психического равновесия слушать категорически не рекомендуется… Желающие могут поподбирать картинку, чтобы получить что-нибудь похожее на транс-музыку от Майкрософта.
Исходный код и все необходимое из статьи можно скачать отсюда.
Готовые программы для подобных упражнений:
Как решить такую задачу:
звук- голос, преобразовать в картинку, а затем через амплитуду картинки пропустить другой голос, чтобы на выходе голос второго превратился в интонацию первого! Это реально? И какой программой это можно осуществить?
Действительно ли вам нужна картинка для этой задачи?