1

Android アプリでオーディオの速度とピッチを変更するために、SoundTouch C++ ライブラリを使用しようとしています。Java byte[] 配列 (.wav から) を JNI 経由で正常にプッシュし、それを返し、AudioTrack.

次のステップでは、SoundTouch パイプラインを介してサンプル byte[] をプッシュしようとしています。ライブラリに含まれている SoundStretch コンソール プログラムのソースを分析し、それを適応させようとしました。テスト目的で、ステレオの 16 ビット ソースを使用しています。

現在の一時的なセットアップでは、RIFF ヘッダーを無視して .wav データと共に変換しています。これは、JavaAudioTrackオブジェクトがヘッダーを読み取る必要がなく、生の PCM を再生するだけだからです。SoundTouch 経由で送信せずに未加工の byte[] を再​​生すると、ヘッダーがある場所で小さなクリックが発生するだけです。

SoundTouch パイプラインを介して送信した後、音声の開始位置でホワイト ノイズを再生しています。関数の最後で問題が発生していると思いwrite()ます。ここで、short を signed char にキャストしています。ここでは、コンソール アプリがベクトルにプッシュするのではなく、ファイルに書き込んでいます。

int res = (int)fwrite(temp, 1, numBytes, fptr);

のドキュメントを読みましたfwriteが、ファイルに書き込むのではなく、この情報を char[] で正しく取得するためにここで何をすべきかを知るために、ビットのいじりやオーディオ処理について十分に知りません。キャストに関する情報が失われていることはわかっていますが、それを修正する方法がわかりません。

やる気のある人のために、SoundStretch のソースはhttp://www.surina.net/soundtouch/sourcecode.htmlにあります。

extern "C" DLL_PUBLIC jbyteArray 
Java_net_surina_soundtouch_SoundTouch_getMutatedBytes
(JNIEnv *env, jobject thiz, jbyteArray input, jint length)
{
    const int BUFF_SIZE = 2048000;

    SoundTouch soundTouch;

    jboolean isCopy;
    jbyte* ar = env->GetByteArrayElements(input, &isCopy);

    signed char* cBufferIn = (signed char*)ar;

    SAMPLETYPE* fBufferIn = new SAMPLETYPE[length];
    vector<signed char> fBufferOut;

    //converts the chars to floats per the SoundTouch console app.
    convertInput16(cBufferIn, fBufferIn, length); 

    //channels, sampling rate, speed, pitch change
    setup(&soundTouch, 2, 44100, 1.0, 0);

    //transform floats from fBufferIn to fBufferout
    process(&soundTouch, fBufferIn, fBufferOut, BUFF_SIZE); 

    signed char* res = &fBufferOut[0];

    jbyteArray result = env->NewByteArray(length);
    env->SetByteArrayRegion(result, 0, fBufferOut.size(), res);

    LOGV("fBufferOut Size: %d", fBufferOut.size());

    delete[] fBufferIn;

    return result;
}

処理する():

static void process(SoundTouch* soundTouch, SAMPLETYPE* fBufferIn, vector<signed char>& fBufferOut, int BUFF_SIZE)
{
    int nSamples = BUFF_SIZE / 2; //2 bytes per sample, using 16 bit sample for testing
    int buffSizeSamples = BUFF_SIZE / 2; //2 channel stereo

    soundTouch->putSamples(fBufferIn, nSamples);

    do
    {
        nSamples = soundTouch->receiveSamples(fBufferIn, buffSizeSamples);
        write(fBufferIn, fBufferOut, nSamples / 2); //2 channels
    } while (nSamples != 0);

    soundTouch->flush();

    do
    {
        nSamples = soundTouch->receiveSamples(fBufferIn, buffSizeSamples);
        write(fBufferIn, fBufferOut, nSamples / 2);
        LOGV("NUMBER OF SAMPLES: %d", nSamples);
    } while (nSamples != 0);
}

書きます():

static void write(const float *bufferIn, vector<signed char>& bufferOut, int numElems)
{
    int numBytes;
    int bytesPerSample;

    if (numElems == 0) return;

    bytesPerSample = 16 / 8; //16 bit test sample / bits in a byte
    numBytes = numElems * bytesPerSample;
    short *temp = (short*)getConvBuffer(numBytes);

    switch (bytesPerSample)
    {

    case 2: //16 bit encoding per the SoundStretch console app
    {
              short *temp2 = (short *)temp;
              for (int i = 0; i < numElems; i++)
              {
                  short value = (short)saturate(bufferIn[i] * 32768.0f, -32768.0f, 32767.0f); //magic to me
                  temp2[i] = value; //works for little endian only.
              }
              break;
    }

    default:
        assert(false);
    }

    for (int i = 0; i < numElems; ++i)
    {
        bufferOut.push_back((signed char)temp[i]); //I think my problem is here.
    }

    delete[] temp;

    //bytesWritten += numBytes;
}
4

1 に答える 1