Samsung Programming Professional Audio
Samsung Programming Professional Audio
Samsung Programming Professional Audio
Programming Guide
Version 2.1.1
COPYRIGHT ............................................................................................................................................................... 45
Professional Audio improves the environment in which virtual instruments are created by adding high-
performance audio processing logic. You can use Professional Audio to create applications without
background knowledge in hardware and high-performance drivers. There is no need to worry about
connecting devices between applications. Using the provided modules and a USB MIDI driver, you can
create virtual instrument applications with ease.
• Create professional musical instrument applications using JACK Audio Connection Kit features.
• Send Musical Instrument Digital Interface (MIDI) data, control audio/MIDI ports, access/use added
plug-in information, and synchronize virtual instruments.
• Create new sound modules and process high-speed audio signals.
ALSA
The Advanced Linux Sound Architecture (ALSA) provides audio and MIDI functionality for the Linux
operating system. ALSA offers the following features:
• Support for all audio interface types, from consumer sound cards to professional multichannel
audio interfaces.
• Fully modularized sound drivers.
• Symmetric multiprocessing (SMP) and thread-safe design. For more information, click this link.
• Library (alsa-lib) to simplify application programming and to provide higher level functionality.
• Support for the older Open Sound System (OSS) API, providing binary compatibility for most OSS
programs.
JACK
JACK is a system for handling real-time, low-latency audio and MIDI. It runs on GNU/Linux, Solaris, FreeBSD,
OS X, and Windows and can be ported to other POSIX-conformant platforms. It can connect a number of
different applications to an audio device and allow them to share audio. Its clients can run in their own
processes as normal applications, or they can run within the JACK server as a plug-in. JACK also supports
distributing audio processing across a network, both in fast and reliable LANs and in slower, less reliable
WANs.
JACK is designed from the ground up for professional audio work, and its design focuses on two key areas:
synchronous execution of all clients and low-latency operation.
APAService provides an environment that enables JACK to run on Android and serves as a connection
between APAClient and Android applications.
Shared Memory Service provides a shared memory for facilitating fast and continual data communications
between processes and for enabling audio, MIDI, and control data sharing between JACK daemons and
JACK clients.
1.2. Architecture
The following figure shows the Professional Audio architecture.
• Applications: One or more applications that are integrated with the Professional Audio package. By
default, Professional Audio does not supply these kinds of applications.
• SapaService: Component for connecting with JACK and for managing ports, processors, and
connections.
• SapaProcessor: Component for managing processors that handle audio stream or MIDI data.
• APAService and APAClient: Components for controlling JACK and making it available on Android .
• Shared Memory Service: Component for facilitating data sharing between processing modules.
• ALSA: Component that provides audio and MIDI functionality for the Professional Audio package.
• Type1: Include your own sound processing modules within your APK.
• Type2: Rely on plug-in processing modules. This approach is useful when you want to concentrate
on UI development only.
• Type3: Include processing modules only. You can develop and distribute your own sound
processing modules without UIs in APKs.
The applications are designed to operate with 2 types of data: audio data and control data. Audio data
flows according to the connection information in the port that is connected through the JACK daemon. The
JACK daemon is responsible for handling the audio data.
APAService connects and disconnects ports between applications and communicates with applications and
processing modules.
The following figure shows an application and a processing module being run at the same time.
Applications are executed in the same manner as an android application process forked by a zygote. They
communicate with ApaService through SapaService. When you create and register a SapaProcessor object
with SapaService, it creates a new process. The new process contains the APAClient and your processing
module. You can use SapaProcessor to communicate with the process that loads the processing module.
1.2.2. Ports
JACK ports connect processing modules. To have a speaker produce a sound for example, the sound data
needs to be transferred to the port linked to the speaker. There are 2 types of ports: input ports, for sound
inputs, and output ports, for sound outputs.
Professional Audio provides the following ports by default. The ports available also depend on the device:
Call the SapaService.getAllPlugin() method to get the list of plug-ins installed on the device. Call the
SapaService.PluginInfo() method to get the following information for each plug-in:
For information on creating Type 2 applications that use already installed plug-ins, see Hello Sapa.
1.6. Components
• Components:
o professionalaudio-v2.1.1.jar
o sdk-v1.0.0.jar
• Imported packages:
o com.samsung.android.sdk.professionalaudio
<uses-permission android:name=
"com.samsung.android.providers.context.permission.WRITE_USE_APP_FEATURE_SURVEY"/>
For more information on additional actions required for developing Type 1 and Type 3 applications, see
sections 4.2 and 4.3.
try {
Sapa sapa = new Sapa();
sapa.initialize(this);
mService = new SapaService();
mService.start(SapaService.START_PARAM_DEFAULT_LATENCY);
} catch (SsdkUnsupportedException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
if (mProcessor != null) {
mProcessor.activate();
}
package com.samsung.android.sdk.professionalaudio.sample.simpleclient;
import android.app.Activity;
import android.os.Bundle;
import android.util.AndroidRuntimeException;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.samsung.android.sdk.SsdkUnsupportedException;
import com.samsung.android.sdk.professionalaudio.Sapa;
import com.samsung.android.sdk.professionalaudio.SapaProcessor;
import com.samsung.android.sdk.professionalaudio.SapaService;
SapaService mService;
SapaProcessor mClient;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_apasimple_client);
try {
Sapa sapa = new Sapa();
sapa.initialize(this);
mService = new SapaService();
mService.stop(true);
mService.start(SapaService.START_PARAM_DEFAULT_LATENCY);
mClient = new SapaProcessor(this, null, new
SapaProcessor.StatusListener() {
@Override
public void onKilled() {
Log.v(TAG, "SapaSimpleClient will be closed. because
of the SapaProcessor was closed.");
mService.stop(true);
finish();
}
});
mService.register(mClient);
} catch (SsdkUnsupportedException e) {
e.printStackTrace();
Toast.makeText(this, "not support professional audio",
Toast.LENGTH_LONG).show();
finish();
return;
} catch (InstantiationException e) {
e.printStackTrace();
Toast.makeText(this, "fail to instantiate", Toast.LENGTH_LONG)
@Override
protected void onDestroy() {
super.onDestroy();
if(mService != null){
if(mClient != null){
mService.unregister(mClient);
}
mService.stop(true);
}
}
}
The monitor service checks whether the Jack daemon is running or not.
You can stop the Jack daemon throught it to save the battery power.
<uses-permission
android:name="com.samsung.android.providers.context.permission.WRITE_USE_APP_FEATURE_SURVEY"
/>
The permission is defined in the Samsung SDK. It is used for statistics of Professional Audio SDK usages. It
is mandatory for the SDK. It is not for the user.
<uses-permission
android:name="com.samsung.android.sdk.professionalaudio.permission.USE_CONNECTION_SERVICE" />
When you use the AudioConnectionService, You have to include this permission. It is for granting access to
the audio connection service.
The Sapa can run only on Samsung Smart Devices. Some Samsung Smart Device models do not support
some of the packages.
You can use a initialize() method to initialize and also to check if the device supports the Sapa. If the
device does not support the Sapa, the method throws an SsdkUnsupportedException exception. You should
handle this exception. If an SsdkUnsupportedException exception is thrown, you can check the exception
type with SsdkUnsupportedException.getType(). If the device is a Samsung model that does not
support the Sapa, the exception type is SsdkUnsupportedException.DEVICE_NOT_SUPPORTED.
• initialize() initializes Professional Audio. You need to initialize the Professional Audio package
before you can use it. If the device does not support Professional Audio,
SsdkUnsupportedException is thrown.
• getVersionCode() gets the Professional Audio version number as an integer.
• getVersionName() gets the Professional Audio version name as a string.
If the Professional Audio package fails to initialize, the initialize() method throws an
SsdkUnsupportedException exception. To find out the reason for the exception, check the exception
message.
• DEVICE_NOT_SUPPORTED: The device does not support the Professional Audio package.
1. Create an instance of the Professional Audio package and initialize it by calling the
Sapa.initialize() method. If Professional Audio is not available on the device, an
SsdkUnsupportedException exception is thrown.
try {
Sapa sapa = new Sapa();
sapa.initialize(this);
} catch (SsdkUnsupportedException e) {
} catch (InstantiationException e) {
}
2. To start the Professional Audio service with the default latency, call the SapaService.start()
method and pass SapaService.START_PARAM_DEFAULT_LATENCY:
try {
(new SapaService()).start(SapaService.START_PARAM_DEFAULT_LATENCY);
} catch (AndroidRuntimeException e) {
}
To use the SapaSimplePlugin plug-in referred in the code below, you need to install SapaSimplePlugin.apk.
Create a new SapaProcessor instance for the plug-in and register it with the SapaService.
(new SapaService()).stop(false);
If you did not unregister the processing module, the service will not stop. Unregister all the processing
modules registered in the service before calling the SapaService.stop() method.
The processing module is loaded on the separated process not UI process. So, You can use only a static
library not shared.
You can use the SapaProcessor class in the SDK to communicate with a UI( Java ).
The following sections demonstrate how to develop an application that plays a sine wave sound, replace
the appropriate sections with your own implementation.
1. Go to the Android NDK website and download the NDK for Windows.
2. Unzip the package to the C:\ drive. This creates a new folder called android-ndk-r9\*.* in the
C:\ drive.
3. To set up the environment variables on your computer; In the window for setting environment
variables, in the Path textbox, add C:\android-ndk-r9.
4. Test your settings in the command line as shown below:
5. Copy and paste the Professional Audio NDK to the Android project folder. Ensure that all the items
in the Professionalaudio_ndk\platforms\android-<version>\arch-<arch>\usr\ folder are copied to
the <project root>\jni\apa\.
#include <stdio.h>
#include <IAPAInterface.h>
#include "JackSimpleClient.h"
#include "APACommon.h"
namespace android {
class APAWave : IAPAInterface {
public:
APAWave();
virtual ~APAWave();
int init();
int sendCommand(const char* command);
IJackClientInterface* getJackClientInterface();
int request(const char* what, const long ext1, const long capacity,
size_t &len, void* data);
private:
JackSimpleClient mSimpleClient;
};
#ifndef ANDROID_JACK_SIMPLE_CLIENT_H
#define ANDROID_JACK_SIMPLE_CLIENT_H
#include <jack/jack.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <signal.h>
#include <unistd.h>
#include "IJackClientInterface.h"
#include "APACommon.h"
namespace android {
class JackSimpleClient: public IJackClientInterface {
#define SIZE_SINE_TABLE (1024)
public:
JackSimpleClient();
virtual ~JackSimpleClient();
int setUp(int argc, char *argv[]);
int tearDown();
int activate();
int deactivate();
int transport(TransportType type);
private:
jack_port_t * outPort;
static jack_client_t *jackClient; // jack client object
#endif // ANDROID_JACK_SIMPLE_CLIENT_H
• When you call SapaService.register, it calls setup() after IAPAInterface.init(). If you have
passed arguments to the constructor while creating SapaProcessor, argv[] of setup() is passed.
• When you close a processing module by calling SapaService.unregister(), it calls teardown().
• When you call SapaProcessor.activate(), it calls activate().
• When you call SapaProcessor.deactivate(), it calls deactivate().
• When you call SapaProcessor.setTransportEnabled(), it calls transport().
5. Create a CUSTOM processing module in a file named libwave.so. Create a makefile as shown below:
include $(CLEAR_VARS)
LOCAL_MODULE := libsapaclient
LOCAL_SRC_FILES := apa/lib/libsapaclient.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libjack
LOCAL_SRC_FILES := apa/lib/libjack.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MULTILIB := 32
# The module name have to be “wave”.
# It could be changable when the module is provided as a plug-in.
LOCAL_MODULE := wave
#include <string.h>
#include "wave.h"
#include <stdio.h>
#include "mylog.h"
namespace android {
IMPLEMENT_APA_INTERFACE(APAWave)
APAWave::APAWave(){
}
APAWave::~APAWave(){
}
int APAWave::init(){
LOGD("wave.so initialized");
return APA_RETURN_SUCCESS;
}
IJackClientInterface* APAWave::getJackClientInterface(){
return &mSimpleClient;
}
int APAWave::request(const char* what, const long ext1, const long capacity,
size_t &len, void*data)
{
return APA_RETURN_SUCCESS;
}
};
IJackClientInterface* APAWave::getJackClientInterface(){
8. Create the JACK client with jack_client_open() and implement the setup() method for initializing.
Register a process function to generate the sine wave with jack_set_process_callback. Because
sounds are output for the speaker, ports are connected to the system left speaker through
jack_connect.
// You have to use argv[0] as the jack client name like below.
jackClient = jack_client_open (argv[0], JackNullOption, NULL, NULL);
if (jackClient == NULL) {
return APA_RETURN_ERROR;
}
bufferSize = jack_get_buffer_size(jackClient);
return APA_RETURN_SUCCESS;
}
9. The JACK client is in standby mode. Activate it by calling the activate() method.
int JackSimpleClient::activate(){
jack_activate (jackClient);
LOGD("JackSimpleClient::activate");
free (systemInputs);
return APA_RETURN_SUCCESS;
}
The above sample activates the Jack client and reconnects to the port. When you call activate(), the
process function is called after a certain time interval.
return 0;
int JackSimpleClient::tearDown(){
jack_client_close (jackClient);
return APA_RETURN_SUCCESS;
12. To build the module, type the following command in the command line:
#>ndk-build –B (which is incorporated into Android NDK)
When you debug, end the process with the processing module and start the debugger. If you debug on
Eclipse without ending this process, the UI process ends, but the processing module does not.
3. Edit the Android make file (Android.mk) located in the jni folder.
LOCAL_MODULE := simple_sine
5. In the Android manifest file, add a service tag and a meta tag under the application tag.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.samsung.android.sdk.professionalaudio.sample.simpleplugin"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<service
android:name="com.samsung.android.sdk.professionalaudio.sample.simpleplugin"
android:label="SapaSimplePlugin" >
<intent-filter>
<action
android:name="com.samsung.android.sdk.professionalaudio.plugin.PICK" />
</intent-filter>
</service>
<meta-data android:name="SapaSimplePlugin" android:value="1;Simple
v1.0;SapaSimplePlugin;libsimple_sine.so;;a simple plugin generating a sine wave" />
</application>
</manifest>
7. In the android:label of the service tag, list your plug-ins. Use a semicolon as a delimiter to add
multiple plug-ins.
android:label=" SapaSimplePlugin"
The name attribute of the meta-data tag should be identical to the android:label attribute of the service tag.
Use a semicolon as a delimiter in the following format.
<uses-permission android:name="com.samsung.android.providers.context.permission.WRITE_USE_APP_FEATURE_SURVEY"/>
<uses-permission android:name="com.samsung.android.sdk.professionalaudio.permission.USE_CONNECTION_SERVICE"/>
<intent-filter>
<action android:name="com.samsung.android.sdk.professionalaudio.action.MAIN" />
<category android:name="com.samsung.android.sdk.professionalaudio.category.LAUNCHER" />
</intent-filter>
This indicates that the application is an audio application, but this declaration is not enough. You also need
to specify your application using meta-data.
Below you can find attributes on which specification are obligatory and those that are optional
accompanied with name of the meta-data object specifying them.
Obligatory
Application name. com.samsung.android.sdk.professionalaudio.sapaappinfo.appname
Action starting
background service com.samsung.android.sdk.professionalaudio.sapaappinfo.backgroundservice
of application.
Category* com.samsung.android.sdk.professionalaudio.sapaappinfo.category
Optional
Name of vendor com.samsung.android.sdk.professionalaudio.sapaappinfo.vendorname
Name of group of
com.samsung.android.sdk.professionalaudio.sapaappinfo.productgroupname
products
Name of application
com.samsung.android.sdk.professionalaudio.sapaappinfo.version
version
Information whether
application handles
com.samsung.android.sdk.professionalaudio.sapaappinfo.multiinstanceenabled
more than one
instance**
Information whether
application has at
com.samsung.android.sdk.professionalaudio.sapaappinfo.midiinportcount
least one midi input
port
Information whether com.samsung.android.sdk.professionalaudio.sapaappinfo.midioutportcount
<activity
android:name="com.samsung.audio.test.testapplication_a.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<meta-data
android:name=" com.samsung.android.sdk.professionalaudio.sapaappinfo.category"
android:value="utility" />
<meta-data
android:name="com.samsung.android.sdk.professionalaudio.sapaappinfo.version"
android:value="v1.0" />
<meta-data
android:name="com.samsung.android.sdk.professionalaudio.sapaappinfo.vendorname"
android:value="samsung" />
<meta-data
android:name="com.samsung.android.sdk.professionalaudio.sapaappinfo.productgroupname"
android:value="samples" />
<meta-data
android:name="com.samsung.android.sdk.professionalaudio.sapaappinfo.appname"
android:value="mixer" />
<meta-data
android:name="com.samsung.android.sdk.professionalaudio.sapaappinfo.multiinstanceenabled"
android:value="false" />
<meta-data
android:name="com.samsung.android.sdk.professionalaudio.sapaappinfo.backgroundservice"
android:value="com.samsung.audio.test.testapplication_a.MainService" />
<meta-data
android:name="com.samsung.android.sdk.professionalaudio.sapaappinfo.midiinportcount"
android:value="1" />
<meta-data
android:name="com.samsung.android.sdk.professionalaudio.sapaappinfo.midioutportcount"
android:value="1" />
<meta-data
android:name="com.samsung.android.sdk.professionalaudio.sapaappinfo.audioinportcount"
android:value="0" />
<meta-data
</activity><service
android:name="com.samsung.audio.test.testapplication_a.MainService"
android:enabled="true"
android:exported="true"
android:permission="com.samsung.android.sdk.professionalaudio.permission.USE_CONNECTION_SERVICE" >
<intent-filter>
<action android:name="com.samsung.audio.test.testapplication_a.MainService" />
</intent-filter>
</service>
«create»
SapaProcessor activate()
«create»
addConnectionListener()
connect()
onStartCommand()
getAppInfo(intent)
onServiceConnected()
setPortFromSapaProcessor()
addActiveApp(appInfo)
«destroy»
SapaProcessor deactivate()
removeFromActiveApps()
disconnect()
@Override
public void onCreate() {
Log.d(TAG, "onCreate");
super.onCreate();
this.mIsSapaAppServiceConnected = false;
this.mSapaAppService = new SapaAppService(this);
try {
this.mSapaAppService.setConnectionListener(this);
} catch (TooManyListenersException e) {
Log.e(TAG, "Listener was already set");
}
this.mSapaAppService.connect();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
SapaAppInfo info = SapaAppInfo.getAppInfo(intent);
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy");
try {
this.mSapaAppService.removeFromActiveApps(this.mMyInfo.getApp());
} catch (SapaConnectionNotSetException e) {
Log.e(TAG,"Instance could not be removed from active list because of connection exception.");
}
this.mSapaAppService.disconnect();
super.onDestroy();
}
@Override
public void onServiceConnected() {
Log.d(TAG, "onServiceConnected");
this.mIsSapaAppServiceConnected = true;
try {
if (this.mMyInfo == null) {
this.mMyInfo = this.mSapaAppService.getInstalledApp(getPackageName());
}
if (this.mMyInfo != null) {
//SapaProcessor needs to be registered and activated before calling this method
this.mMyInfo.setPortFromSapaProcessor(this.mSapaProcessor);
this.mSapaAppService.addActiveApp(this.mMyInfo);
}
} catch (SapaConnectionNotSetException e) {
Log.e(TAG,"App could not be added to active as connection has not been made.");
}
}
@Override
public void onServiceDisconnected() {
«create»
«create»
SapaProcessor activate()
«create»
addConnectionListener()
connect()
onStartCommand()
getAppInfo(intent)
onServiceConnected()
setPortFromSapaProcessor()
addActiveApp(appInfo)
«destroy»
«destroy»
SapaProcessor deactivate()
removeFromAppList()
disconnect()
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
int state = SapaAppInfo.getState(intent);
@Override
public void onServiceConnected() {
this.mIsSapaAppServiceConnected = true;
for(SapaAppInfo info : this.mInstancesToBeStarted){
this.activateInstance(info);
}
}
@Override
public void onServiceDisconnected() {
Summing up:
You will show only one instance of our application at a time so you do not need new activities. You will
need only one background service that will be modified a bit. Your native part will not change because you
will send commands to every instance of it via its SapaProcessor.
5.5. Ports
5.5.1. Declaring ports
When your service is started you can obtain the SapaAppInfo object representing your application in the
following way:
During activation of your application, you shall register your SapaProcessor. After registering the
SapaProcessor and before declaring that your application is activated, you shall add registered ports to
obtain information. You shall do that in the following way:
SapaAppInfo is a Parcelable class. That means that it can be easily saved to for example, a database as an
object and then restored.
<com.samsung.android.sdk.professionalaudio.widgets.FloatingController
xmlns:control="http://schemas.android.com/apk/res-auto"
android:id="@+id/jam_control"
control:type="in_corners"
control:handle_drawable="@drawable/handle"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:soundEffectsEnabled="false" />
layout_width
HORIZONTAL HORIZONTAL
VERTICAL
VERTICAL
layout_height
VERTICAL
VERTICAL
HORIZONTAL HORIZONTAL
The layout_width and layout_height attributes affect basically the invisible area because dimensions of the
bar follow the concept of Soundcamp.
It is best to put the FloatingController on the top of the whole layout of application which will prevent the
bar from being obscured.
Notice!
In order to use in_corners developer should add android:supportsRtl="true" in <application> section in the
AndroidManifest.xml
In the example above you can see that width and height are set to match_parent. The size of the
FloatingController pertains to the size of the space above in which FloatingController can be moved.
To make the control work, you need to give it access to the SapaAppService of your application. You can do
this using the setSapaAppService method on the FloatingController. It is all that is required for basic usage.
The type attribute can be either in_corners (jam control will be located in corners of screen) or
center_on_edges (jam_control will be in the middle of edges). The default value for this attribute is
in_corners.
Initial position of the floating controller can be set with “bar_alignment” attribute. For type in_corners it
can be: topLeft, topRight, bottomLeft or bottomRight. While for type center_on_edges it can be: top, right,
left or bottom.
Attribute orientation works only for type in_corners and sets if floating controller should be vertical or
horizontal.
As said before, the bar of FloatingController moves on the invisible area on the top of the hierarchy.
Therefore if the developer does not want the FloatingController to obscure other widgets, it is the best to
provide the margin of 50 dp to those widgets so that they are drawn next to the bar.
The handle_drawable attribute of the FloatingController is expanded if the state “activated=true” which
will be called when the widget is expanded.
Though every care has been taken to ensure the accuracy of this document, Samsung Electronics Co., Ltd.
cannot accept responsibility for any errors or omissions or for any loss occurred to any person, whether
legal or natural, from acting, or refraining from action, as a result of the information contained herein.
Information in this document is subject to change at any time without obligation to notify any person of
such changes.
Samsung Electronics Co. Ltd. may have patents or patent pending applications, trademarks copyrights or
other intellectual property rights covering subject matter in this document. The furnishing of this document
does not give the recipient or reader any license to these patents, trademarks copyrights or other
intellectual property rights.
No part of this document may be communicated, distributed, reproduced or transmitted in any form or by
any means, electronic or mechanical or otherwise, for any purpose, without the prior written permission of
Samsung Electronics Co. Ltd.
All brand names and product names mentioned in this document are trademarks or registered trademarks
of their respective owners.