Output: Difference between revisions

From Control Systems Technology Group
Jump to navigation Jump to search
No edit summary
No edit summary
 
(36 intermediate revisions by 3 users not shown)
Line 1: Line 1:
Back to main page: [[PRE2015_3_Groep4]]
Back to the main page: [[PRE2015_3_Groep4]]


To the input: [[Input]]
To the input: [[Input]]


To the code: [[Code]]
To the algorithm: [[Algorithm]]


== Light Brightness ==
== Overview ==
== Play Sound ==
The script called "OutputFunc_V3.m" controls the output of the system, which consists of making sounds and turning on lights. It has gone through several changes, with different versions having different pros and cons. This script is version 3, hence the suffix "_V3", and is the first and only script with an acceptable runtime, but previous versions would have been preferred if certain tasks didn't take so much time to complete. Nevertheless, version 3 does a very good job. The following paragraphs give a brief explanation of how the script works.
[[File:MP3PlayerShield.jpg|thumb|The SparkFun MP3 Player Shield]]
The best way to produce sound with an Arduino unit is probably by using the Sparkfun MP3 Player Shield, which is an extra circuit board for the Arduino which can play MP3 files from a microSD card (seen in the picture to the right). The costs, however, are $25, which is too much, so we chose a cheaper option. More info about this product can be found at https://www.sparkfun.com/products/12660.


Since we didn't think it is needed to buy a MP3 player shield we just used the laptop to play sounds. Matlab is doing all the calculations anyway so using the laptop to play sound is more convenient.
=== The 'outside' of the script ===
We've looked at different possibilities for producing sounds. Ideally, we would have connected a speaker to the Arduino, because then it would potentially be able to do all the work, without the aid of a laptop. However, that would require programming the unit in C++, in which most of us are inexperienced, and the Arduino has very limited memory space, so therefore it had been decided to use Matlab and the laptop's speaker instead. The Arduino is used to produce light, though, with added on LEDs. The sound files used for the smart alarm and the standard alarm are "Birds-singing-in-the-morning.mp3" and "Loud-alarm-clock-sound.wav" respectively, both of which have been downloaded from http://www.orangefreesounds.com/. The sound of the smart alarm can be customized by copying a sound file of your own choice to the folder containing the script and changing the filename of the smart alarm, "Alarm1", in "runscript.m". The script is capable of running with sound files of all sizes, but users are advised to use files that aren't too long because it greatly influences the runtime of the script. The audio of "Birds-singing-in-the-morning.mp3" is about 15 minutes long, so the script is guaranteed to work with sound files of 15 minutes long or less. The standard alarm could be customized in the same manner. However, the script doesn't take into account how long the audio is and just repeats it a number of times. The script could have been altered so that it could handle sound files of different sizes, but that would increase the complexity of the code a lot, which we didn't consider to be worth the trouble. After all, it doesn't seem likely to us that the user would wish to customize the standard alarm. The audio of "Loud-alarm-clock-sound.wav" is 11 seconds long, so if you really want to, you could use another audio file of about the same size instead. Longer sound files should work as well, but we haven't tested the limits, so we can't give any guarantees.


