将SoundTouch音频库中的短[]转换为播放

Converting a short[] from SoundTouch audio library for playback

本文关键字:转换 播放 SoundTouch 音频      更新时间:2023-10-16

我正试图在Android应用程序中使用SoundTouch C++库来更改音频速度和音高。我已经成功地通过JNI推送了一个Java byte[]数组(来自.wav),返回了它,并用AudioTrack进行了回放。

下一步是尝试通过SoundTouch管道推送样本字节[]。我已经分析了库中包含的SoundStretch控制台程序的源代码,并试图对其进行调整。我使用立体声16位源代码进行测试。

在我当前的临时设置中,我忽略RIFF标头,并将其与.wav数据一起转换,因为JavaAudioTrack对象不需要读取标头,它只播放原始PCM。在不通过SoundTouch发送的情况下播放原始字节[]只会导致标题所在的位置出现小点击。

在通过SoundTouch管道发送后,我正在播放音频开头的白噪声。我认为我在write()函数的末尾遇到了问题,我正在将short's转换为签名字符。在这里,控制台应用程序正在写入一个文件,而不是推送到一个向量:

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

process():

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

write():

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

我只需要获取char[]中的所有位:

for (int i = 0; i < numElems; ++i)
{
bufferOut.push_back(temp[i] & 0xff);
bufferOut.push_back((temp[i] >> 8) & 0xff);
}