Automatically play sound in IPython notebook

Question:

I often run long-running cells in my IPython notebook. I’d like the notebook to automatically beep or play a sound when the cell is finished executing. Is there some way to do this in iPython notebook, or maybe some command I can put at the end of a cell that will automatically play a sound?

I’m using Chrome if that makes any difference.

Asked By: Thomas Johnson

||

Answers:

You could use an external module with python. Try adding this s.play() call from Snack Sound Toolkit at the end of the cell.

The Snack Sound Toolkit can play wav, au and mp3 files.

s = Sound() 
s.read('sound.wav') 
s.play()

this question is basically a duplicate of:
Play a Sound with Python.

The above code-snipit was from @csexton in that question.

Answered By: agconti

TL;DR

At the top of your notebook

from IPython.display import Audio
sound_file = './sound/beep.wav'

sound_file should point to a file on your computer, or accessible from the internet.

Then later, at the end of the long-running cell

<code that takes a long time>

Audio(sound_file, autoplay=True)

This method uses the Audio tag built into Newer versions of iPython/Jupyter.

Note For Older Versions

Older versions without the Audio tag can use the following method.

Put this in a cell and run it before you want to play your sound:

from IPython.display import HTML
from base64 import b64encode

path_to_audio = "/path/to/snd/my-sound.mp3"
audio_type = "mp3"

sound = open(path_to_audio, "rb").read()
sound_encoded = b64encode(sound)
sound_tag = """
    <audio id="beep" controls src="data:audio/{1};base64,{0}">
    </audio>""".format(sound_encoded, audio_type)

play_beep = """
<script type="text/javascript">
    var audio = document.getElementById("beep");
    audio.play();
</script>
"""

HTML(sound_tag)

At the end of the cell you want to make a noise on completion put this:

HTML(play_beep)

How it works:

It reads a file from the filesystem using iPython’s built in open and read methods. Then it encodes this into base64. It then creates an audio tag with the ID beep and injects the base64 data into it. The final piece of setup creates a small script tag that plays the sound.

This method should work in any browser that supports the HTML5 audio tag.

Note: if you’d rather not display the audio controls in your notebook, just remove the controls attribute from the variable named sound_tag

Answered By: Waylon Flinn

My favorite solution (no need for an external module) :

import os
os.system("printf 'a'") # or '7'

Works on OS X.

However DaveP’s remark still apply : it is not the browser playing the sound but the server.

Answered By: Arthur

What about typing the code once and having it work after execution of every cell if it executes longer than given amount of time?

Just create and execute a cell with following JavaScript (adjusting timeout and sound)

%%javascript
(function(){
    var startTime;
    $([Jupyter.events]).on('execute.CodeCell', function(event, target)
    {
        startTime = performance.now();
    });
    $([Jupyter.events]).on('finished_iopub.Kernel', function(event, target)
    {
        var endTime = performance.now();

        if(endTime - startTime > 5 * 1000)
        {
            var audio = new Audio('http://www.soundjay.com/button/beep-07.wav');
            audio.play();    
        }
    });
})()

After that, you should hear the ‘beep’ after each code execution which took more than 5 seconds. For example:

# cell 0:
from time import sleep
# cell 1:
sleep(6)    # will ring

If you then add another cell

# cell 3:
sleep(3)    # it won't ring

Unless you execute several cells at a time:

# cell 4:
sleep(3)
# cell 5:
sleep(3)    # will ring if queued for execution after cell 4

Tested with Jupyter notebook 5.5.0.

The code might require further improvements (feel free to edit). To make it work on Google Colab, one needs to load jQuery on their own.
Does not seem to work with JupyterLab yet.

To turn off, use:

%%javascript
// a slightly better version would unbind only the handlers defined above 
$([Jupyter.events]).unbind('execute.CodeCell')
$([Jupyter.events]).unbind('finished_iopub.Kernel')

You can also use base64 encoded beep sound (see this answer and this gist):

var audio = new Audio('data:audio/wav;base64,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qJrx4KOQjahZxWbcZgztj2c49nKmkId44S71j0c8eV9yDK6uPRzx5X18eDvjvQ6yKo9ZSS6l//8elePK/Lf//IInrOF/FvDoADYAGBMGb7FtErm5MXMlmPAJQVgWta7Zx2go+8xJ0UiCb8LHHdftWyLJE0QIAIsI+UbXu67dZMjmgDGCGl1H+vpF4NSDckSIkk7Vd+sxEhBQMRU8j/12UIRhzSaUdQ+rQU5kGeFxm+hb1oh6pWWmv3uvmReDl0UnvtapVaIzo1jZbf/pD6ElLqSX+rUmOQNpJFa/r+sa4e/pBlAABoAAAAA3CUgShLdGIxsY7AUABPRrgCABdDuQ5GC7DqPQCgbbJUAoRSUj+NIEig0YfyWUho1VBBBA//uQZB4ABZx5zfMakeAAAAmwAAAAF5F3P0w9GtAAACfAAAAAwLhMDmAYWMgVEG1U0FIGCBgXBXAtfMH10000EEEEEECUBYln03TTTdNBDZopopYvrTTdNa325mImNg3TTPV9q3pmY0xoO6bv3r00y+IDGid/9aaaZTGMuj9mpu9Mpio1dXrr5HERTZSmqU36A3CumzN/9Robv/Xx4v9ijkSRSNLQhAWumap82WRSBUqXStV/YcS+XVLnSS+WLDroqArFkMEsAS+eWmrUzrO0oEmE40RlMZ5+ODIkAyKAGUwZ3mVKmcamcJnMW26MRPgUw6j+LkhyHGVGYjSUUKNpuJUQoOIAyDvEyG8S5yfK6dhZc0Tx1KI/gviKL6qvvFs1+bWtaz58uUNnryq6kt5RzOCkPWlVqVX2a/EEBUdU1KrXLf40GoiiFXK///qpoiDXrOgqDR38JB0bw7SoL+ZB9o1RCkQjQ2CBYZKd/+VJxZRRZlqSkKiws0WFxUyCwsKiMy7hUVFhIaCrNQsKkTIsLivwKKigsj8XYlwt/WKi2N4d//uQRCSAAjURNIHpMZBGYiaQPSYyAAABLAAAAAAAACWAAAAApUF/Mg+0aohSIRobBAsMlO//Kk4soosy1JSFRYWaLC4qZBYWFRGZdwqKiwkNBVmoWFSJkWFxX4FFRQWR+LsS4W/rFRb/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////VEFHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU291bmRib3kuZGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjAwNGh0dHA6Ly93d3cuc291bmRib3kuZGUAAAAAAAAAACU=');
Answered By: krassowski

Here is another version (Python-side mostly) which works well with JupyterLab:

from time import time
from IPython import get_ipython
from IPython.display import Audio, display


class Beeper:

    def __init__(self, threshold, **audio_kwargs):
        self.threshold = threshold
        self.start_time = None    # time in sec, or None
        self.audio = audio_kwargs

    def pre_execute(self):
        if not self.start_time:
            self.start_time = time()

    def post_execute(self):
        end_time = time()
        if self.start_time and end_time - self.start_time > self.threshold:
            audio = Audio(**self.audio, autoplay=True)
            display(audio)
        self.start_time = None


beeper = Beeper(5, url='http://www.soundjay.com/button/beep-07.wav')

ipython = get_ipython()
ipython.events.register('pre_execute', beeper.pre_execute)
ipython.events.register('post_execute', beeper.post_execute)

The beep will automatically be emitted after each code execution which took more than 5 seconds, but the consecutive executions are not counted together.

For example:

# cell 0:
from time import sleep
# cell 1:
sleep(6)    # will ring

If you then add another cell

# cell 3:
sleep(3)    # it won't ring

Tested with JupyterLab 0.32.1 and Jupyter notebook 5.5.0.

Edit: to reduce the clutter of the shown audio players I use following snippet (for Python older than 3.6 you need to use .format() instead of f-strings):

from IPython.display import Audio, display


