Using AudioPlayer node changes sampling frequency to 44,100 - audiokit

I have the following audio engine chain in Audiokit :
48kHz audio file -> AudioPlayer -> some node -> NodeRecorder -> audio file
Even though the input file is 48kHz , the recorded file comes out at 44,100 Hz.
How to force AudioKit to stay at 48KHz throughout ?
I'm using Audiokit 5.3
Btw, If I use microphone as input instead of file, then all is well, the recorded file comes out at 48kHz.
Mic input -> some node -> NodeRecorder -> audio file
So I suspect the AudioPlayer node is doing some unrequested resampling, how to turn that off?

It is possible you can change the value of defaultAudioFormat in the Settings.swift in the AudioKit package. That looks like the only place the sample rate is being set explicitly: https://github.com/AudioKit/AudioKit/search?q=44

Thank you #Nick , this put me in the right direction.
Actually all that was needed to run the engine at 48kHz was to add this line at init():
Settings.audioFormat = AVAudioFormat(standardFormatWithSampleRate: 48000, channels: 2) ?? AVAudioFormat()

Related

Split audio track into segments by BPM and analyse each segment using Superpowered iOS

I have been using the Superpowered iOS library to analyse audio and extract BPM, loudness, pitch data. I'm working on an iOS Swift 3.0 project and have been able to get the C classes work with Swift using the Bridging headers for ObjC.
The problem I am running into is that whilst I can create a decoder object, extract audio from the Music Library and store it as a .WAV - I am unable to create a decoder object for just snippets of the extracted audio and get the analyser class to return data.
My approach has been to create a decoder object as follows:
var decodeAttempt = decoder!.open(self.originalFilePath, metaOnly: false, offset: offsetBytes, length: lengthBytes, stemsIndex: 0)
'offsetBytes' and 'LengthBytes' I think are the position within the audio file. As I have already decompressed audio, stored it as WAV and then am providing it to the decoder here, I am calculating the offset and length using the PCM Wave audio formula of 44100 x 2 x 16 / 8 = 176400 bytes per second. Then using this to specify a start point and length in bytes. I'm not sure that this is the correct way to do this as the decoder will return 'Unknown file format'.
Any ideas or even alternative suggestions of how to achieve the title of this question? Thanks in advance!
The offset and length parameters of the SuperpoweredDecoder are there because of the Android APK file format, where bundled audio files are simply concatenated to the package.
Despite a WAV file is as "uncompressed" as it can be, there is a header at the beginning, so offset and length are not a good way for this purpose. Especially as the header is present at the beginning only, and without the header decoding is not possible.
You mention that you can extract audio to PCM (and save to WAV). Then you have the answer in your hand: just submit different extracted portions to different instances of the SuperpoweredOfflineAnalyzer.

iOS Audio Unit, output each stereo channel from stereo source to 3D Mixer

