Android 版 Javafx 的音频性能(MediaPlayer 和 NativeAudioService)

2023-12-10

我使用 JavaFX 创建了一个运行良好的桌面游戏(20000 Java 行)。由于它是一个游戏,实时约束很重要(玩家操作的响应时间)。

最终目标是在 Android 上运行该应用程序。我几乎已经完成了从PC到Android的“Java代码传输”,即使我遇到了一些实时麻烦。我想现在几乎所有的问题都已经解决了。

例如,我最大限度地减少了用于检测两个移动设备之间影响的 Shape 或 Rectangle.intersect(node1, node2) 调用的 CPU 时间(消耗)。这样,真实时间就被除以3了。太棒了!

为了测试这个 Android 版本,我使用 Eclipse + Neon2、JavaFX、JavaFXports + gluon 和我的手机 (Archos Diamond S)。


但是,对于 Android 手机,我遇到了与 MediaPlayer 和 NativeAudioSrvice 生成的声音相关的实时问题。

然而,我遵循了同步模式的建议:javafxports如何调用android原生媒体播放器

第一个问题:

这个 Mediaplayer 类是否存在异步模式?我认为这可以解决这个延迟问题?在实践中,我尝试了异步解决方案......但没有成功:由于 MediaPlayer 生成音频而导致的实时问题仍然存在:音频生成成本从 50 毫秒到 80 毫秒,而主循环处理每 110 毫秒运行一次。每个音频生成都会干扰主处理执行。

而且,在每个周期性任务(速率:110 毫秒)中,我可以播放几个这样的声音。并且,在一条轨迹中,有多达 6 个声音激活,总共需要大约 300 毫秒(而主要循环任务需要 110 毫秒)

问题:

如何提高 NativeAudio 类的性能(特别是 play() 方法及其调用会产生实时问题: setDataSource(...)、prepare() 和 start() )?


解决方案

主要处理必须是“同步”方法,以确保完整的处理将运行,而不会出现任何音频中断。

此外,生成声音的每个完整处理都在专用线程下进行,并使用 Thread.MIN_PRIORITY 优先级进行定义。

现在,主处理每 110 毫秒运行一次,并且在开始时不会受到任何音频生成的干扰。显示屏非常“柔和”(不再有抖动现象)。

只有一个小问题:当音频seDataSource()、start()或prepare()方法开始时,似乎下一个主要处理应等待方法结束后再开始(TBC)

我希望这个解决方案可以帮助其他人。它适用于使用 MediaPlayer 生成音频的任何情况。


解决方案的JAVA代码

主要处理定义如下:

public static ***synchronized*** void mainProcessing() {
// the method handles the impacts, explosions, sounds, movings, ... , in other words almost the entiere game  .. in a CRITICAL SECTION
}

/****************************************************/

在实现“NativeAudioService”的NativeAudio类中:

    @Override
        public void play() {
            if (bSon) {
                Task<Void> taskSound = new Task<Void>() {

                @Override
                protected Void call() throws Exception {
                generateSound(); 
                return null;
                }}; 

              Thread threadSound = new Thread(taskSound);
              threadSound.setPriority(Thread.MIN_PRIORITY);
              threadSound.start();
            } 
        }


  /****************************************************/    
  private void generateSound() {
        currentPosition = 0;
        nbTask++;
        noTask = nbTask;
        try {
            if (mediaPlayer != null) {
                stop();
            }
            mediaPlayer = new MediaPlayer();

            AssetFileDescriptor afd = FXActivity.getInstance().getAssets().openFd(audioFileName);

            mediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());      

            mediaPlayer.setAudioStreamType(AudioManager.STREAM_RING);

            float floatLevel = (float) audioLevel;
            mediaPlayer.setVolume(floatLevel, floatLevel);

            mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
                @Override
                public void onCompletion(MediaPlayer mediaPlayer) {
                    if (nbCyclesAudio >= 1) {
                              mediaPlayer.start();
                          nbCyclesAudio--;
                        } else {
                        mediaPlayer.stop(); 
                        mediaPlayer.release(); // for freeing the resource - useful for the phone codec 
                        mediaPlayer = null;
                        }
                }
            });

            mediaPlayer.prepare();

            mediaPlayer.start();
            nbCyclesAudio--;

        } catch (IOException e) {
            }
      }

