Изменение громкости с помощью JLayer

У меня очень специфическая проблема с JLayer в моем проекте Music-Player. Я хочу включить что-то для регулировки громкости, но, похоже, это не так просто реализовать.

Это потому, что JLayer не поддерживает его сам по себе. Я исключил класс Player из своего проекта и изменил некоторые методы, и он отлично работает для воспроизведения mp3. Для регулировки громкости я добавил в класс Player этот метод:

public boolean setGain(float newGain) {
        if (audio instanceof JavaSoundAudioDevice) {
            System.out.println("InstanceOf");
            JavaSoundAudioDevice jsAudio = (JavaSoundAudioDevice) audio;
            try {
                jsAudio.write(null, 0, 0);
            } catch (JavaLayerException ex) {
                ex.printStackTrace();
            }
            return jsAudio.setLineGain(newGain);
        }
        return false;
    }

Затем я извлек JavaSoundAudioDevice, декомпилировал его и изменил:

public boolean setLineGain(float gain)
{
    System.out.println("Vor Source");
    if (source != null)
    {
        System.out.println("Nach Source");
        FloatControl volControl = (FloatControl) source.getControl(FloatControl.Type.MASTER_GAIN);
        float newGain = Math.min(Math.max(gain, volControl.getMinimum()), volControl.getMaximum());
        volControl.setValue(newGain);
        return true;
    }
    return false;
}

А также:

public void createSource() throws JavaLayerException {
        Throwable t = null;
        try {
            Line line = AudioSystem.getLine(getSourceLineInfo());
            if (line instanceof SourceDataLine) {
                source = (SourceDataLine) line;
                //source.open(fmt, millisecondsToBytes(fmt, 2000));
                source.open(fmt);
                /*
                if (source.isControlSupported(FloatControl.Type.MASTER_GAIN))
                {
                FloatControl c = (FloatControl)source.getControl(FloatControl.Type.MASTER_GAIN);
                c.setValue(c.getMaximum());
                }*/
                source.start();
            }
        } catch (RuntimeException ex) {
            t = ex;
        } catch (LinkageError ex) {
            t = ex;
        } catch (LineUnavailableException ex) {
            t = ex;
        }
        if (source == null) {
            throw new JavaLayerException("cannot obtain source audio line", t);
        }
    }

Но уже реализованный метод createSource не работает. На линии

Line line = AudioSystem.getLine(getSourceLineInfo());

Я всегда получаю исключение IllegalArgumentException:

java.lang.IllegalArgumentException: No line matching interface SourceDataLine supporting format PCM_SIGNED 0.0 Hz, 16 bit, 0 channels, 0 bytes/frame, little-endian is supported.
at javax.sound.sampled.AudioSystem.getLine(AudioSystem.java:479)
at javazoom.jl.player.JavaSoundAudioDevice.createSource(JavaSoundAudioDevice.java:80)
at javazoom.jl.player.JavaSoundAudioDevice.writeImpl(JavaSoundAudioDevice.java:119)
at javazoom.jl.player.AudioDeviceBase.write(Unknown Source)
at MusikPlayer.Erweiterungen.Players.MyPlayer.setGain(MyPlayer.java:192)
at MusikPlayer.PlayerTest.main(PlayerTest.java:21)
Exception in thread "main" java.lang.IllegalArgumentException: No line matching interface SourceDataLine supporting format PCM_SIGNED 0.0 Hz, 16 bit, 0 channels, 0 bytes/frame, little-endian is supported.
    at javax.sound.sampled.AudioSystem.getLine(AudioSystem.java:479)
    at javazoom.jl.player.JavaSoundAudioDevice.createSource(JavaSoundAudioDevice.java:80)
    at javazoom.jl.player.JavaSoundAudioDevice.writeImpl(JavaSoundAudioDevice.java:119)
    at javazoom.jl.player.AudioDeviceBase.write(Unknown Source)
    at MusikPlayer.Erweiterungen.Players.MyPlayer.decodeFrame(MyPlayer.java:161)
    at MusikPlayer.Erweiterungen.Players.MyPlayer.play(MyPlayer.java:87)
    at MusikPlayer.Erweiterungen.Players.MyPlayer.play(MyPlayer.java:66)
    at MusikPlayer.PlayerTest.main(PlayerTest.java:22)

Кто-нибудь знает, почему это происходит? Кто-нибудь знает, как решить эту проблему?


person Schesam    schedule 29.01.2016    source источник
comment
Вы начали с ccm.libs.javazoom.jl.player.JavaSoundAudioDevice на GitHub? Похоже, что это открытый исходный код, поэтому я не вижу необходимости что-либо декомпилировать.   -  person TT.    schedule 29.01.2016
comment
Насколько я сравнивал, оба класса одинаковы. Я только что декомпилировал класс из моей библиотеки jlayer, которая у меня есть: javazoom.net/javalayer/sources. html   -  person Schesam    schedule 29.01.2016
comment
Загрузите проект с GitHub и отладьте его. Эта проблема требует глубокого знания этой библиотеки. В этом преимущество открытого исходного кода: вы можете найти проблему самостоятельно.   -  person TT.    schedule 29.01.2016
comment
Если я изменю формат, используемый в getSourceDataInfo(), с fmt = new AudioFormat(decoder.getOutputFrequency(), 16, decoder.getOutputChannels(), true, false); на new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, baseFormat.getSampleRate(), 16, baseFormat.getChannels(), baseFormat.getChannels() * 2, baseFormat.getSampleRate(), false);, я не получу исключение, но я ничего не услышу из аудиофайла, только раздражающий крик. Любые предположения для AudioFormat, которые могут работать здесь?   -  person Schesam    schedule 29.01.2016


Ответы (1)


Ну, я решил это. Этот тестовый класс используется:

public class PlayerTest {

public static void main(String[] args) {
    try {
        File f = new File("D:\\Musik\\Musik-Oberordner\\Favoriten\\06-ich_und_ich_-_so_soll_es_bleiben.mp3");
        MyPlayer player = new MyPlayer(new FileInputStream(f));
        player.setGain(-30f);
        player.play();
    } catch (JavaLayerException | FileNotFoundException ex) {
        ex.printStackTrace();
    }

}

установленное усиление будет регулировать громкость от -80,0f до 6f.

Измененный JavaSoundAudioDevice:

package javazoom.jl.player;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.Line;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;

import javazoom.jl.decoder.Decoder;
import javazoom.jl.decoder.JavaLayerException;

/**
 * The <code>JavaSoundAudioDevice</code> implements an audio device by using the
 * JavaSound API.
 *
 * @since 0.0.8
 * @author Mat McGowan
 */
public class JavaSoundAudioDevice extends AudioDeviceBase {

private SourceDataLine source = null;
private AudioFormat fmt = null;
private byte[] byteBuf = new byte[4096];

protected void setAudioFormat(AudioFormat fmt0) {
    fmt = fmt0;
}

protected AudioFormat getAudioFormat() {
//        if (fmt == null) {
    fmt = new AudioFormat(44100,
            16,
            2,
            true,
            false);

    return fmt;
}

protected DataLine.Info getSourceLineInfo() {
    AudioFormat fmt = getAudioFormat();
    //DataLine.Info info = new DataLine.Info(SourceDataLine.class, fmt, 4000);
    DataLine.Info info = new DataLine.Info(SourceDataLine.class, fmt);
    return info;
}

public void open(AudioFormat fmt) throws JavaLayerException {
    if (!isOpen()) {
        setAudioFormat(fmt);
        openImpl();
        setOpen(true);
    }
}

public boolean setLineGain(float gain) {
    System.out.println("Vor Source");
    if (source != null) {
        System.out.println("Nach Source");
        FloatControl volControl = (FloatControl) source.getControl(FloatControl.Type.MASTER_GAIN);
        float newGain = Math.min(Math.max(gain, volControl.getMinimum()), volControl.getMaximum());
        volControl.setValue(newGain);
        return true;
    }
    return false;
}

public void openImpl()
        throws JavaLayerException {
}

// createSource fix.
public void createSource() throws JavaLayerException {
    Throwable t = null;
    try {
        Line line = AudioSystem.getLine(getSourceLineInfo());
        if (line instanceof SourceDataLine) {
            source = (SourceDataLine) line;
            //source.open(fmt, millisecondsToBytes(fmt, 2000));
            source.open(fmt);

//                if (source.isControlSupported(FloatControl.Type.MASTER_GAIN))
//                {
//                    System.out.println("Control");
//                FloatControl c = (FloatControl)source.getControl(FloatControl.Type.MASTER_GAIN);
//                c.setValue(c.getMinimum());
//                }
                source.start();
        }
    } catch (RuntimeException ex) {
        t = ex;
    } catch (LinkageError ex) {
        t = ex;
    } catch (LineUnavailableException ex) {
        t = ex;
    }
    if (source == null) {
        throw new JavaLayerException("cannot obtain source audio line", t);
    }
}

public int millisecondsToBytes(AudioFormat fmt, int time) {
    return (int) (time * (fmt.getSampleRate() * fmt.getChannels() * fmt.getSampleSizeInBits()) / 8000.0);
}

protected void closeImpl() {
    if (source != null) {
        source.close();
    }
}

protected void writeImpl(short[] samples, int offs, int len)
        throws JavaLayerException {
    if (source == null) {
        createSource();
    }

    byte[] b = toByteArray(samples, offs, len);
    source.write(b, 0, len * 2);
}

protected byte[] getByteArray(int length) {
    if (byteBuf.length < length) {
        byteBuf = new byte[length + 1024];
    }
    return byteBuf;
}

protected byte[] toByteArray(short[] samples, int offs, int len) {
    byte[] b = getByteArray(len * 2);
    int idx = 0;
    short s;
    while (len-- > 0) {
        s = samples[offs++];
        b[idx++] = (byte) s;
        b[idx++] = (byte) (s >>> 8);
    }
    return b;
}

protected void flushImpl() {
    if (source != null) {
        source.drain();
    }
}

public int getPosition() {
    int pos = 0;
    if (source != null) {
        pos = (int) (source.getMicrosecondPosition() / 1000);
    }
    return pos;
}

/**
 * Runs a short test by playing a short silent sound.
 */
public void test()
        throws JavaLayerException {
//        try {
        open(new AudioFormat(22000, 16, 1, true, false));
        short[] data = new short[22000 / 10];
        write(data, 0, data.length);
        flush();
        close();
//        } catch (RuntimeException ex) {
//            throw new JavaLayerException("Device test failed: " + ex);
//        }

    }
}

Теперь вам нужно просто внедрить его в свой проект, перезаписать старый JavaSoundDevice и наслаждаться VolumeAdjusting!

person Schesam    schedule 29.01.2016
comment
За решение собственной проблемы =) › +1 - person TT.; 29.01.2016
comment
@TT Который у меня был около 19 месяцев :D Я вижу, что с тех пор я улучшил себя - person Schesam; 29.01.2016
comment
Хорошо, но кажется, что использовать BasicPlayer намного проще. Один просто создает подкласс BasicPlayer и перезаписывает initLine() для установки усиления. - person Stefan Reich; 19.05.2019
comment
Откуда в вашем решении взялся класс MyPlayer? - person Anthone; 16.02.2020
comment
Это был AdvancedPlayer или обычный Player. Я просто скопировал исходный код и добавил методы Volume-Gain. - person Schesam; 21.05.2020