Csound for Android is the name of the software development kit created for building Csound-based Android applications. The SDK was first released with Csound 5.17.3 and new versions have been released with each new version of Csound. The SDK includes shared library versions of libcsound and libsndfile [57], binary class files and headers for the Java Csound API, and Java source files for the CsoundObj API. The SDK also includes an examples project that demonstrates various use cases for how to use the library and a manual is also included.
The Csound for Android SDK is released together with each release of Csound. Like the Csound for iOS SDK, the version of Csound included in the SDK is built using the same source code as that used for Csound desktop and other platform releases. This provides the same benefits as discussed for the Csound for iOS SDK.
About the Platform
Android [76] is a Linux-based platform for mobile devices. On top of the Linux core are two runtimes: Dalvik and Android Runtime (ART) [19]. Dalvik was the initial runtime used on Android and it executes platform-independent bytecode using a just-in-time (JIT) compilation model, similar to the Java Virtual Machine. ART provides ahead-of-time (AOT) compilation model
that compiles the same platform-independent bytecode to native code before running.
Developers primarily use the Java programming language to program Android applications. The Java source is first compiled into Java bytecode, then translated into Dalvik bytecode, before being run on the Android platform. Pre-compiled Java classes may also be used with Android projects and will be translated into Dalvik bytecode format by the platform’s build tools.
Developers may also use C and C++ to generate hardware-dependent li- braries and applications. For Android, developers typically compile dynamically- linked libraries that are then used from Dalvik-compiled programs via the Java Native Interface (JNI) [115]. Libraries must be compiled with support for each CPU-architecture that the application targets.11 Developers also have
the option to write their entire application in natively-compiled C/C++ code, though this is less commonly found than the Java-language based approach. As the Android platform is open-source, it has been used on a much wider variety of hardware platforms when compared to iOS. Android supports not only ARM-based processors but also Intel and MIPS-designed CPUs. These processors vary in endian-ness and word-size (32- and 64-bit). Using natively- compiled code requires that the library or executable be compiled multiple times, once for each target CPU architecture. On the other hand, Dalvik bytecode is platform-independent; once written, it is JIT- or AOT-compiled on each platform.
11For more information on supported CPU architectures for Android are available, see
Android supports both static and dynamic linking of libraries. Static linking is supported when compiling native code. Dynamic linking is supported both at compile-time as well as runtime.
Platform Analysis
Developers building applications for Android most commonly use the Java programming language using the Android Software Development Kit (SDK). They may also use C and C++ using Android’s Native Development Kit (NDK). Like iOS, development is performed on a desktop system and cross-
compiled to run on Android devices. For projects using only Java code, the code is compiled once into platform-independent bytecode and run on multiple CPU architectures. For those using C/C++ code, the code is compiled once per target CPU architecture.
Audio services on Android are most commonly accessed either through the Dalvik layer using the system-provided AudioTrack class or through the native layer using the OpenSL ES C API. At the time of this writing, Android does not provide a standard MIDI API.
For user interfaces, users developing in Java will use the classes in the system-provided android.widgets package. Developers may develop their interfaces directly in Java code but they more commonly use XML interface files that declaratively specify the user-interface. These are either hand- written or developed using a GUI editing program. Native development options are also available, such as using the QT widget toolkit.
In general, developers writing applications for Android face a much more heterogeneous target than iOS. Differences include a wider variety of screen- sizes, CPU-types, as well as capabilities supported (e.g., sample rates allowed
for audio streams). Android as a whole does not support the same low-latency as provided by iOS.12. While Android’s latency limitations make the range
of supported music application use cases smaller, the ubiquitousness and open-source nature of Android make it an attractive target platform.
SDK Design
The Csound for Android SDK was designed to mirror the design of the Csound for iOS SDK as closely as possible, with appropriate differences made to adhere to conventions common to Android. While applications may be developed purely in C and C++, it is more common on Android to use Java, thus that was the target user for this project.
The base of the Csound for Android SDK begins with the same Csound Java API that is available on the desktop. libcsound and libsndfile are provided as shared libraries. A Java language wrapper for Csound is generated using SWIG [29], as it is on the desktop. In addition to the standard libcsnd6 sources, one additional C++ class, called AndroidCsound, is also compiled into the library. This class is a subclass of the Csound class and adds one additional method to register native OpenSL audio callbacks with Csound. This class is also wrapped with SWIG. The Java AudioTrack class is also supported by CsoundObj on Android, but the OpenSL audio path is the default and recommended path to use for performance reasons.
Like Csound for iOS, a CsoundObj API implementation is provided that builds upon the lower-level Java csnd6 API. The methods and classes implemented in the Android version of CsoundObj are closely named to their iOS counterparts. The reason for this was to simplify creating cross-platform
applications between the two platforms. Although the programming languages are different between iOS and Android, both are object-oriented languages. By using the same class designs and similarly named methods, users can easily translate application code between the platforms.
Figure 4.14 illustrates the relationship of the user’s application source and the Csound for Android SDK library parts. Like Csound for iOS, application code may use either CsoundObj or csnd6 Java classes. These Java class libraries in turn communicate over JNI to the natively-compiled libcsnd6 library, built upon libcsound.
Figure 4.14: Android CsoundObj Diagram
Listing 4.2 illustrates the API parity between CsoundObj implementa- tions on iOS and Android. The code fragments both create an instance of CsoundObj, register the current class as a listener to CsoundObj, load a Csound CSD project file, then use the CsoundMotion class to enable different hardware sensors. The iOS version enables accelerometer, attitude, and gyro-
scope, while the Android version only enables the accelerometer, as Android does not support the other two hardware sensors.
/* iOS Objective -C Example */
NSString * tempFile = [[ NSBundle mainBundle ] pathForResource :@" hardwareTest "
ofType :@" csd "];
self . csound = [[ CsoundObj alloc ] init ]; [ self . csound addListener : self ];
CsoundMotion * csoundMotion = [[ CsoundMotion alloc ] initWithCsoundObj : self . csound ];
[ csoundMotion enableAccelerometer ]; [ csoundMotion enableAttitude ]; [ csoundMotion enableGyroscope ];
[ self . csound play : tempFile ];
/* Android Java Example */
CsoundObj csoundObj = new CsoundObj (); csoundObj . addListener ( this );
String csd = getResourceFileAsString (R. raw . hardware_test ); File f = createTempFile ( csd );
CsoundMotion csoundMotion = new CsoundMotion ( csoundObj ); csoundMotion . enableAccelerometer ( AccelerometerActivity . this );
csoundObj . startCsound (f);
One of the goals for Csound on these platforms is that the same Csound CSD project may be used by either Android or iOS applications, even if there are differences in available features. When this happens, the project should degrade gracefully. In the case of Listing 4.2, the iOS version sends values using Csound’s channel system that the Android version would not. On the receiving end in the CSD, as shown in Listing 4.3, the chnget opcode is used to read from channels where hardware sensor values are written. On iOS, all nine of the channels will receive data, while on Android, only the three accelerometer channels will receive data. For the other six channels on Android, the default 0 value for channels will be read.
kaccelX chnget " accelerometerX " kaccelY chnget " accelerometerY " kaccelZ chnget " accelerometerZ "
kgyroX chnget " gyroX " kgyroY chnget " gyroY " kgyroZ chnget " gyroZ "
kattRoll chnget " attitudeRoll " kattPitch chnget " attitudePitch " kattYaw chnget " attitudeYaw "
Listing 4.3: Csound Channel Reading Code
The example Csound code shown can run on either iOS or Android without modification. Additionally, this project can run on any other version of Csound as well, such as on desktop platforms, where no sensors may be available and all channels give 0 values. By having the default values for channels, this simplifies porting the Csound project to various platforms and minimises the worst case scenario such that at least the project will run.
The Csound for Android SDK provides the same SDK features as Csound for iOS. It includes the pre-built native libraries and CsoundObj API, a Csound Examples project, and a user manual. The examples project provides the same examples as those found in the Csound for iOS SDK, with the exception of MIDI examples as Android does not support MIDI. The CSDs used in the example project are the same ones from the iOS SDK, used without modification.
Porting Csound to Android
Google provides both an Android SDK and NDK (Native Development Kit). The SDK provides Java-language, Dalvik bytecode related build tools, while the NDK provides C/C++ build tools. For the NDK, the provided devel- opment headers and libraries and tools resemble closely to those commonly found on desktop Linux distributions.
When Csound for Android work commenced, it was not clear how one would use CMake with Android’s NDK tools. Instead, a typical Android.mk and Application.mk file were written that would work with the NDK- provided ndk-build tool. This made the method of building the native libcsound library consistent with Android-specific development practices, though required having a separate build recipe than the other main Csound builds. The result is that the Android build files must be manually kept in sync with the primary Csound CMake build recipe. This has added a small amount of additional work for the core Csound developers but simplified building Csound for Android.13
13It would be ideal to have an Android build based on CMake. Work towards that goal
Like the desktop Csound Java API, SWIG is used to generate both C and Java class files that wrap Csound. The generated Java classes are compiled and packaged for developers to use. The generated C files are compiled as part of the libcsound.so library.
To simplify the runtime loading of the native library portion of Csound, all of the native components are compiled together. This includes libcsound, libsndfile, generated SWIG C files, and the additional AndroidCsound C++ class. While these could have been compiled into separate libraries, there was little reason found to compile them separately for this project. The decision to provide one monolithic library can be easily changed at a later time should a compelling reason be found.
Unlike iOS, dynamic library loading of plugins is available on Android. However, due to the nature of Android’s sandboxed applications, support for multiple CPU architectures, and concerns for application sizes, it was easier to follow the design used for iOS and provide a CsoundObj API that handled I/O and other features. However, plugins for Csound have been developed and used on Android and are provided as optional features for developers to use.
While the build-time issues were simple to resolve, there was one large issue found in Csound to Android: Csound’s use of temporary files to store pre-processed score did not work. Prior to development with Android, when Csound would load a score file, it would process the score language into a set of score events, write the processed score to a temporary file on disk, then open an input stream with the temporary file to read in score events at runtime. Reading from disk limited the number of events in memory, saving
space. However, since creating temporary files was problematic, the process of score rendering was changed.
To support this situation with Android, ffitch introduced the new COR- FILE system. The CORFILE system mimics the same file saving, opening, and reading functions that were in use with Csound. However, instead of reading from disk, the CORFILE system reads from and writes to memory. Since memory is much more abundant today compared to when Csound was first developed, this solution seemed like a reasonable one to pursue.
The CORFILE API provides a near drop-in replacement for the set of file I/O functions used previously for working with temporary files. Listing 4.4 shows the data structure for a CORFIL. It contains a char* body, the length of the body, and the current position p. CORFILs function much like RAM- based files and are processed using the same kinds of operations as found for file-based I/O.
Listing 4.4 also shows the function prototypes for working with CORFIL structures. The CORFILE API provides funcions for creating and working with single file-like entities, but it does not try to implement a full RAM-based file system (i.e., it does not support directory listing, file metadata, etc.). The API is small in size, uses only standard C library functions, and does not use platform-specific functionality. These qualities make the CORFILE system easily portable across platforms.
// include / csoundCore .h :197 typedef struct CORFIL {
char * body ;
unsigned int len ; unsigned int p; } CORFIL ;
// H/ corfile .h
CORFIL * corfile_create_w ( void );
CORFIL * corfile_create_r ( const char * text ); void corfile_putc ( int c, CORFIL *f);
void corfile_puts ( const char *s, CORFIL *f); void corfile_flush ( CORFIL *f);
void corfile_rm ( CORFIL ** ff); int corfile_getc ( CORFIL *f); void corfile_ungetc ( CORFIL *f); # define corfile_ungetc (f) (--f->p) MYFLT corfile_get_flt ( CORFIL *f); void corfile_reset ( CORFIL *f);
# define corfile_reset (f) (f-> body [f->p =0]= '\0 ') void corfile_rewind ( CORFIL *f);
# define corfile_rewind (f) (f->p =0) int corfile_tell ( CORFIL *f);
# define corfile_tell (f) (f->p) char * corfile_body ( CORFIL *f); # define corfile_body (f) (f-> body ) char * corfile_current ( CORFIL *f);
# define corfile_current (f) (f-> body +f->p)
CORFIL * copy_to_corefile ( CSOUND *, const char *, const char *, int );
CORFIL * copy_url_corefile ( CSOUND *, const char *, int ); int corfile_length ( CORFIL *f);
# define corfile_length (f) ( strlen (f-> body )) void corfile_set ( CORFIL *f, int n);
# define corfile_set (f,n) (f->p = n)
void corfile_preputs ( const char *s, CORFIL *f);
Listing 4.4: CORFILE data structure and function prototypes
After implementing the CORFILE system, ffitch also updated the score processing code in Csound to use the CORFIL system. The result of this is that temporary files were no longer used in Csound’s score processing, and Csound was then able to render without problems on Android. Because the source code is shared across all Csound-supported platforms (i.e., Windows, OSX, Linux, iOS, etc.), the problem of temporary files has been solved for all current and future platforms.14
Summary
Csound for Android provides a working version of Csound ported to the Android platform. It employs the same architecture and design as Csound for iOS, making it easy to port applications from one platform to another. The parity of code and use of same CSDs in the corresponding examples projects demonstrate the viability of cross-platform, Csound-based music application development as well as provide a set of models to use in building new applications. The work also identified the limitation of disk-based temporary file usage and work by ffitch has solved that problem for new platforms moving forward. The resulting Csound for Android SDK has been successfully released with each new version of Csound and has been used by developers and users for their personal and commercial work on Android.
14This proved to be immediately useful in porting Csound to the Web, where temporary