Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Superpowered SDK: Implementing SuperpoweredRecorder.h and setting up recording paths

I have successfully imported Superpowered SDK CrossExample project into android studio and tested it on Samsung galaxy S3 and emulator. Now I’m implementing a record option to record audio played by the app using SuperpoweredRecorder.h. I will need help for how to correctly set *tempPath and *destinationPath variables to successfully save the recording. The project builds OK, but when I’m trying to run the app on the Galaxy S3 or on the emulator I’m getting the following error message: 19565-19565/com.superpowered.crossexample A/libc﹕ Fatal signal 11 (SIGSEGV) at 0x006f0070 (code=1), thread 19565 (ed.crossexample) This error occurred after adding *tempPath and *destinationPath pointers so I believe after successful setting up the recording paths this error will be gone.

The link to SuperpoweredRecorder.h documentation: http://superpowered.com/docs/class_superpowered_recorder.html When looking at the documentation what else do I have to add to the code to use tempPath and destinationPath variables?

The code added myself is between forward double slashes.

SuperpoweredExample.h

#ifndef Header_SuperpoweredExample
#define Header_SuperpoweredExample

#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
#include <math.h>
#include <pthread.h>

#include "SuperpoweredExample.h"
#include "SuperpoweredAdvancedAudioPlayer.h"
#include "SuperpoweredFilter.h"
#include "SuperpoweredRoll.h"
#include "SuperpoweredFlanger.h"
#include "SuperpoweredMixer.h"
#include "SuperpoweredRecorder.h"

#define NUM_BUFFERS 2
#define HEADROOM_DECIBEL 3.0f
static const float headroom = powf(10.0f, -HEADROOM_DECIBEL * 0.025);

class SuperpoweredExample {
public:

SuperpoweredExample(const char *path, int *params);
~SuperpoweredExample();

void process(SLAndroidSimpleBufferQueueItf caller);
void onPlayPause(bool play);
void onCrossfader(int value);
void onFxSelect(int value);
void onFxOff();
void onFxValue(int value);
//Added function declaration
void onRecord(bool record);
//
private:
SLObjectItf openSLEngine, outputMix, bufferPlayer;
SLAndroidSimpleBufferQueueItf bufferQueue;

SuperpoweredAdvancedAudioPlayer *playerA, *playerB;
SuperpoweredRoll *roll;
SuperpoweredFilter *filter;
SuperpoweredFlanger *flanger;
SuperpoweredStereoMixer *mixer;
SuperpoweredRecorder *recorder;
//added object variables
const char *tempPath;
const char *destinationPath;
//
unsigned char activeFx;
float crossValue, volA, volB;
pthread_mutex_t mutex;

float *outputBuffer[NUM_BUFFERS];
int currentBuffer, buffersize;
};

#endif

SuperpoweredExample.cpp

#include "SuperpoweredExample.h"
#include <jni.h>
#include <stdlib.h>
#include <stdio.h>
#include <android/log.h>

static void playerEventCallbackA(void *clientData,         SuperpoweredAdvancedAudioPlayerEvent event, void *value) {
if (event == SuperpoweredAdvancedAudioPlayerEvent_LoadSuccess) {
    SuperpoweredAdvancedAudioPlayer *playerA = *     ((SuperpoweredAdvancedAudioPlayer **)clientData);
    playerA->setBpm(126.0f);
    playerA->setFirstBeatMs(353);
    playerA->setPosition(playerA->firstBeatMs, false, false);
};
}

static void playerEventCallbackB(void *clientData,     SuperpoweredAdvancedAudioPlayerEvent event, void *value) {
if (event == SuperpoweredAdvancedAudioPlayerEvent_LoadSuccess) {
    SuperpoweredAdvancedAudioPlayer *playerB = *((SuperpoweredAdvancedAudioPlayer **)clientData);
    playerB->setBpm(123.0f);
    playerB->setFirstBeatMs(40);
    playerB->setPosition(playerB->firstBeatMs, false, false);
};
}

static void openSLESCallback(SLAndroidSimpleBufferQueueItf caller, void         *pContext) {
((SuperpoweredExample *)pContext)->process(caller);}

static const SLboolean requireds[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };

