I did not found solution for one issue: how to play mp3 file from offset immideately?
I can only play file then send -(void)seekToTime: but in this case sound begins and interrupts then begins from defined offset.
I tried to apply seekToTime method on ASStatusChangedNotification (in different cases of AudioStreamerState) but there were without result.
upd: I think that may set time offset after the file began streaming (before playing). But how?
Thanks.
What I did was create a method to seek to the desired time that I run after [streamer start]:
while(streamer.bitRate == 0) {
sleep(1);
}
If you're concerned about waiting too long, you can add a time out: either a count of times through the loop, or set a start time and compare it to the current time to break out of the loop.
This blog post has another take:
http://www.saygoodnight.com/2009/08/streaming-audio-to-the-iphone-starting-at-an-offset/
Related
IXAudio2SourceVoice has a GetState function which returns an XAUDIO2_VOICE_STATE structure. This structure has a SamplesPlayed member, which is:
Total number of samples processed by this voice since it last started, or since the last audio stream ended (as marked with the XAUDIO2_END_OF_STREAM flag).
What I want to be able to do it stop the source voice, flush all its buffers, and then reset the SamplesPlayed counter to zero. Neither calling Stop nor FlushSourceBuffers will by themselves reset SamplesPlayed. And while flagging the last buffer with XAUDIO2_END_OF_STREAM does correctly reset SamplesPlayed back to zero, this seemingly only works if that last buffer is played to completion; if the buffer is flushed, then SamplesPlayed does not get reset. I have also tried calling Discontinuity both before and after stopping/flushing with no effect.
My current workaround is, after stopping and flushing the source voice, to submit a tiny 1-sample silent buffer with the XAUDIO2_END_OF_STREAM flag set and then let the source voice play to process that buffer and thus reset SamplesPlayed to zero. This works fine-ish for my use case, but it seems pretty hacky/clumsy. Is there a better solution?
Looking at the XAudio2 source, there's no exposed way to do that in the API other than letting a packet play with XAUDIO2_END_OF_STREAM.
Calling Discontinuity sets up the end-of-stream flag on the currently playing buffer, or if there's none playing and a queued buffer it sets it there. You need to call Discontinuity and then let the voice play to completion before you recycle it.
I am developing MIDI Player by referring to the following Web-Page.
http://twocentstudios.com/2017/02/20/bouncing-midi-to-audio-on-ios/
I don't do any recording, I just want to play the SMF file.
However, when I run setPreload (true), it says "ASSERTION FAILED: Preroll mode set during render" and my app hangs.
I searched for "Preroll mode set during render" but couldn't find any valid information.
Please help someone.
EDIT:
hi, #dspr.
The percussion sounds even if I don't do "AudioUnitSetProperty (kAUMIDISynthProperty_EnablePreload: 1)".
I think this is because the BANK for percussion is automatically assigned to ch.10.
However, in this state, the piano and guitar and others do not sound.
AVAudioUnitMIDI Instrument needs kAUMIDISynthProperty_EnablePreload to analyze which tone is assigned to which track in the SMF file, right?
Which method does AVAudioUnitMIDIInstrument use to preload SMF files?
(1) AudioUnitSetProperty (kAUMIDISynthProperty_EnablePreload: 1) to AVAudioUnitMIDISynth
(2) << How to preload? >>
(3) AudioUnitSetProperty (kAUMIDISynthProperty_EnablePreload: 0) to AVAudioUnitMIDISynth
(4) Start AVAudioSequencer
MIDI Player uses the kAUMIDISynthProperty_EnablePreload property of MIDISynth for that purpose. See the Apple comment about it below. Note the It should only be used prior to MIDI playback and must be set back to 0 before attempting to start playback sentence at the end :
/*!
#constant kAUMIDISynthProperty_EnablePreload
#discussion Scope: Global
Value Type: UInt32
Access: Write
Setting this property to 1 puts the MIDISynth in a mode where it will attempt to load
instruments from the bank or file when it receives a program change message. This
is used internally by the MusicSequence. It should only be used prior to MIDI playback,
and must be set back to 0 before attempting to start playback.
*/
EDIT : frankly, I'm a little bit reserved about your link
One strategy I haven’t tried would be to pitch shift the MIDI up one octave, play it back at 2x, record it at 88.2kHz, then downsample to 44.1kHz. AVAudioSession presumably can’t go past 48kHz though.
Clearly, the person who wrote that has a very poor knowledge about audio and sampling. Playing a MIDI song transposed one octave up at double tempo is really not equivalent than playing the same recorded in audio at double speed whatever you make the recording at 88.2kHz or any other sample rate. As a simple example, what happens is the file contains a drum set ? A snare drum (40) will become a Chinese cymbal (52) played two times slower ?
As I can understand this post, the described hack has for unique purpose to make recording. So if you simply want to play your MIDI file back you can certainly find a simpler and better example.
I am using AVAudioUnitSampler to play some midi sounds, i have a soundfont loaded and have sucessfully use start note, stop note and apply pitch bend midi commands. I am now trying to incorporate aftertouch or pressure commands as it is called in AVFoundation.
So my code looks roughly like this (simplified):
self.midiAudioUnitSampler.startNote(60, withVelocity: 60, onChannel: 0)
//some time later...
self.midiAudioUnitSampler.sendPressure(20, onChannel: 0)
The note is humming away but the send pressure commands seem to have no effect on the sound output. I have tried using send pressure and sendPressureForKey to and no luck.
What am i doing wrong or am I misunderstanding what sendPressure does? I expect it to change the volume of the note after it is played.
Btw i have a setup where the note is being played and i have a separate Control to fire pressureCommands into the samplee at some time after the note playback has been started.
My guess is that the sampler does not know what to do with aftertouch messages. If you want to change the volume of the note (and any other notes playing) you could send your value to parameter 7 (volume) instead:
self.midiAudioUnitSampler.sendController(7, withValue: 20, onChannel: 0)
From my experience I have the feeling that the sampler does responds to MIDI controller 7.
I'm trying to reproduce audio from an ICY stream. I'm able to reproduce that with AVPlayer and some good open source library but I'm not able to control the stream. I have no idea how I can get the percentage reproduced or how to seek to a specific time in the stream. Is that possible? Is there a good library that can help me?
Actually I'm using AFSoundManager but I'm always receiving negative numbers for percentage and I get invalid time when trying to seek the stream at a specified time.
That's the code that I'm using:
AFSoundManager.sharedManager().startStreamingRemoteAudioFromURL("http://www.abstractpath.com/files/audiosamples/sample.mp3") { (percentage, elapsedTime, timeRemaining, error, poppi) in
if error == nil {
//This block will be fired when the audio progress increases in 1%
if elapsedTime > 0 {
println(elapsedTime)
self.slider.value = Float(elapsedTime*1000)
}
} else {
//Handle the error
println(error)
}
I'm able of course to get the elapsedTime but not the percentage or the remainingTime. I always get negative numbers.
This code works perfectly with remote or local audio file but not with the stream.
This isn't possible.
These streams are live. There is nothing to seek to because what you haven't heard hasn't happened yet. Even streams that playback music end-to-end are still "live" in the sense that the audio you haven't received hasn't been encoded yet. (Small codec and transit buffers aside, of course.)
I was able to create the exact functionality I wanted to avaudioplayer and avaudiorecorder but of course experienced latency problems. So after reading pretty much every article on the web and reviewing stacks of sample code, I'm still not sure how to achieve the following:
User chooses to record a sample 2 bars long (4 beats per bar) with a pre-roll/count-in
User clicks record
A metronome starts which counts in 4 beats (accent on the first beat)
The app automatically starts recording on the start of the next bar
The app automatically turns off recording at the end of the 3rd bar (the 2 bars + the pre-roll)
The user can then playback their recording or delete it and start again.
So, with avaudioplayer and avaudiorecorder I simply created a 'caf' using audacity with a metronome set at the correct bpm (bpm is set for the app). I then setup and play the avaudioplayer and using the audiodidfinishsuccessfully delegate method, performed some logic to start the recorder, restart the player, maintain a loop count etc. to turn off recording and audio.
As I mentioned, I was pretty much able to achieve the user experience I am after but the latency problems are not acceptable.
I have been working with audio units and the remote IO and have setup a project with a playback callback and recorder callback etc. but now face the problem of working how to make this work based on the description above. I am trying to work out the following things for starters:
If I create a 1 beat caf file, how could I make use of audio units and remote IO to play x amount of beats and then stop?
How could I do the pre-roll and start the recording callback after 4 beats
Can anyone give me some ideas or point me in the right direction. As I have mentioned, I have already done a stack of research including buying the core audio book, reading every article on atastypixel.com, timbolstad.com etc and trawled through the apple docs.
Thanks in advance for your help.
I start an NSTimer. Use values based on BPM (Beats per Minute) / 60. So if user wants to record a 2 bar file with a count in might do something like this:
//timer interval=100BPM/60secs per minute
timerInterval=100/60;
metroTimer = [NSTimer scheduledTimerWithTimeInterval:timerinterval target:self selector:#selector(blinkMetroLight) userInfo:nil repeats:YES];
- (void)blinkMetroLight
{
if(beatNumber == 0)
{
beatNumber = 1;
}
else if (beatNumber == 5)
{
[self audioProcessorStart];
}
if (beatNumber == 8)
{
[self audioProcessorStop];
[metroTimer invalidate]; metroTimer = nil;
}
beatNumber++
}