Java sound - problém prehrať celý klip explózie na Linuxe

Jano7

Zdravím,

vyvíjam jednoduchú hru v Jave; o zvuk sa má postarať javax.sound.sampled.AudioSystem a
javax.sound.sampled.Clip. Prehrávajú sa WAV súbory. Problém je, že na Linuxe (mám Ubuntu 16.04) sa zvuk síce
prehraje, ale preruší sa asi v polke, t.j. neprahraje sa mi celý zvukový efekt. Skúšal som to na Windowse,
tam to funguje v poriadku. Zaujímavé je tiež, že ak nechám prehrať efekt viac krát pomocou loop(),
tak sa prehraje zvuk OK.

Používam efekty zo stránky https://www.freesoundeffects.com/free-sounds/explosion-10070/

Nevie niekto, kde by mohol byť problém?

Nasleduje jednoduchý kód, ktorý demonštruje problém.

Kód: [Vybrat]
package com.zetcode;

import java.awt.EventQueue;
import java.awt.FlowLayout;
import javax.swing.JButton;
import javax.swing.JFrame;

public class GameSoundEx extends JFrame {

    private final GameSound gameSound;

    public GameSoundEx() {
       
        gameSound = new GameSound();
        initUI();
    }
   
    private void initUI() {
       
        // FlowLayout only for testing purposes; never use
        setLayout(new FlowLayout(5));

        JButton button = new JButton("Play explosion");
        button.addActionListener(e -> gameSound.playExplosion());
        add(button);
    }

    private static void createAndShowGUI() {
       
        GameSoundEx game = new GameSoundEx();
       
        game.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        game.setTitle("Testing Java sound");
        game.setSize(300, 200);
        game.setLocationRelativeTo(null);
        game.setVisible(true);
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(() -> createAndShowGUI());
    }
}


Kód: [Vybrat]
package com.zetcode;

import java.io.IOException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;

public class GameSound {

    private Clip explosion;

    public GameSound() {
       
        loadSoundFile();
    }
   
