Node.js: Playing Sounds to Provide Notifications

playing sounds

In a previous tutorial, we learned how to send email notifications Using Nodemailer and Gmail. In today’s session, we will learn how to play sounds using Node.js. As a bonus, we will learn how to continue to play a sound until our notification has been acknowledged by pressing a key on the keyboard. How does that sound? 🙂 Enough bad puns! 🙂 Let’s get started!

Prepare the foundation for playing sounds in Node.js

To play a sound in our Node.js code, we will use an NPM package called (perhaps not surprisingly) play-sound. This package works with a number of different command-line audio players including:

If the play-sound package finds any of the audio players in the list, it will utilize that audio player to play a sound. Before we write some code to play a sound, we need to begin by preparing a few items.

Install play-sound

First, let’s create a directory called notify to run the code and then navigate into it.

$ mkdir notify
$ cd notify

Next, let’s create an empty package.json file to save our NPM dependencies:

$ npm init -y

Finally, let’s install play-sound and save it as a dependency:

$ npm install –save play-sound

Find a sound to play

Let’s find a sound (MP3 file) that we can use to test and verify that we are able to play sounds. I’m into roadrunners so I found this roadrunner sound that you can use—or you can meander the Internet for another MP3 file of choice. If you use the roadrunner sound, you should be able to right click on this link and save it locally to your system.

Next, let’s create a directory in the root of our notify folder so we don’t clutter our main directory with audio files. We’ll only be using one audio file, but we could theoretically add many others.

$ mkdir media

Go ahead and move the roadrunner.mp3 file into the media directory.

Your directory structure should now look like this:

notify/
├── media
│   └── roadrunner.mp3
└── package.json

Verify/install an audio player compatible with the play-sound NPM package

Before we run can use the play-sound package, we need to make sure we have one of the requisite audio players installed on our system. These instructions vary depending on whether you are running Raspberry Pi, Windows, or OS X.

Raspberry Pi – confirm audio player

At the time of this writing, the current version of Raspbian ships with the omxplayer player installed. Go ahead and verify this by running the following command from the terminal:

$ which omxplayer

This command should return a path to the omxplayer executable such as /usr/bin/omxplayer. If it returns nothing, omxplayer is not installed.

In the unlikely event that omxplayer is not installed, go ahead and install an audio player such as mpg123:

$ sudo apt install mpg123

You are ready to go for the Raspberry Pi!

Ubuntu/Debian – install audio player

I tested these steps with Ubuntu 16.10 (yakkety); however, these same steps generally apply to any Debian-flavored Linux system.  Ubuntu 16.10 does not ship with any of the command-line audio players supported by the play-sound package installed by default.  For other Debian distros, you can check if an audio player is installed as follows:

$ which mplayer

This command should return a path to the mplayer executable such as /usr/bin/mplayer. If it returns nothing, mplayer is not installed.

Let’s go ahead and install mplayer:

$ sudo apt install mplayer

Your Ubuntu/Debian system is now ready to play sounds!

Windows – install audio player

Windows definitely does not ship out of the box with a command line audio player that will work with the play-sound NPM package. We will install MPlayer for Windows to accomplish the goal since it works well and is the easiest of the options to install on the Windows platform. Here are the steps:

  • Download MPlayer for Windows. Go to this page, and click on the green Download button.
  • Since the download arrives as a .7z (7-Zip) file, you will need to download and install the excellent 7-Zip program, if you do not already have it installed. You can download it from here. (As a side note, 7-Zip includes an amazing “ultra” compression algorithm that is far superior to the zip compression offered by Windows out of the box.)
  • After 7-Zip is installed, use Windows Explorer to navigate to the MPlayer .7z file you downloaded. Right click on this .7z file and choose 7-Zip | Extract files in the context menu. This will create an MPlayer folder and extract the files to this folder.
  • Navigate to the extracted folder. You should see a file called mplayer.exe in the root of this folder.
  • Add the folder containing the mplayer.exe file to your system path so that mplayer.exe can be invoked from any location on your system. Follow the instructions from this tutorial to add the folder to your system path.

OS X – confirm audio player

For OS X, afplay should be installed by default. You can verify this from the terminal:

$ which afplay

This command should return a path to the afplay executable such as /usr/bin/afplay. If it returns nothing, afplay is not installed.

Create test program to play the sound

Awesome – we’re now positioned to create a simple Node program to play a sound. Let’s call it soundtest.js:

const player = require('play-sound')();

player.play('./media/roadrunner.mp3', (err) => {
    if (err) console.log(`Could not play sound: ${err}`);
});

We are ready to play sounds programmatically using Node.js! Turn the volume up on your system and ensure your headphones or speakers are working. If you are using a Raspberry Pi, you can either plug a set of speakers into the 3.5 mm audio jack, or plug in a pair of earbuds. Be sure the connection is plugged in and fully seated.

Let’s give it a try:

$ node soundtest.js

You should hopefully hear the sound of a roadrunner or the sound from another MP3 file you chose!

Develop framework to play an audio notification when the ISS is near

Now that we are able to play sounds with Node.js, let’s utilize our sound playing prowess by building on our work in a previous tutorial to notify us when the ISS is approaching our geographic location. Create a file called iss.js and add the following:

const got = require('got');
const moment = require('moment');

const loopSeconds = 120;
const myLoc = { latitude: 32.715738, longitude: -117.161084 };
const url = `http://api.open-notify.org/iss-pass.json?lat=${myLoc.latitude}&lon=${myLoc.longitude}`;

const warningMinutes = 30;
let providedNotification = false;
let alarmAcknowledged = false;

const readline = require('readline');
readline.emitKeypressEvents(process.stdin);
process.stdin.setRawMode(true);