我对你的实现做了一些改变提及,假设您有一堆短音频文件要播放,并且您希望在很短的时间内按需播放它们。基本上我会创建AssetFileDescriptor所有文件一次,而且我将使用同一个MediaPlayer一直都有实例。

设计遵循 Charm Down 的图案library,所以你需要保留下面的包名称。

EDIT

根据 OP 的反馈,我更改了实现方式,为每个音频文件配备一个 MediaPlayer,这样您就可以随时播放其中任何一个。

  1. 源码包/Java:

包裹:com.gluonhq.charm.down.plugins

AudioService界面

public interface AudioService {
    void addAudioName(String audioName);
    void play(String audioName, double volume);
    void stop(String audioName);
    void pause(String audioName);
    void resume(String audioName);
    void release();
}

AudioServiceFactory class

public class AudioServiceFactory extends DefaultServiceFactory<AudioService> {

    public AudioServiceFactory() {
        super(AudioService.class);
    }

}
  1. Android/Java 包

包裹:com.gluonhq.charm.down.plugins.android

AndroidAudioService class

public class AndroidAudioService implements AudioService {

    private final Map<String, MediaPlayer> playList;
    private final Map<String, Integer> positionList;

    public AndroidAudioService() {
        playList = new HashMap<>();
        positionList = new HashMap<>();
    }

    @Override
    public void addAudioName(String audioName) {
        MediaPlayer mediaPlayer = new MediaPlayer();
        mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
        mediaPlayer.setOnCompletionListener(m -> pause(audioName)); // don't call stop, allows reuse
        try {
            mediaPlayer.setDataSource(FXActivity.getInstance().getAssets().openFd(audioName));
            mediaPlayer.setOnPreparedListener(mp -> {
                System.out.println("Adding  audio resource " + audioName);
                playList.put(audioName, mp);
                positionList.put(audioName, 0);
            });
            mediaPlayer.prepareAsync();
        } catch (IOException ex) {
            System.out.println("Error retrieving audio resource " + audioName + " " + ex);
        }

    }

    @Override
    public void play(String audioName, double volume) {
        MediaPlayer mp = playList.get(audioName);
        if (mp != null) {
            if (positionList.get(audioName) > 0) {
                positionList.put(audioName, 0);
                mp.pause();
                mp.seekTo(0);
            }
            mp.start();
        }

    }

    @Override
    public void stop(String audioName) {
        MediaPlayer mp = playList.get(audioName);
        if (mp != null) {
            mp.stop();
        }
    }

    @Override
    public void pause(String audioName) {
        MediaPlayer mp = playList.get(audioName);
        if (mp != null) {
            mp.pause();
            positionList.put(audioName, mp.getCurrentPosition());
        }
    }

    @Override
    public void resume(String audioName) {
        MediaPlayer mp = playList.get(audioName);
        if (mp != null) {
            mp.start();
            mp.seekTo(positionList.get(audioName));
        }
    }

    @Override
    public void release() {
        for (MediaPlayer mp : playList.values()) {
            if (mp != null) {
                mp.stop();
                mp.release();
            }
        }

    }

}
  1. Sample

我添加了五个短音频文件(来自here),并向我的主视图添加了五个按钮:

@Override
public void start(Stage primaryStage) throws Exception {

    Button play1 = new Button("p1");
    Button play2 = new Button("p2");
    Button play3 = new Button("p3");
    Button play4 = new Button("p4");
    Button play5 = new Button("p5");
    HBox hBox = new HBox(10, play1, play2, play3, play4, play5);
    hBox.setAlignment(Pos.CENTER);

    Services.get(AudioService.class).ifPresent(audio -> {

        audio.addAudioName("beep28.mp3");
        audio.addAudioName("beep36.mp3");
        audio.addAudioName("beep37.mp3");
        audio.addAudioName("beep39.mp3");
        audio.addAudioName("beep50.mp3");

        play1.setOnAction(e -> audio.play("beep28.mp3", 5));
        play2.setOnAction(e -> audio.play("beep36.mp3", 5));
        play3.setOnAction(e -> audio.play("beep37.mp3", 5));
        play4.setOnAction(e -> audio.play("beep39.mp3", 5));
        play5.setOnAction(e -> audio.play("beep50.mp3", 5));
    });

    Scene scene = new Scene(new StackPane(hBox), Screen.getPrimary().getVisualBounds().getWidth(), 
                        Screen.getPrimary().getVisualBounds().getHeight());
    primaryStage.setScene(scene);
    primaryStage.show();
}

@Override
public void stop() throws Exception {
    Services.get(AudioService.class).ifPresent(AudioService::release);
}

准备步骤在应用程序启动并实例化服务时发生,因此稍后播放任何音频文件时,不会有任何延迟。

