Gabor Szanto

Introducing Superpowered 2.0

Superpowered is more than 5 years old now and has gotten through 52 updates with a stable API. However thanks to WebAssembly, the audio developer and audio SDK worlds will see a dramatic change.

Developers can now use Superpowered audio features in any major web browser in JavaScript without the need of building, initializing or even touching WebAssembly. All WebAssembly audio magic and complexity happens conveniently inside Superpowered.

We will continue to maintain Superpowered for all native platforms (C++ on Android, iOS, macOS, tvOS, Windows and Linux) on all major CPU architectures (32-bit ARM, 64-bit ARM, x86, x64) and for all web browsers (JS on Chrome, Firefox, Safari, Edge, Opera and Brave) from one single API.

Yes, we know: a JavaScript and C++ API can never be *exactly* the same, but we designed the Superpowered APIs to be virtual twins as close as programmatically possible; behaving, performing and sounding identically on all platforms.

This massive shift required deep changes in our existing C++ API. So if we need to change our old API, let's improve and modernize all the less-than-optimal portions, because during the past 5 years we have received an enormous volume of feedback from our developer community regarding feature requests, bugs, platform support etc etc.

The following will help you to understand the most important changes in the Superpowered API.

General C++ API Improvements

No Callbacks

Callbacks were replaced with event getters that can be placed into loops or periodical UI update functions. This mechanism allows to read events on the thread you prefer (such as the main thread to update the UI) instead of getting a notification on a background thread and figuring out how to move the message to the main thread.

No Read-Only Properties

C++ doesn't support read-only (class) properties, they are always read-write. So despite called out in the documentation that a property should only be read, it was still super easy to modify.

The downside was odd behavior or a strange crash that only happened for 1% of the users and often very, very hard to replicate. Now every property is truly read-write and all read-only properties were changed to getter methods, so it's always crystal clear what can be written and what not.

No Output Arguments

Honestly, input-output or output-only arguments are confusing for many developers, because they require a developer to always check and re-check documentation on how they will be modified and under what conditions. They are just not natural.

Fortunately most functions and methods have only one output value and that is easy to handle as the return value. Output arguments of complex methods were replaced with getter methods.

Reduced Custom Memory Management

Previously, some methods required that you maintain some memory for the results. Now Superpowered allocates everything for you, making your code shorter and removing one easy crash risk.

Superpowered Namespace

All the Superpowered features can be found under the Superpowered namespace now (except the open-source classes) to shorten your code and to avoid naming conflicts. The word "Superpowered" is not easy to write and you will write it less often now.

Samples and Frames

We were using "samples" and "frames" in a confusing and inconsistent way in our API and documentation. Now we follow how Apple understands samples and frames:

Because most Superpowered methods are stereo, we are using "numberOfFrames" instead of "numberOfSamples" on most methods.

The Superpowered Folder

The SuperpoweredSDK/Superpowered folder has better organization. It has:

New Bitcrusher Fx

The bitcrusher can be used to "degrade" audio to simulate old sound cards with reduced bit depths and frequency coverage, such as 8-bit "lo-fi" sound or chiptunes.

Absolutely critical for 80s nostalgia for sure, go Mario go!

Updated I/O and Open Source Classes

Android

We've observed unusual additional audio input latency on a handful of audio devices (such as the Xiaomi Redmi 5). It was caused by some poor internal Xiaomi system-space implementation being able to start the output buffer queues much later than the input buffer queues.

Now the class will truncate those excess input buffers for a lower latency start, and this solution works for all Android devices.

Windows

The audio processing callback is extended by a notification of the I/O startup. If the audio processing callback is called with no audio buffer (audio == NULL), the value of samplerate can indicate 3 different things:

iOS, macOS (OSX) and tvOS

All the delegate methods are optional now, saving you from writing empty unused methods that balloon your code.

Updated NBandEQ Class

Thanks to the shorter Superpowered Filter API, most of the source code of this class fits onto one screen. Developers know this is great for ease of understanding.

Updated Documentation

The documentation in the header files have better compatibility with the quick help/inline help features of all C++ IDEs.

Thanks to the namespacing the Doxygen-generated HTML docs are easier to read, but long-term we're going to replace this outdated Doxygen solution to the new "pseudo-examples" approach of the Superpowered Web Audio JavaScript and WebAssembly SDK Documentation, if it receives good response.

Please let us know what you think at hello@superpowered.com.

Updated Example Projects