SuperpoweredExample::SuperpoweredExample(const char *path, int *params) :     currentBuffer(0), buffersize(params[5]), activeFx(0), crossValue(0.0f),     volB(0.0f), volA(1.0f * headroom) {
pthread_mutex_init(&mutex, NULL); // This will keep our player volumes and     playback states in sync.

for (int n = 0; n < NUM_BUFFERS; n++) outputBuffer[n] = (float     *)memalign(16, (buffersize + 16) * sizeof(float) * 2);

unsigned int samplerate = params[4];

playerA = new SuperpoweredAdvancedAudioPlayer(&playerA ,     playerEventCallbackA, samplerate, 0);
playerA->open(path, params[0], params[1]);
playerB = new SuperpoweredAdvancedAudioPlayer(&playerB,     playerEventCallbackB, samplerate, 0);
playerB->open(path, params[2], params[3]);

playerA->syncMode = playerB->syncMode =     SuperpoweredAdvancedAudioPlayerSyncMode_TempoAndBeat;

roll = new SuperpoweredRoll(samplerate);
filter = new SuperpoweredFilter(SuperpoweredFilter_Resonant_Lowpass,     samplerate);
flanger = new SuperpoweredFlanger(samplerate);

mixer = new SuperpoweredStereoMixer();

//Create SuperpoweredRecorder and allocate memory for it
recorder = new SuperpoweredRecorder(tempPath, samplerate);
//

// Create the OpenSL ES engine.
slCreateEngine(&openSLEngine, 0, NULL, 0, NULL, NULL);
(*openSLEngine)->Realize(openSLEngine, SL_BOOLEAN_FALSE);
SLEngineItf openSLEngineInterface = NULL;
(*openSLEngine)->GetInterface(openSLEngine, SL_IID_ENGINE,     &openSLEngineInterface);
// Create the output mix.
(*openSLEngineInterface)->CreateOutputMix(openSLEngineInterface, &outputMix,     0, NULL, NULL);
(*outputMix)->Realize(outputMix, SL_BOOLEAN_FALSE);
SLDataLocator_OutputMix outputMixLocator = { SL_DATALOCATOR_OUTPUTMIX,     outputMix };

// Create the buffer queue player.
SLDataLocator_AndroidSimpleBufferQueue bufferPlayerLocator = {     SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, NUM_BUFFERS };
SLDataFormat_PCM bufferPlayerFormat = { SL_DATAFORMAT_PCM, 2, samplerate *     1000, SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,     SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT, SL_BYTEORDER_LITTLEENDIAN };
SLDataSource bufferPlayerSource = { &bufferPlayerLocator,     &bufferPlayerFormat };
const SLInterfaceID bufferPlayerInterfaces[1] = { SL_IID_BUFFERQUEUE };
SLDataSink bufferPlayerOutput = { &outputMixLocator, NULL };
(*openSLEngineInterface)->CreateAudioPlayer(openSLEngineInterface,     &bufferPlayer, &bufferPlayerSource, &bufferPlayerOutput, 1,     bufferPlayerInterfaces, requireds);
(*bufferPlayer)->Realize(bufferPlayer, SL_BOOLEAN_FALSE);

// Initialize and start the buffer queue.
(*bufferPlayer)->GetInterface(bufferPlayer, SL_IID_BUFFERQUEUE,     &bufferQueue);
(*bufferQueue)->RegisterCallback(bufferQueue, openSLESCallback, this);
memset(outputBuffer[0], 0, buffersize * 4);
memset(outputBuffer[1], 0, buffersize * 4);
(*bufferQueue)->Enqueue(bufferQueue, outputBuffer[0], buffersize * 4);
(*bufferQueue)->Enqueue(bufferQueue, outputBuffer[1], buffersize * 4);
SLPlayItf bufferPlayerPlayInterface;
(*bufferPlayer)->GetInterface(bufferPlayer, SL_IID_PLAY, &bufferPlayerPlayInterface);
(*bufferPlayerPlayInterface)->SetPlayState(bufferPlayerPlayInterface,     SL_PLAYSTATE_PLAYING);
}

