Streaming Music from SoundCloud in Ionic 2: Part 1

Streaming Music from SoundCloud in Ionic 2: Part 1

Follow Josh Morony on

SoundCloud is one of the biggest platforms in the world for not only sharing music, but sounds of any kind from podcasts to character voices. They also provide a nice API that we can use to integrate some of those sounds into our own applications. In this tutorial we will be walking through how to stream music from SoundCloud in an Ionic 2 application. This is going to be a two-part series that will cover:

  • How to integrate the SoundCloud API in Ionic 2
  • How to fetch songs from the SoundCloud API
  • How to play songs from SoundCloud in Ionic 2
  • Defining types with interfaces in Ionic 2
  • How to create an interface that will allow the user to control the music and;
  • Display information about the track that is currently playing

In this first part of the tutorial, we are going to focus on getting a bare bones application set up, and we will leave the interface and other finishing touches to the next part.

Before we proceed, it’s important to understand that we will be using content created by others in our application, and it’s important to understand the rules around that. Specifically, SoundCloud specifies that you must:

  1. Credit the uploader as the creator of the sound
  2. Credit SoundCloud as the source by including one of the logos found here
  3. Link to the SoundCloud URL containing the work
  4. If the sound is private link to the profile of the creator

We are not going to be displaying that information until Part 2, so if you are planning on publishing this application make sure that you do include this information before doing so.

Before we Get Started

Before you go through this tutorial, you should have at least a basic understanding of Ionic 2 concepts. You must also already have Ionic 2 set up on your machine.

If you’re not familiar with Ionic 2 already, I’d recommend reading my Ionic 2 Beginners Guide first to get up and running and understand the basic concepts. If you want a much more detailed guide for learning Ionic 2, then take a look at Building Mobile Apps with Ionic 2.

1. Generate a new Ionic 2 Application

We’re going to start off by generating a new Ionic 2 application and setting up a provider.

Run the following command to generate a new application

ionic start ionic2-soundcloud blank --v2

We’ll only be using a single page in this application, so we don’t need to generate any pages. We will be using a provider to handle the integration with the SoundCloud API though, so let’s set that up now.

Run the following command to generate the SoundCloud provider

ionic g provider SoundCloud

2. Set up the SoundCloud Javascript SDK

SoundCloud offers a Javascript, Ruby, and Python SDK that developers can use to interact with their HTTP API. We will of course be making use of the Javascript SDK.

