这可能不是一个非常可接受的问题,但我现在非常绝望。
我需要一个快速搜索和平衡修改的同步java媒体播放器。
情景:
我有一个javaFX项目,我必须在一个循环中播放一个非常短(50-100毫秒)的媒体文件。问题是,我需要在重新启动之前保留。
简而言之: 播放声音 - >满足某些要求(平衡将被修改)(每120ms) - >如果声音从开始播放重播。
javafx提供了我修改过的媒体播放器。但需要更高的效率:
// this method is just to show how the modified mediaplayer class is called
public void updateSoundBalance(double currentTimeInCycle) {
// control the balance of the sound
if(playingSound && mediaPlayer != null)
{
long b = System.nanoTime();
// 0 <= balance < 4. balance represents the cycle phase.
double balance = currentTimeInCycle % RADIANCE_FULL_CYCLE / RADIANCE_QUARTER_CYCLE;
boolean firstHalfCycle = balance < 2;
double quarterCycleIndex = Math.floor(balance);
long a = System.nanoTime();
if(swingingSound)
mediaPlayer.setBalance(firstHalfCycle ? 1 - balance : balance - 3);
else
mediaPlayer.setBalance(balance > 1 && balance < 3? -1 : 1);
System.out.println("C ::: sound balance = " + (System.nanoTime() - a));
if ((quarterCycleIndex == 1 | quarterCycleIndex == 3) &&
balance - quarterCycleIndex <= Settings.DEFAULT_PATTERN_SMOOTHNESS)
{
a = System.nanoTime();
if (mediaDone){
mediaPlayer.reset();
mediaDone = false;
}
System.out.println("B ::: call reset = " + (System.nanoTime() - a));
}
System.out.println("A ::: total time = " + (System.nanoTime() - b));
}
}
import java.util.concurrent.ScheduledThreadPoolExecutor;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.util.Duration;
public class MultiMediaPlayer
{
private MediaPlayer mp1, mp2;
private boolean usingMp1 = true;
private ScheduledThreadPoolExecutor seekService = new ScheduledThreadPoolExecutor(2);
private Runnable seekRun = new Runnable() {
@Override
public void run() {
try
{
long a = System.nanoTime();
if(usingMp1) {
usingMp1 = false;
mp1.stop();
mp2.play();
mp1.seek(new Duration(0));
} else {
usingMp1 = true;
mp2.stop();
mp1.play();
mp2.seek(new Duration(0));
}
System.out.println("D ::: reset sound time taken = " + (System.nanoTime() - a));
}
catch (Exception e){
e.printStackTrace();
}
}
};
public MultiMediaPlayer(Media value)
{
mp1 = new MediaPlayer(value);
mp2 = new MediaPlayer(value);
mp1.balanceProperty().bindBidirectional(mp2.balanceProperty());
mp1.onEndOfMediaProperty().bindBidirectional(mp2.onEndOfMediaProperty());
}
public void setBalance(double value){
mp1.setBalance(value);
}
public void reset(){
seekService.execute(seekRun);
}
public void play(){
if(usingMp1) {
mp1.play();
} else {
mp2.play();
}
}
public void stop(){
mp1.stop();
mp2.stop();
}
public void pause(){
mp1.pause();
mp2.pause();
}
public void setOnEndOfMedia(Runnable r) {
mp1.setOnEndOfMedia(r);
}
}
如果有人能指出我正确的方向(图书馆/我错过的东西),我将不胜感激
ps允许的java版本是最新的
答案 0 :(得分:0)
在这种情况下,您可以更好地使用更低级别的Java Sound API。它已经是标准API的一部分。你提到你的音频信号很短。因此,您可以将其缓冲在内存中,然后将其写入轮廓几次。 乍一看你的代码似乎pan可能是正确的选择,而不是平衡。以下示例分别演示了切换平衡和平移到其最大值和最小值。 Java Sound API本身适用于WAVE和AIFF。如果您有其他文件格式,则需要查看Java Sound API FormatConversionProviders,例如mp3spi和vorbisspi。
import java.io.File;
import java.io.IOException;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.UnsupportedAudioFileException;
public class Main {
public static void main(String[] args) {
try {
int loops = 20;
File waveFile = new File(yourWaveFilePath);
AudioInputStream stream = AudioSystem.getAudioInputStream(waveFile);
AudioFormat format = stream.getFormat();
// reading complete audio file into memory
byte[] frames = new byte[format.getFrameSize() * (int) stream.getFrameLength()];
stream.read(frames, 0, frames.length);
DataLine.Info lineInfo = new DataLine.Info(SourceDataLine.class, format);
SourceDataLine line = (SourceDataLine) AudioSystem.getLine(lineInfo);
line.open(format);
line.start();
FloatControl balance = (FloatControl) line.getControl(FloatControl.Type.BALANCE);
FloatControl pan = (FloatControl) line.getControl(FloatControl.Type.PAN);
for (int i = 0; i < loops; i++) {
// switching balance and pan with every iteration
if (i % 2 == 0) {
balance.setValue(balance.getMinimum());
pan.setValue(pan.getMinimum());
} else {
balance.setValue(balance.getMaximum());
pan.setValue(pan.getMaximum());
}
// playing complete audio file
line.write(frames, 0, frames.length);
System.out.println("iteration: " + i + ", balance: " + balance.getValue() + ", pan: " + pan.getValue());
}
line.drain();
line.close();
stream.close();
} catch (UnsupportedAudioFileException | IOException | LineUnavailableException e) {
e.printStackTrace();
}
}
}
答案 1 :(得分:0)
感谢Olof Kohlhaas,但我认为我为我的具体问题找到了更好的解决方案,它使用了javafx的AudioClip。但是,由于我缺乏知识,我使用一种基本方法来获取仅支持Wave和其他特定格式的文件的长度。如果改变了,这个类适用于javafx的媒体包支持的任何格式:
import java.io.IOException;
import java.net.URL;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.media.AudioClip;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
/**
*
* this class is to optimize sound pattern playing
*
* reasoning:
* - audio clips are for short repetitive files that don't need live changes to balance
* - media players are for long files that will have the ability to swing with the visual patted
* by updating the balance whenever needed (every few updates)
*/
public class AudioPlayer
{
public enum Controller{
MEDIA_PLAYER, AUDIO_CLIP;
}
/**
* this class is to help other classes keep track of this particular state of the AudioPlayer
*/
public class ControllerProperty extends SimpleObjectProperty<Controller>{
SimpleBooleanProperty isMediaPlayerProperty = new SimpleBooleanProperty();
@Override
public void set(Controller newValue) {
super.set(newValue);
if (newValue == Controller.MEDIA_PLAYER)
isMediaPlayerProperty.set(true);
else
isMediaPlayerProperty.set(false);
}
public ReadOnlyBooleanProperty isMediaPlayerProperty() {
return isMediaPlayerProperty;
}
}
// different controllers used
private Media media;
private MediaPlayer mediaPlayer;
private AudioClip audioClip;
// controllerProperty property indicator
private ControllerProperty controllerProperty = new ControllerProperty();
private boolean mediaDonePlaying = true;
private double durationMillis;
/**
* Constructor. This will be the place where you can
* @param srcUrl
*/
public AudioPlayer(String srcUrl) {
boolean formatSupported = true;
try {
durationMillis = getLength(srcUrl);
} catch (IOException | LineUnavailableException e) {
e.printStackTrace();
} catch (UnsupportedAudioFileException e) {
formatSupported = false;
}
// if file is long or format unsupported (not one of these: AudioSystem.getAudioFileTypes())
if (durationMillis > 400 | !formatSupported){
media = new Media(srcUrl);
mediaPlayer = new MediaPlayer(media);
controllerProperty.set(Controller.MEDIA_PLAYER);
mediaPlayer.setOnEndOfMedia(() -> mediaDonePlaying = true);
}
else {
audioClip = new AudioClip(srcUrl);
controllerProperty.set(Controller.AUDIO_CLIP);
}
}
/**
* gets the audio duration of the provided source in milliseconds
* @param path url string representation of the path
* @return the length in milliseconds
* @throws IOException
* @throws UnsupportedAudioFileException
* @throws LineUnavailableException
*/
public static double getLength(String path) throws IOException, UnsupportedAudioFileException, LineUnavailableException
{
AudioInputStream stream;
stream = AudioSystem.getAudioInputStream(new URL(path));
AudioFormat format = stream.getFormat();
if (format.getEncoding() != AudioFormat.Encoding.PCM_SIGNED) {
format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, format
.getSampleRate(), format.getSampleSizeInBits() * 2, format
.getChannels(), format.getFrameSize() * 2, format
.getFrameRate(), true); // big endian
stream = AudioSystem.getAudioInputStream(format, stream);
}
DataLine.Info info = new DataLine.Info(Clip.class, stream.getFormat(),
((int) stream.getFrameLength() * format.getFrameSize()));
Clip clip = (Clip) AudioSystem.getLine(info);
clip.close();
return clip.getBufferSize()
/ (clip.getFormat().getFrameSize() * clip.getFormat()
.getFrameRate());
}
public void stop(){
switch (controllerProperty.get())
{
case AUDIO_CLIP:
if (audioClip != null)
audioClip.stop();
break;
case MEDIA_PLAYER:
if (mediaPlayer != null && media != null){
mediaPlayer.stop();
mediaDonePlaying = true;
}
break;
}
}
public void play(){
switch (controllerProperty.get())
{
case AUDIO_CLIP:
if (audioClip != null)
if(!audioClip.isPlaying()){
audioClip.play();
}
break;
case MEDIA_PLAYER:
if (mediaPlayer != null && media != null){
mediaPlayer.play();
mediaDonePlaying = false;
}
break;
}
}
public void pause(){
switch (controllerProperty.get())
{
case AUDIO_CLIP:
if (audioClip != null)
audioClip.stop();
break;
case MEDIA_PLAYER:
if (mediaPlayer != null && media != null)
mediaPlayer.pause();
break;
}
}
/**
* sets the balance of the player, if the controller is an {@link AudioClip}, the balance is
* updated at the next play cycle, if the controller is a {@link MediaPlayer} the balance is
* updated at the next time the {@link MediaPlayer} has Status.READY (read
* {@link MediaPlayer#setBalance(double)} for more details)
* @param balance
*/
public void setBalance(double balance){
switch (controllerProperty.get())
{
case AUDIO_CLIP:
if (audioClip != null)
audioClip.setBalance(balance);
break;
case MEDIA_PLAYER:
if (mediaPlayer != null && media != null)
mediaPlayer.setBalance(balance);
break;
}
}
public String getSource(){
switch (controllerProperty.get())
{
case AUDIO_CLIP:
if (audioClip != null)
return audioClip.getSource();
break;
case MEDIA_PLAYER:
if (mediaPlayer != null && media != null)
return media.getSource();
break;
}
return null;
}
/**
* @return if the file is done
*/
public boolean isDonePlaying(){
switch (controllerProperty.get())
{
case AUDIO_CLIP:
if (audioClip != null)
return !audioClip.isPlaying();
break;
case MEDIA_PLAYER:
if (mediaPlayer != null && media != null)
return mediaDonePlaying;
break;
}
throw new IllegalStateException("Internal Error");
}
public ControllerProperty controllerProperty() {
return controllerProperty;
}
}