How to Create Looping Sound with Amazon Alexa Skill App

An example how to play audio with AWS Lambda function for your Amazon Alexa Skill:

The way to do this is to append the same mp3 file to the play queue once the current file is “nearly finished” playing.


const Alexa = require('ask-sdk-core');

const soundURL = 'https://urltosomefile.mp3';
let expectedPreviousToken = '';

const LaunchRequestHandler = {
	canHandle(handlerInput) {
	  return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
	},
	handle(handlerInput) {

	expectedPreviousToken = 'sometoken' + Math.random();
	return handlerInput.responseBuilder
	 .speak('start playing sound')
	 .addAudioPlayerPlayDirective('REPLACE_ALL', soundURL, expectedPreviousToken, 0, null)
	 .withSimpleCard('Example', 'Example')
	 .getResponse();
	}
};

const StartSoundHandler = {
	canHandle(handlerInput) {
		return handlerInput.requestEnvelope.request.type === 'IntentRequest'
		&& handlerInput.requestEnvelope.request.intent.name === 'StartSoundHandler';
	},
	handle(handlerInput) {

	 expectedPreviousToken = 'sometoken'+Math.random();
	 return handlerInput.responseBuilder

	 .addAudioPlayerPlayDirective
              ('REPLACE_ALL', soundURL, expectedPreviousToken, 0, null)
	 .withSimpleCard('Example', 'Example')
	 .getResponse();
	}
};


const ExitHandler = {
	canHandle(handlerInput) {
	const request = handlerInput.requestEnvelope.request;

	return request.type === 'IntentRequest' &&
		(request.intent.name === 'AMAZON.StopIntent' ||
		request.intent.name === 'AMAZON.CancelIntent');
	},
	handle(handlerInput) {
		return handlerInput.responseBuilder
		.addAudioPlayerStopDirective()
		.getResponse();
	}
};

const SessionEndedRequestHandler = {
	canHandle(handlerInput) {
		return handlerInput.requestEnvelope.request.type === 'SessionEndedRequest';
	},
	handle(handlerInput) {
		//any cleanup logic goes here
		return handlerInput.responseBuilder.getResponse();
	}
};

const PausePlaybackHandler = {
	canHandle(handlerInput) {

	const request = handlerInput.requestEnvelope.request;

	return request.type === 'IntentRequest' &&
		request.intent.name === 'AMAZON.PauseIntent';
	},
	handle(handlerInput) {
		return handlerInput.responseBuilder
			.speak('Sound is paused.')
			.addAudioPlayerStopDirective()
			.withSimpleCard('Example', 'Bye!')
			.getResponse();
	},
};


const AudioPlayerEventHandler = {
	canHandle(handlerInput) {
		const request = handlerInput.requestEnvelope.request;

		return request.type === 'AudioPlayer.PlaybackStarted' ||
			request.type === 'AudioPlayer.PlaybackStopped' ||
			request.type === 'AudioPlayer.PlaybackNearlyFinished' ||
			request.type === 'AudioPlayer.PlaybackFailed';
	},
	handle(handlerInput) {
		const request = handlerInput.requestEnvelope.request;

		switch (request.type) {

		 case 'AudioPlayer.PlaybackStarted':
		   expectedPreviousToken = request.token;
		   return handlerInput.responseBuilder
		     .getResponse();

		 case 'AudioPlayer.PlaybackFinished':
		  return handlerInput.responseBuilder
		  .getResponse();

		 case 'AudioPlayer.PlaybackStopped':
			 return handlerInput.responseBuilder
			 .getResponse();

		 case 'AudioPlayer.PlaybackNearlyFinished':

		   return handlerInput.responseBuilder
		      .addAudioPlayerPlayDirective
                       ('ENQUEUE', soundURL, 'sometoken', 0, expectedPreviousToken)
		       .getResponse();

		 case 'AudioPlayer.PlaybackFailed':
			 console.log('Playback Failed');
			 break;
		}

		return handlerInput.responseBuilder.getResponse();
	},
};

const ErrorHandler = {
	canHandle() {
		return true;
	},
	handle(handlerInput, error) {
		console.log(`Error handled: ${error.message}`);

		return handlerInput.responseBuilder
			.getResponse();
	}
};


exports.handler = Alexa.SkillBuilders.custom()
	.addRequestHandlers(
		LaunchRequestHandler,
		StartSoundHandler,
		ExitHandler,
                SessionEndedRequestHandler,
		PausePlaybackHandler,
		AudioPlayerEventHandler)
	.addErrorHandlers(ErrorHandler)
	.lambda();