All the example projects were updated for the latest Android Studio, Xcode and Visual Studio versions as usual. But the new Superpowered 2.0 API required significant updates in the example code as well.

All projects are now smaller because the new Superpowered API reduced the need of housekeeping around sample rates, buffers and player events.

All iOS example projects are better organized and smaller because the removal of many unused supporting files (such as precompiled headers or empty launch screens) and optional SuperpoweredIOSAudioIO methods.

Removed the Android SuperpoweredHLS project, because the difference to the SuperpoweredPlayer example is minimal now: use the player->openHLS method instead of player->open. All documented in the code SuperpoweredPlayer project.

The Android SuperpoweredPlayer example project uses a periodically called function to poll the player status for UI updates and is able to handle progressive downloads and HLS playback now.

The Windows SuperpoweredPlayer project also uses a periodically called function to poll the player status for updates. It runs with the maximum FPS of the UWP framework.

The iOS and Linux offline example projects don't only show how to handle local file decoding, but how to decode a progressive download too (with all buffering handled by sleeping). The offline time stretcher function is significantly shorter and easier to understand by not using the Superpowered AudioBuffers API.

The Unity Native Audio Plugin example projects were temporarily removed, because we simply didn't have time to update and test them for the latest Unity version. Superpowered can still be used in any Unity Native Audio Plugin and as a Unity Spatializer.

Updated Fx (effect) classes

Removed the setSamplerate and enable methods: samplerate and enabled are simple read/write properties now. Every Fx class interface became significantly simpler by the removal of these and the property setter methods, therefore it's much easier to understand thread safety:

ThreeBandEQ

The Superpowered3BandEQ was renamed to ThreeBandEQ, because classnames can not begin with a number. The bands[0..2] property was replaced with separate low, mid and high properties for easier understanding.

Echo

Use the good old setMix method for a nice dry/wet balance, or the new writeable dry and wet properties individually for a custom setting.

Filter

Frequency, decibel, resonance, octave, slope and type are simple properties now that can be read/write on any thread without any synchronization issues. Yes, you can change the filter type on-the-fly without any worries on audio artifacts or threading.

Updated Stereo and Mono Mixer

The class takes care of the input gain, output gain or peak result values, no need to memory-manage them with the process method anymore and it has a much cleaner interface.

Updated Recorder

The createWAV and closeWAV functions were moved to SuperpoweredSimple.h, because they are simple functions not directly related to the recorder. In order to avoid confusion around the standard fwrite API, there is a new writeWAV function if you don't like fwrite.

The confusing start method is renamed to prepare, and received the settings that can be changed individually for each recording, such as fadeInFadeOut or sample rate.

The "stopped" callback is replaced by the isFinished method to poll the status of the recorder writing the last bits of the recording to disk. It can be used in an UI thread for example.

The process method also turned out to be confusing, so it was renamed to recordNonInterleaved and recordInterleaved.

Updated Spatializer

The global reverb's properties and process method can be accessed as static Spatilizer members instead of being a separate entity before, indicating the strong relationship between the individual Spatializer instances and the one and only one global Spatializer reverb.

Updated Time Stretching

The setRateAndPitchShift… methods have been removed. Rate and pitchShiftCents are read/write properties now.

The class is extended with an easy-to-use addInput + getOutput method pair, eliminating the need of the complex AudioBuffer class. Memory bandwidth can be further decreased and multi-channel time stretching to be handled with the older advancedProcess method and the AudioBuffer class.

Updated Analyzer

Many users found the getresults API confusing and had problems with memory allocation. Now the analyzer allocates everything inside and you can just read or take the results in a convenient way.

Updated Bandpass Filterbank

The trouble with memory management of the individual frequency bands and understanding the add/no-add behavior is removed. The memory is handled by the class internally and it's much easier to understand how bands, peak, average and sum volume work.

Updated Decoder

The open method returns with a well defined set of values: success, buffering (try again later), error value or HTTP status code. Instead of a generic "error" now you can get the exact reason why the error happened. Use the statusCodeToString method to return with a human readable error string.

The decodeAudio method also returns with a clear set of values:

The set position behavior is also easier to understand with the separated setPositionPrecise method (will always seek to the exact position) and the setPositionQuick method (will seek to the file format's frame beginning).

Parsing of custom ID3 frames is easier now using iterator methods replacing the previous callback-based solution (startParsingID3Frames, readNextID3Frame, etc.).

Updated Advanced Audio Player

The player is our most complex class. Now it has a fresh start with a much cleaner API, the same strong features extended with new ones and the same performance.

Events

The getLatestEvent method replaces the player callbacks with only 5 different event types: opening, open failed, opened, connection lost, progressive download finished.

Previously, the player might call the player event callback on different internal threads or even the audio processing thread, resulting in difficult management issues, such as not being able to update UI elements on a background thread.

getLatestEvent can be called on any thread now, solving all synchronization headaches you might have. The primary intent of this method is to be periodically called on the UI thread (main thread) using a screen-synchronized callback (such as CADisplayLink) or just a simple timer.

Open

The open method doesn't return with a value anymore, because opening any audio takes time and immediate result can only be returned by blocking for significant time. The result of the open method now should be handled with getLatestEvent.

If getLatestEvent returns with OpenFailed, use the getOpenErrorCode method to read the reason of the error. It will return with a value from a well-defined set of error values or a HTTP status code. Use the statusCodeToString method to translate this number to a human readable error string.

End of file

The eofRecently method replaces the end of file event. It's similar to getLatestEvent (designed to be used periodically on the UI thread, etc.), but returns true if the player has reached end of file since the last call of eofRecently.

Certainly, this mechanism is not fast enough to react to an end of file event in real-time, therefore you can use the loopOnEOF property to handle that usecase. If loopOnEOF is true, playback will jump back to start automatically with no delay. If loopOnEOF is false, playback will stop at the end of file, and you can handle what happens next with the eofRecently method.

Tempo, Time Stretching, Pitch Shifting

Removed setTempo and setPitchShift, now you can just read/write the playbackRate, timeStretching and pitchShiftCents properties on any thread.

Play, Pause

Removed the play(synchronized) method, because it resulted in confusing player->play(false) code, which looks like a pause honestly. Now there is play(), playSynchronized() and pause().

Quantum and Phase Synchronization

Previously, synchronization of multiple players to any player or an imaginary timeline was only possible using the msElapsedSinceLastBeat property of the process method. Now it's moved to the syncToMsElapsedSinceLastBeat property of the player, eliminating the need to read the value before the process method (as it was in the cross example before).

But there is an additional and more advanced synchronization method too: quantum and phase synchronization goes beyond the simple 1 beat sync. Quantum and phase synchronization is similar to Ableton Link, and therefore is very easy to integrate with Ableton Link.

Quantum represents the desired unit of synchronization in beats. If quantum is 1, then the player will synchronize to every beat. If quantum is 4, then the player will synchronize to a 4 beat long period (such as a bar). Using this feature synchronizing different length loops is easy and quantum can have any value.

Phase represents where the player currently is inside the quantum. Phase == 0 is the beginning of the quantum and phase == 1 is the end of the quantum. For example, if quantum is 4 and phase is 0.5, then the player is at the beginning of the third beat (the half of the quantum).

You can also use the getMsDifference method to retrieve any deviation from any quantum and phase values.

STEMS

The handling of the Native Instruments STEMS format is much easier. The new API removed the need of managing a separate compressor and limiter, they are all built-in now.

In order to handle STEMS:

  1. Call the processMulti method to retrieve the four distinct audio buffers (the stems).
  2. Perform your custom manipulation on those buffers (such as effects).
  3. Mix them together to a stereo mix (use the Superpowered Stereo Mixer for example).
  4. Call processSTEMSMaster to apply the final compressor and limiter for 100% compliance with the STEMS format.

Conclusion

There you have it. Keep going and keep making incredible stuff. Thanks for using Superpowered.

Recap:

Superpowered 1.3.x are all deprecated and unsupported on October 1st 2019. Apps using Superpowered 1.3.x after October 1st 2019 will see interruption of service. Upgrade ASAP!

Superpowered 1.4 is a mandatory upgrade and requires a valid license key. It has an almost identical API to Superpowered 1.3.x for a super-easy upgrade path. Read this article to answer any questions or email us hello@superpowered.com.

Superpowered 2.0 is *NOT* mandatory at this time - but highly recommended. For most of your apps, updating to Superpowered 2.0 will not take more than 1 hour or so. Re-read this blog post. :)

With Superpowered 2.0, we will be able to provide even more leverage, power, performance and simplicity to Superpowered developers. We love seeing and hearing all the wonderful apps and products you build. Keep on' keepin' on.

  • wasm
  • c++
  • dsp
  • superpowered
  • superpowered audio