1
votes

I have an mp3 sound I'd like to play with pan, pitch/samplerate, and volume controls set once (not changing in realtime). I am using AVAudioPlayer at the moment, which works, but the rate setting performs time stretching instead of performing the samplerate change where the lower values cause a sound to get slower and lower-pitched, and the higher ones cause a sound to get faster and higher-pitched (kind of like tape speed). E.g., setting samplerate to 88200 HZ when your sound is actually 44100 will result in it playing at 200% speed/pitch. Is something like this possible with AVAudioPlayer, or is there another way to accomplish this? Here's what I have so far:

player=[[AVAudioPlayer alloc] initWithContentsOfURL:
[NSURL fileURLWithPath: @"sound.mp3"] error: nil];
player.volume=0.4f;
player.pan=-1f;
player.enableRate=YES;
player.rate=2.0f;
[player play];

Note: I'm not referring to the pitch method where time stretching is used in combination to keep the length of the sound aproximetly the same, or any "advanced" algorithms.

1
It doesn’t look like AvAudioPlayer provides that kind of functionality developer.apple.com/documentation/avfoundation/avaudioplayerfdcpp
Changing playback speed is not necessarily as simple as changing sample rate. There are on a set number of sample rates supported so you will likely have to take a different approach. The most obvious way I can think of is with an Audio Unit that interpolates between sample values, either dropping or adding in value accordinglyfdcpp
@fdcpp Is there another way I can do it then? Perhaps another class? Another framework?NS studios
If it is for non-real-time you could ingest the audio file, get the samples, apply the interpolation save a new audio file and play it back out.fdcpp
For Frameworks: JUCE is perhaps heavy handed. OpenAL could do it, but is old fashioned. AudioUnits do seem to be the most likely contenderfdcpp

1 Answers

2
votes

AVAudioEngine can give you pitch, rate, pan and volume control:

self.engine = [[AVAudioEngine alloc] init];

NSError *error;

AVAudioPlayerNode *playerNode = [[AVAudioPlayerNode alloc] init];
AVAudioMixerNode *mixer = [[AVAudioMixerNode alloc] init];
AVAudioUnitVarispeed *varispeed = [[AVAudioUnitVarispeed alloc] init];

[self.engine attachNode:playerNode];
[self.engine attachNode:varispeed];
[self.engine attachNode:mixer];

[self.engine connect:playerNode to:varispeed format:nil];
[self.engine connect:varispeed to:mixer format:nil];
[self.engine connect:mixer to:self.engine.mainMixerNode format:nil];

BOOL result = [self.engine startAndReturnError: &error];
assert(result);

AVAudioFile *audioFile = [[AVAudioFile alloc] initForReading:url error:&error];
assert(audioFile);

// rate & pitch (fused), pan and volume controls
varispeed.rate = 0.5; // half pitch & rate
mixer.pan = -1;       // left speaker
mixer.volume = 0.5;   // half volume

[playerNode scheduleFile:audioFile atTime:nil completionHandler:nil];
[playerNode play];

If you want separate rate & pitch control, replace the AVAudioUnitVarispeed node with an AVAudioUnitTimePitch node.