Alexaで手持ちの音楽(mp3)を再生するスキルの作成

こんてんつ

自分の音声を再生するためのスキルを作成していきます。本ページは、下記の神みたいなサイトを参考に作成しました。下記のサイトが非常に分かりやすいのですが、更に入門レベルまで加えた手順書を作成しました。

  • ①スキルそのものの作成
  • ②Audio PlayerのインターフェースをONにする
  • ③MP3のアップロード
  • ④コードの実装
  • ⑤スキルの有効化
  • ⑥終了

android-smart.com

手順

①スキルそのものの作成

  • alexa developer consoleにアクセス(https://developer.amazon.com/alexa/console/ask
  • スキルの作成でスキルを作成する。
  • スキル名、デフォルトの言語を選択
  • スキルに追加するモデル→カスタムを選択
  • バックエンドリソースをホスティングする方法→Node.jsを選択 f:id:norunblog:20210206112936p:plain

②Audio PlayerのインターフェースをONにする

  • ビルドタブのインターフェースを選択
  • その中にあるAudio PlayerのスイッチをONにする
  • その後、「インターフェースを保存」「モデルをビルド」の順にクリック

f:id:norunblog:20210206140727p:plain

③MP3のアップロード

  • コードエディタタブのS3ストレージを選択 f:id:norunblog:20210206114707p:plain
  • awsのページに飛んだらアップロードを選択しファイルをアップする f:id:norunblog:20210206115358p:plain
  • アップロードにはそれなりの時間がかかる
  • 進行中→成功しましたとなったらOK f:id:norunblog:20210206115653p:plain f:id:norunblog:20210206122142p:plain

④コードの実装

冒頭で紹介した、神みたいなサイトで提供されている下記のコードを実装する。コードエディタを開いて、下記のコードをコピペする。

const playlist_title=       'マイミュージック'; //ファイル名・スキル名とは別に表示させたいテキスト(アーティストなど)
const playlist_cover=       '';                 //カバー画像(推奨サイズ480x480ピクセル以上)
const playlist_background=  '';                 //背景画像ファイル(推奨サイズ1024x640ピクセル)
const playlist_tracks=[                         //メディアストレージに置いた音楽ファイル名を下記の例のように列挙
'sample01.mp3',
'sample02.mp3',
];

const token_prefix='mymusic001';                //画像などを変更したとき、この識別子を変更するとすぐに反映される

const Util = require('util.js');
const Alexa = require('ask-sdk-core');
exports.handler=Alexa.SkillBuilders.custom().addRequestHandlers({
    canHandle(input){return true;},
    handle(input){
        var a=[],i;
        var behavior='REPLACE_ALL';
        for(i=0;i<playlist_tracks.length;i++){
            a.push(i);
        }
        for(i=a.length-1;i>0;i--){
            var r=Math.floor(Math.random()*(i+1)),t=a[i];a[i]=a[r];a[r]=t;
        }
        
        var token=(input.requestEnvelope.context.AudioPlayer||{}).token||token_prefix+":::0";
        var is_once=token.match(/:once:/)?'once':'';
        var shuffled=token.match(/:[\d,]+:/)?(token.split(/:/)||["","",a.join(',')])[2].split(/,/):null;
        var pos=(token.split(/:/)||["","","","0"])[3]*1;
        var offset=(input.requestEnvelope.context.AudioPlayer||{}).offsetInMilliseconds||0;
        
        var meta={title:'',subtitle:playlist_title};
        if(playlist_cover){
            meta.art={contentDescription:'カバーアート',sources:[{url:Util.getS3PreSignedUrl('Media/'+playlist_cover)}]};
        }
        if(playlist_background){
            meta.backgroundImage={contentDescription:'背景画像',sources:[{url:Util.getS3PreSignedUrl('Media/'+playlist_background)}]};
        }
        
        switch(('IntentRequest'===input.requestEnvelope.request.type)?input.requestEnvelope.request.intent.name:input.requestEnvelope.request.type){
            case('AMAZON.StartOverIntent'):
            case('AMAZON.RepeatIntent'):
            case('PlayMyMusicIntent'):
            case('LaunchRequest'):{
                offset=0;
                break;
            }
            
            case('AMAZON.LoopOffIntent'):{
                is_once='once';
                break;
            }
            case('AMAZON.LoopOnIntent'):{
                is_once='';
                break;
            }
            
            case('AMAZON.ShuffleOffIntent'):{
                shuffled=null;
                break;
            }
            case('AMAZON.ShuffleOnIntent'):{
                shuffled=shuffled||a;
                break;
            }
            
            case('AudioPlayer.PlaybackNearlyFinished'):
            case('AMAZON.NextIntent'):
            case('PlaybackController.NextCommandIssued'):{
                pos++;
                offset=0;
                if(pos<playlist_tracks.length){
                }else if(is_once){
                    return input.responseBuilder.withShouldEndSession(true).getResponse();
                }else{
                    pos=0;
                    shuffled=shuffled&&a;
                }
                if(input.requestEnvelope.request.type==='PlaybackNearlyFinished'){
                    behavior='REPLACE_ENQUEUED';
                }
                
                break;
            }
            case('AMAZON.PreviousIntent'):
            case('PlaybackController.PreviousCommandIssued'):{
                pos--;
                offset=0;
                if(pos<0){
                    return input.responseBuilder.withShouldEndSession(true).getResponse();
                }
                break;
            }
            case('AMAZON.StopIntent'):
            case('AMAZON.CancelIntent'):{
                return input.responseBuilder.addAudioPlayerClearQueueDirective('CLEAR_ALL').withShouldEndSession(true).getResponse();
            }
            case('PlaybackController.PauseCommandIssued'):
            case('AMAZON.PauseIntent'):{
                return input.responseBuilder.addAudioPlayerStopDirective().withShouldEndSession(true).getResponse();
            }
            case('PlaybackController.PlayCommandIssued'):
            case('AMAZON.ResumeIntent'):{
                break;
            }
            
            case('AudioPlayer.PlaybackFailed'):{
                return input.responseBuilder.speak('再生できませんでした').withShouldEndSession(true).getResponse();
            }
            case('System.ExceptionEncountered'):{
                return input.responseBuilder.withShouldEndSession(true).getResponse();
            }
            case('AMAZON.HelpIntent'):
            case('AudioPlayer.PlaybackStarted'):
            case('AudioPlayer.PlaybackFinished'):
            case('AudioPlayer.PlaybackStopped'):
            case('SessionEndedRequest'):
            default:{
                return input.responseBuilder.withShouldEndSession(true).getResponse();
            }
        }
        meta.title=playlist_tracks[shuffled?shuffled[pos]:pos].replace(/\.\w+$/,'');
        return input.responseBuilder.addAudioPlayerPlayDirective(
            behavior,
            Util.getS3PreSignedUrl('Media/'+playlist_tracks[shuffled?shuffled[pos]:pos]),
            token_prefix+':'+is_once+':'+(shuffled||[]).join(',')+':'+pos,
            offset,void(0),meta
        ).withShouldEndSession(true).getResponse();
    }
}).addErrorHandlers({
    canHandle(){return true;},
    handle(input, error) {
        return input.responseBuilder.speak('すみません わかりません').withShouldEndSession(true).getResponse();
    }
}).lambda();

f:id:norunblog:20210206125510p:plain

  • 自分の音楽の名前に変更する。 f:id:norunblog:20210206125628p:plain

⑤スキルの有効化

  • 保存&デプロイを押す
  • テストタブに移って「開発中」を選択 f:id:norunblog:20210206125746p:plain f:id:norunblog:20210206125844p:plain

⑥終了

スキルを試してみましょう!