com.microsoft.cognitive.speakerrecognition.SpeakerIdentificationRestClient.java Source code

Java tutorial

Introduction

Here is the source code for com.microsoft.cognitive.speakerrecognition.SpeakerIdentificationRestClient.java

Source

//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license.
//
// Microsoft Cognitive Services (formerly Project Oxford): https://www.microsoft.com/cognitive-services
//
// Microsoft Cognitive Services (formerly Project Oxford) GitHub:
// https://github.com/Microsoft/Cognitive-SpeakerRecognition-Android
//
// Copyright (c) Microsoft Corporation
// All rights reserved.
//
// MIT License:
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
package com.microsoft.cognitive.speakerrecognition;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;

import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.microsoft.cognitive.speakerrecognition.contract.CreateProfileException;
import com.microsoft.cognitive.speakerrecognition.contract.DeleteProfileException;
import com.microsoft.cognitive.speakerrecognition.contract.EnrollmentException;
import com.microsoft.cognitive.speakerrecognition.contract.GetProfileException;
import com.microsoft.cognitive.speakerrecognition.contract.ResetEnrollmentsException;
import com.microsoft.cognitive.speakerrecognition.contract.identification.CreateProfileResponse;
import com.microsoft.cognitive.speakerrecognition.contract.identification.EnrollmentOperation;
import com.microsoft.cognitive.speakerrecognition.contract.identification.IdentificationException;
import com.microsoft.cognitive.speakerrecognition.contract.identification.IdentificationOperation;
import com.microsoft.cognitive.speakerrecognition.contract.identification.OperationLocation;
import com.microsoft.cognitive.speakerrecognition.contract.identification.Profile;

/**
 * This class abstracts all the identification service calls
 */
public class SpeakerIdentificationRestClient implements SpeakerIdentificationClient {

    /**
     * The Http client used to communicate with the service
     */
    private HttpClient defaultHttpClient;

    /**
     * Address of the identification profiles API
     */
    private static final String IDENTIFICATION_PROFILE_URI = "https://westus.api.cognitive.microsoft.com/spid/v1.0/identificationProfiles";

    /**
     * Address of the identification API
     */
    private static final String IDENTIFICATION_URI = "https://westus.api.cognitive.microsoft.com/spid/v1.0/identify";

    /**
     * The operation location header field
     */
    private static final String _OPERATION_LOCATION_HEADER = "Operation-Location";

    /**
     * Json Serializer / deserializer
     */
    private Gson gson;

    /**
     * The locale parameter
     */
    private static final String LOCALE_PARAM = "locale";

    /**
     * The short audio parameter name
     */
    private static final String SHORT_AUDIO_PARAM = "shortAudio";

    /**
     * Speaker client clientHelper
     */
    private SpeakerRestClientHelper clientHelper;

    //----------------------------------------------------------------------------------------------

