web-dev-qa-db-fra.com

Jouer automatiquement au son dans Ipython Notebook

J'exécute souvent des cellules longues dans mon ordinateur portable Ipython. J'aimerais que le cahier soit automatiquement émettrice ou joue un son lorsque la cellule est terminée en cours d'exécution. Existe-t-il un moyen de le faire dans Ipython Notebook ou peut-être une commande que je peux mettre à la fin d'une cellule qui jouera automatiquement un son?

J'utilise Chrome si cela fait une différence.

33
Thomas Johnson

Tl; dr

En haut de votre cahier

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

sound_file devrait pointer vers un fichier sur votre ordinateur ou accessible depuis Internet.

Puis plus tard, à la fin de la cellule longue courante

<code that takes a long time>

Audio(sound_file, autoplay=True)

Cette méthode utilise la balise audio intégrée à des versions plus récentes d'Ipython/Jupyter.

Note pour les versions plus anciennes

Les versions plus anciennes sans la balise audio peuvent utiliser la méthode suivante.

Mettez ceci dans une cellule et passez-la avant de vouloir jouer votre son:

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)

À la fin de la cellule, vous voulez faire un bruit à la fin, mettez ceci:

HTML(play_beep)

Comment ça fonctionne:

Il lit un fichier à partir du système de fichiers à l'aide des méthodes intégrées _ et open et read. Ensuite, il code cela dans la base64. Il crée ensuite une balise audio avec l'identifiant beep et injecte les données de base64. Le dernier élément de configuration crée une petite balise de script qui joue le son.

Cette méthode doit fonctionner dans n'importe quel navigateur prenant en charge la balise audio HTML5.

Remarque: Si vous préférez ne pas afficher les commandes audio de votre ordinateur portable, supprimez simplement l'attribut controls de la variable nommée sound_tag

36
Waylon Flinn

Ma solution préférée (pas besoin d'un module externe):

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

Fonctionne sur OS X.

Cependant, la remarque de Davep s'applique toujours: ce n'est pas le navigateur qui joue le son, mais le serveur.

7
Arthur

Voici une autre version (Python-côté surtout) qui fonctionne bien avec 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)

Le bip sera automatiquement émis après chaque exécution de code qui a pris plus de 5 secondes, mais les exécutions consécutives ne sont pas comptées ensemble.

Par exemple:

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

Si vous ajoutez ensuite une autre cellule

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

Testé avec JupyterLab 0.32.1 et Jupyter Notebook 5.5.0.

EDIT : Pour réduire le fouillis des lecteurs audio montrés, j'utilise à la suite d'un extrait (pour Python plus âgé que 3,6, vous devez utiliser .format() [Fonction ()] _ au lieu de 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>'

puis utilisez InvisibleAudio au lieu de Audio in post_execute.

6
krassowski

Mention honorable.

Je suis venu chercher cette réponse particulière plusieurs fois, mais je ne pouvais pas le trouver ici.

winsound.Beep(freq, time_in_millisec)

J'utilise:

winsound.Beep(300, 200)
winsound.Beep(300, 200)
winsound.Beep(300, 700)
0
Krishna Chouhan

Basé sur la réponse de @ Krassowski, voici ma solution à cela. La principale différence est que @ Krassowski exécute après chaque chaque exécution de cellules "longue" (où vous définissez ce que vous définissez quel "long" signifie); Je préfère dire explicitement quand je veux que le son joué, alors j'ai enveloppé cela dans une magie (gist - ici et copiée ci-dessous).

"""
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
run_long_function()
```

If you want, you can specify the URL of the audio file to play:
```
%%notify -u "{URL}"
run_long_function()
```

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

from IPython.display import HTML, Audio, display
from IPython.core.magic_arguments import magic_arguments, argument, parse_argstring
from IPython.core.magic import cell_magic, Magics, magics_class
from IPython import get_ipython

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.
    """

    @cell_magic
    @magic_arguments()
    @argument(
        "-u",
        "--url",
        help="URL of sound to play",
        default="https://freewavesamples.com/files/E-Mu-Proteus-FX-CosmoBel-C3.wav",
    )
    def notify(self, line: str, cell: str):
        args = parse_argstring(self.notify, line)
        ret = self.Shell.ex(cell)

        audio = _InvisibleAudio(url=args.url, autoplay=True)
        display(audio)

        return ret


get_ipython().register_magics(NotificationMagics)
0
Nathan