Превращаем картинку в звук

Попробуем заняться довольно бессмысленным занятием, а именно получением звука с определенной  спектрограммой. Вдруг ряды любителей  ЭГФ сократятся, не в обиду фанатам фильма «Белый шум» 🙂
Итак,  начнем с моделирования на 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 файлов пока не разобрался)

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

  1. Будут четко видны границы кадров(так как все гармоники будут с одинаковой фазой в каждом кадре
  2. Составляющие с нечетным числом периодов синусоиды не склеятся гладко

Первая проблема пока остается, для решения второй я пытался подкручивать фазу при сборке полукадров — через один полукадр. Делалось это примерно так:


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;» сделав негативное изображение, чтобы наш мишка выглядел яркими пятнами на темном, а не наоборот. Иначе — было бы слишком много шума.

Полученный звук, естественно, для сохранения психического равновесия слушать категорически не рекомендуется… Желающие могут поподбирать картинку, чтобы получить что-нибудь похожее на транс-музыку от Майкрософта.

Исходный код и все необходимое из статьи можно скачать отсюда.

Готовые программы для подобных упражнений:

  • ARSS (Analysis & Resynthesis Sound Spectrograph)
  • Coagula

Комментарии:

Превращаем картинку в звук: 2 комментария

  1. Как решить такую задачу:
    звук- голос, преобразовать в картинку, а затем через амплитуду картинки пропустить другой голос, чтобы на выходе голос второго превратился в интонацию первого! Это реально? И какой программой это можно осуществить?

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *