Skip to main content

mod_portaudio

About

mod_portaudio is used for interfacing with sound card. This scenario is usually used when FreeSWITCH is used for a softphone basis, or as an easy way to get a local connection for development.

Interaction with mod_portaudio usually happens at the Freeswitch CLI, including setup, placing calls, answering calls, etc.

Many Softphones use embedded FreeSWITCH and portaudio at their core. Two examples are FSComm a QT4 cross platform communicator and FSClient a Windows .NET 4.0 softphone.

Portaudio (PA) is a cross-platform library.

Beware mod_portaudio recently had a large hunk of code injected into it and the configuration related to Shared Streams. For most scenarios this is not required and my throw (generally harmless) errors. Please see the note on #Shared_Streams_and_Endpoints for more details.

Features

  • Mulitiple calls with hold/call switching.
  • Inbound calls can play a ring file on specified device. (global and per call)
  • Optional hold music for backgrounded calls. (global and per call)
  • Switch audio devices even during a call (see live-stream-switching)
  • DTMF and deaf/mute options

Click here to expand Table of Contents

Command Reference

  • A device_ident is either part of the device name or the device ID number (prefixed with a #) seen in pa devlist.

pa help - print usage summary

Will show the available commands

pa devlist [xml] - list audio devices

The list will be in the following format: device number;short name;input channels;output channels;freeswitch device configuration

The freeswitch device configuration is a comma separated list of these elements:

r device is ring device i device is input device o device is output device

For example:

API CALL [pa(devlist)] output:
0;AC97 Audio (PCM);2;0
1;AC97 Audio (PCM);0;2
2;AC97 Audio (SPDIF);0;2
3;Jabra BT500;1;1

pa indev (device_ident), pa outdev (device_ident), pa switchstream (in_device_ident) (out_device_ident) - Set input/output device streams

Set the input (indev) or output (outdev) audio stream identified by device_ident. pa switchstream can switch both streams at once. These functions can only be use during a call if live-stream-switch configuration option is on.

pa ringdev (device_ident)

Set the device for portaudio to ring on a ringing call.

pa preparestream (in_device_ident) (out_device_ident) - Prepare an input and output device stream for use

Not required but preparing a stream prior to switching to it using the indev/outdev/switchstream allows portaudio to open the stream prior to using it. This makes for much more seamless switching between streams (no audio cut out). You only need to call this once before switching to that stream, streams are kept open until the end of all active calls. Calling it multiple times on the same stream will not cause delays or have negative effects.

pa call (number) [(dialplan)] [(cid_name)] [(cid_num)] - Place a new call

If you currently are on a call the existing call is placed on hold.

pa switch [(call_id)|none] - swap to a new active call

If used without args will switch to the previously active call. If a call_id (from pa list) is past will make that call active. If none is past then all calls are placed on hold.

pa list - view the current calls

pa list

pa dtmf (dtmf_digits) - send a dtmf string (1234) to the current call

pa dtmf 1234

pa answer [(call_id)] - Answer a ringing call

Without a call_id will answer the oldest call, with the call ID will answer that specific call

pa hangup [(call_id)] - End a call

Will end the current call if no ID is given otherwise will end the call with that ID.

pa rescan - Look for new devices

Portaudio assumes devices will not change during use, if they do you will need to use rescan for portaudio to see them. As this causes portaudio to re-init several things it cannot be used during an active call.

pa play [ringtest|(filename)] [(seconds)] [no_close] - Play an audio file or test stream to a device

Allows playing of arbitrary audio on the outdev device, optionally for only a specific number of seconds. no_close causes the audio stream to remain open after playing (allows for faster concurrent playing)

pa playdev (device_ident) [ringtest|(filename)] [(seconds)] [no_close] - Play an audio file or test stream to a specific device

Same as play but takes the device to play to also

pa flags on|off [ear] [mouth] - Set mute or deaf

Mutes the indev so no audio is directed to an active call, or deaf will silence the outdev from playing and audio

pa closestreams - Will close all active open streams

By default all streams are only closed at the end of all active calls, you can force them to close using pa closestreams as long as there is not an active call.

pa dump - Debug dump of all the devices

Dumps a variety of information about the various audio devices PA sees

Sample Implementations

Sample Softphone Configuration

For a sample Softphone configuration, see: Freeswitch_Softphone

Sample Mumble <-> Conference Bridge

Available with full instructions for Linux-based configurations at: Mumble_conference_with_alsa

Sample mod_portaudio intercom (auto answer)

In your dialplan, you can easily setup a portaudio intercom to dial into.

<extension name="Beaker portaudio intercom <pain>">
<condition field="destination_number" expression="^7246$">
<action application="bridge" data="portaudio/auto_answer"/>
</condition>
</extension>

Note, to easily remember the extension number, we use the T9 translate of the acronym "PortAudio INtercom" aka PAIN, which is 7246. Go ahead and dial 7246 and you will immediately be able to talk over the portaudio speaker :-)

Note too, in real life you should probably have some kind of notifying tone so nobody can silently eavesdrop on you.

PA System w/ Chime

A modification to the Intercom sample can be used to feed to a Public Address system. Chime plays before and after paging.

<extension name="paging-with-chime">
<condition field="destination_number" expression="^7243$">
<action application="set" data="bridge_pre_execute_bleg_app=playback"/>
<action application="set" data="bridge_pre_execute_bleg_data=/tmp/2_tone_chime.wav"/>
<action application="set" data="api_after_bridge=luarun post-chime.lua"/>
<action application="bridge" data="portaudio/auto_answer"/>
</condition>
</extension>

post-chime.lua:

   api = freesswitch.API();
api:execute("pa", "play /tmp/2_tone_chime.wav");

Web GUI

mod_xml_rpc loaded portaudio supplies it's own little GUI http://your.box:8080/api/pa

Install Notes

RHEL/CentOS

When building from source, make sure the "alsa-lib-devel" package is installed or you may get build errors.

Windows

Portaudio for windows by default builds with DirectX used for audio. It seems to provide the best results but it is possible to use the two other audio modes if desired by changing the build options.

macOS

There is a Homebrew formula for portaudio.

FreeBSD

Tested on FreeBSD-8.1 AMD64 on hardware which meets the 'Intel HD Audio' spec. Add to the FreeBSD kernel config file:

device snd_hda device sound. Build and install the new kernel, no additional audio config is required. The configuration in portaudio.conf.xml has indev, outdev and ringdev value="0" Also requires: <param name="dual-streams" value="true"/>

-kimc

Reference

portaudio.conf settings

These options are set in the <settings> block of the config.

no-ring-during-call

Prevent an incoming call from ringing the ring device if you are already on a call

no-auto-resume-call

Do not auto resume the last call when the current call ends

live-stream-switch

Allows using the indev,outdev, and switchstream commands during a call

indev, outdev, ringdev

Allows you to preset the specific input and output devices you want portaudio using you can use the same device_ident values as used in the command reference

debug

Set the debug level for portaudio

dialplan

Set the default dialplan for making calls

context

Set the default context for calls

dual-streams

The default value is false which means one thread is handling the audio reading/writing from/to the core. Value true means 2 threads are used instead of one.

Shared Streams and Endpoints

Shared streams and endpoints are completely different than everything else mentioned on this page and are not required for most cases of using portaudio (especially if you are just using FS as a single softphone). They are specifically meant for if you need to use multiple audio devices on different calls at the same time. All of the stream commands above are NOT related to shared streams (just confusing terminology). The only documentation on them is found in the configuration file itself and the code was not tested by a large number of people so stability is not guaranteed. Most of the code paths for shared streams/endpoints are completely different from the rest of portaudio so most of the normal portaudio commands do not work with the shared stream/endpoint system. The commands that related to shared streams are the pa shtreams and pa endpoints commands, not the other stream commands. While you may see errors about shared streams in the console if you do not remove the configuration (per the portaudio.conf note below) these will not stop portaudio from working normally.

portaudio.conf <streams> and <endpoints>

See the note on shared streams and endpoints above. You can safely remove the <streams> and <endpoints> configuration sections from portaudio.conf if you do not need them, otherwise they will throw some console errors by default (that can be ignored).

Custom Events

Portaudio emits several custom events for major changes in portaudio. Every event will contain the pa_call_id variable to indicate which call is being changed.

portaudio::ringing

Is emitted when incoming call is received and portaudio plays the ring on the audio device.

Event portaudio::ringing

Event-Subclass: portaudio::ringing
Event-Name: CUSTOM
Core-UUID: 950e273a-f74b-11de-bf7c-a394ee5f864b
FreeSWITCH-Hostname: jmesquita.dyndns.org
FreeSWITCH-IPv4: <IP>
FreeSWITCH-IPv6: ::1
Event-Date-Local: 2010-01-02 00:11:00
Event-Date-GMT: Sat, 02 Jan 2010 03:11:00 GMT
Event-Date-Timestamp: 1262401860975758
Event-Calling-File: mod_portaudio.c
Event-Calling-Function: channel_on_init
Event-Calling-Line-Number: 265
event_info: BRRRRING! BRRRRING! call 2

call_id: 2
Channel-State: CS_INIT
Channel-State-Number: 1
Channel-Name: portaudio/1000
Unique-ID: 740f37a8-f74c-11de-bf81-a394ee5f864b
Call-Direction: outbound
Presence-Call-Direction: outbound
Answer-State: ringing
Channel-Read-Codec-Name: L16
Channel-Read-Codec-Rate: 48000
Channel-Write-Codec-Name: L16
Channel-Write-Codec-Rate: 48000
Caller-Username: 1000
Caller-Dialplan: XML
Caller-Caller-ID-Name: Mesquita
Caller-Caller-ID-Number: 1000
Caller-Network-Addr: <IP>
Caller-ANI: 1000
Caller-Unique-ID: 740f37a8-f74c-11de-bf81-a394ee5f864b
Caller-Source: mod_sofia
Caller-Context: public
Caller-Channel-Name: portaudio/1000
Caller-Profile-Index: 1
Caller-Profile-Created-Time: 1262401860435762
Caller-Channel-Created-Time: 1262401860435762
Caller-Channel-Answered-Time: 0
Caller-Channel-Progress-Time: 1262401860975758
Caller-Channel-Progress-Media-Time: 0
Caller-Channel-Hangup-Time: 0
Caller-Channel-Transfer-Time: 0
Caller-Screen-Bit: true
Caller-Privacy-Hide-Name: false
Caller-Privacy-Hide-Number: false
Other-Leg-Username: 1000
Other-Leg-Dialplan: XML
Other-Leg-Caller-ID-Name: Mesquita
Other-Leg-Caller-ID-Number: 1000
Other-Leg-Network-Addr: <IP>
Other-Leg-ANI: 1000
Other-Leg-Destination-Number: 1001
Other-Leg-Unique-ID: 73b8a0dc-f74c-11de-bf80-a394ee5f864b
Other-Leg-Source: mod_sofia
Other-Leg-Context: public
Other-Leg-Channel-Name: sofia/softphone/1000@<IP>
Other-Leg-Profile-Created-Time: 0
Other-Leg-Channel-Created-Time: 0
Other-Leg-Channel-Answered-Time: 0
Other-Leg-Channel-Progress-Time: 1262401860975758
Other-Leg-Channel-Progress-Media-Time: 0
Other-Leg-Channel-Hangup-Time: 0
Other-Leg-Channel-Transfer-Time: 0
Other-Leg-Screen-Bit: true
Other-Leg-Privacy-Hide-Name: false
Other-Leg-Privacy-Hide-Number: false
variable_channel_name: portaudio/1000
variable_is_outbound: true
variable_max_forwards: 67
variable_originator_codec: PCMU@8000h@20i
variable_originator: 73b8a0dc-f74c-11de-bf80-a394ee5f864b
variable_signal_bond: 73b8a0dc-f74c-11de-bf80-a394ee5f864b
variable_switch_m_sdp: v=0
o=FreeSWITCH 1262373184 1262373185 IN IP4 <IP>
s=FreeSWITCH
c=IN IP4 <IP>
t=0 0
m=audio 18000 RTP/AVP 0 8 3 101 13
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:3 GSM/8000
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-16
a=rtpmap:13 CN/8000
a=ptime:20

variable_originate_early_media: true
variable_pa_call_id: 2
variable_read_codec: L16
variable_read_rate: 48000
variable_write_codec: L16
variable_write_rate: 48000

portaudio::makecall

Is emitted when a call is placed using pa call API command. fail header is set to true _ONLY_ the mod_portaudio fails to spawn the session, not if call is not completed.

Event portaudio::makecall

Event-Subclass: portaudio::makecall
Event-Name: CUSTOM
Core-UUID: 950e273a-f74b-11de-bf7c-a394ee5f864b
FreeSWITCH-Hostname: <HOSTNAME>
FreeSWITCH-IPv4: <IP>
FreeSWITCH-IPv6: ::1
Event-Date-Local: 2010-01-02 00:05:45
Event-Date-GMT: Sat, 02 Jan 2010 03:05:45 GMT
Event-Date-Timestamp: 1262401545047757
Event-Calling-File: mod_portaudio.c
Event-Calling-Function: place_call
Event-Calling-Line-Number: 1810
Channel-State: CS_INIT
Channel-State-Number: 1
Channel-Name: portaudio/1000
Unique-ID: b7c096e6-f74b-11de-bf7e-a394ee5f864b
Call-Direction: inbound
Presence-Call-Direction: inbound
Answer-State: answered
Channel-Read-Codec-Name: L16
Channel-Read-Codec-Rate: 48000
Channel-Write-Codec-Name: L16
Channel-Write-Codec-Rate: 48000
Caller-Dialplan: XML
Caller-Caller-ID-Name: FreeSWITCH
Caller-Caller-ID-Number: 0000000000
Caller-Network-Addr: <IP>
Caller-Destination-Number: 1000
Caller-Unique-ID: b7c096e6-f74b-11de-bf7e-a394ee5f864b
Caller-Source: mod_portaudio
Caller-Context: default
Caller-Channel-Name: portaudio/1000
Caller-Profile-Index: 1
Caller-Profile-Created-Time: 1262401544513764
Caller-Channel-Created-Time: 1262401544513764
Caller-Channel-Answered-Time: 1262401545047757
Caller-Channel-Progress-Time: 0
Caller-Channel-Progress-Media-Time: 0
Caller-Channel-Hangup-Time: 0
Caller-Channel-Transfer-Time: 0
Caller-Screen-Bit: true
Caller-Privacy-Hide-Name: false
Caller-Privacy-Hide-Number: false
variable_channel_name: portaudio/1000
variable_endpoint_disposition: ANSWER
variable_pa_call_id: 1
variable_read_codec: L16
variable_read_rate: 48000
variable_write_codec: L16
variable_write_rate: 48000
fail: false
portaudio::callheld

Event when call is put on hold

portaudio::callresumed

Event when a call is taken off hold

See Also