    private void loadSoundFile() {

        try {
            URL url = this.getClass().getResource(
                    "/resources/explosion.wav");
           
            AudioInputStream ais = AudioSystem.getAudioInputStream(url);
            explosion = AudioSystem.getClip();
            explosion.open(ais);
        } catch (LineUnavailableException | IOException |
                UnsupportedAudioFileException ex) {
            Logger.getLogger(GameSound.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void playExplosion() {
       
        if (explosion.isRunning()) {
            explosion.stop();
        }
       
        explosion.setFramePosition(0);
//        explosion.loop(1);
        explosion.start();
    }
}


anonym

Re:Java sound - problém prehrať celý klip explózie na Linuxe
« Odpověď #1 kdy: 18. 06. 2018, 00:57:42 »
Synku, ty jsi na mě teď uvalil pochmurnou náladu. Já jsem nikdy v Javě se zvukem nedělal, ale řeknu ti, v čem je na 80% problém: v tom, že k něčemu takovému používáš standardní Java knihovnu. Uvědom si, že ta STD lib je 20 let starý shit. Dobře, sorry za sprosté slovo, věci v STD lib jsou prostě zastaralé, protože se udržuje zpětná kompatibilita. Máš na výběr, buďto v STD lib budou redundantní věci, nebo budou zastaralé. To že něco v STD lib je ještě neznamená že se to dá rozumně používat. Pokud chceš dělat opravdu s Javou, potřebuješ Maven a vždy mrknout, co je k dispozici pro danou věc v komunitních balíčcích - a jsou tam často luxusní knihovny. Takto se dělá s Javou.

anonym

Re:Java sound - problém prehrať celý klip explózie na Linuxe
« Odpověď #2 kdy: 18. 06. 2018, 01:02:14 »
Ikdyž teď jsem googlil a ta STD knihovna pro zvuky by měla být ok. Tak jinak, jedná se zřejmě o bug. Vsadím se, že používáš na Linuxu OpenJDK. Začal bych hledat zakopaného psa tam - nainstaluj si plnotučné JDK od Oraclu.

technomaniak

Re:Java sound - problém prehrať celý klip explózie na Linuxe
« Odpověď #3 kdy: 18. 06. 2018, 05:58:49 »
Nevie niekto, kde by mohol byť problém?
A co přehrávání přes JavaFX s mp3, děje se to taky?

Re:Java sound - problém prehrať celý klip explózie na Linuxe
« Odpověď #4 kdy: 18. 06. 2018, 13:47:05 »
Přehrává se to na pozadí v jiném vlákně, neukončuje vám něco to vlákno nebo přehrávání v něm?

Pokud spustím ukázku pomocí jshell (v java9):

Kód: [Vybrat]
$ jshell Sample.jsh
a Sample.jsh obsahuje

Kód: [Vybrat]
import javax.sound.sampled.*;

Clip clip = AudioSystem.getClip();
clip.open(AudioSystem.getAudioInputStream(new File("explosion.wav")));
clip.setFramePosition(0);
clip.start();
clip.loop(2);

tak fo funguje v pořádku (přehraje se to 3x). Zrovna tak funguje

Kód: [Vybrat]
$ javac Sample.java; java Sample;
a Samaple.java obsahuje

Kód: [Vybrat]
import javax.sound.sampled.*;
import java.io.*;

class Sample {

public static void main(String[] args) throws Throwable {
Clip clip = AudioSystem.getClip();
clip.open(AudioSystem.getAudioInputStream(new File("explosion.wav")));
clip.setFramePosition(0);
clip.start();
clip.loop(2);
Thread.sleep(2500*3);
clip.flush();
clip.close();
}

}

Akorát tam musím počkat pomocí sleep, aby se program neukončil předčasně (audio běží asi v daemon vlákně, takže neblokuje ukončení programu).


Alone

Re:Java sound - problém prehrať celý klip explózie na Linuxe
« Odpověď #5 kdy: 18. 06. 2018, 14:04:22 »
Ja nevim, ale nebylo by vhodnejsi podobny dotaz umistit na stackoverflow? Ne ze bych chtel hanit mistni lidi, ale preci jenom je jich tam vyrazne vice...

Jano7

Re:Java sound - problém prehrať celý klip explózie na Linuxe
« Odpověď #6 kdy: 18. 06. 2018, 16:12:26 »
Citace
To že něco v STD lib je ještě neznamená že se to dá rozumně používat. Pokud chceš dělat opravdu s Javou, potřebuješ Maven a vždy mrknout, co je k dispozici pro danou věc v komunitních balíčcích - a jsou tam často luxusní knihovny.

Pozeral som Java sound knižnice, našiel som dve: beads a Jsyn. Beads nie je v Maven repozitáry a na svoje stránke píšu, že pri práci s UI môžu nastať problémy. JSyn je napohľad veľmi komplikovaný, ktovie ako je to tiež s integráciou so Swingom. Preto by som radšej zostal pri štandardnej knižnici.

Citace
Vsadím se, že používáš na Linuxu OpenJDK. Začal bych hledat zakopaného psa tam - nainstaluj si plnotučné JDK od Oraclu.

Používam Oracle JDK.

Citace
A co přehrávání přes JavaFX s mp3, děje se to taky?

Otestoval som to aj s JavaFx, tam sa používa javafx.scene.media.Media a javafx.scene.media.MediaPlayer.  Všetko fungovalo OK.

Citace
Ja nevim, ale nebylo by vhodnejsi podobny dotaz umistit na stackoverflow? Ne ze bych chtel hanit mistni lidi, ale preci jenom je jich tam vyrazne vice...

https://stackoverflow.com/questions/50797938/trouble-playing-the-whole-audio-clip

Problém so SO je ten, že na náročnejšie otázky ti len zriedka príde nejaká kvalifikovaná odpoveď, pokiaľ tam nedáte tučné bounty. S tým nemám problém, akurát ak ti nepríde správna odpoveď, tak to bounty stratíš. Navyše, bolo by zrejme treba vytvoriť ďalšiu presnejšiu otázku, lebo som medzitým viac zúžil problém, ale obávam sa, že by mi novú otázku zatvorili, ako duplikát. Majú tam také svoje prísnejšie pravidlá.

Citace
Přehrává se to na pozadí v jiném vlákně, neukončuje vám něco to vlákno nebo přehrávání v něm?

Nemalo by mi tam nič ukončovať. Vy tam používate loop, a ako som už spomenul, pri loope sa tie klipy prehrajú OK. Problém nastane, ak sa prehráva iba raz.


Re:Java sound - problém prehrať celý klip explózie na Linuxe
« Odpověď #7 kdy: 18. 06. 2018, 20:27:42 »
Citace
Přehrává se to na pozadí v jiném vlákně, neukončuje vám něco to vlákno nebo přehrávání v něm?
Nemalo by mi tam nič ukončovať. Vy tam používate loop, a ako som už spomenul, pri loope sa tie klipy prehrajú OK. Problém nastane, ak sa prehráva iba raz.

Samozřejmě jsem to zkusil bez loopu a to také fungovalo.

Zkusil jsem i openjdk 8, také funguje. Mám linux Mint 18.3.

Na ten clip lze zaregistrovat event listener, zkusil bych tím zjistit, zda to něco neukončuje, normálně to oddebugovat.

Jano7

Re:Java sound - problém prehrať celý klip explózie na Linuxe
« Odpověď #8 kdy: 19. 06. 2018, 00:47:44 »
Citace

Samozřejmě jsem to zkusil bez loopu a to také fungovalo.
Zkusil jsem i openjdk 8, také funguje. Mám linux Mint 18.3.
Na ten clip lze zaregistrovat event listener, zkusil bych tím zjistit, zda to něco neukončuje, normálně to oddebugovat.

Myslím si, že sa mi už podarilo problém lokalizovať. Ide o to, že clipy s určitou veľkosťou (okolo 2 sekúnd) sa neprehrajú na Linuxe celé. Tie, ktoré sú o niečo dlhšie, tie sa prehrajú v poriadku.  Teda z tej spomínanej stránky,
https://www.freesoundeffects.com/free-sounds/explosion-10070/
explózia 7 sa neprehrá dobre, explózia 11 sa prehrá v poriadku. Na moju smolu väčšina clipov na tej stránke je v rozmedzí tej chyby, tak mi to dlho trvalo, kým som sa to dovtípil. Tá chyba sa prejavuje na Linuxe, je jedno v akom programe, či v plnej hre v game cykle, pri stlačení buttonu, alebo vo Vašom testovacom programe. Na Windowse sa chyba neprejavuje.

V knihe Killer Java programming, ktorá vyšla v češtine ako Programování dokonalých her, (z roku 2005!) sa presne o podobnej chybe píše. A autor tiež píše, že ak dáme loop tak sa to prehrá OK.

Ďalej, pri použití klipov so „správnou“ dĺžkou sa na Linuxe (Windows je OK)po čase pri hre začnú vyhadzovať chyby typu
javax.sound.sampled.LineUnavailableException: line with format PCM_UNSIGNED 11025.0 Hz, 8 bit, mono, 1 bytes/frame,  not supported.

Zrejme je to API zabugované a veľa s tým nedokážeme spraviť.

dustin

Re:Java sound - problém prehrať celý klip explózie na Linuxe
« Odpověď #9 kdy: 19. 06. 2018, 01:24:30 »
Zřejmě ta knihovna nepodporuje všechny možné PCM varianty. Nemáš nějakou jinou knihovnu, kterou bys mohl stream průběžně konvertovat do něčeho běžného (např. klasika 44.1/16/stereo) a to teprve předhodit java soundu?

PeterK

Re:Java sound - problém prehrať celý klip explózie na Linuxe
« Odpověď #10 kdy: 19. 06. 2018, 06:17:10 »
Workaround hlavneho problemu: skonvertuj vsetky efekty na jednotnu kvalitu a doplni ich tichom na 2 sekundy

Medzi OpenJDK a Oracle JDK je rozdiel v nativnej implementacii prehravania (java9+ uz nepoznam)
- OpenJDK pouzival ALSA
- Oracle pouzival OSS (preto nemozes mixovat rozne frekvencie samplov; problem LineUnavailableException by sa mal dat riesit zatvorit/otvorit zvukovku)


dustin

Re:Java sound - problém prehrať celý klip explózie na Linuxe
« Odpověď #11 kdy: 19. 06. 2018, 06:52:15 »
Oracle java určitě používá alsu, oss už je v linuxu hluboká historie. U tebe se zřejmě ještě bude míchat  problém s nedostupnou zvukovkou, která je již otevřená/používaná pulseaudiem. Ověř si, že pokud máš PA, java ti používá alsí zařízení default, které je nadefinované pluginem pulse - tím pádem pojede vše přes PA a zvukovka bude vždy k dispozici. 

Re:Java sound - problém prehrať celý klip explózie na Linuxe
« Odpověď #12 kdy: 19. 06. 2018, 13:42:05 »
Citace

Samozřejmě jsem to zkusil bez loopu a to také fungovalo.
Zkusil jsem i openjdk 8, také funguje. Mám linux Mint 18.3.
Na ten clip lze zaregistrovat event listener, zkusil bych tím zjistit, zda to něco neukončuje, normálně to oddebugovat.

Myslím si, že sa mi už podarilo problém lokalizovať. Ide o to, že clipy s určitou veľkosťou (okolo 2 sekúnd) sa neprehrajú na Linuxe celé. Tie, ktoré sú o niečo dlhšie, tie sa prehrajú v poriadku.  Teda z tej spomínanej stránky,
https://www.freesoundeffects.com/free-sounds/explosion-10070/
explózia 7 sa neprehrá dobre, explózia 11 sa prehrá v poriadku. Na moju smolu väčšina clipov na tej stránke je v rozmedzí tej chyby, tak mi to dlho trvalo, kým som sa to dovtípil. Tá chyba sa prejavuje na Linuxe, je jedno v akom programe, či v plnej hre v game cykle, pri stlačení buttonu, alebo vo Vašom testovacom programe. Na Windowse sa chyba neprejavuje.

V knihe Killer Java programming, ktorá vyšla v češtine ako Programování dokonalých her, (z roku 2005!) sa presne o podobnej chybe píše. A autor tiež píše, že ak dáme loop tak sa to prehrá OK.

Ďalej, pri použití klipov so „správnou“ dĺžkou sa na Linuxe (Windows je OK)po čase pri hre začnú vyhadzovať chyby typu
javax.sound.sampled.LineUnavailableException: line with format PCM_UNSIGNED 11025.0 Hz, 8 bit, mono, 1 bytes/frame,  not supported.

Zrejme je to API zabugované a veľa s tým nedokážeme spraviť.

Aha, tak máte pravdu, klip 7 se mi také nedohraje do konce. To je dost hloupý bug...  :(

Jano7

Re:Java sound - problém prehrať celý klip explózie na Linuxe
« Odpověď #13 kdy: 19. 06. 2018, 13:54:54 »
Vdaka za tipy. Pozriem si to. Som tyzden mimo, potom sa tomu povenujem. Ziadnu kniznicu na konverziu nemam, so zvukom som doteraz nepracoval. Mate niekto tip na tool pre konverzie resp. pridanie toho ticha?

Co sa mysli pod zatvorit/otvorit zvukovku?

dustin

Re:Java sound - problém prehrať celý klip explózie na Linuxe
« Odpověď #14 kdy: 19. 06. 2018, 14:34:30 »
S audio javou nemám žádné zkušenosti, nicméně.... používáš správně metodu drain https://docs.oracle.com/javase/7/docs/api/javax/sound/sampled/DataLine.html#drain() ? V ukázce p. Němečka vidím metodu flush, která data zahodí. Nemůže se stát, že po nějakém zastavení máš ještě audio data v bufferu, ale nezavoláš na ně drain, takže se ta data nikdy nepřehrají?

Ticho bych tam nedával, IMO to nějak jít musí. Takový minecraft hraje dobře :-)

V dokumentaci píší, že si máš zjistit podporované formáty https://docs.oracle.com/javase/7/docs/api/javax/sound/sampled/DataLine.Info.html#getFormats() . Pokud máš jiný, pak si jej musíš zkonvertovat sám, ať už v javě /třeba https://github.com/a-schild/jave2 nebo https://github.com/corballis/sox-wrapper-java nebo něco čistě javovského). Min. tím soxem můžeš dělat zcela cokoliv.

Citace
Co sa mysli pod zatvorit/otvorit zvukovku?

Drtivá většina dnešních zvukovek podporují jen jeden stream, tj. jakmile si ji jeden proces zabere pro sebe (otevře zařízení, zjistíš výpisem sudo lsof /dev/snd/* ), jiný se k ní už nedostane a dle googlu java v takovém případě také hlásí tu chybu nepodporovaného formátu linky. Proto se používají mixery/zvukové servery typu pulseaudio, plugin dmix apod. Takže buď ten formát tvé zvukové zařízení (softwarové, hw je ještě daleko) nepodporuje, nebo je zvukovka už obsazená. Nejjednodušší je jet přes pulseaudio, ale to asi oracle java ještě napřímo neumí (ale může použít alsí zařízení "default", který je ve spoustě distribucí rovnou namířené pluginem pulse na pulseaudio).