%% EPS 109 Project  - Day and Night (Cellular Automaton) Soundboard
% Ryan O'Bannon 
% 12/2/15 
% Much of the code used to produce sound comes from Mark Belbin and Athan Bourganos' Matlab Juke Box 
% This code demonstrates a central property of the Day and Night rules - that the
% inverse of an evolving pattern behaves identically - by showing an evolving pattern
% and its inverse side-by-side
% Day and Night cellular automaton: B3678/S34678
% Use periodic boundary conditions

clear
close all
clc

%% Set initial conditions
maxX = 20; % set board size
maxY = 20;
A = zeros(maxX,maxY); % initialize board 1
B = ones(maxX,maxY); % initialize board 2
startX1 = 6;
startY1 = 10;
startX2 = 16;
startY2 = 16;

% SMALL ROCKET (DAY/NIGHT)
C = [0 1 1 0 0 0 0 0;
    1 0 1 1 0 0 0 0;
    1 1 1 1 1 1 1 1;
    1 0 1 1 0 0 0 0;
    0 1 1 0 0 0 0 0];

Cinv = [1 0 0 1 1 1 1 1; % the inverse of C above, use for inverted board b
    0 1 0 0 1 1 1 1;
    0 0 0 0 0 0 0 0;
    0 1 0 0 1 1 1 1;
    1 0 0 1 1 1 1 1];

Cdim = size(C);
A(startY1 - 2:startY1 + 2,startX1 - 3:startX1 + 4) = C; % add small rocket to board 1
B(startY1 - 2:startY1 + 2,startX1 - 3:startX1 + 4) = Cinv; % add small rocket to board 2

% DIAGONAL SPACESHIP (DAY/NIGHT)
D = [0 1 1 1 0;
     1 0 1 1 1;
     1 1 0 0 0;
     1 1 0 0 0;
     0 1 0 0 0];
 
 Dinv = [1 0 0 0 1; % the inverse of D above, use for inverted board b
     0 1 0 0 0;
     0 0 1 1 1;
     0 0 1 1 1;
     1 0 1 1 1];

Ddim = size(D);
A(startY2 - 2:startY2 + 2,startX2 - 2:startX2 + 2) = D; % add diagonal spaceship to board 1
B(startY2 - 2:startY2 + 2,startX2 - 2:startX2 + 2) = Dinv; % add diagonal spaceship to board 2

A(4:5,15:20) = 1; % add noise to board 1
A(3:4,6:14) = 1;
A(2:3,11:18) = 1;
A(1:3,8:12) = 1;
B(4:5,15:20) = 0; % add noise to board 2 (the inverse of that added to A)
B(3:4,6:14) = 0;
B(2:3,11:18) = 0;
B(1:3,8:12) = 0;

