← strona główna

Sonifikacja interpretowana i wizualizacja liter Pana Tadeusza w postaci video

W tym wypadku bajty nie będą sonifikowane bezpośrednio, ale posłużą jako parametry syntezy dźwięku. Generowane będą proste tony sinusoidalne o częstotliwości równej liczbie kodującej dany znak. Najniższy dźwięk ma częstotliwość 32 Hz, co odpowiada liczbie 32 kodującej spację w systemie ASCII. W przypadku polskich znaków diakrytycznych dwa bajty reprezentujące taki znak są sumowane, stąd polskie znaki słyszalne będą jako dźwięki wyższe od innych. Próbki będą generowane z szybkością 8 dźwięków na sekundę. Do dźwięku dołączona jest prosta interpretacja wizualna. Kolor kwadratu (jego jasność) określa również liczba kodująca znak. W przypadku polskich znaków obraz jest jaśniejszy i kolorowy, a dźwięk przedłużony:

UWAGA! nagranie zawiera niskie częstotliwości (min. 32 Hz):

Kod w języku matlab:

% -------------------------
% Interpretowanie znaków tekstu jako danych sterująctych w procedurze generatywnej.
% W rezultacie: 8 dźwięków na sekundę i klatki wideo o rónych stopniach szarości
% % polskie znaki mają wyższe częstotliwości a obraz zyskuje kolor
% -------------------------
% ustawienia
text_file_name = 'PanTadeusz-Unicode.txt';
frameRate = 32;
videoQuality = 95;
sound_len_sec = .125;
fs = 44100;
sound_num = 1000;

% wyniki
outFolder = "konf"; outId = randi([1, 1000000]);
outVideo = char(join([outFolder, '/', num2str(outId), '.avi'], ''));
outAudio = char(join([outFolder, '/', num2str(outId), '.wav'], ''));
outMerged = char(join([outFolder, '/', num2str(outId), '.mp4'], ''));

% otwieranie tekstu
fileID = fopen(text_file_name,'r');
A = fscanf(fileID,'%c');
fclose (fileID);

% przygotowanie pojemnikow
a = double(A);
max_f = max(a);
sound_length = round(fs *  sound_len_sec);
frames_per_sound = frameRate * sound_len_sec;
X = zeros(sound_length * sound_num, 1);

% mapy kolorow
gray_map = gray(256);
hot_map = flipud(jet(max_f - 255));

% przygotowanie video
v = VideoWriter(outVideo,'Motion JPEG AVI');
v.Quality = videoQuality;
v.FrameRate = frameRate;
v.open;

% czytaj po literze
for k=1:sound_num   

    disp(num2str(k))    
    f = a(k);

    % czy polski znak?  
    if (f > 255)
        l = sound_length * 10;
        map = hot_map;
        polski_znak = 1;
        p = f - 255;
    else
        l = sound_length;
        map = gray_map;
        polski_znak = 0;
        p = f;
    end 

    % ramka video   
    frame = (ones(480, 640) * p) / max_f + 1;
    frame = im2frame(round(frame * 255), map);  
    for m = 1 : frames_per_sound; writeVideo(v,frame); end  

    % wstaw sinusoidę
    x = (sinusoida(f, 1, l , fs))';  
    x = fadeinout (x, 300); 
    if (polski_znak); x = x .* hann(length(x)); end 
    start_point = (k - 1) * sound_length + 1;
    stop_point = start_point + length(x) - 1;   
    if length(X) < stop_point; X = [X; zeros(stop_point - length(X) + 10, 1)];  end
    X(start_point:stop_point) = X(start_point:stop_point) + x;    

end
v.close;

% normalizuj i zapisz audio
X = X - min(X); X = X / max(X); X = X * 2 - 1;
audiowrite (outAudio, X, fs, 'BitsPerSample', 16)

% zrób obraz
en = enframe(a, 663); en = round ((en / max(en(:))) * 100);
imwrite(en,gray,'konf/szum_gray_1.jpeg')

% złącz audio i video
command = ['/usr/local/bin/ffmpeg -i ', outVideo , ' -i ', outAudio, ' -c:v copy -c:a aac -strict experimental ', outMerged];
system(command);

return
% podprogram do generowania sygnału sinusoidalnego
function x = sinusoida(f, a, L, Fs, phase)
% x = sinusoida(f, a, L, Fs, p)
if nargin<5
    phase = 0;
end
T = 1/Fs;
t = (0:L-1)*T;
period = Fs/f;
pshift = round ((phase/360) * period);
x = a * sin(2 * pi * f * t + pshift);

Autorstwo kodu, video i próbek dźwiękowych: Marcin Strzelecki