AVAudioPlayer 从 C++ 开始回调调用 audioPlayerDidFinishPlaying 不知为何晚了

发布于 2024-11-07 15:32:49 字数 3203 浏览 0 评论 0原文

您能给我一些关于问题可能出在哪里的提示吗?我对 ObjC 很陌生,所以我有点陷入困境。

我使用 C++ 构建的库来生成各种声音,我想使用 AVAudioPlayer 来播放这些声音。然而,我观察到 audioPlayerDidFinishPlaying 的某种令人惊讶的行为。我将在下面的例子中进行说明。

这是我的 .h 文件(省略了不相关的内容):

#import <AVFoundation/AVFoundation.h>
@interface mytest_appViewController : UIViewController <AVAudioPlayerDelegate> {
    AVAudioPlayer *player;
    ....
}

void HaveSoundCallback(const short *psSamples, long lSamplesNum, void *pOwner);
....
@property (nonatomic, retain)   AVAudioPlayer   *player;

@end

这是实现的一部分:

....
#import "MyCplusplusSoundGeneratingLibrary.h"
....
- (IBAction)GoButtonTouched:(UIButton *)sender{
    // calling the C++ made GenerateSound and passing it the callback
    int ret=GenerateSound(couple_of_parameters, HaveSoundCallback, self);
}

通常我不知道GenerateSound函数会产生多少声音(每次调用),但每当它产生一个声音时,它调用 HaveSoundCallback 来处理它(它还发送“self”作为声音的“所有者”)。

现在这是回调实现(它位​​于相同的 .mm 中,但在 @implementation...@end 之外):

void HaveSoundCallback(const short *psSamples, long lSamplesNum, void *pOwner){
    NSString *OutWave;

    NSLog(@"Entered the callback function");
    OutWave=[pOwner getDocumentFile:@"out.wav"];
    [pOwner SaveSoundData:OutWave pcm:psSamples len:lSamplesNum];
    [pOwner PlaySound:OutWave];
    [OutWave release];
}

这意味着回调函数将声音数据保存到 wav(通过调用 SaveSoundData),然后调用 PlaySound 来播放它们。 PlaySound 和 audioPlayerDidFinishPlaying 实现再次位于 mytest_appViewController 类中:

- (void)PlaySound:(NSString *)WaveFile{
    NSURL *fileURL = [[NSURL alloc] initFileURLWithPath:WaveFile];

    NSLog(@"Playing started");
    self.player =[[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil];   
    self.player.delegate = self;
    [self.player play];
    while (self.player.playing) {
        sleep(0.01);
    }
    [fileURL release];
    NSLog(@"Playing finished");
}


- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)p successfully:(BOOL)flag
{
    NSLog(@"hi from audioPlayerDidFinishPlaying");
}

我故意在 while 循环中等待,以防止播放器同时播放所有可能返回的声音 - 在播放器停止播放当前声音后会发生另一个回调。但我想触发player.delegate完成的播放。因此,为了我的实验(和 ObjC“自学”)目的,我实现了 audioPlayerDidFinishPlaying 委托。

现在是测试情况:假设GenerateSound 产生3 种声音(它们之间有很短的延迟)。我希望输出控制台类似于:

Entered the callback function
Playing started
hi from audioPlayerDidFinishPlaying
Playing finished
Entered the callback function
Playing started
hi from audioPlayerDidFinishPlaying
Playing finished
Entered the callback function
Playing started
hi from audioPlayerDidFinishPlaying
Playing finished

但不是这样,而是在最后调用了 audioPlayerDidFinishPlaying 3 次,即:

Entered the callback function
Playing started
Playing finished
Entered the callback function
Playing started
Playing finished
Entered the callback function
Playing started
Playing finished
hi from audioPlayerDidFinishPlaying
hi from audioPlayerDidFinishPlaying
hi from audioPlayerDidFinishPlaying

我做错了什么?为什么当播放真正结束时,audioPlayerDidFinishPlaying 没有立即执行,而是堆在某处,最后执行了 3 次?

这是否与程序实际上一直在C++库函数GenerateSound内部有关,因此一旦离开GenerateSound就执行audioPlayerDidFinishPlaying?如果是,有没有更好的方法来实现我想要获得的功能?

