Superpowered SDK:实现SuperpoweredRecorder.h并设置录制路径

时间:2015-03-11 14:22:41

标签: android audio sdk android-ndk record

我已成功将Superpowered SDK CrossExample项目导入android studio并在三星galaxy S3和仿真器上进行了测试。现在我正在实现一个记录选项,使用SuperpoweredRecorder.h记录应用程序播放的音频。我将需要有关如何正确设置*tempPath*destinationPath变量以成功保存录音的帮助。 该项目构建正常,但当我尝试在Galaxy S3或模拟器上运行应用程序时,我收到以下错误消息: 19565-19565/com.superpowered.crossexample A/libc﹕ Fatal signal 11 (SIGSEGV) at 0x006f0070 (code=1), thread 19565 (ed.crossexample) 添加* tempPath和* destinationPath指针后发生此错误,所以我相信在成功设置录制路径后,此错误将消失。

SuperpoweredRecorder.h文档的链接:http://superpowered.com/docs/class_superpowered_recorder.html 在查看文档时,我还需要添加哪些代码才能使用tempPathdestinationPath变量?

我自己添加的代码是正向双斜线。

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");
}
}

2 个答案:

答案 0 :(得分:2)

如果您遇到细分错误,您的应用可能不会声明WRITE_EXTERNAL_STORAGE权限(CrossExample不会,因为它没有写任何东西)。如果它仍然存在,则包含目录可能​​不存在 - 例如&#34; / SD卡/不存在的输入/输出&#34;

答案 1 :(得分:1)

我意识到这已经很晚了,但如果它有用,我相信moe是正确的;但是,从API 23开始,仅在清单中包含WRITE_EXTERNAL_STORAGE权限是不够的。由于新的权限模型,您还需要明确检查并请求此权限。在开发过程中,您还可以转到应用的应用信息 - >权限页面,并在那里手动启用权限(假设它在您的清单中)。

我有一个段错误,直到我采取了最后一步,它现在不再发生了。

我希望这可以帮助像我一样困惑的人。