% display initial pattern in two colored grids side-by-side
suptitle('''Day and Night'' Soundboard - Inverse Comparison')
subplot(1,2,1)
pcolor(A) % board 1
set(gca,'Xtick',1:37,'XTickLabel',{'G88', 'Ab88', 'D8', 'Gb8','Ab8','A8','Bb8','B8','C','Db','D',...
                                    'Eb','E','F','Gb','G','Ab','A','Bb','B','c','db','d','eb',...
                                    'e','f','gb','g','ab','a','bb','b','c8','db8','d8','eb8','e8'})  % label each square on x axis with corresponding note (see below)
set(gca,'Ytick',1,'YTickLabel',{}) % blank y axis
subplot(1,2,2)
pcolor(B) % board 2
set(gca,'Xtick',1:37,'XTickLabel',{'G88', 'Ab88', 'D8', 'Gb8','Ab8','A8','Bb8','B8','C','Db','D',...
                                    'Eb','E','F','Gb','G','Ab','A','Bb','B','c','db','d','eb',...
                                    'e','f','gb','g','ab','a','bb','b','c8','db8','d8','eb8','e8'})  % label each square on x axis with corresponding note (see below)
set(gca,'Ytick',1,'YTickLabel',{}) % blank y axis
%% Generate sound based on board condition

% Sound frequency
Fs = 2000;

% Note frequencies
G88 = 97.9989;
Ab88 = 103.826;
D8 = 146.832;
Gb8 = 184.997;
Ab8 = 207.652;
A8 = 220.000;
Bb8 = 233.082;
B8 = 246.942;
C = 261.626;
Db = 277.183;
D = 293.665;
Eb = 311.127;
E = 329.628;
F = 349.228;
Gb = 369.994;
G = 391.995;
Ab = 415.305;
Anote = 440.000;
Bb = 466.164;
Bnote = 493.883;
c = 523.251;
db = 554.365;
d = 587.330;
eb = 622.254;
e = 659.255;
f = 698.456;
gb = 739.989;
g = 783.991;
ab = 830.609;
a = 880.000;
bb = 932.328;
b = 987.767;
c8 = 1046.50;
db8 = 1108.73;
d8 = 1174.66;
eb8 = 1244.51;
e8 = 1318.51;

Notes = [G88; Ab88; D8; Gb8; Ab8;
        A8; Bb8; B8; C; Db;
        D; Eb; E; F; Gb;
        G; Ab; Anote; Bb; Bnote;
        c; db; d; eb; e;
        f; gb; g; ab; a;
        bb; b; c8; db8; d8;
        eb8; e8];
    
t = 1/Fs:1/Fs:(.1); % time to play sound
        
% Initialize "soundboard" with notes corresponding to each cell
% This loop assigns each column a different note
% Each board uses the same soundboard
Soundboard = zeros(maxX,maxY);
for row = 1:maxX
    Soundboard(:,row) = Notes(row - 37*floor((row-1)/37));
end

% Show soundboards (blue = lower notes, yellow = higher notes)
% suptitle('''Day and Night'' Soundboard - Inverse Comparison Soundboards')
% subplot(1,2,1)
% pcolor(Soundboard)
% set(gca,'Xtick',1:37,'XTickLabel',{'G88', 'Ab88', 'D8', 'Gb8','Ab8','A8','Bb8','B8','C','Db','D',...
%                                     'Eb','E','F','Gb','G','Ab','A','Bb','B','c','db','d','eb',...
%                                     'e','f','gb','g','ab','a','bb','b','c8','db8','d8','eb8','e8'})
% set(gca,'Ytick',1,'YTickLabel',{})
% subplot(1,2,2)
% pcolor(Soundboard)
% set(gca,'Xtick',1:37,'XTickLabel',{'G88', 'Ab88', 'D8', 'Gb8','Ab8','A8','Bb8','B8','C','Db','D',...
%                                     'Eb','E','F','Gb','G','Ab','A','Bb','B','c','db','d','eb',...
%                                     'e','f','gb','g','ab','a','bb','b','c8','db8','d8','eb8','e8'})
% set(gca,'Ytick',1,'YTickLabel',{})
    
%% Run Simulation
% Lifespan conditions
iterations = 30;
soundvec = []; % initialize sound vector to be exported as .wav file 
counter = 1; % counter used to name PNG files
tic % start timer 

for n = 1:iterations
    % UPDATE MATRIX A
    Anew = A;
    for x = 1:maxX % iterate over all the cells in board 1
        for y = 1:maxX
            
            xR = x+1;
            xL = x-1;
            yU = y+1;
            yD = y-1;
            
            % apply periodic boundary conditions
            if x == maxX
                xR = 1;
            elseif x == 1
                xL = maxX;
            end

            if y == maxY
                yU = 1;
            elseif y == 1
                yD = maxY;
            end

            % count neighbors
            nb = 0;
            for i = [xL, x, xR]
                for j = [yD, y, yU]
                    if A(i,j) == 1;
                        nb = nb + 1;
                    end
                end
            end
            if A(x,y) == 1
                nb = nb - 1; % because we counted the cell itself
            end
            
            % implement birth/survival conditions
            if A(x,y) == 0
                if nb == 3 || nb == 6 || nb == 7 || nb == 8 % cell becomes alive if 3,6,7, or 8 neighbors
                    Anew(x,y) = 1;
                end
            else % if == 1
                if nb == 0 ||nb == 1 || nb == 2 || nb == 5 %|| nb == 6 || nb == 7 || nb == 8
                        Anew(x,y) = 0; % cell dies if only 0, 1, 2, or 5 neighbors
                end
                
            end
        end
    end
    A = Anew;
    
    % display pattern in two colored grids side-by-side
    suptitle('''Day and Night'' Soundboard - Inverse Comparison')
    subplot(1,2,1)
    pcolor(A) % board 1
    set(gca,'Xtick',1:37,'XTickLabel',{'G88', 'Ab88', 'D8', 'Gb8','Ab8','A8','Bb8','B8','C','Db','D',...
                                        'Eb','E','F','Gb','G','Ab','A','Bb','B','c','db','d','eb',...
                                        'e','f','gb','g','ab','a','bb','b','c8','db8','d8','eb8','e8'}) % label each square on x axis with corresponding note
    set(gca,'Ytick',1,'YTickLabel',{})
    subplot(1,2,2)
    pcolor(B) % board 2
    set(gca,'Xtick',1:37,'XTickLabel',{'G88', 'Ab88', 'D8', 'Gb8','Ab8','A8','Bb8','B8','C','Db','D',...
                                        'Eb','E','F','Gb','G','Ab','A','Bb','B','c','db','d','eb',...
                                        'e','f','gb','g','ab','a','bb','b','c8','db8','d8','eb8','e8'}) % label each square on x axis with corresponding note
    set(gca,'Ytick',1,'YTickLabel',{})
    
%   save current figure as PNG file (comment out for faster simulation)    
%   saveas(gcf, ['Inverse_Comp_',sprintf('%03d',counter),'.png'])
%   counter = counter+1;
    
    cells = find(A == 1); % all cells where soundboard is 'activated' by Day and Night automaton
    soundsum = 0;
    for i = 1:numel(cells)
        soundsum = soundsum + sin(Soundboard(cells(i))*2*pi*t); % create sinewave at current note frequency and add all sine waves
    end
    soundsum = soundsum/max(abs(soundsum)); % normalize note frequencies
    sound(soundsum,Fs) % play sound
    
    time = toc; % end timer
    tic % restart timer
    waitvec = zeros(1,round(time*Fs)); % this vector adds silence to the saved .wav file so that there is proper spacing between the notes
    soundvec = [soundvec,waitvec,soundsum]; % create final sound vector to be exported as .wav file
    
    pause(.25)
    
    % UPDATE MATRIX B
    Bnew = B;
    for x = 1:maxX % iterate over all the cells in board 2
        for y = 1:maxX
            
            xR = x+1;
            xL = x-1;
            yU = y+1;
            yD = y-1;
            
            % apply periodic boundary conditions
            if x == maxX
                xR = 1;
            elseif x == 1
                xL = maxX;
            end

            if y == maxY
                yU = 1;
            elseif y == 1
                yD = maxY;
            end

            % count neighbors
            nbb = 0;
            for i = [xL, x, xR]
                for j = [yD, y, yU]
                    if B(i,j) == 1;
                        nbb = nbb + 1;
                    end
                end
            end
            if B(x,y) == 1
                nbb = nbb - 1; % because we counted the cell itself
            end
            
            % implement birth/survival conditions
            if B(x,y) == 0
                if nbb == 3 || nbb == 6 || nbb == 7 || nbb == 8 % cell becomes alive if 3,6,7, or 8 neighbors
                    Bnew(x,y) = 1;
                end
            else % if == 1
                if nbb == 0 ||nbb == 1 || nbb == 2 || nbb == 5
                        Bnew(x,y) = 0; % cell dies if only 0, 1, 2, or 5 neighbors
                end
                
            end
        end
    end
    B = Bnew;
    
    
    % display pattern in two colored grids side-by-side
    suptitle('''Day and Night'' Soundboard - Inverse Comparison')
    subplot(1,2,1)
    pcolor(A) % board 1
    set(gca,'Xtick',1:37,'XTickLabel',{'G88', 'Ab88', 'D8', 'Gb8','Ab8','A8','Bb8','B8','C','Db','D',...
                                        'Eb','E','F','Gb','G','Ab','A','Bb','B','c','db','d','eb',...
                                        'e','f','gb','g','ab','a','bb','b','c8','db8','d8','eb8','e8'}) % label each square on x axis with corresponding note
    set(gca,'Ytick',1,'YTickLabel',{})
    subplot(1,2,2)
    pcolor(B) % board 2
    set(gca,'Xtick',1:37,'XTickLabel',{'G88', 'Ab88', 'D8', 'Gb8','Ab8','A8','Bb8','B8','C','Db','D',...
                                        'Eb','E','F','Gb','G','Ab','A','Bb','B','c','db','d','eb',...
                                        'e','f','gb','g','ab','a','bb','b','c8','db8','d8','eb8','e8'}) % label each square on x axis with corresponding note
    set(gca,'Ytick',1,'YTickLabel',{})
    
%     saveas(gcf, ['Inverse_Comp_',sprintf('%03d',counter),'.png'])
%     counter = counter+1;
    
    cells = find(B == 1);
    soundsum = 0;
    for i = 1:numel(cells)
        soundsum = soundsum + sin(Soundboard(cells(i))*2*pi*t); % create sinewave at current note frequency and add all sine waves
    end
    soundsum = soundsum/max(abs(soundsum)); % normalize note frequencies
    sound(soundsum,Fs) % play sound
    
    time = toc; % end timer
    tic % restart timer
    waitvec = zeros(1,round(time*Fs)); % this vector adds silence to the saved .wav file so that there is proper spacing between the notes
    soundvec = [soundvec,waitvec,soundsum]; % create final sound vector to be exported as .wav file
      
    pause(.25)
end

% save soundvec as a .wav file (comment out when not needed)
% audiowrite('Inverse_Comp.wav',soundvec,Fs)