SuperpoweredExample::~SuperpoweredExample() {
for (int n = 0; n < NUM_BUFFERS; n++) free(outputBuffer[n]);
delete playerA;
delete playerB;
delete mixer;
pthread_mutex_destroy(&mutex);
}

void SuperpoweredExample::onPlayPause(bool play) {
pthread_mutex_lock(&mutex);
if (!play) {
    playerA->pause();
    playerB->pause();
} else {
    bool masterIsA = (crossValue <= 0.5f);
    playerA->play(!masterIsA);
    playerB->play(masterIsA);
};
pthread_mutex_unlock(&mutex);
}
//onRecord function
void SuperpoweredExample::onRecord(bool record) {
pthread_mutex_lock(&mutex);
if (!record) {
    recorder->stop();
} else {
    recorder->start(destinationPath);
};
pthread_mutex_unlock(&mutex);
}
//

void SuperpoweredExample::onCrossfader(int value) {
pthread_mutex_lock(&mutex);
crossValue = float(value) * 0.01f;
if (crossValue < 0.01f) {
    volA = 1.0f * headroom;
    volB = 0.0f;
} else if (crossValue > 0.99f) {
    volA = 0.0f;
    volB = 1.0f * headroom;
} else { // constant power curve
    volA = cosf(M_PI_2 * crossValue) * headroom;
    volB = cosf(M_PI_2 * (1.0f - crossValue)) * headroom;
};
pthread_mutex_unlock(&mutex);
}

void SuperpoweredExample::onFxSelect(int value) {
__android_log_print(ANDROID_LOG_VERBOSE, "SuperpoweredExample", "FXSEL %i",     value);
activeFx = value;
}

void SuperpoweredExample::onFxOff() {
filter->enable(false);
roll->enable(false);
flanger->enable(false);
}

#define MINFREQ 60.0f
#define MAXFREQ 20000.0f

static inline float floatToFrequency(float value) {
if (value > 0.97f) return MAXFREQ;
if (value < 0.03f) return MINFREQ;
value = powf(10.0f, (value + ((0.4f - fabsf(value - 0.4f)) * 0.3f)) *     log10f(MAXFREQ - MINFREQ)) + MINFREQ;
return value < MAXFREQ ? value : MAXFREQ;
}

void SuperpoweredExample::onFxValue(int ivalue) {
float value = float(ivalue) * 0.01f;
switch (activeFx) {
    case 1:
        filter->setResonantParameters(floatToFrequency(1.0f - value), 0.2f);
        filter->enable(true);
        flanger->enable(false);
        roll->enable(false);
        break;
    case 2:
        if (value > 0.8f) roll->beats = 0.0625f;
        else if (value > 0.6f) roll->beats = 0.125f;
        else if (value > 0.4f) roll->beats = 0.25f;
        else if (value > 0.2f) roll->beats = 0.5f;
        else roll->beats = 1.0f;
        roll->enable(true);
        filter->enable(false);
        flanger->enable(false);
        break;
    default:
        flanger->setWet(value);
        flanger->enable(true);
        filter->enable(false);
        roll->enable(false);
};
}

void SuperpoweredExample::process(SLAndroidSimpleBufferQueueItf caller) {
pthread_mutex_lock(&mutex);
float *stereoBuffer = outputBuffer[currentBuffer];

bool masterIsA = (crossValue <= 0.5f);
float masterBpm = masterIsA ? playerA->currentBpm : playerB->currentBpm;
double msElapsedSinceLastBeatA = playerA->msElapsedSinceLastBeat; // When playerB needs it, playerA has already stepped this value, so save it now.

bool silence = !playerA->process(stereoBuffer, false, buffersize, volA, masterBpm, playerB->msElapsedSinceLastBeat);
if (playerB->process(stereoBuffer, !silence, buffersize, volB, masterBpm, msElapsedSinceLastBeatA)) silence = false;

roll->bpm = flanger->bpm = masterBpm; // Syncing fx is one line.

if (roll->process(silence ? NULL : stereoBuffer, stereoBuffer, buffersize) && silence) silence = false;
if (!silence) {
    filter->process(stereoBuffer, stereoBuffer, buffersize);
    flanger->process(stereoBuffer, stereoBuffer, buffersize);
    //adding buffer to process function
    recorder->process(stereoBuffer, 0, buffersize);
    //
};


pthread_mutex_unlock(&mutex);

// The stereoBuffer is ready now, let's put the finished audio into the requested buffers.
if (silence) memset(stereoBuffer, 0, buffersize * 4); else SuperpoweredStereoMixer::floatToShortInt(stereoBuffer, (short int *)stereoBuffer, buffersize);

(*caller)->Enqueue(caller, stereoBuffer, buffersize * 4);
if (currentBuffer < NUM_BUFFERS - 1) currentBuffer++; else currentBuffer = 0;
}