class InvisibleAudio(Audio):
    def _repr_html_(self):
        audio = super()._repr_html_()
        audio = audio.replace('<audio', f'<audio onended="this.parentNode.removeChild(this)"')
        return f'<div style="display:none">{audio}</div>'

and then use InvisibleAudio instead of Audio in post_execute.

Answered By: krassowski

Based on @krassowski’s answer, here’s my solution to this. The main difference is that @krassowski’s runs after every every “long” cell execution (where you define what “long” means); I prefer to explicitly say when I want the sound played, so I’ve wrapped this into a line / cell magic (on GitHub and copied below):

"""
Adds a magic to IPython which will play a given sound when a cell finishes running.
Requires Python 3.6+.
Put this file in, e.g., ~/.ipython/profile_default/startup to load this magic on startup.

Usage:
``
%notify [-u/--url URL] [command]
``

Examples
``
%notify # no command needed
%notify run_long_command()
%notify -u https://www.example.com/sound.wav run_long_command()
``

There's also a cell magic version (don't put commands on the first line if using this).
``
%%notify [-u/--url URL]
command1()
command2()
...
``

To always play your preferred audio file, just change the default below.
"""

from typing import Optional
from IPython import get_ipython
from IPython.display import Audio, display
from IPython.core.magic import line_cell_magic, Magics, magics_class
from IPython.core.magic_arguments import argument, magic_arguments, parse_argstring


class _InvisibleAudio(Audio):
    """
    An invisible (`display: none`) `Audio` element which removes itself when finished playing.
    Taken from https://stackoverflow.com/a/50648266.
    """

    def _repr_html_(self) -> str:
        audio = super()._repr_html_()
        audio = audio.replace(
            "<audio", '<audio onended="this.parentNode.removeChild(this)"'
        )
        return f'<div style="display:none">{audio}</div>'


@magics_class
class NotificationMagics(Magics):
    """
    Inspired by https://stackoverflow.com/a/50648266.
    """

    @magic_arguments()
    @argument(
        "-u",
        "--url",
        default="https://freewavesamples.com/files/E-Mu-Proteus-FX-CosmoBel-C3.wav",
        help="URL of audio file to play.",
    )
    @argument(
        "line_code",
        nargs="*",
        help="Other code on the line will be executed, unless this is called as a cell magic.",
    )
    @line_cell_magic
    def notify(self, line: str, cell: Optional[str] = None):
        args = parse_argstring(self.notify, line)

        code = cell if cell else " ".join(args.line_code)
        try:
            ret = self.shell.ex(code)
        finally:
            audio = _InvisibleAudio(url=args.url, autoplay=True)
            display(audio)

        return ret


get_ipython().register_magics(NotificationMagics)
Answered By: Nathan

Honorable mention.

I came looking for this particular answer multiple times, but couldn’t find it here.

winsound.Beep(freq, time_in_millisec)

I use:

winsound.Beep(300, 200)
winsound.Beep(300, 200)
winsound.Beep(300, 700)
Answered By: Krishna Chouhan

If you don’t want to rely on any external modules, you could just use this code snippet in the Python Notebook. You can run this cell subsequently after the first one, making a beep noise after your current running cell has finished execution.

from IPython.display import Audio
import numpy as np

beep = np.sin(2*np.pi*400*np.arange(30000*2)/10000)
Audio(beep, rate=10000, autoplay=True)
Answered By: Hemanth Kollipara

Slightly different take based on krassowski’s solution: play a sound only after there are no more cells queued for execution. Just copy this code in a cell and run it once:

%%javascript
(function(){ // ding after some cell(s) was running for more than 5 s
    var startTime = performance.now();
    var running = 0;
    $([Jupyter.events]).on('execute.CodeCell', function(event, target)
    {
        startTime = performance.now();
    });
    
    $([Jupyter.events]).on('finished_execute.CodeCell', function(event, target)
    {
        var endTime = performance.now();   
        //console.log(`elapsed time from last run command [ms] ${endTime - startTime}` );
        if(endTime - startTime > 5 * 1000)
         {
             // check how many running cells there are based on their css class
             // changing this class is asynchronous and may take a while
             // --> introduce 200 ms delay to get the correct reading
             // (if last cell(s) take less than 200 ms the sound will be played multiple times)
             setTimeout(() => {  
                 var running = document.querySelectorAll('.running');
                 //console.log(`remaining running cells: ${running.length}`);             
                 if(running.length == 0){
                     var audio = new Audio("data:audio/mpeg;base64,SUQzAwAAAAABLFRSQ0sAAAACAAAAMVRZRVIAAAAFAAAAMjAyMlREUkMAAAAFAAAAMjAyMlRYWFgAAAAhAAAARW5jb2RlZCBieQBMQU1FIDMuOTggKE1heCAwLjkuMSlUWFhYAAAAIgAAAEVuY29kaW5nIHRpbWUAMjAxMC0wOS0xN1QxMjo1NDo1OVRYWFgAAAAhAAAAVGFnZ2luZyB0aW1lADIwMTAtMDktMTdUMTI6NTQ6NTn/+5REAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABYaW5nAAAADwAAAB4AAEIAAA4ODh0dHSwsLDs7OztHR0dTU1NdXV1daGhocXFxeHh4eICAgIeHh46Ojo6WlpacnJyjo6OjqampsbGxt7e3t76+vsTExMTKysrR0dHX19fd3d3d5eXl6+vr6/Hx8ff39////wAAAFBMQU1FMy4xMDAEuQAAAAAAAAAANSAkAoiNAAHgAABCAIc9HmoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/++REAAACLipQfQSgCESDqn+giAEhigkr+YoADHZA5f81UAiJdnhmpQDAQBXAAGMYxgBmMbq/n8ggHBQhKE//zh8CBwOEb//IQhCSCAfD51AgGD/+H4IAh//gh/y4Pg+DgIBiQB99YEBB3Z4m7iEcAwSnGMYxjGMYAJYAAqc5/U5zv5znOc//qcAABBDmsHw/8uH//wQDETmv/4Ph//Lg+D4OAgCAIBgHwfB+BAQUmmZVYJWAAOZAF9ABBjiBASxgrzF+EASBIFiR4EtLXL5oIhMCPQ8lkBRRCWOaLsXMHziOQxGMaIDAatOYkBAyyo6OcAavAxRcOiBsoFFoDSYqgLFBCUB5MQWHSGRQTLgYlifQWCISBj1xmWzAAUMBwDIGIAgBBS4MmISh8QXxDeA/AMbDWFzASKB84bCsVwSgOUFuADURFhxGJdMAtGAzRk0daR0BwEDVhisoxTROJGJ8A4+MqZNRWRoNwGty+TcfgSCmDjNEGIqTJMl42D/ha+OSpZ50EEyCh6xks0KpFxzy6TgdQFAgyhipJJaKKlbMv9/mjIZopWfL5f2Xar////qN6X////ToFdIyRWA3KrR2UzB6UADwAACwpAUARczAAxAQiSKboLDgkmFDJ2UEhcwQszFc2pMwAAeVqVgPCQGEwUHzGIlwLAgGwIBYEgGC5hlAMMDUeBKYGSR8BgIIgEhADDYaAwwAhZAX5AXAYZfEfiFhjQtdBCOwMqlFbGwGFgEBgouFUi4aoAzqKANuSsG2IXOkCHOIEiBiAIiAwto5QYzFkgECoP+RggMJOAEB0A6AAgHC5hzjEnWE5gPB5efFjBwoAJMQ7RbKJMpF5IawNtTVVlT4KBYtyDkFTLQpEDIAMGy4gsLmJkZUZkvDnB+Qb4RXU6SBkVxcqkywXCCIIGIuw0JFkknotf29rfWbl9ImC6dKJfJwi5HGn+v/////////qNCqVbQYllYkAEAAy084oDD0JwkBZG18IRgwAQESJLLnQXo0uFpwIlqUPBKkHANgRIZSwG1OwK+dAnqARRq47+D6alNuGJE/MeqtJVHYg2XyqHLd5uwLvEpQwyVxBrliH4y592QOBSQ5g+baQhvrDQFUF8wO11GHj/N5H4HfzOjpX/XJOv/T5ZwHKouqRyHLcBXDvxlvpM0dA59F5rCJhNPUZRrRRlCXSh6WgKgWcEAQz6aA4895PI2GNHS5We19BuJLrUvbSVs8L0pFMwBBy31Aqm2CaXQ3dOuTum7cEQ6zirDbiQ/p+Xhi0FP/++REdAAIgmtNf2sAAt9sGc/n5AAgebM/zGWXxFk2JvmMs9k4lEU1SOmy9lbeSh3KR8nZZfPPq2sKhirL96t2+97SXvyr58/953MVnZWk2dUAIAAP4WErAhws4I2LWLGhAnoZND1D8XzTUx/jeU2UsLsOGmQ9qy6axL+zu04KW3VtRyWUtHyNzlHhdh+tM39QdYijhj+7jSrLVjmcTjb/59h/lJboIddp0Y1Ko7GrjCEfcZmYpJzeesMM+b7vKp8MTt+UXb9juN90nVpbktfd+ZqIvdZgSF1bVPG+4sPhpMR2XXldRyGeucpY2BhcODAIGPZFRrBr4LqQC/MpeVt45IIbdxuEMQRXhmCmwsH7KH4pKCO26C80h3IzCMrzX7lNRY2+8l2nJOYg8MasGiKJCAAAAXPJsFyBnIgQCgIPqDl3wcBek09bhvDB8YcRL9Q6KousCet22eUDgZPrKkLHlf+RQqPxupZh+PNccNohduHsbTlw+1uVUrgo/LkglocYdx03LeWJtLbendKdga29jdkaHGsvkiBEzIcPttTpprD4adEhCVoU0VxcayWfiTgJCIBE1J1+2VtvSwRYg6LWmsR1VRLgsmz5mic6mCsEOQEyxiDKJazNLhljcG9h1cyrH0ppU3NTNp8qjKmbfTiPDLWdwPNxeX/b1McpK0rp62/x02rcYWc5p/c4ZL4NyMH4gF0S7LDy2mDJuDQdICQeq7tn7yxYsoYPzKjTMt4/sIZMhiAAAAACFcNF/DOkKIbRQdMMDHATGmJvoT4bh2F0jqO4zxZsCqPM/ibUqkVgGC2RNPw+a7XhMYjE9MTj/taU3ZlBLV2HvMgnkTJSp8zsSoaWxhTBqzIYMQzLtrXU3cth8olbWGSKNmBgBayIaWiqVAmoggZVJmDQVyOyw9lUteJYN3qW05qgaY7J42z9AemGqgqiieoVAMJd9CYXdOu0oiMI4FIMEUDBIJa932vsPh9a8wyd/4fe6A4kpvCKjLKdVRAA9qx2tgkdGIwx1RPbfj8NyOP0fJRJWJuW19/61fnzKC6vRq4CWIZbLg6GYJgmO5wegfVqymjOBAA+qULDBKjMV6QGhYOzMjCQIkX06ZumNXCCs3H3SpBsAZMBQU1Za20tZHKZlG1jMKd0uc5aEoGgfxpj3KBFVqsTOX2SeMr0xV8LWFhJ3JhIrS9eDMRE1kg64CyBRlkshlPaRzn/MVSzIwMRCY8UImlrnHARKqqg6nijapKG1xv0iTEF0jQtNl0kCSJKFg0Xm1khmHz/++REPQAKM21OAxvWsTsNmcBjetZWcbNTzA6WQt8t6r2KP5EdrPmEDAiBAYEpHpYOVDThJ5CRgKgwKIioHJKBAiKAaPICBQsEs0bMqJ9l6w8xhT6X5omUZ1amGqAMDDLDRaSoICQIOIoOpXiwBMJgbPFkzqsTDmtNhUjEYgukwwUwQRGYIBp/mIImBIFzzdsAfxUydqOLqSzWg7FqLK2ppvs4MMOe76mTWoCZa15/qaIrtZCzGSNjh8kBtyV4MgDcUMyscxdp7tMpsTUZRRER5ez51IsIAKcq9IIT2dCggGWKWOvLae3vFhMDqxtxgaV1Zd2dEIhRYGAxWzDVC9KtkSaS/UGtHUuaQy1Sp5ILjS/mRqhUDjTbqqP6GAjTD2YMnRiVsYRDqw6yVAjeZjQoV62hMnWAGFFpTF40x02MGEVyCQWjylOLACgRa2Rs2V9JW+lVAsdbSkn8BoqoEOgwGJCoYEBuClM1xyM8CwcRKwhgOrlypU/DcX/eVsj7jwrI5YDkVc4wBrOHgMOCZQw5bzPhEAkpcMM5v0IZWfGLhRjiYNCFuQEVByZBFAJWFYUsRb7tL6y62SG3SSqXisC85MKSHFno4JYsiwYicVG57T6Vi/oFXotKEwSvQs2CRqqj5stRyjzgQZEHRYNFYZkWn2ay4TEG3bmLBE5YPJRhmyJhTciaJyy4lurMrYarKWbvwlUsCpOGpesKu9UiDyz1OL3/jvu2qQtljsTfcxsMymWIBgCgCzRAwOCPXCEgYLjqPpbtRWBW9CXQbLCalLA1qzHYKnr9vOrZglH6NWtYmJBMomhyOqzeR4TaGMLmS1uylF59mUyqzdiTJ4fQfMArHTNSkovI0SmiXqCTLRsiy6S2WiBEUMmgoXIHokWRIa5fKCZqUjybM7GBsdPkXFysgRhbM0AwGHSnFqSeiZJLRUgk6S2a/6nRMUTIkyKst1s/RMlPu60jZTNpVvzZZ5Xy8azuym2AAMCTiMAQuBEAQlSZAwU+kLlXUyaCv+J7P/AD+xSD4fnI3MRq3X1aq2VGL0/vO1eqX8MbtPzuWVrvPsUS0Q+Gd2tfzyzydklt7MamzmRNHR/BQgLJLpVOGq7lhmOrUmtJM6VUVJnklMoUAFOJcJAmIsRCRQneQAkNkjIhT9pth6okWPSPBzHLBnfQ4kVJD0hFPN5+de15fuNvX//+f//rcX22xA5GbZq83lYrnFDof/8l2Gr6qEMykABQHaCwkd1hAsFdBhCl+AogKbKBAoDmSstH5yoiDKhsTb//++REFIAFNFrUew+i4KmLmp9h9E4VSW1L7D6Nwqitan2NU5yBZ1WtshkTX+9YtXEkVxOVtWowPpDoBENLiR1Wkp0FaqlmikkVE+KoTiECECJIc8njqR1nqSc1rrdG9bNuUQ/ACxcmDYiosoZYmBrDklsc0qJIoorUxOF0gBbKKh1HWKRucJwEyYwE1tdFSdr1e//1smamcq7got06UrdFgZ2Xu1iFVawABwwKxEgRFVQERVwF2S2aSKNUIgQzGM/FWaJuMe0JUzk9mgYe/RvCb0rd7FpbLZGhKlVda6BsTwdwBhKTCa2erXaurUlNjhgTYxgDREmCuedHWbLuk6VC7fnDYGhQA6CYLC6QjsPTFyh6xRFLC4h7JcaZES2ogpESBjhTHWQwZ8iaSKaJidC1AZU1Sv9ui1X/WbJpmR0xMgJFxosXVLMScdYOyHjz2EMiwABAhRaS6PANEiiUEDBIRIdjKX2xGhijyApDlqkR6frHMswsPcd6egQ99m9/Fe0xM9Y4G65x95clcG0ANzXjRc3r7syvQscWUiOHCAgiCvQONK5aNlJVKQOIUlIGKCnqZXLox4LW0jUWoW4bRaJw1PDMkiSqRiux9MvJn0iq6TJmBuMQGqcgaG++71atBS6alImK0UWMTUtifD54IqYXtQVW9no+Zr5alZYgABQDElNNhCKwAKpYW2R+bYqjf1JZjmN2AK7s2JuM26WEVLdWtaxgFTa1dp7fOc+thUyufv+/+55+iEQK/LsMqvf5z+6/+8/P+bs/SX23EYgWH2L9jHtvncccb123lhrGd53LLuONaOiGsYcK/0gL1K2rYcta1pnTtxWST8ht5VLUHQzZkkEuy3ZRSZAcghQKQBKR02ZRlt3vZdL/V2wUEj///Yrtq/qphkMIAHCBoBYiVYFITIQPQDrVKDpr0yYSmsNP5A0ahUpib+0lNAFm/Xr4VJtH6/3WsULUTAwpL7cpOQ8FDxXNE0dmWhSfTsyaCZIi4hJgSUAcwyLkM2TZJkETxkZvTSdRgl1JJLKAJnQNiAJUeQbxDyOcJtEal8jyEJgVsT5OjhMiacuCkpPBlwgwrRakS4Q8WwBzUFD7fUurrd9FX9SlrZIzD3jZI1jgyW77z3u7Z5iEBwQYjARtXUTJTHS/ZqsdILjM0aXeiBlI5HH6XU4NWbtLUsOA3UFZV3i1I9d67YtqfVUto6wUPFdv3orWyddrVoMOcCh83smttnetFU3NKG2yRdAeOAbDk8woocqI4HEQYeiIpESJ4cgqmF3/+9REkAAFbFvUewOlmI5ras9h9GsXwX9H7G6c6mew6T2DUggDchhuNQdpMCkarkwRcCIwNIf/9Xr//XYLSkPXkXls8MiBgADgt8GHSRUGdESQBnoBljoiTKnacMMyio7r33IjGZX8DVrmW6XCqRUs65+7urdyVW6uH/+9cznHVKjxGKI633HX75hle33P7lvPO9NQy1MOFjBXA5kvRSls9Lcta5QVfo8aT6buO7N/n3a2VK3AUXzMBRwY0WtYU3ycjtTMrZakzBFWD4Q056JXH41S34Id6IGRPHSwHXEegYZCC5ocKTJbrekZspOpMrWb6SdJaQ+AvYRZtvMbY5tZ6RTIiAAA4AOGnMNJMJaUDEGjKpA55mZVT/UBiMOMwWFe+kfWWk5AcmHIPJDg+c+hetrJLRuy1quSAyINCYSCENdkl7KpevSZdMqkVEtAIAF17qUplGz7qeihX1qcoAPAgFpiJl0boiwrYRsSpPEDLpfKReLRWQRkwalouFAWcGaSpLH0bClwcZdrf1KU/f/+spAMCUVf/NL/u+jKZVLIAHCOAUIYJABCOJBqgLjNKLTFtJ5HdK+7YkLSoPkc1utuJ5aps/uwYj3W7l7VMYwYu5zlBIXRHIIh4Wja9S+r0+o3MCHkyPoLHwGKMHbKpeZPWkec/UmYMp+panQJMBYGgiZAwTYW0ZkU8P0HSJuKgypdQLzKWcFmEkUB2j8QY6I+RSOpC9MQIBAFCsTqTO2/9dZ6pftXzQrB92b/Oq2t32aYdUwABwBAWJKstHRTIZihSOqogoPSxxZSH0slWBpV5zK2FEP1Haj2vhfE2n38Yv/FzK7f1TiBFQ5EEXQbx+ravVutekkicKx0wAGUk0fRTbZBX1vqqdFJIojkA1ngZUsKBLgIRYfILgFQIeIuQcgRLDnkRSKiCU+UiaIqOAogsJTRZiwOQBFOCiUyNUl/9vb/6smQFBzPYK/dXyXLMYYAA4gB0YhomNBWSHPKAqdFBxArbZSab6uPNuzDEgu6hm//+9REtAAVJGBS+yOtmJwrWm9l9E8VrYdF7CKWYkataX2H0PRZkXKvd3ImECv4duPEU1IXLf05AbHoIBQAUknFUWdSNST9bLUxkgRMPMBIaCWsDcvBIxllnjFs1WmbVGRsYfuitEpgIUAfAiIIEDCxgL9iNxXhmS0X0pZImfKRLGw7iJEWIePxIjKidVpLPjVIOED4C1o1SXqVvq9jek/V82K5DBAh+/9HN8q96XuHYQAmMzYYaHBg4oGNMAoQS0qH4ZgeoNlQnUYyQ2lSzEAQ7sM+p2ECM+6VTmpgZ0qtNAlxtA0HAaYGRBPvpNWq1SN1GSlESDBwhY3e3ptd63rt2U5MjnAIggD1BAE+RcR+J7EFhPgswwI4qFdAl0SgTJJF4ZcmjI1HcIJr2THgEC0Tir+//T//y8AMFb9N25v8eXdQwABwB6wqUuZfRVR7HmtCV+BiKmCg02eclkcgSSS6TZTCiVmrlcs0xdBaB7O0Uq1FMpL1dEri4gQqAD+OI1SR/U/u6kUDMjiEBvCBgYYgbQDAWZGSKpsaq7rf/1qRNTg/ghIQGGQqMcTIaGMaGpkEFvLy2nSKuXThkMQpE8bTIcogtNAzMRhgmOgUCyP9Lqvuh//qIY74ku63zeqdwgCBQAdAWGFjICzMMu2GJGhDwiImTCE8s6rSG4OQ3ZYZvY7EFjPlnfrX4iPoPTS9klOcOoL23MR7DpgHiQDI4PFlGpsjVqpKUlutS0EnGXGkEAODJ0F36kH849SGigmpM0K4ZgAa2h2C2HMNyBieSdKJePjMnEmnEiVN2Lqkx8j63MCHjtARkLvPJW//1f/1E6BlMkv/56ae+RKdlCAAHBCBjAAQFUiPBbsMGzIOKy31gGhay9yoHg2cgWIvY5ze0nbtySqPA09Nv6R0p8Sa/li22BYSTgC4BAGcQqZUNtS1q60O6jcqEXFmA4FASCwGAEQBwQ3g3MHeRRNbocpm3/dZgsuESNQMEiIEW7DgiIBaOF1AyQNok0V26molI3Wgusn/+8RE4gAE5lrRexOtOJyMOi9is60UaW097Bq2Yritp72NzvSAxVoOQwPECEsCUi+hZ+j6/Mf/XrPh7f65t74FpzcAAAcAzHARhCMsolIXjBy3WCKwJ7RmCZ1IjbcqJxWBccWj4yCe1K3QeMFan/3h+X/zm+bx1+u1IDgWBACHnghiZ8Uv2Py/uW8su65r62u1ZiZf9rwKAjoQtGqBbdnv/2e///eP/jS/+8crNLBCBYIgjgDliEBl4A4LRaTAXs1yD+SV7LOv5nhNtNn7jmxYFBNM8kOkZUAwgLeKqSb//6P/+QYDRk1CvqD6rMrSynVwwABwQEJP1VULKAChlpQqIxR6NNiXQmi7Ty1JPajsOZW1G70SleVO6Dxgr0vw3fFByiA3EfsfIQTiDQSgQCBDTJFBaSH/1pImxDQ8YBQaAwafANmjsBgHCySLTZeuUf/uzpF8ggBo9A0SEhOZcC6Yog4SJjIf+L82d8agWmoayUGKBEVAsAD7f/+Zf/Xc8Ig72i13lchU7uGAAOAOMCJqRGFAARYOTKfFjaktLBNQ37iR+X5SuKXI0sK23ZuRXoelIuCiy5sOaOjsJ2R2PioBe0CkcABQMaUi6pdX9S9egRcTARqEVoGvaCkyYPH31o//1nESdNxegL5APuZEpEyG/BaaS5EDM8bTpOtr1EONlIEDBY0s6imyYNSRNIqq//zb/+oioCRSTf/L1ZXe0ZMKMAAOGNY6NFECqAOAjwHEEgq36UWSYt8dCVtZqxeEUz9L1aFduZXpdKRME735SEwW1mihv6ziBBQuiBZKC2n2f/61stkzAdY4SAAYDCYG2woF3jyfNUm87/9SKR8mRcwQEcDDwSHJKQociw4C+eb/qekyI6hxbolAkgTBAMAj////+8RE2oAUv1rP+wOtiJeMOf9gdLER5WtB7Bq2IhYt6D2H1Syo1rb/0j4Z+36LzK9TyGYQAmNKskOhQUOhlFutEGqqF6D/K38wi5trC1pR8xDYjXkzCbRuBfNWykKTukyl3qWWDAXCAQAQJQASUiRqbN/1JbrQM1F0doDgOBZkCSm55X//60LHxogCgAEUqEzLQbWKAEEDrp//TRScioUAGksoHxZQeZ2//9v/6i8FtTV/kq3+9lyUc8AgcIMSnmTQQiB1jIsugWeZ+idIFwxm4iD6MVhNNNoGmN4ymC0S4B4kyWolCuinmpqO/7OgS42ggIAZwKRBNT6P+lW7OaE2IEAkFBBjAi0FpJoumVDzFD/61OalgBYiBfoKorjeE/kHFkM3/W/ofKRWD/BQKl//8///sKYlO7nC+S8DAADgmMKXg4ovMQzTNGoJesjYZto6XPaEVAxDZMekXnQDDD3oE4KDrAKFG7VIvRe6VdrJlwYQfYGjIDgHRWhTSW3//uaDuGkEBADIFygigl+tX/7rOkcA9yB4SAuMuB+4lgygndX/6kmRKwN/8yJwCo0OybM///T//zULUFb8vW5fHd07CgByAcskKWbFjGcCgqi7AZZtsqFsSrRp/JyrN1MbDntm1nqvDDxgsU/h9cr7DHJhX6RiVxSwEQgBjkAjSLqR7Q//Wosj8QQLqQMEC0DbwOBQAjSKpuir5q3/u0wGfAcKAMvA0WAvhc4KTHsVoir/jdP3ykLg8sHxfAwCP//9D//cU+c7/NcxoEAoHDBTl3mJLgtVDnBgVMIwAAZE5KemzyXlAjUStaqPeLE756oi6DiBaf2s6Z/61GJJiPgFzgHZEili6bL9BfqQ8zUisdwIQwElxJNR/t/9kkjEfgScgZb/+7RE9oEEF1tQewyiWITLef9hlGcQ9Ws/7Bq2Ifkt6D2H0SwIOFQ1SLEiZoN/9FsjRQnpk4EyZF0v//q//oE8Dcs9+Srfz+SdlYGAAGBBHGqiObZihS6S5hmAmGXH9oqccx1r8hg+pJJZzqj+8akikLjRkTZO9+9S01vWFncCfqMy0WgKgEApDDTNH7//rQNyACBwuUA4mAaYGocoRcwW7/b/6kTU2I0ELgDMjyDFEcogpBjBN/9alN1ix+WHH4GAn/////2H7PzvlOunDAYHBOViYDXx4S7wudUjFUv0lbYACNvXVRoVodz1vbAvSH4iQW5Ij6DFaXe/o/oFMkSWAiNA74QW80TU/b6l+tBZ1IkwGlZb0v//7qSPFsBwsEbAaJsKUJcXOM4zP/79YnnymVASDhpaH//1//7CwH8/9643YkUAAcEBrlCIG/Fnyy1saJXwY5KVoMEHXEhqkca1CTSsg0NUtdRW1FDJNgCTkz4Rbvaoq/3JAZEEiIGECEykilr//0iiMMLTgBYAH/ShjIgpsikv7f/VQMhwhYkFVZCmgmY2C4M7/8bjL7DA85L4Xk///0v/9yNz9/TTaeFQCBwgR01NJCqbkhF2mIaaCLKmxQFdralNN9RNP4uh7OWp5IaIFrACHFdDvtUa99kh4EGgUTgZokOaXkX/+/0EycHkIhwCkRPqdH///agfAoMALCkQNx2DPmZAF/+WXbnRcXllEcIUDN//9//9Yn5qzf3WfbVhwABgQWdugBM+yrT/+6RE/YAEFFrP+xWmOHZLag9h9FkPcWtB7L6H4dUtqD2H0PTI9SJcous5nEdFvXsX4jcPSWGtc6o9l/dV30buAxS/DOhLBi6LTA1+pZxIuifQcqRiH2Zqv/1JmA5YqIyAGARCBtURBe8hC+ybeo1//dSRDgInwOGGHPNBW5DCIl9//LCPuNvywgGphQS///0f/9iOz9/13rmzQGBwzuRL7I9hQGQIEr5WKk2w3RYEMEZ2ZQcw2QzZVW4GhYvPzo1CouhQQWnrZXun9SziRdFUC6kYh9nb/rV6DpGpOhkEAqYRFJlf//7IqI0JmAx8pGZMJk+c//9zATHzqYY4HBI/////6g+V83+9N25kUBAcEQDj4lhJ1FNOaPLHQCpK8sgVCNPu0nspXJ/OV5W5oxK0lxMgocKr0DV/Ub/0CXGkERgGAAkwtSf/+tnNT4wQvaBCCBmkArUvGqSP1f/rSSRIYEBkAZATqRKlY8X2b/0fWMp5w0DRxDf//3//5jeZ/mfdMqAECgkJBGUYIu6AizSe4KWn2VC4KpN5/u26rTYgRFfrtOVTxG1VlM0z1R4Clrb1gVUbj/pmgww5wRaAZI+OQdUv/1o/TMBvjuBoKA0BUgCcx///qWiXjMCJQBU2NI//+6RE5oAECFrPexWlyHJLah9llGcOxWtB7D6JYeUtZ72BUsQQqig3/9XSFL90SfCRAPt/////1iE1zO35ntlzwABgAVUFPJpL/QPZ8W8pokXTqCqiJFvzAbHamJUctnoO2DbEGEuTYAX7Z9j7KWqsrf1pkSDIQEpZEjW3//qWWDIgoXCg/QJKWzyv1f/9i2ISg6yQh8li2m5X//+l6jwYcef//lq3+6D7Kg8CAcMyjkpbcmEI0pwChG5phi1oopZtfm42xJEve+jEKeSesqkXYCtH1+gujWdV+o6XxkgHhgC04txVS//+moxSJ4MXAtLIki6H///rJwMDgNZiJoDbIocRNP/19EUB6B8IQBU/////1uHs7vb8tt2wwBDkofA4ilAjCTNEqu8X+YLeWOox39Wqb4RVnIumot/ndZxCAww1vn7M9cRJ/OGEsDUCAU+J9CvV//QTLg3w+wQLgNoXHILKk///+pSYNAwByEnEyYI8+gf//9aXzQNaf//8r3/vpH5FrgQjlgeRUsLmf5L2IjyZ0W5DMkDCZjaTS8p7IRX7LLD2z5xQougWIHn0/rNP1JlokQahwApRE0P//7LLp4SMG5xnp///9JZkEw4fEmopoGCf//1EP84fE9DEf///+6RE0wEDVknQ+w+jSHBrWg9h9E0NMSNB7BaWYZgtaL2H0PT///RF8svf9o76gQAgYAkUAFhQ7sTw0ZNSH3LWtiqmizrmE7L5Iv59M6ywUxvKtZh4ugpB59vz39I0IiCRgH4TqS///0jEkxSwC5QDqiRSxVNm///7JEyDUEA0PLyzZC3//1FT1sJaPf//09v95T+5T4EY0ToNlATV0oBxpYZWAg6jm5shzyXYmVDqFO4vYhFhYxosFdFCKcA8OZL1tXqNf6RUFwggIgaIEQVJFL//63QGiDQaI9LqTof//+syBoBAssKrmrM///1lLrLBeGbBwk2q//6v/9x9zXdzXmWp4AAoAcMFiywIpFcpViK0K7WFyVywiDhYIk9JjcjsCxSzOhU1s2fMa0dY0DS629cCt4QL+oQcvh6AEkKPB9rUf/1LQIuMQVEBIjA0SGhB5ccy///6nUOIFC8SbFc0Qb//6jf1nhaRNW//8rW73O39dHAIjtC3GYl7EKeOOq10HCWZTiiSYdjNajdn4lrBJ3vCU8O4WMKdwIDCDW+f7PXQ/6hBRfC6ADJkbB9rf/+tRoaikQDkBq7P///7JizARRSo44CbNz3//P5O6qSg74kqv//t//WxDK3+9WzqgUD/+6RE24ADL0nQ+xKlOG5LWh9h9D0OCSc97Iq2Ya6taD2BUswAUhg4hCBLoYqEAU4FENpJg0riQlPr3gZYVXF1sKlH0ziGfg6wGAxu1Svzb+xXHcEIgL9mSP//9FIhBBoEE4G2JC4S8i////rLgaOFlR1A6cQ///t62Loq///p3u/3btyiwGBkR4xp6SBLflwEwWAioIHQryUoRi56hMTWxDXuPARDN21lI7F0FmHn/7/uxJixBDICJyHLU3//01G5sJAA0k02///+ZhIQYlQmS3Pf//WW/sNU1/////8v5X98N1zIpAAsBzHlyiZNt1LEg2ftJR4BRFitFVrqFlj2VFJoAyGaWE+ZTqLoKFCq9aP1v+o6mXhWwKVySNX//+pMoFYzDVgBcUWw+zt9b//7GwaiCIIa7p///13zqI1AzTP/eIf5XP7vJfunWAgG5EqQjE8xQE8vZDkjqmomgACsFQU2uW01VDIrNTSECk3rMJcmcAD4Wf1s/WbfqOrIMGXgVLjaPO3//rUibE8IRAMKys3///2cuhfoCx8vTZ3b//7EhSROHSqMQaRpvSSOMtKtGrMP/+aV3e71j+11wEBYBiya4oSqoI3VorgRZJHS2EmrPuy8EOTb7yyvZLNMcZX/+6RE4YADEEnQew+heGArWh9hk2cMzSdB7L6JYcOtaD2X0TRKB0U4Akpkiq7es0/plokQz8A5RfV//+yaBLixBDgIDlJFX///80BYp5zRa///9+txdCBlZn/63/dD4DA8B0SysYYaZOsRONkVG4Q0nRKAMRP4vfWiGCqcvtxRe7mfnrOUPGZwU+HdIJt1nv6ZLDtCFoG8RNoL///ZZ41FPBs8mr////IGC4jdErmBc///SIH3xQiH1N////z95/eV/kMqAgKwMBHgAwb8K6BgkYmTIT1YPfBQSnmnCh1d0HuhZ7to+Ov1nEGZnMkvw37L8T/QQREL0gMOCqbe3/9NIzNxxAPAANnxpGpsz///+ZiWCyUF3f76v/V63I4Yv/VXf/qvZbrAQDsGIKvVqL0VXRtRrjopIiOhzCo3pRo03FtJb1cSGvWbF0GECq+vtUa/rSNBwgQDgFbB2myKv//Uus1FChQYWn////mAZ+GWk1HTJBPnfu1F56smPUmKcPH//yrc3dZ93YHAAFYU3AAwGJhTxtfWNEDtP/AYwNTfQ+ilcjDaEafYCCC+i2jMqOLoOOFp86/3/WgfLYYsAUkkg///60DMeBbQTMgbcUQdBav///dQpwc96TP7f/6frcn/+5RE8oAC6UnQ+wiciGWLWh9rEz8MUSdB7AqWYYsk6D2H0OxBi/9d7/cqfmq+BkPAMugcoewIkSkE/SwrhxTRIyt30Wo09pOxFhXBeIL/NoJVAAN9n4Umqpjhv+swLZLgkCAGykQdvv/9TqMnGcAkBQdl///+yQdUEQw1lVFA5/2apXlnzBx1CaJ/6N/v2W7ac4BAagEmLooci4hJFtknR4KpU++I9qMa3jIn9uNwi2dRF7CiiZDpF8Da5e71dz/9iuO4GsAXl5F///1mRIClQIDwNkEHCXkf///0T4dp6a3fqW6/uvaYK50+LwTV2pKa/94628lMDkZgrgLKmWIICBU0eSe1mifItVtWcMZlGcSCyv0VZnSAKJUU+m8/BnwDjFdX/f+iS4pYENAPZyDGKS/Uv/2embCJCqQ1f///SIACpTznk3W9Ovs6DqtqNvUgMeKr/pqu7vaf7XOAIfYBToQLPQCrpeGib1ZCXWQQ4HFyqgjjbY53v4rIFmKH5xL/+6RE3IADBUnP+w+h6GRpOg9h9E0MkSVB7EKTIZQk5/2XzZypDvAET//s/zb+tlEHBSMS7f//WpMwLZXCywAcCOw+1v///Lwd8QSRSN1UF9aXUi6T1NM/XGoPV93eUdlueBUegEbdAW0rA2Fi7DFqLHDhczQy6ZTxY1A5qmLSISN0vY2NQWCHn1uvUtL9R08gLLBF2IU89//+tSJ8vCvAHIyszr///9RiGcBq1SzB02d2WmknZtSSbpyd+wvyRvN70ztmBwIHqC0plAYlQ62JfcTdBI5EzbGQhc1MqE6H0y82VuNHefGmVRqFEFp9P6zT+5aIiEhhkI6pf//VWkiS4sQJyA3lIsik3///oGokIdg87f/3q+3smGSD1soXpru7mTcuBwIB0EFTCZAYyscIODqhYi2AQCOPki6dhUjkmjSmYY47UfqsGEuSOEB0vv0TX9nJAW0GlQTMUUl///oLNDUU8GxRv////eJECiz8nkXSSWZHlOs6urW5+xv9hWV1/v/4ft51yCJ6DYIu+CDoyRcrOY4xTDWLhaRV6zFdqx4fuWxVAMATf1WEpRfA2HL3W3qPf1IF0Y0FFBVf//+o6iZDuBS8QrP///9RuTIOAmtv1PXXUtS1LTr9A3DyMT//+5RE8wADC0lQew+iaGNpKg9h9EkMASdB7D5s4Yakp/2HzSzRXf3su3kGgQDwDe2Xp1gUi+QcpLKXDgS9tgUAqq9xV23iEyeECMG3rG7qRJgMUfX7fo/rOIERDGoEvg0T6P//1LUiajOgiKFp////7EDBYcbzc0Z3TtWq6jq0E0VKk5tOsH9JP+kzObuK2ZKjgAWsFZBgZjEpo/gs5c8Yfd8smQIxY9jOUxVd6tzBKak1JkwM+AUovs8uJtzh3+gWx6BoYAOJcX6H/+x8eBPoRQgYkYTZxFX6r//2HMDBgIiJaT26P1Kf+l6nEuJDR2a6//4pzegcBpegHFSVQnh1VZAcYik8ICG4mmqJ1Xb1jWXttUltMFTNAtqRDog4B59H6m/oEuPQQKA8DIotT///dArFYO4AQEwXb///usvBMYekZpLZToOi6bJHkzxuxxTsmbF/U62GdE1sh4nzas3t97/so8AjbASWyEDhRpc9IietS1IO4SCEhUuT+z92pdn/+6RE3YAC/0jQ+w+heGYJKf9h9E0MtSc77E6TIbOkp/2DTkRNrax3Mr3KadeY1B4Fp9b/N/63J4PyAmZs3//6lpGhOhkEBqISLt/3//pFMNGHLMFuhV1fof29RsKGFRu820bNxlgOR8AEIjCXqZpH3oC4nH61fSdyrO/fzylTLJ2hqoI29rRSKItQH5e7t6zb9RiVxkgKigAtY7jV29Bv+paC1GwzwKDj7f///1ChQWAHnKiZ2iyJgXDVF1Ga5x3WZOc1POjoDtSsqBn4mN3vfxxu054CC1C9Q5YBCxAOYt0vUXXM4l2DOF5Ptzgt8I4ocjsJaz4rFhIkXwNhy93Y42srf1njwjMCzYrt//+pNAlySBoEACmk4nX///9RHDDektSvUzLrq9F0/UkTwqGZvfC9uSOB4NgdoloiUcSfO84QhO9htFxUwrN85kNQUYdbLO3iElPq24Z2EeGrk+gtLrV+ozLRaC/YB14ibP9v/2WYnhPYZpNVH///6ZDA0FqCbM/V0KtVtZg2ioUILd5TwzWs7shc24HAQeoJnhU5tgzUMQtFh1lPOUgcRab1fMPCyr9wwjhX69oZ2JMDFfX921LPf54iIQBCyAxS///uiWxkgQoAD+RJF////0S+Jkn/+5RE7wAC7ElQ+xKlOG8pOe9iFJMMCSU/7D6HoYok5/2H0WQz7aFTalf0UfSQI8hKrs41z7kcDw+hPkdGJKbnQCMN4yuEC5hkTasbEFkJIy00H4pq4tEZ2C7Tz6bMtq2/uVBkQSsDgCKpI/X/+zlwkQz0OWN1LS////EsHyqZLsZLWb1Oux/dqDJepx9G3+rN/vd++6PYgvoFRTAmMU9ey9VB1I/TpBqjvIm3ZntUsm38N8GMrIeLRUEahSJ59b+o9+s46JVCnS0///6aCzxoK+BPRXb///9ZsKyLiTo7rV69f6m+piyWv663u473dg8DoegmCdJALasSw8fbgyufWG2p2nLDETl9WLS5h8ouXkVma95jWiJHBZWl2b1Gv6zjmI4AU0jwfb7//QWpkSKgNGCqlX///9EjhD3opqCDMrsiMLTrKhAx+hhIb5Nqm+3jzcxzwEL6FZ0VTGZ20EqAswblELpk2QAyvHruj7CwuZ2UA/MW1awlKNYAYy93bzj/+6RE1QAC1ElP+w+Z+F+JKf9h8i8LOSdD7L5l4Y+kZ/2KFrSP9j5JBeAYBMF///0XLQ7QgkG1C6kj///8xYTyHMPKTdmWijrag7spKpqH0CVLUdvci9tOcBUPQGTXarEIov2z1TddjTlANsJanh8ekEETjkSm/VaPzUssEeHpJ9CrWl/sWx3BOgKCZLU3//3UblYRILOoLdX///1FMSZ4dEvx455IREZ+gtZqSJdBPuq+31fcqAvQC6BwhpNIxZLqz4npoXICU9Oyvm18m7XqIqq9Xw3nYNcG1jf3QuyR/+tMrCegRJysj//+pNSi6LlBiYlnb///90BVI0U0nUklqV2qdK1X0DE9Ob2wmZkDgYP0AchaoMI2ie0lfdv3hYLaaA1fn9rzsQcu/lXWFc9bmTFdAOye9XQm/6jrk8I6AtfHo87f/+mgtSi8FAR9v/q1f9aAeUYxFVveeXy5fpN39WppQ/n9xf2V7v/tntyRgEJ4DLHAOJQlOhYIaDTfgSCom14BIlB9i6UlXsiscJ8AwDyri0zSwOTz9D1v/WeNRIwJxv///vPkkGeA3NQX////YXgsJosugLDAgdD5MDCe3W7MrmZhPmWx4FQqAcZealbPFsLraO90vYPpng9LHcL/+5RE9wIDA0lP+w+ReF4oqf9iQ58LDSU/7D6F4X4kZ/2KGnxs0t1kFinf4cJh+7OUpeIIPTY/6LVr3/QTJYaIQkwNGKIOpbez//RTJ0nQ1oW0MUqv///uRoeBpPOtEdTOmPWU/4N77v1W7ag8AiaBf4GWMQRGS0U+pmmqu5+UBXZw5ocyK0x4XhCzNFqwYR+pIBHtr9bedV/niqHVAaIav///Us+TwcUA3ckjV////zovhOYqlZSiXck763aZzu1M3cVcDg6gyDL1hy3YROdEiUXNeVpGkCi8jE6bN/MXsgOL/SMOg7rMBrg3WM1fXdZ7+keIiCAGAdMIibN9av/UnolERq3///+xGCSoSOLqR7pylKY1Ud/VAovxas7u5N7MdcBhbAzUziIEBboOXA67IU7alPtUTGgeZrSORzEUpblcRgtqrOkYL8Gyi+rpUrIGn9TpjkAi6aP///ZMtFoOuAqorof///1OKvP0btmc9DIfP9hMM/EM7+omk7d0h7X/+5RE6YAC0TnQey+DOF9pKc9igrsK4OM/7D5n4Xel572KCnwQsBy6wBAl5SMQNAumtErrxfpRZzO/D8DtfqRi3qq0fHPuVmLxgf7IO82/0T36ZgVyXBAEAC4jgPoff/6k6nGoH0b//dla1XsozDXCE6UjV3MazPBiNau36D/NPV3itVOo4FCkArJVQKuSpcWYeKBIgLFuEIEmL+Fiq+8hjdnXWQ0a3c8VyoCzklnaX3Z3dA7/YrjSBqMASBJpaurb/vMUiWFKgULgaoMOFJH///9EUOI+TUa3VFnnvOVOea6FccsFy23PzSWrqWc3chM3WPA8WgBRFMi8SFqOLaPo/0HNA4sdq2X26tnjdZRapl2r/rWZGJHA2LNugj6n/sVxxA1YAeyeRf6v/0mL5JBnQY6cXdXWr//6iODxvciqhcqq1XTsab7gh/XVyd3Kashh0GFYE0iMowAXuSceMWAWi4MZYgg4WNs6+njIq6Qigd7i1hKU7gBHG1/G1eJ4EJX/+6RE4AAC+1JP+zMs+F/JOc9igrsNpT017FDz6Xekp72JCnx/Wo6iXhzgYfJdnb/9ugmcPpiEgDV0kE7////SHyJ8RdFrMtHWky3W9V0P1sSrhD92/3ccftu+By/QWeMZBx6w6ar/M4gteKkPbGnDTYwG4ywsudqZ+sti1dJZKCngLhDNWpL3P/1soZcEUYk2/b/6ZhUo3Bwc+3///9iPD7PMqvZpSEZVjXFHfFPK4QfEd3macXksOBg6wcC4JNA5TIoJtQtmKqLh8zwPNnBNqtdxWGNehImTVqSog9wFqPT/3lz7Xff3PEiF4hYcZq//rfomzFsdwQwFuzJH///9SJMBpJo7rd9SFCnVem1kEPyi6qu8wHvKUUDhRAP2JZwqBEVDGwl/UJZfeN+2Fg+sZQ6dmXuTEp7rjOZql8mAGwlRPdtTIv+x8eBOINPgUgTJkil63/3TUauaFUP6GEEUlpf/V62+XhLRciM5O61LlR5Tqbf0cF0+lc3L1N3KgcAi0gFQQeAIi4oVQx5IAcIpVSMgZ2RcF8OpWxoSnxDEXWVqdh7NAWiWn7rQ1mn9TGonkEWCq///9S5sLOBf5WZ//S6k6vqNxFBKNpSZgW3UC/TcZ3IuXMDgYKsHeJnWGHn/+5RE+IADLUjOey+ieF3JKf9ihZ8MFSU57L5J4ZYkpr2ZinwxdJBOqEwATfEJLjtFHjxFe4qZRRp8lTW2pnydJoCQc26DalLNP1nFE6IVBTwNk2b//3QTus8CgU9///+1iiHgZBpYu0QOBtQsDgQ5D/MxW70VuU44FExCH4EICgyFlrQUz0JSwxeayODHx8rR3ymhMP1x8s9M1fKUdQNizbsy0e39R8+KEAmTR/1q6/dk0FniqGfgKYvq////dAawc9JmQUukdHnGJUAjIP9MXu655eKeBSsgWpQCICmShjlwL3ac19k3EqGz8+GZ6Ez0hrUVxoKsa3olgXYW0Oq0Gemmpv6BLjuCWAeGXE1L//+lSNhWBZaWr///7j4E+oV70S1QGmLtzyxWL6P0qojeyVvJccCCUBIIWINKg9IVyTGNIWCVjwQgrvjL54yJxO7oMhLNUWR8DXCyZNWg57qf+x8ng1ILFzZH//9aajxEQxaAzQiKSP/qb+prOmTYbEb/+5RE4YACwTlO+w+Y+F1HKc9h9EsLqPU77D4s4XYipz2JDnztMO4+7wb4Xr7nZhxlywwHMyASSCCB0VGszhUAqsTRYonDpRxCKvWbSltWYVIb+DRsnugTZQCx00fWyNVBP9SzccQWXAKXx3Gr/U3/UtVaRHCTPr//urZvNRLhO6WkPi+kKDqAVB04BD2t63saN22PIo2QLAFhRJkDFEoy9BB3z6AKYQ7VKzKGKwseIo+kZ0DcuAjUtPpq1Zv/WyibBaj7f/96aC0D58UIA2pon/9P96nTU6hKxnFpvX81AiX1oBPkYq7wHqaUcDAQA2UHpAhQOUziNU7L3IZfpsSfH0m3svahFmtdfpvXRZImSHASNlZtJk+o2/QMyWHkEJEDihhxmid+z/9ToLsPsNDP6v///rMhVvDGZXT2pe4LtAKQmpu6wqrIUcBBQCWhd4ytUjBpC07Vg8OZwNxqc6YY4qOP/FSjYd6rZhGeAO2VmzZpiazpEv2c8REM9D8DFX//+6RE1wAC1TnOew+Z2mEn+c9mg58K8Pk97D5D4XUipn2KDnz/9Sy2O4EyAFnJ5F////5RFU2vMpwH/aP3HsZu673dsOB1cA7IccIKTAZSqxkTxrqRwsCoCJFrs1e7nJ8LctSnt1OdLA/iWL7PUyl/6RUHCCA4AbCCpI///ouaGolocgtJl/7MluvUm+s4KHDfjerahQ4Th3sqJEETebvTO5rHwQWgIdxMwoQIKuKTNouhJYOTZEYiYlPwmTJBdpp0qGYL0X7KUssDXC3S1Zo63Z5p+s4giTIOYef//tpqWyiBgjEW2/+vp963oKGoI1QwGE3sLUprd7oXe6DwOfgDGMaUkoyWlhpoCBqgBQV6DPK3fc3TxToxcqdHkMOt2oF8oBaSaPuy9S0P1qdMWYCPpUdvv/9SdbmAZk8//1LRZJezopLZMOqGMC6j/zOd5GSlFVXN3ah93ZHA6+AFJDqTJNb4+zCYCk3nRPyGz9CYtdq5jOOCCDSWcmhEzALknuY6L1N/ubC6Eeof9SSf9M0ctDtCChhAopL/9RxGk+rzJESIXGbPfFrmvKm6pzd053aYcDyaBJwICHORGQfegtIBSOuZ4jMKVDHr9dOaNtgzt7JGJkB5n22XR1t/YtHkAQj/+5RE/IACuTlNew+Zel7Iic9iYp8LLOU77D5soXCc572HzSUEuZIq+r/9JjYeQGoADZeWkn///9JEVnE33lHGRRsw3KP8vJzeXiPeWo4EDgAphpg6JjZSo6AqB9odyrKX6rRYQuIfs0YhSE60i6RwDRzZtDSepP/c2E9AiebI///WktU1GVBehadv/v9er0hijsJDnxiKiD47t1YzNc7ALCIQBQ5bduixtsrYO9Xp2qjpOynWcNuRRSmy475VJHSUJUSxfNdJDX+pcyHEDE5JM/2/+mhamG/P//qWtY2o4+2nQoANr2Q2WRZq+tEZ12tKy7zWvMpngMNgAWRVMFEa49sfl0Rh9K7JO0v3SVobxp5ZIonTaY3Z/uV2SKzg11fDfqT9b/1swxwEcm36C1pKtddbGbHykDQAUJh/99qutbrPMYDWG86U991DcXpY6UZSzOZcvly4oHdoADGsoAgjDKKzstIfmLTwMWULsZARdO3zv2pB3fmrC8hgDSmj6a7/+5RE9IAiwjlPew+I6FhnGe9h7S0KJOM37D5j4V6gJ32KLnXPp/0yQHaE7ABIpqX9b//W5qVRERbEVUrvU2mymt7pJMmsZ4SdNSTKSO1N9ub2c0c2VkulXRDgIVAL9ByjD0mDGGlqnYLXdamR5b/P6s5ZJgqGCjEQVEf1OgQwZgBkK3QoIPrMzL9SB4iAYPBvAOw0bq/9TU0zNyXC8AG9ily+6v+rpS5WpRxaZmLWKUMG1/cic3eZ8vCGA7uAVMFSGwKdLOGCiwy14VOoNHE0CI8b+YhiyYk4iYIidBx9zMmgWDm6b1ppJNWr6kDBiRDWgSkClC4y/T/+pkEDQa4gwnEK3er+ynUtVClWYuIc86pFbppGa0VPWlso+uaMprtrpakSRZYQzYwB0BgAJXjzMx3osiGJDEjUHeqnERYKADQpiK9UHvSaABFDoGo2CIN0CBIkfvw8dEX2qY4EuwdCBcKq/fowz+gckgqW83cL/GgI5/dHAYZazxtswZatSJz/+5RE9YADAUJOew0d2l8Hib9hkmdMFOcz9YmAIaEiJz6xQAX1S1cbPVy/eaUSsD6PXLmnWx4iYttQ//1C1Rpis53/vM/VNVt54d//+zz//+ff/6shl8otSaX3f/BgEzYRDdTFRMTUxSCMSgA2dsSgCAAmJIimBjBdkx4PL7mEAoidlLyVIAAEY+mCyFmRCzvGKjIYFwcn5YU6MmDMEu94TdQuR4GlbrXQaEZzlLUxpDLgcfdNgH7wJg4UV/quWlNServ68w4UMobFQD+8//UHhzDG3bHACNCsC54k32C6jFBZjP95oyhEae5W40xZQCCq+TodBuv/390nP//7qr+3nddwKr2tShiNf///////8////+dl92tYsV8qOk//g6BBCD5ABs/////ywDEwIE3d4d3h1CUpmUBYLAkGmlQqAUUtKoWaRBYEg1hJJyKLVXqgrZmZqqqqrf/3nGJALDiSXnGrtVb3me/qtciRqUZatmdeZ/o4lkJ8UV/76b0FYCn/+7RE34AFCTxNfmtEAMypaX/N6AAKvMkn/JMACYqY5X+SgAWAp8UXjEOwNMOydgAAoLBYEibYxz+UhUKmioBQAnEg1FagWZlXFRUVNWpVVrZr+VFaKBUAsaULHStFCywLHNqq3Kr7M1Q3K8NWzNcqKtgrgoZl///jei8QXgUwFPBRcV0I7/BeHc/8akxBTUUzLjEwMKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqo=");
                     audio.play(); 
                 };
              }, 200);   
         } 
    }); 
})()

The difference is that it checks the number of remaining queued cells based on their CSS class and plays the sound only if there are no more cells to be run.

# cell 0:
from time import sleep
sleep(6)
# cell 1:
sleep(6)    # will ring only once if queued for execution

Also note that the script starts the timer with the last execution request, so if you happen to queue a cell right before the previous one finishes, it will not ring.

# cell 2:
sleep(6)
# cell 3: 
sleep(2)  # queued right after cell 2
# cell 4:
sleep(2)  # will NOT ring if queued during execution of cell 3 (after cell 2 is finished)   
Answered By: Honza Klusáček
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.