Two sound files have been downloaded from the internet to serve as an alarm: the file "Birds-singing-in-the-morning.mp3" is a relaxing audio with, as the name suggests, singing birds and a bit of wind. This file will serve as the Smart Alarm that softly wakes the user up. The file "Loud-alarm-clock-sound.wav" is a standard alarm sound, which will naturally serve as the standard alarm that urges the user to wake up.  
=== The 'inside' of the script ===
[[File:SoundDevelopment.png|thumb|400px|Figure 1: Graph showing the development of the sound volume over time. The green line indicates the smart alarm is playing and the red line indicates the standard alarm is playing. "tWake" is the moment the standard alarm is initiated if the user hasn't woken up yet.]]
[[File:LightDevelopment.png|thumb|400px|Figure 2: Graph showing the development of the LED voltage over time. The increase during the first 5 minutes is quadratic. "tWake" is the moment the standard alarm is initiated if the user hasn't woken up yet. Please ignore the red line.]]
'''Initialization'''


When running the script, the user is first asked whether he wants to use the Smart Alarm at all. If he does, the user is then asked to give the time interval (in seconds) in which he wants to wake up. If he doesn't, the script only asks for the time the user wants to wake up. When using the Smart Alarm, the sound must gradually increase over the course of the 'wake up interval'. This is done by creating an array called f filled with linearly increasing numbers between 0 and fmax. The length of f is equal to the number of samples of the Smart Alarm sound file necessary to fill the wake up time interval. This array is then multiplied with y1, the matrix with the sound samples of the Smart Alarm. Thus during the wake up time interval, the sound increases from a maximal amplitude of 0 to a maximal amplitude of fmax. The sound is produced by using the function sound(). Using the function pause(), the script waits until the end of the wake up interval and then stops the sound, after which it starts to play the standard alarm. By using the function pause (without parameters), the script waits until the user presses a button to continue. Afterwards, the user is given the option to stop the script or continue. If he chooses to continue, he can then give feedback on the Smart Alarm (if this was enabled). The feedback consists of choosing between "Too soft", "Soft", "OK", "Loud" and "Too loud", for which the script assigns the values 2, 1, 0, -1 and -2 respectively. This number is added to the value of fmax and stored within the array Fb. The new value for fmax is determined by taking the mean of this array. To make sure that only the most recent feedback is taken into account, and limit the memory required, the maximal length of this array is set to a value called Fbmax, which is for now set to 5. When surpassing this limit, the script deletes the first element of the array Fb. The script also makes sure that the value of fmax stays between fmin and 5, and it gives a notification when it does so. From there on, the script keeps repeating itself until the user decides to stop it.
The output script is initiated when "Complete_script.m" finds a suitable moment to wake up its user. The script is a function with 6 parameters: a connection with the Arduino, the name of the pin that controls the LEDs, the file names of the smart and standard alarm, the moment the user must be awake (the moment the standard alarm is used as a last resort to waking up the user) and finally a vector containing the number of seconds in a second, a minute, an hour, a day, a month and a year, which is used to translate the time and date into seconds. First of all, the script stops the measuring of sound, because the Arduino can't send and receive data at the same time. Then both sound files are translated into sound samples "y1" and "y2", which are lists of numbers between -1 and 1, and sample frequencies "Fs1" and "Fs2" which indicate how many samples need to be played per second to create the proper sound. Afterward, the maximal sound and light intensities, "Smax" and "Lmax", are calculated using a file called "Feedback.mat". If this file doesn't exist yet, it's created and the maximal intensities are set to their defaults, which is 3 in both cases. For the light this means a maximal Arduino pin voltage of 3 V. If the maximal intensities are calculated with the feedback file, it is checked whether these values are within their boundaries, which is between 1 and 5 in both cases. If not, the value is set to 1 or 5 and the user is notified that a boundary has been reached. The minimal value is 1 so that the sound/light is still audible/visible. The maximal value of the light intensity is 5 because the Arduino has a maximal output voltage of 5 V. There is no particular reason why the maximal sound volume is 5, but it seemed appropriate.


The actual script is slightly different than described above, but the description clearly shows the thought process behind this.
'''Producing sound and light'''


=== Script ===
The sound samples are multiplied with "Smax" to adjust their amplitude and with that the volume of the sound output. The first set of samples of the smart alarm, however, are scaled in such a way that during the first 5 minutes that the alarm is being played, the volume gradually increases from complete silence to maximal volume in order to wake up the user as lightly as possible. Every second while the volume is increasing, the voltage on the LEDs is increased as well, from 0 Volts to "Lmax" Volts. Contrary to the sound volume, this increase is quadratic instead of linear, as the relation between voltage and light intensity isn't linear. We don't know what the relation is exactly, but the increase in light intensity seems to be quite linear now, so we decided to keep it like this. The smart alarm keeps playing and is, if necessary, repeated until the user presses a button on the keyboard or until the standard alarm jumps into action. The standard alarm is at most played 10 times in a row while waiting for the user to press a button. Allowing it to be repeated indefinitely would have made the script much more complex, and we assumed anyone would have woken up during those 10 times, so we decided not to invest extra effort in that cause. Figures 1 and 2 to the right visualize the development of the sound volume and LED voltage.
clear variables;close all;clc;
 
'''Providing feedback'''
%% Alarm sounds
 
Alarm1 = 'Birds-singing-in-the-morning.mp3';% The 'Smart Alarm'
When the user presses a button, the sound stops and the lights are shut off. Afterwards, the user is given the opportunity to provide feedback about the sound and light intensity separately. The feedback is given as a number between 1 and 5, with 1 meaning "Too soft/dim", 2 meaning "Soft/dim", 3 meaning "OK", 4 meaning "Loud/bright" and 5 meaning "Too loud/bright" (although you technically could also pick decimal numbers between 1 and 5). This number is subtracted from the number 3, so 1, 2, 3, 4 and 5 turns into 2, 1, 0, -1 and -2, and the result is saved in "Feedback.mat" together with the value of "Smax" or "Lmax" during that night. At the beginning of the following night, the new values for the maximal intensities are calculated by taking the mean of the sum of the value of "Smax" or "Lmax" and the corresponding feedback number. So picking numbers 1 or 2 as feedback would increase the maximal intensity and 4 and 5 would decrease it while the number 3 makes it stay the same. To make sure the size of "Feedback.mat" wouldn't grow indefinitely, and to make more recent feedback more important than older ones, the file has a size limit of 5 nights. So if this limit is passed, the oldest night is deleted from the file. And that's all you need to know about the script and how it works.
  Alarm2 = 'Loud-alarm-clock-sound.wav';     % The 'Standard Alarm'
 
[y1,Fs1] = audioread(Alarm1);               % Alarm 1 translated into sound samples y1 and sample frequency Fs1
== Code ==
[y2,Fs2] = audioread(Alarm2);               % Alarm 2 translated into sound samples y2 and sample frequency Fs2
[[File:OutputFunc_V3.m]]
 
%% Feedback matrix
  function OutputFunc_V3(Arduino,Pin,Alarm1,Alarm2,tWake,times)
Fb = [];                                   % n*2 matrix which contains the user's feedback
    %% Stop logging the sleep cycle
Fbmax = 5;                                 % Maximal number of rows of the feedback matrix
    !taskkill -f -im Gobetwino.exe
   
%% Maximal sound amplitude boundaries
     %% Alarm sounds
fmin = 1;                                   % This variable is to make sure that the alarm always makes an audible sound
    [y1,Fs1] = audioread(Alarm1);                               % Smart alarm translated into sound samples y1 and sample frequency Fs1
fmax = 3;                                   % Maximal sound amplitude in volts (for the speaker)
    [y2,Fs2] = audioread(Alarm2);                               % Standard alarm translated into sound samples y2 and sample frequency Fs2
   
while 1
    %% Maximal sound and light intensity boundaries
    %% Use Smart Alarm? (Alarm 1)
    if exist('Feedback.mat','file') == 0
    Smart = input('Use Smart Alarm?\n 1 = Yes\n 0 = No\n');
        Smax = [];                                              % Array containing the values of Smax
        SFb = [];                                               % Array containing the feedback on Smax
        Lmax = [];                                             % Array containing the values of Lmax
        LFb = [];                                              % Array containing the feedback on Lmax
        save('Feedback.mat','Smax','SFb','Lmax','LFb','-v7.3'); % Creates feedback matrix
        Fb = matfile('Feedback.mat','Writable',true);          % Loads feedback matrix
        Smax = 3;                                              % Maximal sound amplitude
        Lmax = 3;                                              % Maximal light voltage
    else
        Fb = matfile('Feedback.mat','Writable',true);          % Loads feedback matrix
        Smin = 1;                                               % This variable is to make sure that the alarm always makes an audible sound
        Smax = mean(Fb.Smax+Fb.SFb);                            % New maximal volume is determined by looking at the total feedback
        if Smax < Smin
            Smax = Smin;                                       % Maximal volume needs to have a certain level so that the sound is still audible
            disp('Notice: minimal sound level reached');        % The user is notified of the corrected value for Smax
        elseif Smax > 5
            Smax = 5;                                          % Maximal volume can't be higher than 5
            disp('Notice: maximal sound level reached');        % The user is notified of the corrected value for Smax
        end
        Lmin = 1;                                              % This variable is to make sure that the light is always visible
        Lmax = mean(Fb.Lmax+Fb.LFb);                            % New maximal brightness is determined by looking at the total feedback
        if Lmax < Lmin
            Lmax = Lmin;                                        % Maximal brightness needs to have a certain level so that the light is still visible
            disp('Notice: minimal light level reached');        % The user is notified of the corrected value for Lmax
        elseif Lmax > 5
            Lmax = 5;                                          % Maximal voltage on an Arduino pin is 5 V
            disp('Notice: maximal light level reached');       % The user is notified of the corrected value for Lmax
        end
    end
      
      
     %% Wake up interval/time
     %% Smart alarm properties (Alarm1)
     if Smart
    Interval = tWake - clock*times;                            % Time between the start of the script and the moment the user needs to wake up
         tStart = input('Start of the time interval the user wants to wake up: ');
    BuildUp = [0 0 0 0 5 0]*times;                              % Time interval in which the sound and light increase in intensity
         tEnd = input('End of the time interval the user wants to wake up: ');
    y1max = Fs1*Interval;                                      % Number of sound samples to be used during the 'wake up interval'
    y1BUp = Fs1*BuildUp;                                        % Number of sound samples to be used during the build up of the sound and light intensity
    [Nrow1,Ncol1] = size(y1);                                  % Number of rows (number of sound samples) and collumns (number of audio channels) in y1
    if y1BUp > Nrow1
        BuildUp = Nrow1/Fs1;                                    % The 'build up' must happen within the duration of the audio file
        y1BUp = Nrow1;
    end
     if Interval > BuildUp
         dS = Smax/y1BUp;                                        % Step size of the 'sound multiplication factor array'
        S = dS:dS:Smax;                                        % Determines the maximal volume of the sound for each sample during the 'build up'
        S2 = repmat(S,Ncol1,1);                                 % All collumns of y1 need to be scaled
         [y1_1,y1_2] = deal(Smax*y1);                            % The sound samples of y1 are scaled using Smax
        y1_2(1:y1BUp,:) = S2'.*y1(1:y1BUp,:);                  % The samples belonging to the 'build up' need to increase in amplitude
        n1 = floor(y1max/Nrow1);                               % Number of times the sound file needs to be repeated at most
     else
     else
         tWake = input('Time the user wants to wake up: ');
         BuildUp = Interval;                                    % The 'build up' must be over by the end of Interval
        dS = Smax/y1max;                                        % Step size of the 'sound multiplication factor array'
        S = dS:dS:Smax;                                        % Makes sure the smart alarm gradually increases in volume during the 'build up'
        S2 = repmat(S,Ncol1,1);                                % All collumns of y1 need to be scaled
        y1_2 = S2'.*y1(1:y1max,:);                             % The samples for the 'build up' are created
     end
     end
   
   
     %% Alarm 1 properties
     %% Smart light properties
     if Smart
     dL = Lmax/ceil(BuildUp);                                   % Step size of the 'light multiplication factor array'
        y1max = Fs1*(tEnd-tStart);         % Number of samples to be used during the 'wake up interval'
    L = dL:dL:Lmax;                                             % Light multiplication factor array: makes sure the lights gradually increase in brightness
        df = fmax/y1max;                    % Step size of the 'multiplication factor array'
        f = df:df:fmax;                     % Multiplication factor array: makes sure the Smart Alarm gradually increases in volume
    %% Play smart alarm
    y1Play1 = audioplayer(y1_2,Fs1);                            % Convert the sound file to an audioplayer (works faster than simply using the sound() function)
    play(y1Play1);                                              % Play the smart alarm sound which gradually increases in volume due to S
    for t = 1:ceil(BuildUp)
        writePWMVoltage(Arduino,Pin,(L(t)^2)/Lmax);            % Gradually increases the voltage on the pin that's connected to the lights
        k = getkeywait(1);                                      % Voltage is increased every second
        if k > -1
            break;                                              % If a button is pressed, the smart alarm needs to stop
        end
     end
     end
    if Interval > BuildUp && k == -1
    %% Play alarm 1
        tAlarm1 = Nrow1/Fs1;                                    % Duration of the smart alarm sound file
    if Smart
        if tAlarm1 > ceil(BuildUp)
        pause(tStart);                     % Wait until it's time to turn on the Smart Alarm
            k = getkeywait(ceil(tAlarm1-BuildUp));             % If the sound file hasn't ended yet after the 'build up', it needs to be finished
         disp('tStart');
        end
         sound([f' f'].*y1(1:y1max,:),Fs1); % Play the Smart Alarm which gradually increases in volume due to f
         stop(y1Play1);                                         % Stop the smart alarm sound if it hasn't stopped yet
        pause(tEnd-tStart);                 % Wait until the user must be woken up
         if k == -1
        disp('tEnd');
            y1Play2 = audioplayer(y1_1,Fs1);                   % Convert the sound file to an audioplayer
        clear sound;                       % Stop the Smart Alarm if it hasn't stopped yet
            for i = 1:n1
                play(y1Play2);                                  % Repeat the smart alarm
                k = getkeywait(ceil(tAlarm1));                 % Wait for the user to press any key
                stop(y1Play2);                                 % Stop the sound if the user has pressed a key or if the audio file has ended
                if k > -1
                    break;                                     % If the user has pressed a key, the smart alarm needs to stop
                end
            end
        end
     else
     else
         pause(tWake);                       % Wait until it's time to wake up the user
         stop(y1Play1);                                         % Stop the smart alarm sound if it hasn't stopped yet
        disp('tWake');
     end
     end
    %% Play alarm 2
    sound(y2,Fs2);                          % Play the standard alarm
    pause;                                  % Wait until the user is awake and presses a button
    clear sound;                            % Stop the standard alarm
      
      
     %% Stop script?
     %% Play standard alarm (Alarm2)
     Stop = input('Stop script?\n 0 = No\n 1 = Yes\n');
     if k == -1                                                  % If no key has been pressed, the user isn't awake yet, so the standard alarm jumps into action
    if Stop
        n2 = 10;                                                % Number of times the standard alarm needs to be played at most
         break;                             % Stops the script
        y2_1 = Smax*y2;                                        % The volume of the alarm sound is scaled using Smax
        y2_2 = repmat(y2_1,n2,1);                              % To make the alarm loop, it's simply copied after eachother a couple of times
        Nrow2 = size(y2,1);                                     % Number of rows (number of sound samples) in y2
        sound(y2_2,Fs2);                                        % Play the standard alarm
        writePWMVoltage(Arduino,Pin,Lmax);                      % Set the lights to maximal brightness
        getkeywait(ceil(n2*Nrow2/Fs2));                        % Wait until the user is awake and presses a button
         clear sound;                                           % Stop the standard alarm sound
     end
     end
    writePWMVoltage(Arduino,Pin,0);                            % Shut the lights
      
      
     if Smart
     %% Feedback
        %% Feedback
    Fb.Smax(1,end+1) = Smax;                                    % Give feedback about this volume setting
        Fb(end+1,1) = fmax;                 % Give feedback about this volume setting
    Fb.Lmax(1,end+1) = Lmax;                                   % Give feedback about this brightness setting
        Fb(end,2) = 3 - input('Rate your sound experience:\n 1 = Too soft\n 2 = Soft\n 3 = OK\n 4 = Loud\n 5 = Too loud\n');
    Fb.SFb(1,end+1) = 3 - input('Rate your sound experience:\n 1 = Too soft\n 2 = Soft\n 3 = OK\n 4 = Loud\n 5 = Too loud\n');
        if size(Fb,1) > Fbmax;             % Feedback matrix can't be larger than Fbmax rows
    Fb.LFb(1,end+1) = 3 - input('Rate your light experience:\n 1 = Too dim\n 2 = Dim\n 3 = OK\n 4 = Bright\n 5 = Too bright\n');
            Fb = Fb(2:end,:);               % Delete first row
    Fbmax = 5;                                                  % Maximal number of rows of the feedback matrix
         end
    if size(Fb.Smax,2) > Fbmax;                                 % Feedback matrix can't have more than Fbmax collumns
        Fb.Smax = Fb.Smax(1,2:end);                             % Delete first element
        %% Maximal sound amplitude
         Fb.Lmax = Fb.Lmax(1,2:end);                             % Delete first element
        fmax = mean(Fb(:,1)+Fb(:,2));       % New maximal volume is determined by looking at the total feedback
         Fb.SFb = Fb.SFb(1,2:end);                               % Delete first element
         if fmax < fmin                      % Maximal volume needs to have a certain level so that the sound is still audible
         Fb.LFb = Fb.LFb(1,2:end);                               % Delete first element
            fmax = fmin;
            disp('Notice: minimal sound level reached');
         end
        if fmax > 5                        % Maximal voltage for the speaker is 5 V
            fmax = 5;
            disp('Notice: maximal sound level reached');
        end
     end
     end
  end
  end
== Feedback Statistics ==
== Graphic Simulation ==

Latest revision as of 22:42, 22 April 2016

Back to the main page: PRE2015_3_Groep4

To the input: Input

To the algorithm: Algorithm

Overview

The script called "OutputFunc_V3.m" controls the output of the system, which consists of making sounds and turning on lights. It has gone through several changes, with different versions having different pros and cons. This script is version 3, hence the suffix "_V3", and is the first and only script with an acceptable runtime, but previous versions would have been preferred if certain tasks didn't take so much time to complete. Nevertheless, version 3 does a very good job. The following paragraphs give a brief explanation of how the script works.

The 'outside' of the script

We've looked at different possibilities for producing sounds. Ideally, we would have connected a speaker to the Arduino, because then it would potentially be able to do all the work, without the aid of a laptop. However, that would require programming the unit in C++, in which most of us are inexperienced, and the Arduino has very limited memory space, so therefore it had been decided to use Matlab and the laptop's speaker instead. The Arduino is used to produce light, though, with added on LEDs. The sound files used for the smart alarm and the standard alarm are "Birds-singing-in-the-morning.mp3" and "Loud-alarm-clock-sound.wav" respectively, both of which have been downloaded from http://www.orangefreesounds.com/. The sound of the smart alarm can be customized by copying a sound file of your own choice to the folder containing the script and changing the filename of the smart alarm, "Alarm1", in "runscript.m". The script is capable of running with sound files of all sizes, but users are advised to use files that aren't too long because it greatly influences the runtime of the script. The audio of "Birds-singing-in-the-morning.mp3" is about 15 minutes long, so the script is guaranteed to work with sound files of 15 minutes long or less. The standard alarm could be customized in the same manner. However, the script doesn't take into account how long the audio is and just repeats it a number of times. The script could have been altered so that it could handle sound files of different sizes, but that would increase the complexity of the code a lot, which we didn't consider to be worth the trouble. After all, it doesn't seem likely to us that the user would wish to customize the standard alarm. The audio of "Loud-alarm-clock-sound.wav" is 11 seconds long, so if you really want to, you could use another audio file of about the same size instead. Longer sound files should work as well, but we haven't tested the limits, so we can't give any guarantees.

The 'inside' of the script

Figure 1: Graph showing the development of the sound volume over time. The green line indicates the smart alarm is playing and the red line indicates the standard alarm is playing. "tWake" is the moment the standard alarm is initiated if the user hasn't woken up yet.
Figure 2: Graph showing the development of the LED voltage over time. The increase during the first 5 minutes is quadratic. "tWake" is the moment the standard alarm is initiated if the user hasn't woken up yet. Please ignore the red line.

Initialization

The output script is initiated when "Complete_script.m" finds a suitable moment to wake up its user. The script is a function with 6 parameters: a connection with the Arduino, the name of the pin that controls the LEDs, the file names of the smart and standard alarm, the moment the user must be awake (the moment the standard alarm is used as a last resort to waking up the user) and finally a vector containing the number of seconds in a second, a minute, an hour, a day, a month and a year, which is used to translate the time and date into seconds. First of all, the script stops the measuring of sound, because the Arduino can't send and receive data at the same time. Then both sound files are translated into sound samples "y1" and "y2", which are lists of numbers between -1 and 1, and sample frequencies "Fs1" and "Fs2" which indicate how many samples need to be played per second to create the proper sound. Afterward, the maximal sound and light intensities, "Smax" and "Lmax", are calculated using a file called "Feedback.mat". If this file doesn't exist yet, it's created and the maximal intensities are set to their defaults, which is 3 in both cases. For the light this means a maximal Arduino pin voltage of 3 V. If the maximal intensities are calculated with the feedback file, it is checked whether these values are within their boundaries, which is between 1 and 5 in both cases. If not, the value is set to 1 or 5 and the user is notified that a boundary has been reached. The minimal value is 1 so that the sound/light is still audible/visible. The maximal value of the light intensity is 5 because the Arduino has a maximal output voltage of 5 V. There is no particular reason why the maximal sound volume is 5, but it seemed appropriate.

Producing sound and light

The sound samples are multiplied with "Smax" to adjust their amplitude and with that the volume of the sound output. The first set of samples of the smart alarm, however, are scaled in such a way that during the first 5 minutes that the alarm is being played, the volume gradually increases from complete silence to maximal volume in order to wake up the user as lightly as possible. Every second while the volume is increasing, the voltage on the LEDs is increased as well, from 0 Volts to "Lmax" Volts. Contrary to the sound volume, this increase is quadratic instead of linear, as the relation between voltage and light intensity isn't linear. We don't know what the relation is exactly, but the increase in light intensity seems to be quite linear now, so we decided to keep it like this. The smart alarm keeps playing and is, if necessary, repeated until the user presses a button on the keyboard or until the standard alarm jumps into action. The standard alarm is at most played 10 times in a row while waiting for the user to press a button. Allowing it to be repeated indefinitely would have made the script much more complex, and we assumed anyone would have woken up during those 10 times, so we decided not to invest extra effort in that cause. Figures 1 and 2 to the right visualize the development of the sound volume and LED voltage.

Providing feedback

When the user presses a button, the sound stops and the lights are shut off. Afterwards, the user is given the opportunity to provide feedback about the sound and light intensity separately. The feedback is given as a number between 1 and 5, with 1 meaning "Too soft/dim", 2 meaning "Soft/dim", 3 meaning "OK", 4 meaning "Loud/bright" and 5 meaning "Too loud/bright" (although you technically could also pick decimal numbers between 1 and 5). This number is subtracted from the number 3, so 1, 2, 3, 4 and 5 turns into 2, 1, 0, -1 and -2, and the result is saved in "Feedback.mat" together with the value of "Smax" or "Lmax" during that night. At the beginning of the following night, the new values for the maximal intensities are calculated by taking the mean of the sum of the value of "Smax" or "Lmax" and the corresponding feedback number. So picking numbers 1 or 2 as feedback would increase the maximal intensity and 4 and 5 would decrease it while the number 3 makes it stay the same. To make sure the size of "Feedback.mat" wouldn't grow indefinitely, and to make more recent feedback more important than older ones, the file has a size limit of 5 nights. So if this limit is passed, the oldest night is deleted from the file. And that's all you need to know about the script and how it works.

Code

File:OutputFunc V3.m

function OutputFunc_V3(Arduino,Pin,Alarm1,Alarm2,tWake,times)
    %% Stop logging the sleep cycle
    !taskkill -f -im Gobetwino.exe
    
    %% Alarm sounds
    [y1,Fs1] = audioread(Alarm1);                               % Smart alarm translated into sound samples y1 and sample frequency Fs1
    [y2,Fs2] = audioread(Alarm2);                               % Standard alarm translated into sound samples y2 and sample frequency Fs2
    
    %% Maximal sound and light intensity boundaries
    if exist('Feedback.mat','file') == 0
        Smax = [];                                              % Array containing the values of Smax
        SFb = [];                                               % Array containing the feedback on Smax
        Lmax = [];                                              % Array containing the values of Lmax
        LFb = [];                                               % Array containing the feedback on Lmax
        save('Feedback.mat','Smax','SFb','Lmax','LFb','-v7.3'); % Creates feedback matrix
        Fb = matfile('Feedback.mat','Writable',true);           % Loads feedback matrix
        Smax = 3;                                               % Maximal sound amplitude
        Lmax = 3;                                               % Maximal light voltage
    else
        Fb = matfile('Feedback.mat','Writable',true);           % Loads feedback matrix
        Smin = 1;                                               % This variable is to make sure that the alarm always makes an audible sound
        Smax = mean(Fb.Smax+Fb.SFb);                            % New maximal volume is determined by looking at the total feedback
        if Smax < Smin
            Smax = Smin;                                        % Maximal volume needs to have a certain level so that the sound is still audible
            disp('Notice: minimal sound level reached');        % The user is notified of the corrected value for Smax
        elseif Smax > 5
            Smax = 5;                                           % Maximal volume can't be higher than 5
            disp('Notice: maximal sound level reached');        % The user is notified of the corrected value for Smax
        end
        Lmin = 1;                                               % This variable is to make sure that the light is always visible
        Lmax = mean(Fb.Lmax+Fb.LFb);                            % New maximal brightness is determined by looking at the total feedback
        if Lmax < Lmin
            Lmax = Lmin;                                        % Maximal brightness needs to have a certain level so that the light is still visible
            disp('Notice: minimal light level reached');        % The user is notified of the corrected value for Lmax
        elseif Lmax > 5
            Lmax = 5;                                           % Maximal voltage on an Arduino pin is 5 V
            disp('Notice: maximal light level reached');        % The user is notified of the corrected value for Lmax
        end
    end
    
    %% Smart alarm properties (Alarm1)
    Interval = tWake - clock*times;                             % Time between the start of the script and the moment the user needs to wake up
    BuildUp = [0 0 0 0 5 0]*times;                              % Time interval in which the sound and light increase in intensity
    y1max = Fs1*Interval;                                       % Number of sound samples to be used during the 'wake up interval'
    y1BUp = Fs1*BuildUp;                                        % Number of sound samples to be used during the build up of the sound and light intensity
    [Nrow1,Ncol1] = size(y1);                                   % Number of rows (number of sound samples) and collumns (number of audio channels) in y1
    if y1BUp > Nrow1
        BuildUp = Nrow1/Fs1;                                    % The 'build up' must happen within the duration of the audio file
        y1BUp = Nrow1;
    end
    if Interval > BuildUp
        dS = Smax/y1BUp;                                        % Step size of the 'sound multiplication factor array'
        S = dS:dS:Smax;                                         % Determines the maximal volume of the sound for each sample during the 'build up'
        S2 = repmat(S,Ncol1,1);                                 % All collumns of y1 need to be scaled
        [y1_1,y1_2] = deal(Smax*y1);                            % The sound samples of y1 are scaled using Smax
        y1_2(1:y1BUp,:) = S2'.*y1(1:y1BUp,:);                   % The samples belonging to the 'build up' need to increase in amplitude
        n1 = floor(y1max/Nrow1);                                % Number of times the sound file needs to be repeated at most
    else
        BuildUp = Interval;                                     % The 'build up' must be over by the end of Interval
        dS = Smax/y1max;                                        % Step size of the 'sound multiplication factor array'
        S = dS:dS:Smax;                                         % Makes sure the smart alarm gradually increases in volume during the 'build up'
        S2 = repmat(S,Ncol1,1);                                 % All collumns of y1 need to be scaled
        y1_2 = S2'.*y1(1:y1max,:);                              % The samples for the 'build up' are created
    end

    %% Smart light properties
    dL = Lmax/ceil(BuildUp);                                    % Step size of the 'light multiplication factor array'
    L = dL:dL:Lmax;                                             % Light multiplication factor array: makes sure the lights gradually increase in brightness

    %% Play smart alarm
    y1Play1 = audioplayer(y1_2,Fs1);                            % Convert the sound file to an audioplayer (works faster than simply using the sound() function)
    play(y1Play1);                                              % Play the smart alarm sound which gradually increases in volume due to S
    for t = 1:ceil(BuildUp)
        writePWMVoltage(Arduino,Pin,(L(t)^2)/Lmax);             % Gradually increases the voltage on the pin that's connected to the lights
        k = getkeywait(1);                                      % Voltage is increased every second
        if k > -1
            break;                                              % If a button is pressed, the smart alarm needs to stop
        end
    end
    if Interval > BuildUp && k == -1
        tAlarm1 = Nrow1/Fs1;                                    % Duration of the smart alarm sound file
        if tAlarm1 > ceil(BuildUp)
            k = getkeywait(ceil(tAlarm1-BuildUp));              % If the sound file hasn't ended yet after the 'build up', it needs to be finished
        end
        stop(y1Play1);                                          % Stop the smart alarm sound if it hasn't stopped yet
        if k == -1
            y1Play2 = audioplayer(y1_1,Fs1);                    % Convert the sound file to an audioplayer
            for i = 1:n1
                play(y1Play2);                                  % Repeat the smart alarm
                k = getkeywait(ceil(tAlarm1));                  % Wait for the user to press any key
                stop(y1Play2);                                  % Stop the sound if the user has pressed a key or if the audio file has ended
                if k > -1
                    break;                                      % If the user has pressed a key, the smart alarm needs to stop
                end
            end
        end
    else
        stop(y1Play1);                                          % Stop the smart alarm sound if it hasn't stopped yet
    end
    
    %% Play standard alarm (Alarm2)
    if k == -1                                                  % If no key has been pressed, the user isn't awake yet, so the standard alarm jumps into action
        n2 = 10;                                                % Number of times the standard alarm needs to be played at most
        y2_1 = Smax*y2;                                         % The volume of the alarm sound is scaled using Smax
        y2_2 = repmat(y2_1,n2,1);                               % To make the alarm loop, it's simply copied after eachother a couple of times
        Nrow2 = size(y2,1);                                     % Number of rows (number of sound samples) in y2
        sound(y2_2,Fs2);                                        % Play the standard alarm
        writePWMVoltage(Arduino,Pin,Lmax);                      % Set the lights to maximal brightness
        getkeywait(ceil(n2*Nrow2/Fs2));                         % Wait until the user is awake and presses a button
        clear sound;                                            % Stop the standard alarm sound
    end
    writePWMVoltage(Arduino,Pin,0);                             % Shut the lights
    
    %% Feedback
    Fb.Smax(1,end+1) = Smax;                                    % Give feedback about this volume setting
    Fb.Lmax(1,end+1) = Lmax;                                    % Give feedback about this brightness setting
    Fb.SFb(1,end+1) = 3 - input('Rate your sound experience:\n 1 = Too soft\n 2 = Soft\n 3 = OK\n 4 = Loud\n 5 = Too loud\n');
    Fb.LFb(1,end+1) = 3 - input('Rate your light experience:\n 1 = Too dim\n 2 = Dim\n 3 = OK\n 4 = Bright\n 5 = Too bright\n');
    Fbmax = 5;                                                  % Maximal number of rows of the feedback matrix
    if size(Fb.Smax,2) > Fbmax;                                 % Feedback matrix can't have more than Fbmax collumns
        Fb.Smax = Fb.Smax(1,2:end);                             % Delete first element
        Fb.Lmax = Fb.Lmax(1,2:end);                             % Delete first element
        Fb.SFb = Fb.SFb(1,2:end);                               % Delete first element
        Fb.LFb = Fb.LFb(1,2:end);                               % Delete first element
    end
end