预先非常感谢您的帮助!

could you please give me any tips on where the problem might be? I'm very new to ObjC, so I sort of got stuck.

I use a C++ built library which generates various sounds and I would like to play these sounds using AVAudioPlayer. However, I observe some kind of suprising behaviour of audioPlayerDidFinishPlaying. I'll illustrate it on the following example.

This is my .h file (with the irrelevant stuff left out):

#import <AVFoundation/AVFoundation.h>
@interface mytest_appViewController : UIViewController <AVAudioPlayerDelegate> {
    AVAudioPlayer *player;
    ....
}

void HaveSoundCallback(const short *psSamples, long lSamplesNum, void *pOwner);
....
@property (nonatomic, retain)   AVAudioPlayer   *player;

@end

And this is one part of the implementation:

....
#import "MyCplusplusSoundGeneratingLibrary.h"
....
- (IBAction)GoButtonTouched:(UIButton *)sender{
    // calling the C++ made GenerateSound and passing it the callback
    int ret=GenerateSound(couple_of_parameters, HaveSoundCallback, self);
}

Generally I don't know how many sounds the GenerateSound function will produce (per one calling), but whenever it produces one, it calls HaveSoundCallback that is supposed to take care of it (it also sends "self" as the "owner" of the sound).

Now this is the callback implementation (it's in the same .mm but outside @implementation...@end):

void HaveSoundCallback(const short *psSamples, long lSamplesNum, void *pOwner){
    NSString *OutWave;

    NSLog(@"Entered the callback function");
    OutWave=[pOwner getDocumentFile:@"out.wav"];
    [pOwner SaveSoundData:OutWave pcm:psSamples len:lSamplesNum];
    [pOwner PlaySound:OutWave];
    [OutWave release];
}

It means that the callback function saves the sound data to a wav (by calling SaveSoundData) and then it calls PlaySound to play them. PlaySound and audioPlayerDidFinishPlaying implementation are again in the mytest_appViewController class:

- (void)PlaySound:(NSString *)WaveFile{
    NSURL *fileURL = [[NSURL alloc] initFileURLWithPath:WaveFile];

    NSLog(@"Playing started");
    self.player =[[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil];   
    self.player.delegate = self;
    [self.player play];
    while (self.player.playing) {
        sleep(0.01);
    }
    [fileURL release];
    NSLog(@"Playing finished");
}


- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)p successfully:(BOOL)flag
{
    NSLog(@"hi from audioPlayerDidFinishPlaying");
}

I intentionally wait here in the while loop so as to prevent the player from playing all possible returning sounds concurrently - another callback happens after the player stops playing the current sound. But I would like to trigger the finished playing by the player.delegate. So for my experimental (and ObjC "self-tutoring") purposes I implemented the audioPlayerDidFinishPlaying delegate.

And now the testing situation: lets say that GenerateSound produces 3 sounds (with short delays between them). I would expect the output console to be something like:

Entered the callback function
Playing started
hi from audioPlayerDidFinishPlaying
Playing finished
Entered the callback function
Playing started
hi from audioPlayerDidFinishPlaying
Playing finished
Entered the callback function
Playing started
hi from audioPlayerDidFinishPlaying
Playing finished

But instead of this, the audioPlayerDidFinishPlaying is called at the very end 3 times, i.e.:

Entered the callback function
Playing started
Playing finished
Entered the callback function
Playing started
Playing finished
Entered the callback function
Playing started
Playing finished
hi from audioPlayerDidFinishPlaying
hi from audioPlayerDidFinishPlaying
hi from audioPlayerDidFinishPlaying

What do I do wrong? Why isn't the audioPlayerDidFinishPlaying executed immediately when the playing really finished, and instead it gets stacked somewhere and is executed 3 times at the end?

Has it something to do with the fact that the program is actually all the time inside the C++ library function GenerateSound, and thus the audioPlayerDidFinishPlaying is executed as soon as GenerateSound is left? And if yes, is there any better way implement the functionality which I'd like to get?

Thanks a lot in advance for any help!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文