Apple's 3D Mixer Audio Unit guide states:
To use a stereo source, you may treat its left and right channels as two independent single-channel sources, and then feed each side of the stereo stream to its own input bus.
https://developer.apple.com/library/ios/qa/qa1695/_index.html
However, I can not figure out how to send each channel of my stereo Audio Unit into the 3D Mixer. How does one do this?
bascially, you'll need to do something like
#interface AudioEngine () {
AVAudioEngine *_engine;
AVAudioEnvironmentNode *_environment;
AVAudioPCMBuffer *_ouputBuffer;
NSMutableArray <AVAudioPlayerNode*> *_PlayerArray;
AVAudioPlayerNode *_soundPlayer;
AVAudioPCMBuffer *_soundBuffer;
bool _multichannelOutputEnabled
;
load a file and grab it from the buffer.
to split the stereo into multichannel, you need something like
outputLayoutTag = kAudioChannelLayoutTag_AudioUnit_2;
_multichannelOutputEnabled = true;
this '_multichannelOutputEnabled = true;' is usually set to false
then setup the algorithm to do something with your channels
AVAudio3DMixingRenderingAlgorithm renderingTHIS = _multichannelOutputEnabled ?
AVAudio3DMixingRenderingAlgorithmSoundField : AVAudio3DMixingRenderingAlgorithmEqualPowerPanning;
newPlayer.renderingAlgorithm = renderingTHIS;
somewhere in a view controller your might have something like this tied to an object in a game
[self.epicAudioEngine.updateListenerOrientation:AVAudioMake3DAngularOrientation([ang.x,ang.y,ang.z])
[self updateEulerAnglesAndListenerFromDeltaX:dX DeltaY:dY]
look through ffmpeg source code for libavformat,libavutil to get an idea of how to process audio

Using AVAudioEngine to schedule sounds for low-latency metronome

I am creating a metronome as part of a larger app and I have a few very short wav files to use as the individual sounds. I would like to use AVAudioEngine because NSTimer has significant latency problems and Core Audio seems rather daunting to implement in Swift. I'm attempting the following, but I'm currently unable to implement the first 3 steps and I'm wondering if there is a better way.
Code outline:
Create an array of file URLs according to the metronome's current settings (number of beats per bar and subdivisions per beat; file A for beats, file B for subdivisions)
Programmatically create a wav file with the appropriate number of frames of silence, based on the tempo and the length of the files, and insert it into the array between each of the sounds
Read those files into a single AudioBuffer or AudioBufferList
audioPlayer.scheduleBuffer(buffer, atTime:nil, options:.Loops, completionHandler:nil)
So far I have been able to play a looping buffer (step 4) of a single sound file, but I haven't been able to construct a buffer from an array of files or create silence programmatically, nor have I found any answers on StackOverflow that address this. So I'm guessing that this isn't the best approach.
My question is: Is it possible to schedule a sequence of sounds with low latency using AVAudioEngine and then loop that sequence? If not, which framework/approach is best suited for scheduling sounds when coding in Swift?
I was able to make a buffer containing sound from file and silence of required length. Hope this will help:
// audioFile here – an instance of AVAudioFile initialized with wav-file
func tickBuffer(forBpm bpm: Int) -> AVAudioPCMBuffer {
audioFile.framePosition = 0 // position in file from where to read, required if you're read several times from one AVAudioFile
let periodLength = AVAudioFrameCount(audioFile.processingFormat.sampleRate * 60 / Double(bpm)) // tick's length for given bpm (sound length + silence length)
let buffer = AVAudioPCMBuffer(PCMFormat: audioFile.processingFormat, frameCapacity: periodLength)
try! audioFile.readIntoBuffer(buffer) // sorry for forcing try
buffer.frameLength = periodLength // key to success. This will append silcence to sound
return buffer
}
// player – instance of AVAudioPlayerNode within your AVAudioEngine
func startLoop() {
player.stop()
let buffer = tickBuffer(forBpm: bpm)
player.scheduleBuffer(buffer, atTime: nil, options: .Loops, completionHandler: nil)
player.play()
}
I think that one of possible ways to have sounds played at with lowest possible time error is providing audio samples directly via callback. In iOS you could do this with AudioUnit.
In this callback you could track sample count and know at what sample you are now. From sample counter you could go to time value (using sample rate) and use it for your high level tasks like metronome. If you see that it is time to play metronome sound then you just starting to copy audio samples from that sound to buffer.
This is a theoretic part without any code, but you could find many examples of AudioUnit and callback technique.
To expand upon 5hrp's answer:
Take the simple case where you have two beats, an upbeat (tone1) and a downbeat (tone2), and you want them out of phase with each other so the audio will be (up, down, up, down) to a certain bpm.
You will need two instances of AVAudioPlayerNode (one for each beat), let's call them audioNode1 and audioNode2
The first beat you will want to be in phase, so setup as normal:
let buffer = tickBuffer(forBpm: bpm)
audioNode1player.scheduleBuffer(buffer, atTime: nil, options: .loops, completionHandler: nil)
then for the second beat you want it to be exactly out of phase, or to start at t=bpm/2. for this you can use an AVAudioTime variable:
audioTime2 = AVAudioTime(sampleTime: AVAudioFramePosition(AVAudioFrameCount(audioFile2.processingFormat.sampleRate * 60 / Double(bpm) * 0.5)), atRate: Double(1))
you can use this variable in the buffer like so:
audioNode2player.scheduleBuffer(buffer, atTime: audioTime2, options: .loops, completionHandler: nil)
This will play on loop your two beats, bpm/2 out of phase from each other!
It's easy to see how to generalise this to more beats, to create a whole bar. It's not the most elegant solution though, because if you want to say do 16th notes you'd have to create 16 nodes.

How do I connect mic input to an effect unit and then to a mixer (using AudioCore)?

I've been able to get microphone input, connect it to a mixer, connect the mixer to a reverb2 effect, and then output all that to the speaker with CoreAudio. It looks like this:
Mic -> Mixer -> Reverb -> Speaker
This works just fine.
However, what I really want to do is this:
Mic -> Reverb -> Mixer -> Speaker
THE PROBLEM IS: I can't seem to grasp how to tell the reverb unit where/how to get its input from the mic.
If I do this, there is an error (-10865) later when the graph is initialized:
AUGraphConnectNodeInput(graph, ioNode, 0, reverbNode, 0);
I've been struggling with this for days. Any help is greatly appreciated.
You need to first set the output scope format of the mic (RemoteIO Bus 1) to the input format of the reverb audio unit. On iOS, these 2 units have different and incompatible default or canonical data type formats, but the output format of the mic can be reconfigured.

play multi-instrument MIDI file

I have been working with the MusicPlayer and MusicSequence classes to play MIDI files. However, I am wondering how to specify instruments for different channels. What is the MusicPlayer's default response to program change events? Can I tell the MusicPlayer to use certain samplers for certain channels? Or is the only way to set my own function as a MIDI Endpoint, and then handle the playing of notes myself using MusicDeviceMIDIEvent() to specify the sampler?
You can specify different instruments for different tracks in a MusicSequence. (MusicTrack API) I don't think MusicPlayer does anything with channels per se. You can specify that channels be converted to tracks when loading a MIDI file.
Music Sequence Load Flags
Flags used to configure the behavior of the MusicSequenceFileLoad and
MusicSequenceFileLoadData functions.
enum { kMusicSequenceLoadSMF_ChannelsToTracks = (1 << 0) }; typedef
UInt32 MusicSequenceLoadFlags;
Constants
kMusicSequenceLoadSMF_ChannelsToTracks
If this flag is set the resultant Sequence will contain a tempo track, 1 track for each MIDI Channel that is found in the SMF, 1 track
for SysEx or MetaEvents - and this will be the last track in the
sequence after the LoadSMFWithFlags calls.
Available in OS X v10.3 and later.
Declared in MusicPlayer.h.

Resources