Reputation: 336
I'm using the Oboe C++ library for playing sounds in my android application. I want to change the pitch of my audio samples. So, I started creating "mPos" float value to hold the current played frame and adding the "mPitch" value every step.
It seems like the audio played correctly with the new Pitch but it's double it's self when the pitch is high(e.g 1.2) and make a weird noise and when the pitch is low (e.g 0.212).
This is my first-time audio programming, I did a lot of research before I post this question. I even send messages directly to "Oboe" supports but no response. Does anyone have any idea how to implement the Pitch correctly?
streamLength always 192
channelCount always 2
Code:
void Player::renderAudio(float *stream, int32_t streamLength){
const int32_t channelCount = mSound->getChannelCount();
if (mIsPlaying){
float framesToRenderFromData = streamLength ;
float totalSourceFrames = mSound->getTotalFrames()/mPitch;
const float *data = mSound->getData();
// Check whether we're about to reach the end of the recording
if (mPos + streamLength >= totalSourceFrames ){
framesToRenderFromData = (totalSourceFrames - mPos);
mIsPlaying = false;
}
for (int i = 0; i < framesToRenderFromData; ++i) {
for (int j = 0; j < channelCount; ++j) {
if(j % 2 == 0){
stream[(i*channelCount)+j] = (data[((size_t)mPos * channelCount)) + j] * mLeftVol) * mVol;
}else{
stream[(i*channelCount)+j] = (data[((size_t)mPos * channelCount)) + j] * mRightVol) * mVol;
}
}
mPos += mPitch;
if(mPos >= totalSourceFrames){
mPos = 0;
}
}
if (framesToRenderFromData < streamLength){
renderSilence(&stream[(size_t)framesToRenderFromData], streamLength * channelCount);
}
} else {
renderSilence(stream, streamLength * channelCount);
}
}
void Player::renderSilence(float *start, int32_t numSamples){
for (int i = 0; i < numSamples; ++i) {
start[i] = 0;
}
}
void Player::setPitch(float pitchData){
mPitch = pitchData;
};
Upvotes: 1
Views: 1309
Reputation: 353
When you multiply a float variable (mPos) by an integer-type variable (channelCount), the result is a float. You are, at the least, messing up your channel interleaving. Instead of
(size_t)(mPos * channelCount)
try
((size_t)mPos) * channelCount
EDIT:
You are intentionally looping the source when reaching the end, with the if statement that results in mPos = 0;
. Instead of doing this, you could calculate the number of source samples independently of the pitch, but break out of the loop when your source samples are exhausted. Also, your comparison of the source and destination samples isn't useful because of the pitch adjustment:
float framesToRenderFromData = streamLength ;
float totalSourceFrames = mSound->getTotalFrames(); // Note change here
const float *data = mSound->getData();
// Note: Only check here is whether mPos has reached the end from
// a previous call
if ( mPos >= totalSourceFrames ) {
framesToRenderFromData = 0.0f;
}
for (int i = 0; i < framesToRenderFromData; ++i) {
for (int j = 0; j < channelCount; ++j) {
if(j % 2 == 0){
stream[(i*channelCount)+j] = (data[((size_t)mPos * channelCount)) + j] * mLeftVol) * mVol;
}else{
stream[(i*channelCount)+j] = (data[((size_t)mPos * channelCount)) + j] * mRightVol) * mVol;
}
}
mPos += mPitch;
if ( ((size_t)mPos) >= totalSourceFrames ) { // Replace this 'if' and its contents
framesToRenderFromData = (size_t)mPos;
mPos = 0.0f;
break;
}
}
A note, however, for completeness: You really shouldn't be accomplishing pitch change in this way for any serious application -- the sound quality will be terrible. There are free libraries for audio resampling to an arbitrary target rate; these will convert your source sample to a higher or lower number of samples, and provide quality pitch changes when replayed at the same rate as the source.
Upvotes: 2