process.stdin.on('keypress', (str, key) => {
    if (key.ctrl && key.name === 'c') {
        process.exit();
    } else if (key.name === 'a') {
        if (!alarmAcknowledged) {
            console.log('alarm acknowledged');
        }
        alarmAcknowledged = true;
    }
});

function sendNotification(message) {
    console.log(`Could send email here: ${message}`);
}

function soundAlarm() {
    console.log('playing sound');
}

function loop() {
    got(url, { json: true })
        .then(iss => {
            const nextPasses = iss.body.response;
            const now = moment();
            const pass = nextPasses[0];
            const passTime = moment.unix(pass.risetime);
            const timeFromNow = moment.duration(passTime.diff(now));
            const minutesFromNow = Number(timeFromNow.asMinutes()).toFixed(1);
            console.log(`${passTime} (in ${minutesFromNow} minutes) for ${pass.duration} seconds`);

            if (minutesFromNow <= warningMinutes) { if (!alarmAcknowledged) { soundAlarm(); } if (!providedNotification) { sendNotification(`The ISS will pass over in ${minutesFromNow} minutes`); providedNotification = true; } } else { providedNotification = false; alarmAcknowledged = false; } }) .catch(error => {
            console.log(error.response.body);
        });
    setTimeout(loop, loopSeconds * 1000);
}

loop();

There’s a lot going on here and much of this is a continuation of a previous tutorial so you may want to go back and read it if you have not already. This program notifies us when the ISS is within 30 minutes of passing over us. We create a function called soundAlarm as a placeholder that we will ultimately use to play sounds when the ISS is approaching. For now, we simply print to the console.

We also listen for keypress events building on the material we covered in our Making Interactive Node.js Console Apps That Listen for Keypress Events tutorial. We press the “a” key to acknowledge an alarm/notification and keep it from continuing to play during every loop.

To get this working, we will need to npm install both got to help us retrieve ISS data from the web and moment to help us manipulate dates and times. We can install both of these at the same time using the following command:

$ npm install –save got moment

Let’s run the code to confirm it works:

$ node iss.js

Play an audio notification when the ISS is near

Let’s update our soundAlarm function to play sounds based on the test program we wrote earlier:

function soundAlarm() {
    console.log('playing sound');
    player.play('./media/roadrunner.mp3', (err) => {
        if (err) console.log(`Could not play sound: ${err}`);
    });
}

We are now ready to put everything together including playing our roadrunner sound to notify us when the ISS is within 30 minutes of passing over us. Here’s the final result:

const got = require('got');
const moment = require('moment');
const player = require('play-sound')();

const loopSeconds = 120;
const myLoc = { latitude: 32.715738, longitude: -117.161084 };
const url = `http://api.open-notify.org/iss-pass.json?lat=${myLoc.latitude}&lon=${myLoc.longitude}`;

const warningMinutes = 30;
let providedNotification = false;
let alarmAcknowledged = false;

const readline = require('readline');
readline.emitKeypressEvents(process.stdin);
process.stdin.setRawMode(true);

process.stdin.on('keypress', (str, key) => {
    if (key.ctrl && key.name === 'c') {
        process.exit();
    } else if (key.name === 'a') {
        if (!alarmAcknowledged) {
            console.log('alarm acknowledged');
        }
        alarmAcknowledged = true;
    }
});

function sendNotification(message) {
    console.log(`Could send email here: ${message}`);
}

function soundAlarm() {
    console.log('playing sound');
    player.play('./media/roadrunner.mp3', (err) => {
        if (err) console.log(`Could not play sound: ${err}`);
    });
}

function loop() {
    got(url, { json: true })
        .then(iss => {
            const nextPasses = iss.body.response;
            const now = moment();
            const pass = nextPasses[0];
            const passTime = moment.unix(pass.risetime);
            const timeFromNow = moment.duration(passTime.diff(now));
            const minutesFromNow = Number(timeFromNow.asMinutes()).toFixed(1);
            console.log(`${passTime} (in ${minutesFromNow} minutes) for ${pass.duration} seconds`);

            if (minutesFromNow <= warningMinutes) { if (!alarmAcknowledged) { soundAlarm(); } if (!providedNotification) { sendNotification(`The ISS will pass over in ${minutesFromNow} minutes`); providedNotification = true; } } else { providedNotification = false; alarmAcknowledged = false; } }) .catch(error => {
            console.log(error.response.body);
        });
    setTimeout(loop, loopSeconds * 1000);
}

loop();

We are successfully playing sounds through Node when the ISS is within 30 minutes of flying over us. We can press the “a” key to acknowledge this notification so that we will not continue to hear the sound every time the code in the loop runs again. Feel free to experiment with the code and decrease the loopSeconds to speed up the time it takes to confirm that the alarm has been acknowledged. You can also increase the warningMinutes to test the notification features even if the ISS will not be passing over you for quite a while.

Conclusion

We can now play sounds programmatically with Node.js. I hope you have enjoyed this tutorial, and learned something in the process. Go out and create something awesome with Node, and use sounds to take it to the next level!

Follow @thisDaveJ (Dave Johnson) on Twitter to stay up to date on the latest tutorials and tech articles.

Additional Articles

Node.js: Sending Email Notifications Using Nodemailer and Gmail
Making Interactive Node.js Console Apps That Listen for Keypress Events
Node.js IoT – Tracking the ISS through the Sky
Beginner’s Guide to Installing Node.js on a Raspberry Pi
Upgrading to more recent versions of Node.js on the Raspberry Pi

One thought on “Node.js: Playing Sounds to Provide Notifications

Leave a Reply

Your email address will not be published. Required fields are marked *