Often when installing 3rd party services into an Ionic 2 application we would install an npm package and import it wherever we want to use it. We’re going to do this old school though and follow the instructions [here](https://developers.soundcloud.com/docs/api/sdks#javascript by just including the SDK directly in our index.html file.

Modify your index.html file to include the following script:

<script src="build/js/app.bundle.js"></script>
  <script src="https://connect.soundcloud.com/sdk/sdk-3.1.2.js"></script>

This will make an object called SC globally available that we can use to interact with the SoundCloud API.

3. Get a Client ID from SoundCloud

In order to use the SoundCloud API you will need your own Client ID. You can do this by visiting developers.soundcloud.com:

SoundCloud Developer

and clicking on the ‘Register a new app’ button. Follow the prompts to create your SoundCloud account and app, and you should receive a Client ID to use. Make note of this for the next step.

4. Implement the SoundCloud Provider

Now that we have the SDK available within our application, we are going to work on our provider so that we can fetch tracks from the API and play them within the application. Let’s start our with a skeleton of what we will be building and work on it from there.

Modify sound-cloud.ts to reflect the following:

import { Injectable } from '@angular/core';
import { Platform } from 'ionic-angular';
import 'rxjs/add/operator/map';

declare var SC;

@Injectable()
export class SoundCloud {

    private clientId: string = 'YOUR_CLIENT_ID';
    private tracks: any[] = [];
    private playTrack: number = 0;
    public currentTrack: Track;

    constructor(private platform: Platform) {

        this.platform.ready().then(() => {
            SC.initialize({
                client_id: this.clientId
            });
        });

    }

    fetchTracks(bpm: number, genre:string): void {

    }

    startStreaming(){

    }

}

This is a fairly basic skeleton, but there are a few things to take note of. First we add this line above the @Injectable decorator:

declare var SC

This is because we will be using the SC object from SoundCloud, which the TypeScript compiler doesn’t know about, so we add this line to suppress any warnings that TypeScript might throw at us.

We’ve declared a few different member variables. The clientId which you should replace with your own Client ID, a tracks array to hold the tracks we will fetch from SoundCloud, playTrack which will indicate the track we are up to, and currentTrack that will hold information about the track that is currently playing.

All of these are given types, most of which are pretty normal – string, any, type – but we have also given currentTrack a type of Track. We don’t have an appropriate type to represent the structure of the object that currentTrack will contain (one that will be returned from the SoundCloud API), so we are going to create our own with an “interface”. We will talk more about this in just a moment.

The constructor handles initialising the SoundCloud API, and then we have two more functions. The fetchTracks function will pull in a list of songs to stream from SoundCloud, and startStreaming will play the list of songs that were fetched. We will implement both of these soon.

4.1 Fetch Tracks from SoundCloud

We are going to start off by implementing the fetchTracks function. This function will take in parameters of a bpm which will represent the desired “beats per minute” and a genre string that will represent one or more desired genres of music.

Modify the fetchTracks function to reflect the following:

fetchTracks(bpm: number, genre:string): void {

        SC.get('/tracks', {

            genres: genre,
            bpm: { 
                from: bpm - 5, 
                to: bpm + 5
            },
            filter: 'public'

        }).then((tracks) => {

            console.log(tracks);

            this.tracks = tracks;

            console.log("Playing " + tracks.length + " tracks");

            this.startStreaming();

        });

    }

We make a call to the /tracks endpoint of the SoundCloud API, and supply our options to it. We extend the range of the supplied BPM by 5 on both sides, and we are also supplying a public filter – this will only return songs that are available to the public and allowed to be streamed.

This call will return a promise that resolves with an array of tracks – we assign those tracks to our member variable tracks and then call the startStreamin function, which will handle playing the tracks we just received.

4.2 Play the List of Fetched Songs

Now that we have our tracks we just need to play them. SoundCloud makes it pretty easy to play a specific track, but we don’t want to just play one track. We want to play all of them, which means when one song finishes we want to play the next. This will involve setting up some listeners for events and handling them.

Modify the startStreaming function to reflect the following:

startStreaming(){

        this.currentTrack = this.tracks[this.playTrack];

        console.log("Playing track " + this.playTrack);
        console.log(this.currentTrack);

        SC.stream('/tracks/' + this.currentTrack.id).then((player) => {

            player.play();

            player.on('buffering_start', () => {
                console.log('buffering...');
            });

            player.on('buffering_end', () => {
                console.log('party!');
            });

            player.on('finish', () => {

                if(this.playTrack < this.tracks.length - 1){
                    this.playTrack++;
                } else {
                    this.playTrack = 0;
                }

                console.log('time to move on...');
                this.startStreaming();

            });
        });

    }

First we set the currentTrack as the track in our tracks array with an index that matches playTrack – this starts at 0, so will get the first track, and will be incremented each time we finish playing a song.

We then use the stream method from the SoundCloud SDK to stream the track from SoundCloud – we simply have to supply its id. This will return to us a player that we can use to play the track (which we do), but the player also allows us to listen for various events. We are using three here: buffering_start which indicates when a track is loading, buffering_end which indicates when a track has finished loading, and finish which indicates when a track has finished playing. You can find a full list of available events here.

When a track does finish playing we increment the playTrack (or reset it to 0 if there are no tracks left) and then call the startStreaming function again so that the next track will play.

4.3 Create an Interface

As you may be aware, when building Ionic 2 applications we are using TypeScript. TypeScript likes to complain about things it doesn’t know about – and it certainly doesn’t know about the data structure returned from SoundCloud. You may have experienced this before when attempting to reference things from the global cordova object in your code, TypeScript doesn’t know what that is so it will throw up a warning (which prevents your application from building).

One way to solve this problem is to install typings, which defines types for whatever it is that you are using (this makes TypeScript happy). This is quite easy to do through the use of the typings package, but not everything has typings available.

Another way to solve this problem of missing types is to define an interface. An interface will allow you to define simple or complex type definitions – it allows you to create your own “types”. As I mentioned before, we want to use our own Track type, and we are going to define that now.

Modify sound-cloud.ts by adding the following above the @Injectable decorator:

interface User {
    permalink_url: string;
    username: string;
}

interface Track {
    id: number;
    title: string;
    artwork_url: string;
    permalink_url: string;
    user: User;
}

The format here is reasonably simple. The Track object that will be returned to our application will look something like this:

{
    id: 123,
    title: 'Some Song',
    permalink_url: 'http://someurl',
    artwork_url: 'http://someurl',
    user: {
        permalink_url: 'http://someurl',
        username: 'joshuamorony'
    }
}

So we define the same properties in our interface, and give an appropriate type to each property. The only complex part here is that the user property is in itself an object with its own properties, so we define another interface to create a User type. This doesn’t cover the entire structure of the data sent back by SoundCloud, but it does cover everything we use, so TypeScript won’t throw any errors at us when we try to access them.

Since the data will take a little while to load in from SoundCloud, but we will (later) be trying to access it straight away, we are going to create a dummy track in the constructor.

Modify the constructor in sound-cloud.ts to reflect the following:

constructor(private platform: Platform) {

        this.currentTrack = {
            id: 0,
            title: 'Fetching tracks...',
            permalink_url: '',
            artwork_url: '',
            user: {
                permalink_url: '',
                username: ''
            }
        };

        this.platform.ready().then(() => {
            SC.initialize({
                client_id: this.clientId
            });
        });

    }

That’s it for our provider! Now let’s move on to actually making use of it.

5. Inject the SoundCloud Provider

We are going to make a single instance of the SoundCloud provider available globally, so we are going to inject it into our root component. If you want to learn more about providers, and the difference between adding it to app.ts and a normal component, take a look at this tutorial.

Modify app.ts to reflect the following:

import { Component } from '@angular/core';
import { ionicBootstrap, Platform } from 'ionic-angular';
import { StatusBar } from 'ionic-native';
import { SoundCloud } from './providers/sound-cloud/sound-cloud';
import { HomePage } from './pages/home/home';

@Component({
  template: '<ion-nav [root]="rootPage"></ion-nav>'
})
export class MyApp {
  rootPage: any = HomePage;

  constructor(platform: Platform, soundCloud: SoundCloud) {
    platform.ready().then(() => {
      StatusBar.styleDefault();
      soundCloud.fetchTracks(120, 'electronic');
    });
  }
}

ionicBootstrap(MyApp, [SoundCloud]);

We’ve imported the provider, and added it to ionicBootstrap here. We’ve also injected it into the constructor and we make a call to the fetchTracks function as soon as the ready() method resolves. We supply a bpm of 120 and a genre of “electronic” but you can change this to whatever you like. Now as soon as we start the app the music will be fetched and played right away.

Party!

If you run the application now with ionic serve you will see the normal blank starter app, but if you wait a few seconds some music should start playing! In the next part of the tutorial we will focus on creating a proper interface, that will display the track being played (along with the appropriate attribution to the creators) and allow the user to skip songs.

Check out my latest videos: