Record the voice of the Caller



By studying the following article, you can learn how to record the voice of the caller and how to play it back. First of all, please install an API Extension and subscribe to Incoming Call URL with the URL of your application.
Learn how to record the voice of the caller and how to play it back. First of all, please login and connect to the API Extension. Wait for an Incoming call, and when it is occured just attach a WaveStreamRecorder object into the call and start it.


Example code coming soon...
[HttpPost]
public ActionResult RecordCall(string notificationName, string callLegID, string caller, string apiExtension)
{
	return Content(
	"<Response>" +
	  "<UserInput timeout=\"10\" repeat=\"true\">" +
		"<InitialCommands>" +
		  "<Speak>Hello.</Speak>  " +
		  "<Speak>To record your own voice, please press 1.</Speak>" +
		  "<Speak>To hang up the line, please press 2.</Speak>" +
		"</InitialCommands>" +
		"<Inputs>" +
		  "<Input key=\"1\">" +
			"<Speak>Press 9 for stop recording! Recording start now:</Speak>" +
			"<Record finishedUrl=\"http://yoursite.com/Home/RecordFinished\" FinishDigits=\"9\"/>" +
		  "</Input>" +
		  "<Input key=\"2\">" +
			"<Speak>Good bye!</Speak>" +
			"<Hangup></Hangup>" +
		  "</Input>" +
		"</Inputs>" +
	  "</UserInput>" +
	"</Response>", "text/xml");
}
class RecordCallController < ApplicationController
  
  protect_from_forgery except: :index

  # In routes.rb you need to set the following routing rule
  # post '/record_call' => 'record_call#index'
  
  def index

    render :xml => '<?xml version="1.0" encoding="UTF-8"?>
    <Response>
      <UserInput timeout="10" repeat="true">
        <InitialCommands>
          <Speak>Hello.</Speak>
          <Speak>To record your own voice, please press 1.</Speak>
          <Speak>To hang up the line, please press 2.</Speak>
        </InitialCommands>
        <Inputs>
          <Input key="1">
            <Speak>Press 9 for stop recording! Recording start now:</Speak>
            <Record finishedUrl="http://yoursite.com:3000/record_finished" FinishDigits="9"/>
          </Input>
          <Input key="2">
            <Speak>Good bye!</Speak>
            <Hangup></Hangup>
          </Input>
        </Inputs>
      </UserInput>
    </Response>'
  end

end
<?php
	print "<Response>";
		print "<UserInput timeout=\"10\" repeat=\"true\">";
			print "<InitialCommands>";
				print "<Speak>Hello.</Speak>";	
				print "<Speak>To record your own voice, please press 1.</Speak>";	
				print "<Speak>To hang up the line, please press 2.</Speak>";
			print "</InitialCommands>";	
			print "<Inputs>";	
				print "<Input key=\"1\">";
					print "<Speak>";
						print "Press 9 for stop recording! Recording start now:";
					print "</Speak>";
					print "<Record finishedUrl=\"http://yoursite.com/RecordFinished.php\" FinishDigits=\"9\"/>";	
				print "</Input>";
				print "<Input key=\"2\">";	
					print "<Speak>Good bye!</Speak>";	
					print "<Hangup></Hangup>";
				print "</Input>";
			print "</Inputs>";	
		print "</UserInput>";
	print "</Response>";
?>
using System;
using OPSSDK;
using OPSSDKCommon.Model.Call;
using Ozeki.Media.MediaHandlers;
using Ozeki.VoIP;

namespace OPS_QuickStartExample_CSharp
{
    class Program
    {
        static void Main(string[] args)
        {
            var client = new OpsClient();
            client.ErrorOccurred += (sender, info) =>
                Console.WriteLine(info.Message);

            if (!client.Login("ozekixepbx.ip", "admin", "12345"))
                return;

            var apiExtension = client.GetAPIExtension("9000");
            apiExtension.IncomingCall += IncomingCall;
            Console.ReadLine();
        }

        private static void IncomingCall(object sender, VoIPEventArgs e)
        {
            var call = e.Item;
            var lastRecordedFilePath = "c://" + Guid.NewGuid() + ".wav";
            WaveStreamRecorder recorder = null; 
            WaveStreamPlayback player = null;
            var tts = new TextToSpeech();
            call.ConnectAudioSender(tts);

            call.DtmfReceived += (s, ev) =>
            {
                if (ev.Item.Signal == DtmfNamedEvents.Dtmf1)
                {
                    if (tts == null)
                        return;
                    tts.Dispose();
                    tts = null;
                    recorder = new WaveStreamRecorder(lastRecordedFilePath);
                    call.ConnectAudioReceiver(recorder);
                    recorder.StartStreaming();
                }
                else if (ev.Item.Signal == DtmfNamedEvents.Dtmf9)
                {
                    if (recorder == null)
                        return;
                    recorder.Dispose();
                    recorder = null;

                    player = new WaveStreamPlayback(lastRecordedFilePath);
                    call.ConnectAudioSender(player);
                    player.StartStreaming();
                    player.Stopped += (sen, eve) => call.HangUp();
                }
            };

            call.CallStateChanged += (s, ev) =>
            {
                if (ev.Item == CallState.Answered)
                    tts.AddAndStartText("To record your own voice, please press 1. " +
                                        "Press 9 for stop recording!");
                else if (ev.Item.IsCallEnded())
                {
                    if (recorder != null)
                        recorder.Dispose();
                    if (tts != null)
                        tts.Dispose();
                    if (player != null)
                        player.Dispose();
                }
            };

            call.Accept();
        }
    }
}
#!c:/Perl64/bin/perl.exe   
print "Content-Type: text/plain\n\n";
#You have to add the directory route of perl.exe, and print the content type

#Send response by print
print 
'<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <UserInput timeout="10" repeat="true">
    <InitialCommands>
      <Speak>Hello.</Speak>			
      <Speak>To record your own voice, please press 1.</Speak>
      <Speak>To hang up the line, please press 2.</Speak>
    </InitialCommands>
    <Inputs>
      <Input key="1">
        <Speak>Press 9 for stop recording! Recording start now:</Speak>
        <Record finishedUrl="http://yoursite.com/RecordFinished.pl" FinishDigits="9"/>
      </Input>
      <Input key="2">
        <Speak>Good bye!</Speak>	
        <Hangup></Hangup>
      </Input>
    </Inputs>
  </UserInput>
</Response>'
Imports Ozeki.Media.MediaHandlers
Imports OPSSDKCommon.Model.Call
Imports OPSSDK
Imports Ozeki.VoIP

Module Module1
    Public Sub Main(args As String())
        Dim client = New OpsClient()
        AddHandler client.ErrorOccurred, Sub(sender, info)
                                             Console.WriteLine(info.Message)
                                         End Sub

        If Not client.Login("ozekixepbx.ip", "admin", "12345") Then
            Return
        End If

        Dim apiExtension = client.GetAPIExtension("9000")
        AddHandler apiExtension.IncomingCall, AddressOf IncomingCall
        Console.ReadLine()
    End Sub

    Private Sub IncomingCall(sender As Object, e As VoIPEventArgs(Of ICall))
        Dim [call] = e.Item
        Dim lastRecordedFilePath = "c://" & Guid.NewGuid().ToString() & ".wav"
        Dim recorder As WaveStreamRecorder = Nothing
        Dim player As WaveStreamPlayback = Nothing
        Dim tts = New TextToSpeech()
        [call].ConnectAudioSender(tts)

        AddHandler [call].DtmfReceived,
            Sub(s, ev)
                If ev.Item.Signal = DtmfNamedEvents.Dtmf1 Then
                    If tts Is Nothing Then
                        Return
                    End If
                    tts.Dispose()
                    tts = Nothing
                    recorder = New WaveStreamRecorder(lastRecordedFilePath)
                    [call].ConnectAudioReceiver(recorder)
                    recorder.StartStreaming()
                ElseIf ev.Item.Signal = DtmfNamedEvents.Dtmf9 Then
                    If recorder Is Nothing Then
                        Return
                    End If
                    recorder.Dispose()
                    recorder = Nothing

                    player = New WaveStreamPlayback(lastRecordedFilePath)
                    [call].ConnectAudioSender(player)
                    player.StartStreaming()
                    AddHandler player.Stopped, Sub(sen, eve)
                                                   [call].HangUp()
                                               End Sub
                End If
            End Sub

        AddHandler [call].CallStateChanged,
            Sub(s, ev)
                If ev.Item = CallState.Answered Then
                    tts.AddAndStartText("To record your own voice, please press 1. " &
                                        "Press 9 for stop recording!")
                ElseIf ev.Item.IsCallEnded() Then
                    If recorder IsNot Nothing Then
                        recorder.Dispose()
                    End If
                    If tts IsNot Nothing Then
                        tts.Dispose()
                    End If
                    If player IsNot Nothing Then
                        player.Dispose()
                    End If
                End If
            End Sub

        [call].Accept()
    End Sub
End Module
import urllib.parse
import datetime
from cgi import escape

def application(environ, start_response):
   
    if(environ["PATH_INFO"] == ''):
        result = receiveCall(environ)
    elif(environ["PATH_INFO"] == '/RecordFinished'):
        result = recordFinished(environ)

    response_headers = [('Content-type', 'text/plain'), ('Content-Length', str(len(result)))]
    start_response('200 OK', response_headers)

    return [result]

def receiveCall(environ):
    return """<?xml version="1.0" encoding="UTF-8"?>
            <Response>
              <UserInput timeout="10" repeat="true">
                <InitialCommands>
                  <Speak>Hello.</Speak>			
                  <Speak>To record your own voice, please press 1.</Speak>
                  <Speak>To hang up the line, please press 2.</Speak>
                </InitialCommands>
                <Inputs>
                  <Input key="1">
                    <Speak>Press 9 for stop recording! Recording start now:</Speak>
                    <Record finishedUrl="{0}" FinishDigits="9"/>
                  </Input>
                  <Input key="2">
                    <Speak>Good bye!</Speak>
                    <Hangup></Hangup>
                  </Input>
                </Inputs>
              </UserInput>
            </Response>""".format("http://yoursite.com/RecordFinished")
package pbxsampleapp;

import com.sun.net.httpserver.*;
import java.io.*;
import java.net.*;
import java.util.*;

public class RecordCall 
{
  public static void main(String[] args)
    {
        try
        {
            System.out.println("Starting http server...");
            HttpServer server = HttpServer.create(new InetSocketAddress(InetAddress.getByAddress(new byte[] { 0, 0, 0, 0 }), 12345), 0);
            server.createContext("/callrecoding", new PbxSampleApp.PBXRequestHandler());
            server.start();
            System.out.println("http server running on " + server.getAddress().toString());
        }
        catch (IOException ex) { System.out.println("Error" + ex.toString()); }
    }

    static class PBXRequestHandler implements HttpHandler
    {
        @Override
        public void handle(HttpExchange httpExchange) throws IOException
        {
            Map parameters = parseQuery(httpExchange.getRequestURI().getQuery());
            String response = GetResponse(parameters);
            httpExchange.getResponseHeaders().add("Content-type", "text/xml");
            if (response.length()==0)
                httpExchange.sendResponseHeaders(404, response.length());
            else
                httpExchange.sendResponseHeaders(200, response.length());
            OutputStream os = httpExchange.getResponseBody();
            os.write(response.getBytes());
            os.close();
        }

        private String GetResponse(Map queryparams)
        {
            if (queryparams.containsKey("action"))
            {
                switch (queryparams.get("action").toString())
                {
                    case "RecordCall":
                       return getRecordCallOzML(queryparams);
                    case "RecordFinished":
                        return getRecordFinishedOzMl(queryparams);
                }
            }
            return "";
        }
        
        private String getRecordCallOzML(Map queryparams)
        {
             return "<?xml version=\"1.0\"?>"
                + "<Response>"
                + "<UserInput timeout=\"10\" repeat=\"true\">"
                + "<InitialCommands>"
                + "<Speak>Hello.</Speak>  "
                + "<Speak>To record your own voice, please press 1.</Speak>"
                + "<Speak>To hang up the line, please press 2.</Speak>"
                + "</InitialCommands>"
                + "<Inputs>"
                + "<Input key=\"1\">"
                + "<Speak>Press 9 for stop recording! Recording start now:</Speak>"
                + "<Record finishedUrl=\"http://yoursite.com/callrecoding?action=RecordFinished\" FinishDigits=\"9\"/>"
                + "</Input>"
                + "<Input key=\"2\">"
                + "<Speak>Good bye!</Speak>"
                + "<Hangup></Hangup>"
                + "</Input>"
                + "</Inputs>"
                + "</UserInput>"
                + "</Response>";
        }
        
        private Map<String, String> parseQuery(String params) throws UnsupportedEncodingException
        {
            Map<String, String> query_pairs = new LinkedHashMap<String, String>();
            String[] pairs = params.split("&");
            String paramname;
            String value;
            for (String pair : pairs)
            {
                int idx = pair.indexOf("=");
                paramname = pair.substring(0, idx);
                value = pair.substring(idx + 1);
                query_pairs.put(paramname, value);
                System.out.println(String.format("%s: %s", paramname, value));
            }
            return query_pairs;
        }
    }
}
Code example 1 - Recording the voice of the Caller by pressing button

At the beginning of the call, the caller is asked, whether he accept the Call Recording or not. If the Caller presses the 1 button, the call recording is started by Record command. Else if the pressed button is 2 then the call will hang up by the Hangup command. When the record is finished (by HangUp, or pressing the 9 button), an HTTP Request will be sent to the specified finishedUrl URL with the help of the API Extension. Please change the yoursite.com text to that ip address where the sample applications are running. At this URL you can add more commands as response, for example playing back his own voice to the Caller.
Write this code to the file at the given finishedUrl URL:

IN MORE DETAILS

Get the OPSSDK.dll

First you need to add the OPSSDK.dll reference to your project
(You can find OPSSDK.dll at C:\Program Files\Ozeki\Ozeki Phone System\Examples.NET API\OPSSDK.dll).

Login and connect

Create a new instance of OpsClient, and subscribe to the ErrorOccurred event. Your program will communicate with the Ozeki Phone System through this client. Try to login with the client into the running Ozeki Phone System, with the address of the server(ozekixepbx.ip) and a valid username, password combination.

If you are ready, try to get an existing API Extension, with the GetApiExtension method of the OpsClient. Read more about installation of API Extension.

Further steps

When you are ready with the initial steps above, subscribe the IncomingCall event of the apiExtension. This event will be triggered every time when the selected API Extension is called. In the mentioned event handler you will see the e.Item parameter, this parameter will be the call object. This call object has a CallState property, when the CallState is Ringing subscribe the CallStateChanged and the DtmfReceived events of the call object and Accept the call.

The CallStateChanged event will trigger when the call is going to another state . When the CallState is Answered, you can connect the devices to the call. When the call has ended, please do not forget to disconnect all devices from the call.

When the CallState is Answered, start and connect a TextToSpeech object to the call, that will read to the caller the "To record your own voice, please press 1. Press 9 for stop recording!! When button 1 is pressed the program will start and connect a WaveStreamRecorder object to the call. This object will record the caller's voice into a file (what you can give). When the caller press button 9 what will stop the recording, and start a WaveStreamPlayback to play back the recorded voice to the caller.

With these steps, you learnt how to receive an incoming call, record the caller's voice and play back it to him/her.



Example code coming soon...
[HttpPost]
public ActionResult RecordFinished(string success, string duration, string recordURL, string callId, string caller, string callee, string notificationName)
{
	return Content(
   "<Response>" +
	   "<Speak>Recorded voice:</Speak>" +
	   "<Play Repeat=\"false\">" + recordURL + "</Play>" +
	   "<Speak>Good bye!</Speak>" +
   "</Response>", "text/xml");
}
class RecordFinishedController < ApplicationController

  protect_from_forgery except: :index

  # In routes.rb you need to set the following routing rule
  # post '/record_finished' => 'record_finished#index'
  
  # http://yoursite.com:3000/record_finished
  def index

    render :xml => '<?xml version="1.0" encoding="UTF-8"?>
    <Response>
      <Speak>Recorded voice:</Speak>
      <Play>' + params['RecordURL'] + '</Play>
      <Speak>Good bye!</Speak>
    </Response>'
  end

end
<?php
print "<Response>";
	print "<Speak>Recorded voice:</Speak>";
	print "<Play>".$_REQUEST['RecordURL']."</Play>";
	print "<Speak>Good bye!</Speak>";
print "</Response>";
?>
#!c:/Perl64/bin/perl.exe   
print "Content-Type: text/plain\n\n";
#You have to add the directory route of perl.exe, and print the content type

use CGI qw(:cgi-lib :standard);  # Use CGI modules that let read data passed
&ReadParse(%in); #Read in from HTTPPost parameters

#Send response by print
print 
'<?xml version="1.0" encoding="UTF-8"?>
<Response>
	<Speak>Recorded voice:</Speak>
	<Play>'. $in{'RecordURL'} .'</Play>
	<Speak>Good bye!</Speak>
</Response>'
def recordFinished(environ):
    try:
        length= int(environ.get('CONTENT_LENGTH', '0'))

        if(length != 0):
            body = environ['wsgi.input'].read(length)
            receivedData = urllib.parse.parse_qs(body.decode("utf-8"))
            recordURL = escape(receivedData['RecordURL'][0])
            
            return """<?xml version="1.0" encoding="UTF-8"?>
            <Response>
                <Speak>Recorded voice:</Speak>
                <Play>{}</Play>
                <Speak>Good bye!</Speak>
            </Response>""".format(recordURL)

    except IOError:
        return ""
	
    private String getRecordFinishedOzMl(Map queryparams)
    {
        return "<Response>"
                            + "<Speak>Recorded voice:</Speak>"
                            + "<Play Repeat=\"false\">" 
                            + queryparams.get("recordurl").toString() + "</Play>"
                            + "<Speak>Good bye!</Speak>"
                            + "</Response>";
    }

Code example 2 - Handling the finishing of the CallRecord
When the sent HTTP Request is arrived, the parameters of this request are processed into a variable. The important parameter now is the RecordUrl. You can execute the Play command, with this parameter. With that command in the response, the caller will hear his own recorded voice.

More information