extern "C" {
JNIEXPORT void Java_com_superpowered_crossexample_MainActivity_SuperpoweredExample(JNIEnv *javaEnvironment, jobject self, jstring apkPath, jlongArray offsetAndLength);
JNIEXPORT void Java_com_superpowered_crossexample_MainActivity_onPlayPause(JNIEnv *javaEnvironment, jobject self, jboolean play);
//connect onRecord with java
JNIEXPORT void Java_com_superpowered_crossexample_MainActivity_onRecord(JNIEnv *javaEnvironment, jobject self, jboolean record);
JNIEXPORT void Java_com_superpowered_crossexample_MainActivity_onCrossfader(JNIEnv *javaEnvironment, jobject self, jint value);
JNIEXPORT void Java_com_superpowered_crossexample_MainActivity_onFxSelect(JNIEnv *javaEnvironment, jobject self, jint value);
JNIEXPORT void Java_com_superpowered_crossexample_MainActivity_onFxOff(JNIEnv *javaEnvironment, jobject self);
JNIEXPORT void Java_com_superpowered_crossexample_MainActivity_onFxValue(JNIEnv *javaEnvironment, jobject self, jint value);
}

static SuperpoweredExample *example = NULL;

// Android is not passing more than 2 custom parameters, so we had to pack file offsets and lengths into an array.
JNIEXPORT void Java_com_superpowered_crossexample_MainActivity_SuperpoweredExample(JNIEnv *javaEnvironment, jobject self, jstring apkPath, jlongArray params) {
// Convert the input jlong array to a regular int array.
jlong *longParams = javaEnvironment->GetLongArrayElements(params, JNI_FALSE);
int arr[6];
for (int n = 0; n < 6; n++) arr[n] = longParams[n];
javaEnvironment->ReleaseLongArrayElements(params, longParams, JNI_ABORT);

const char *path = javaEnvironment->GetStringUTFChars(apkPath, JNI_FALSE);
example = new SuperpoweredExample(path, arr);
javaEnvironment->ReleaseStringUTFChars(apkPath, path);

}

JNIEXPORT void Java_com_superpowered_crossexample_MainActivity_onPlayPause(JNIEnv *javaEnvironment, jobject self, jboolean play) {
example->onPlayPause(play);
}
//connect onRecord with java code
JNIEXPORT void Java_com_superpowered_crossexample_MainActivity_onRecord(JNIEnv *javaEnvironment, jobject self, jboolean record) {
example->onRecord(record);
}
//

JNIEXPORT void Java_com_superpowered_crossexample_MainActivity_onCrossfader(JNIEnv *javaEnvironment, jobject self, jint value) {
example->onCrossfader(value);
}

JNIEXPORT void Java_com_superpowered_crossexample_MainActivity_onFxSelect(JNIEnv *javaEnvironment, jobject self, jint value) {
example->onFxSelect(value);
}

JNIEXPORT void Java_com_superpowered_crossexample_MainActivity_onFxOff(JNIEnv *javaEnvironment, jobject self) {
example->onFxOff();
}

JNIEXPORT void Java_com_superpowered_crossexample_MainActivity_onFxValue(JNIEnv *javaEnvironment, jobject self, jint value) {
example->onFxValue(value);
}

MainActivity.java

package com.superpowered.crossexample;

import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.media.AudioManager;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;

import java.io.IOException;

public class MainActivity extends ActionBarActivity {
boolean playing = false;
//Added variable
boolean recording = false;
//

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // Get the device's sample rate and buffer size to enable low-latency Android audio output, if available.
    String samplerateString = null, buffersizeString = null;
    if (Build.VERSION.SDK_INT >= 17) {
        AudioManager audioManager = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
        samplerateString = audioManager.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
        buffersizeString = audioManager.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
    }
    if (samplerateString == null) samplerateString = "44100";
    if (buffersizeString == null) buffersizeString = "512";

