Play sound each time button is clicked

I have a sample script here which play sound only once after it is loaded.

import dash
import dash_html_components as html
import base64
from dash.dependencies import Input, Output


app = dash.Dash(__name__) 


# Encode the local sound file.
sound_filename = 'path_to_your_file.mp3'  # replace with your own .mp3 file
encoded_sound = base64.b64encode(open(sound_filename, 'rb').read())


app.layout = html.Div(children=[
    html.H1(children='Demo for Audio with Dash'),

    html.Div(children='''
        Click the button to play your local .mp3 sounds.
    '''),


    html.Button(id="button1", children="Click me for sound"),
    html.Div(id="placeholder", style={"display": "none"})])


@app.callback(Output("placeholder", "children"),
              [Input("button1", "n_clicks")],
              )
def play(n_clicks):
    if n_clicks is None:
        n_clicks = 0
    if n_clicks != 0:
        return html.Audio(src='data:audio/mpeg;base64,{}'.format(encoded_sound.decode()),
                          controls=False,
                          autoPlay=True,
                          )


if __name__ == '__main__':
    app.run_server(debug=True)

what I want is to play sound each time the button is clicked. Let me know if this is possible. Thank you!

1 Like

I found the below mentioned Github repo for dash audio component. It looks like it is not working anymore. Can you guys have a look into this.

Is there any update on this @chriddyp ?

I was solving the same issue and to my knowledge, unfortunately there is no straightforward solution available. Custom dash component would be a solution, but that was too complicated for my use case.

I ended up with javascript callbacks:

from dash import html, Input, Output, Dash
import base64
app = Dash(__name__)

# Encode the local sound file.
sound_filename = 'audio_file.mp3'  # replace with your own .mp3 file
encoded_sound = base64.b64encode(open(sound_filename, 'rb').read())


app.layout = html.Div(children=[
    html.H1(children='Demo for Audio with Dash'),

    html.Div(children='''
        Click the button to play your local .mp3 sounds.
    '''),


    html.Button(id="button1", children="Click me for sound"),
    html.Audio(id='audio-player', src='data:audio/mpeg;base64,{}'.format(encoded_sound.decode()),
                          controls=True,
                          autoPlay=False,
                          ),
    html.Div(id="placeholder", style={"display": "none"})])

app.clientside_callback(
    """
    function(n) {
      var audio = document.querySelector('#audio-player');
      if (!audio){
        return -1;
      }
      audio.play();
      return '';
   }
    """, Output('placeholder', 'children'), [Input('button1', 'n_clicks')],
    prevent_initial_call=True
)


if __name__ == '__main__':
    app.run_server(debug=True)

It should be possible to also interconnect some events from some slider component to have seekable audio element…

Note that with javascript callbacks you need to hard-refresh browser cache whenever you modify javascript code because it won’t update automatically as in python callbacks.

EDIT: updated with working example

2 Likes

Hello nixie,

Thank you for posting this answer.

Hi Yadvendar

This may be a little bit of a kluge but doing the following seems to work.

from dash import Dash,Output,Input,html
import base64
from dash.dependencies import Input, Output


app = Dash()


# Encode the local sound file.
sound_filename = 'EndMovement.mp3'  # replace with your own .mp3 file
encoded_sound = base64.b64encode(open(sound_filename, 'rb').read())
#Initiallize plot with some arbitrary data

app.layout = html.Div(children=[
    html.H1(children='Demo for Audio with Dash'),

    html.Div(children='''
        Click the button to play your local .mp3 sounds.
    '''),


    html.Button(id="button1", children="Click me for sound"),
    html.Div(id="soundHandle", style={"display": "none"})])


@app.callback(Output("soundHandle", "children"),
              Input("button1", "n_clicks"),
              running = [(Output("soundHandle", "children"),None,None)])
def play(n_clicks):
    if n_clicks is None:
        n_clicks = 0
    if (n_clicks != 0):
        return html.Audio(src='data:audio/mpeg;base64,{}'.format(encoded_sound.decode()),
                          controls=False,
                          autoPlay=True,
                          )

if __name__ == '__main__':
    app.run(debug=True)

Essentially whats going on is the audio playback doesn’t activate unless HTML Audio is reassigned or the sound file is changed. If you add a Running command that just sets the audio player to a None during execution then hand it the actual audio player you want to play afterwards it should go right into playing the sound.

As a note this solution will probably make some issues if you try to use the progress to play the sound, and it will make a little overhead on execution. but for a quick fix maybe this will be useful.