    /**
     * Initializes an instance of the service client
     *
     * @param subscriptionKey The subscription key to use
     */
    public SpeakerIdentificationRestClient(String subscriptionKey) {
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        // Increase max total connection to 200
        cm.setMaxTotal(200);
        // Increase default max connection per route to 20
        cm.setDefaultMaxPerRoute(20);

        CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm).build();
        defaultHttpClient = httpClient;//new DefaultHttpClient();
        gson = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:SS.SSS").create();
        clientHelper = new SpeakerRestClientHelper(subscriptionKey);
    }

    //----------------------------------------------------------------------------------------------

    /**
     * Creates a new speaker profile
     *
     * @param locale The speaker profile locale
     * @return The profile object encapsulating the response object of the create request
     * @throws CreateProfileException Thrown on cases of internal server error or an invalid locale
     * @throws IOException Signals an invalid locale encoding, a connection abortion, or an invalid response content
     */
    @Override
    public CreateProfileResponse createProfile(String locale) throws CreateProfileException, IOException {

        HttpPost request = (HttpPost) clientHelper.createHttpRequest(IDENTIFICATION_PROFILE_URI, RequestType.POST);

        List<NameValuePair> paramsList = new ArrayList<>();
        paramsList.add(new BasicNameValuePair(LOCALE_PARAM, locale));
        request.setEntity(new UrlEncodedFormEntity(paramsList));

        HttpResponse response = defaultHttpClient.execute(request);

        int statusCode = clientHelper.getStatusCode(response);
        String stringResponse = clientHelper.httpResponseToString(response);

        if (statusCode == HttpStatus.SC_OK) {
            return gson.fromJson(stringResponse, CreateProfileResponse.class);
        } else {
            ErrorResponse errorResponse = gson.fromJson(stringResponse, ErrorResponse.class);
            if (errorResponse != null) {
                throw new CreateProfileException(errorResponse.error.message);
            } else {
                throw new CreateProfileException(String.valueOf(statusCode));
            }
        }
    }

    /**
     * Retrieves a speaker profile from the service
     *
     * @param id The ID of the speaker profile to get
     * @return The requested profile
     * @throws GetProfileException Thrown in cases of invalid ID or an internal server error
     * @throws IOException Signals a connection abortion, or an invalid response content
     */
    @Override
    public Profile getProfile(UUID id) throws GetProfileException, IOException {

        HttpGet request = (HttpGet) clientHelper.createHttpRequest(IDENTIFICATION_PROFILE_URI + "/" + id.toString(),
                RequestType.GET);

        HttpResponse response = defaultHttpClient.execute(request);

        int statusCode = clientHelper.getStatusCode(response);
        String stringResponse = clientHelper.httpResponseToString(response);

        if (statusCode == HttpStatus.SC_OK) {
            return gson.fromJson(stringResponse, Profile.class);
        } else {
            ErrorResponse errorResponse = gson.fromJson(stringResponse, ErrorResponse.class);
            if (errorResponse != null) {
                throw new GetProfileException(errorResponse.error.message);
            } else {
                throw new GetProfileException(String.valueOf(statusCode));
            }
        }
    }

    /**
     * Gets all speaker profiles from the service
     *
     * @return An array containing a list of all profiles
     * @throws GetProfileException Thrown in case of an internal server error
     * @throws IOException Signals a connection abortion, or an invalid response content
     */
    @Override
    public List<Profile> getProfiles() throws GetProfileException, IOException {

        HttpGet request = (HttpGet) clientHelper.createHttpRequest(IDENTIFICATION_PROFILE_URI, RequestType.GET);

        HttpResponse response = defaultHttpClient.execute(request);

        int statusCode = clientHelper.getStatusCode(response);
        String stringResponse = clientHelper.httpResponseToString(response);

        if (statusCode == HttpStatus.SC_OK) {
            Type listType = new TypeToken<List<Profile>>() {
            }.getType();
            return gson.fromJson(stringResponse, listType);
        } else {
            ErrorResponse errorResponse = gson.fromJson(stringResponse, ErrorResponse.class);
            if (errorResponse != null) {
                throw new GetProfileException(errorResponse.error.message);
            } else {
                throw new GetProfileException(String.valueOf(statusCode));
            }
        }
    }

    /**
     * Deletes a given speaker profile
     *
     * @param id The ID of the speaker profile to be deleted
     * @throws DeleteProfileException Thrown on case of an internal server error or an invalid ID
     * @throws IOException Signals a connection abortion, or an invalid response content
     */
    @Override
    public void deleteProfile(UUID id) throws DeleteProfileException, IOException {

        HttpDelete request = (HttpDelete) clientHelper
                .createHttpRequest(IDENTIFICATION_PROFILE_URI + "/" + id.toString(), RequestType.DELETE);

        HttpResponse response = defaultHttpClient.execute(request);

        int statusCode = clientHelper.getStatusCode(response);
        String stringResponse = clientHelper.httpResponseToString(response);

        if (statusCode != HttpStatus.SC_OK) {
            ErrorResponse errorResponse = gson.fromJson(stringResponse, ErrorResponse.class);
            if (errorResponse != null) {
                throw new DeleteProfileException(errorResponse.error.message);
            } else {
                throw new DeleteProfileException(String.valueOf(statusCode));
            }
        }
    }

    /**
     * Enrolls a speaker profile from an audio stream
     *
     * @param audioStream The audio stream to use for enrollment
     * @param id The speaker profile ID to enroll
     * @return An object encapsulating the Url that can be used to query the enrollment operation status
     * @throws EnrollmentException Thrown in case of an invalid audio format, internal server error or an invalid ID
     * @throws IOException Signals an I/O issue while reading the audio stream, a connection abortion, or an invalid response content
     */
    @Override
    public OperationLocation enroll(InputStream audioStream, UUID id) throws EnrollmentException, IOException {

        return enroll(audioStream, id, false);
    }

    /**
     * Enrolls a speaker profile from an audio stream
     *
     * @param audioStream The audio stream to use for enrollment
     * @param id The speaker profile ID to enroll
     * @param forceShortAudio Instruct the service to waive the recommended minimum audio limit needed for enrollment
     * @return An object encapsulating the Url that can be used to query the enrollment operation status
     * @throws EnrollmentException Thrown in case of an invalid audio format, internal server error or an invalid ID
     * @throws IOException Signals an I/O issue while reading the audio stream, a connection abortion, or an invalid response content
     */
    @Override
    public OperationLocation enroll(InputStream audioStream, UUID id, boolean forceShortAudio)
            throws EnrollmentException, IOException {

        String requestUrl = IDENTIFICATION_PROFILE_URI + "/" + id.toString() + "/enroll?" + SHORT_AUDIO_PARAM + "="
                + forceShortAudio;
        HttpPost request = (HttpPost) clientHelper.createHttpRequest(requestUrl, RequestType.POST);

        String fileName = id.toString() + "_" + new Date();
        HttpEntity entity = clientHelper.addStreamToEntity(audioStream, "enrollmentData", fileName);
        request.setEntity(entity);

        HttpResponse response = defaultHttpClient.execute(request);

        int statusCode = clientHelper.getStatusCode(response);

        if (statusCode == HttpStatus.SC_ACCEPTED) { //  202 Accepted (HTTP/1.0 - RFC 1945)
            String header = response.getFirstHeader(_OPERATION_LOCATION_HEADER).getValue();
            if (header.trim().length() != 0) {
                OperationLocation opLoc = new OperationLocation();
                opLoc.Url = header;
                return opLoc;
            } else {
                throw new EnrollmentException("Incorrect server response");
            }
        } else {
            String stringResponse = clientHelper.httpResponseToString(response);
            ErrorResponse errorResponse = gson.fromJson(stringResponse, ErrorResponse.class);
            if (errorResponse != null) {
                throw new EnrollmentException(errorResponse.error.message);
            } else {
                throw new EnrollmentException(String.valueOf(statusCode));
            }
        }
    }

    /**
     * Gets the enrollment operation status or result
     *
     * @param location The Url returned upon calling the enrollment operation
     * @return The enrollment operation object encapsulating the result
     * @throws EnrollmentException Thrown in case of an internal server error or an invalid URL
     * @throws IOException Signals a connection abortion, or an invalid response content
     */
    @Override
    public EnrollmentOperation checkEnrollmentStatus(OperationLocation location)
            throws EnrollmentException, IOException {

        HttpGet request = (HttpGet) clientHelper.createHttpRequest(location.Url, RequestType.GET);

        HttpResponse response = defaultHttpClient.execute(request);

        int statusCode = clientHelper.getStatusCode(response);
        String stringResponse = clientHelper.httpResponseToString(response);

        if (statusCode == HttpStatus.SC_OK) {
            return gson.fromJson(stringResponse, EnrollmentOperation.class);
        } else {
            ErrorResponse errorResponse = gson.fromJson(stringResponse, ErrorResponse.class);
            if (errorResponse != null) {
                throw new EnrollmentException(errorResponse.error.message);
            } else {
                throw new EnrollmentException(String.valueOf(statusCode));
            }
        }
    }

    /**
     * Deletes all enrollments associated with the given speaker identification profile permanently from the service
     *
     * @param id The speaker ID
     * @throws ResetEnrollmentsException Thrown in case of internal server error or an invalid ID
     * @throws IOException Signals a connection abortion, or an invalid response content
     */
    @Override
    public void resetEnrollments(UUID id) throws ResetEnrollmentsException, IOException {

        String requestUrl = IDENTIFICATION_PROFILE_URI + "/" + id.toString() + "/reset";
        HttpPost request = (HttpPost) clientHelper.createHttpRequest(requestUrl, RequestType.POST);

        HttpResponse response = defaultHttpClient.execute(request);

        int statusCode = clientHelper.getStatusCode(response);
        String stringResponse = clientHelper.httpResponseToString(response);

        if (statusCode != HttpStatus.SC_OK) {
            ErrorResponse errorResponse = gson.fromJson(stringResponse, ErrorResponse.class);
            if (errorResponse != null) {
                throw new ResetEnrollmentsException(errorResponse.error.message);
            } else {
                throw new ResetEnrollmentsException(String.valueOf(statusCode));
            }
        }
    }

    /**
     * Gets the identification operation status or result
     *
     * @param location The Url returned upon calling the identification operation
     * @return The identification operation object encapsulating the result
     * @throws IdentificationException Thrown in case of an internal server error or a wrong URL
     * @throws IOException Signals a connection abortion, or an invalid response content
     */
    @Override
    public IdentificationOperation checkIdentificationStatus(OperationLocation location)
            throws IdentificationException, IOException {

        HttpGet request = (HttpGet) clientHelper.createHttpRequest(location.Url, RequestType.GET);

        HttpResponse response = defaultHttpClient.execute(request);

        int statusCode = clientHelper.getStatusCode(response);
        String stringResponse = clientHelper.httpResponseToString(response);

        if (statusCode == HttpStatus.SC_OK) {
            return gson.fromJson(stringResponse, IdentificationOperation.class);
        } else {
            ErrorResponse errorResponse = gson.fromJson(stringResponse, ErrorResponse.class);
            if (errorResponse != null) {
                throw new IdentificationException(errorResponse.error.message);
            } else {
                throw new IdentificationException(String.valueOf(statusCode));
            }
        }
    }

    /**
     * Identifies a given speaker using the speaker ID and audio stream
     *
     * @param audioStream The audio stream to identify
     * @param ids The list of possible speaker profile IDs to identify from
     * @return An object encapsulating the Url that can be used to query the identification operation status
     * @throws IdentificationException Thrown in case of an internal server error, invalid IDs or a wrong audio format
     * @throws IOException Signals an I/O issue while reading the audio stream, a connection abortion, or an invalid response content
     */
    @Override
    public OperationLocation identify(InputStream audioStream, List<UUID> ids)
            throws IdentificationException, IOException {

        return identify(audioStream, ids, false);
    }

    /**
     * Identifies a given speaker using the speaker ID and audio stream
     *
     * @param audioStream The audio stream to identify
     * @param ids The list of possible speaker profile IDs to identify from
     * @param forceShortAudio Instruct the service to waive the recommended minimum audio limit needed for identification
     * @return An object encapsulating the Url that can be used to query the identification operation status
     * @throws IdentificationException Thrown in case of an internal server error, invalid IDs or a wrong audio format
     * @throws IOException Signals an I/O issue while reading the audio stream, a connection abortion, or an invalid response content
     */
    @Override
    public OperationLocation identify(InputStream audioStream, List<UUID> ids, boolean forceShortAudio)
            throws IdentificationException, IOException {

        String testProfileIds = clientHelper.buildProfileIdsString(ids);
        String requestUrl = IDENTIFICATION_URI + "?identificationProfileIds=" + testProfileIds.toString() + "&"
                + SHORT_AUDIO_PARAM + "=" + forceShortAudio;
        HttpPost request = (HttpPost) clientHelper.createHttpRequest(requestUrl, RequestType.POST);

        String fileName = "identificationsIds" + "_" + new Date();
        HttpEntity entity = clientHelper.addStreamToEntity(audioStream, "identificationData", fileName);
        request.setEntity(entity);

        HttpResponse response = defaultHttpClient.execute(request);

        int statusCode = clientHelper.getStatusCode(response);

        if (statusCode == HttpStatus.SC_ACCEPTED) { //  202 Accepted (HTTP/1.0 - RFC 1945)
            String header = response.getFirstHeader(_OPERATION_LOCATION_HEADER).getValue();
            if (header.trim().length() != 0) {
                OperationLocation opLoc = new OperationLocation();
                opLoc.Url = header;
                return opLoc;
            } else {
                throw new IdentificationException("Incorrect server response");
            }
        } else {
            String stringResponse = clientHelper.httpResponseToString(response);
            ErrorResponse errorResponse = gson.fromJson(stringResponse, ErrorResponse.class);
            if (errorResponse != null) {
                throw new IdentificationException(errorResponse.error.message);
            } else {
                throw new IdentificationException(String.valueOf(statusCode));
            }
        }
    }
}