    // Files under res/raw are not compressed, just copied into the APK. Get the offset and length to know where our files are located.
    AssetFileDescriptor fd0 = getResources().openRawResourceFd(R.raw.lycka), fd1 = getResources().openRawResourceFd(R.raw.nuyorica);
    long[] params = {
            fd0.getStartOffset(),
            fd0.getLength(),
            fd1.getStartOffset(),
            fd1.getLength(),
            Integer.parseInt(samplerateString),
            Integer.parseInt(buffersizeString)
    };
    try {
        fd0.getParcelFileDescriptor().close();
        fd1.getParcelFileDescriptor().close();
    } catch (IOException e) {}

    // Arguments: path to the APK file, offset and length of the two resource files, sample rate, audio buffer size.
    SuperpoweredExample(getPackageResourcePath(), params);

    // crossfader events
    final SeekBar crossfader = (SeekBar)findViewById(R.id.crossFader);
    crossfader.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {

        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
            onCrossfader(progress);
        }

        public void onStartTrackingTouch(SeekBar seekBar) {}
        public void onStopTrackingTouch(SeekBar seekBar) {}
    });

    // fx fader events
    final SeekBar fxfader = (SeekBar)findViewById(R.id.fxFader);
    fxfader.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {

        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
            onFxValue(progress);
        }

        public void onStartTrackingTouch(SeekBar seekBar) {
            onFxValue(seekBar.getProgress());
        }

        public void onStopTrackingTouch(SeekBar seekBar) {
            onFxOff();
        }
    });

    // fx select event
    final RadioGroup group = (RadioGroup)findViewById(R.id.radioGroup1);
    group.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
        public void onCheckedChanged(RadioGroup radioGroup, int checkedId) {
            RadioButton checkedRadioButton = (RadioButton)radioGroup.findViewById(checkedId);
            onFxSelect(radioGroup.indexOfChild(checkedRadioButton));
        }
    });
}

public void SuperpoweredExample_PlayPause(View button) {  // Play/pause.
    playing = !playing;
    onPlayPause(playing);
    Button b = (Button) findViewById(R.id.playPause);
    b.setText(playing ? "Pause" : "Play");
}
//Added the following Record method
public void SuperpoweredExample_Record(View button) {  // Record/Stop Recording.
    recording = !recording;
    onRecord(recording);
    Button r = (Button) findViewById(R.id.rec);
    r.setText(recording ? "Start Recording" : "Stop Recording");
}
//
@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.menu_main, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();

    //noinspection SimplifiableIfStatement
    if (id == R.id.action_settings) {
        return true;
    }

    return super.onOptionsItemSelected(item);
}

private native void SuperpoweredExample(String apkPath, long[] offsetAndLength);
private native void onPlayPause(boolean play);
private native void onCrossfader(int value);
private native void onFxSelect(int value);
private native void onFxOff();
private native void onFxValue(int value);
//Added the following line
private native void onRecord(boolean record);
//

static {
    System.loadLibrary("SuperpoweredExample");
}
}
like image 745
Jakub H. Avatar asked Sep 29 '22 13:09

Jakub H.


2 Answers

If you're getting a segmentation fault, it's likely that your application doesn't declare the WRITE_EXTERNAL_STORAGE permission (which CrossExample doesn't, as it's not writing anything). If it persists, the containing directory may not exist - e.g. "/sdcard/nonexistent/out".

like image 121
moe Avatar answered Oct 22 '22 23:10

moe


I realise this is very late but if it's any use, I believe moe is correct; However, as of API 23, it's not enough to just have the WRITE_EXTERNAL_STORAGE permission inside your manifest. You will also need to explicitly check for and ask for this permission due to the new permissions models. During development you can also go to the app info->permissions page for your app and manually enable the permission there (assuming it's in your manifest).

I had a seg fault until I took this last step and it's now not happening anymore.

I hope this helps someone as confused as I was.

like image 2
seanAshmore Avatar answered Oct 22 '22 23:10

seanAshmore