我还没有检查在添加多个带有大音频文件的媒体播放器时是否存在任何内存问题,因为这不是最初的情况。也许缓存策略在这种情况下会有所帮助(请参阅 Gluon Charm Down 中的 CacheService)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android 版 Javafx 的音频性能(MediaPlayer 和 NativeAudioService) 的相关文章

  • Android SoundPool 堆限制

    我正在使用 SoundPool 加载多个声音剪辑并播放它们 据我所知 它的功能 100 正确 但在 load 调用期间 我的日志中充斥着以下内容 06 09 11 30 26 110 ERROR AudioCache 23363 Heap
  • 在我的Android中,当其他应用程序想要录制音频时如何停止录音?

    在我的应用程序中 服务通过 AudioRecord 持续录制音频 当我的应用程序运行时 其他与音频记录相关的应用程序 例如 Google 搜索 无法工作 如何知道何时有其他应用想要录制音频 以便我可以停止录制以释放资源 答案是MediaRe
  • 有没有更简单的方法来处理复选框?

    在 vb net 中 我有一个包含一组四个复选框的表单 每个复选框都表示 选中时 用户想要向其订单添加特殊指令 代码如下所示 If SpecialInstruction1CheckBox Checked Then AddSpecialIns
  • React Native:加载图像后应用程序性能不佳

    加载图像似乎没有问题 但是加载完毕后就出现问题了 在我的应用程序中 我在整个游戏中一张一张地加载卡片图像 一旦我加载了 40 张卡片图像 整个应用程序就会变得很慢 它总是发生在第 40 个图像处 当我在第 40 个图像之后继续加载更多卡片图
  • R 数据结构的运算效率

    我想知道是否有任何关于操作效率的文档R 特别是那些与数据操作相关的 例如 我认为向数据框添加列是有效的 因为我猜您只是向链接列表添加一个元素 我想添加行会更慢 因为向量保存在数组中C level你必须分配一个新的长度数组n 1并将所有元素复
  • 在什么情况下 do-while 比 while 更高效?

    while 与 do while while 和 do while 在功能上是等效的当块为空时 虽然 while 看起来更自然 do while keepLooping while keepLooping 使用空块的 while do wh
  • getAudioInputStream() 期间标记/重置异常

    我发布了问题的修复 如下所述 但无法确认它是否解决了问题 使用 Java 7 的人会尝试以下 Applet 并报告吗 我们将非常感激 音频混合器演示 http www hexara com VSL AudioMixerDemoWarOfWo
  • JavaFX ScrollPane 样式

    我正在尝试在 JavaFX 中创建一个黑白 ScrollPane 我已经创建了一个 CSS 文件 它工作得很好 除了这个小方块 无论我尝试什么 我都无法将其变黑 这是我的 CSS 文件 scroll pane fx background c
  • 如果 jQuery 脚本是在所有页面 HTML 之后加载的,那么它们还需要 $(document).ready 吗?

    如果我在所有页面 HTML 下方加载 jQuery 脚本 我是否还需要等待 document ready能够使用jQuery来查找页面中的元素 否 因为文档已经加载 Dom 从上到下加载 我个人喜欢把所有的js放在页面底部而不是放在头部 然
  • SSL 速度:128 位与 256 位

    我决定使用 SSL 加密我的整个网站 即使实际上只有部分网站是必要的 最终结果是该网站现在有点慢 所以 我的问题是 我是否应该只加密网站的会员部分 请记住我在首页上有登录表单 我是否应该将加密降低到 128 位 如果站点总体较小 速度差异是
  • 计算元组中与模式匹配的元素

    我有一个矩阵m我想计算零的数量 m 2 0 2 2 4 4 5 4 0 9 4 8 2 2 0 0 我当前的代码如下 def zeroCount M return item for row in M for item in row coun
  • C#:将音频文件从服务器流式传输到客户端

    我目前正在编写一个应用程序 该应用程序将允许用户安装某种形式的应用程序 可能是 Windows 服务 该应用程序将在其 PC 上打开一个端口 并在硬盘上指定一个特定的目的地 然后能够流式传输 mp3 文件 然后 我将有另一个应用程序 该应用
  • 使用 VBScript 切换当前活动声音设备?

    我想在连接到我的计算机 Windows 7 32 位 的两个音频设备之间切换 我看了一下question https stackoverflow com questions 35709 change active sound card on
  • 捕获当前正在播放的声音

    是否可以捕获计算机上当前播放的声音 如果能够将其保存为 mp3 就好了 但我认为这样做会存在一些法律问题 所以 wav 也可以 我环顾四周 有人建议使用虚拟音频线之类的东西 在 C 中捕获声音输出 https stackoverflow c
  • Android:了解 OnDrawFrame、FPS 和 VSync (OpenGL ES 2.0)

    一段时间以来 我在 Android 游戏中遇到了运动精灵间歇性 卡顿 的情况 这是一个非常简单的 2D OpenGL ES 2 0 游戏 这是一个持续存在的问题 我已经多次重新访问过 在我的游戏循环中 我有 2 个 计时器 一个用于记录前一
  • 在 unix 中编译 dhrystone 时出错

    我是使用基准测试和 makefile 的新手 我已经从下面的链接下载了 Dhrystone 基准测试 我正在尝试编译它 但我遇到了奇怪的错误 我尝试解决它 但没有成功 有人可以帮助我运行 dhrystone 基准测试吗 以下是我尝试编译的两
  • 使用 Google Translate API 获取单词的发音

    我正在尝试将法语单词的发音保存到 wav 或 mp3 文件中 我想知道 Google Translate API 上是否有任何地方 因为它有发音功能 可以让我实现这个目标 其他库也可以工作 自从提出这个问题以来 从谷歌翻译中 抓取 MP3
  • 在python中将数据库表写入文件的最快方法

    我正在尝试从数据库中提取大量数据并将其写入 csv 文件 我正在尝试找出最快的方法来做到这一点 我发现在 fetchall 的结果上运行 writerows 比下面的代码慢 40 with open filename a as f writ
  • 为什么python+sqlite3特别慢?

    我尝试使用 Python 2 7 4 sqlite3 和 Firefox SQLite Manager 0 8 0 处理对同一数据库的相同请求 在小型数据库 8000 条记录 上 Python 和 Firefox 都运行得很快并且给出了相同
  • 记录类名、方法名和行号的性能影响

    我正在我的 java 应用程序中实现日志记录 以便我可以调试应用程序投入生产后可能出现的潜在问题 考虑到在这种情况下 人们不会奢侈地使用 IDE 开发工具 以调试模式运行事物或单步执行完整代码 因此在每条消息中记录类名 方法名和行号将非常有

随机推荐