实例解析使用Java实现基本的音频播放器的编写要点
| 
                         Java音频播放,因为必须依赖到本地环境,所以JAVA在音频处理方面优势不大,或者说打从Java体系开发时就没太多的考虑音频播放因素,要知道最早的Java 1.1版本中,没有后来的javax.sound包,音频只能通过Applet包调取…… 遗憾的是,在图形程序开发中,我们的程序却又难免要使用到背景音乐、效果音等配合图像操作,哎,这实在是Sun大神给我们开的一个不打不小的玩笑。万幸后来Sun大神开眼,提供了javax.sound包,才解救我们于水深火热当中~ 但是继之而来的问题是,在javax.sound包的使用中,如同Java多媒体工具类的通病般,并没有提供十分完善的释放机制。如果我们做Windows 开发,调用MediaPlayer反复N次可能没也什么大碍,但在Java中,如果音频程序反复运行的话,极容易出现内存累计损耗的情况,以至于最后抛出一个java.lang.OutOfMemoryError,然后……程序就挂了,用户就傻了,我们就疯了…… 这已经是“是可忍孰不可忍 ”的问题了,有鉴于此,所以在本人的Loonframework框架开发中,二次整合了sound下的相关方法,力求以最简单的代码,做出最完善的音频控制类。在Loonframework-game还没有大成的现在,先摘录一部分方法,以供各位看官――拍砖! 对应网络资源调用,在Loonframework中建立了自己的uri用类,基本内容如下: 
package org.loon.framework.game.net;
import org.loon.framework.game.helper.StreamHelper;
/** *//**
 * <p>
 * Title: LoonFramework
 * </p>
 * <p>
 * Description:Loonframework专用uri(统一资源标识符)
 * </p>
 * <p>
 * Copyright: Copyright (c) 2007
 * </p>
 * <p>
 * Company: LoonFramework
 * </p>
 * 
 * @author chenpeng
 * @email:ceponline@yahoo.com.cn
 * @version 0.1
 */
public class URI ...{
  //传输协议类型
  public static final int _L_URI_HTTP = 1;
  public static final int _L_URI_UDP = 2;
  private String _uri;
  private int _type;
  /** *//**
   * 析构函数,用于注入uri和type
   * 
   * @param uri
   * @param type
   */
  public URI(String uri,int type) ...{
    _uri = new String(uri);
    _type = type;
  }
  /** *//**
   * 析构函数,用于注入uri
   * 
   * @param uri
   */
  public URI(String uri) ...{
    _uri = new String(uri);
    _type = URI._L_URI_HTTP;
  }
  /** *//**
   * 返回uri所在位置资源的byte数组。
   * 
   * @return
   */
  public byte[] getData() ...{
    if (_uri == null) ...{
      return null;
    }
    return StreamHelper.getHttpStream(_uri);
  }
  public String getURI() ...{
    return _uri;
  }
  public int getType() ...{
    return _type;
  }
}
在Loonframework框架中,定制了一个基础的SoundData类,用以统一管理音频数据源。
package org.loon.framework.game.sound;
import org.loon.framework.game.helper.StreamHelper;
import org.loon.framework.game.net.URI;
/** *//**
 * <p>
 * Title: LoonFramework
 * </p>
 * <p>
 * Description:用以获得并缓存声音文件数据(更进一步内容操作请见Loonframework-game框架)
 * </p>
 * <p>
 * Copyright: Copyright (c) 2007
 * </p>
 * <p>
 * Company: LoonFramework
 * </p>
 * 
 * @author chenpeng
 * @email:ceponline@yahoo.com.cn
 * @version 0.1
 */
public class SoundData ...{
  private byte[] _data;
  private boolean _loop;
  private int _type;
  public static final int _L_SOUNDTYPE_MIDI = 1;
  public static final int _L_SOUNDTYPE_WAV = 2;
  /** *//**
   * 析构函数,用以注入uri,type,loop
   * 
   * @param uri
   * @param type
   * @param loop
   */
  public SoundData(URI uri,int type,boolean loop) ...{
    if (uri != null) ...{
      _data = uri.getData();
    }
    _type = type;
    _loop = loop;
  }
  
  /** *//**
   * 析构函数,用以注入data,loop
   * 
   * @param data
   * @param type
   * @param loop
   */
  public SoundData(byte[] data,boolean loop) ...{
    if (data != null && data.length > 0) ...{
      _data = new byte[data.length];
      // 直接copy byte数组
      System.arraycopy(data,_data,_data.length);
    }
    _type = type;
    _loop = loop;
  }
  
  /** *//**
   * 析构函数,用以注入限定位置的resName,loop
   * @param resName
   * @param type
   * @param loop
   */
  public SoundData(String resName,boolean loop) ...{
    this(StreamHelper.GetDataSource(resName),loop);
  }
  public byte[] getData() ...{
    return _data;
  }
  public boolean getLoop() ...{
    return _loop;
  }
  public void setLoop(boolean loop) ...{
    _loop = loop;
  }
  public int getType() ...{
    return _type;
  }
}
Loonframework将音频播放相关方法,封装与SoundPlay之中,程序员可以不必理会javax.sound内部细节,而直接调用SoundPlay完成相关操作。 
package org.loon.framework.game.sound;
import java.io.ByteArrayInputStream;
import javax.sound.midi.MetaEventListener;
import javax.sound.midi.MetaMessage;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import org.loon.framework.game.net.URI;
/** *//**
 * <p>
 * Title: LoonFramework
 * </p>
 * <p>
 * Description:用以进行声音文件操作(仅为Loonframework中部分方法,更详细请参见Loonframework-game框架)
 * </p>
 * <p>
 * Copyright: Copyright (c) 2007
 * </p>
 * <p>
 * Company: LoonFramework
 * </p>
 * 
 * @author chenpeng
 * @email:ceponline@yahoo.com.cn
 * @version 0.1
 */
public class SoundPlay implements MetaEventListener,Runnable ...{
  private int _sleepTime;
  private Clip _audio;
  private Sequencer _midi;
  private boolean _loop;
  private int _soundType;
  private boolean _playing;
  private Thread _thread = null;
  private boolean _isRun = false;
  /** *//**
   * 析构函数,初始化SoundPlay
   * 
   */
  public SoundPlay() ...{
    _loop = false;
    _soundType = 0;
    _sleepTime = 1000;
    _playing = false;
  }
  // 载入声音文件
  public boolean load(SoundData data) ...{
    reset();
    if (data == null || data.getData() == null) ...{
      return false;
    }
    return init(data.getData(),data.getType(),data.getLoop());
  }
  /** *//**
   * 直接播放url文件
   * 
   * @param uri
   * @param ftype
   * @param loop
   * @return
   */
  public boolean load(URI uri,int ftype,boolean loop) ...{
    // 刷新数据
    reset();
    if (uri == null) ...{
      return false;
    }
    // 获得SoundData
    SoundData data = new SoundData(uri,ftype,loop);
    if (data == null || data.getData() == null) ...{
      return false;
    }
    return init(data.getData(),data.getLoop());
  }
  /** *//**
   * 初始化sound相关数据
   * 
   * @param data
   * @param ftype
   * @param loop
   * @return
   */
  private boolean init(byte[] data,boolean loop) ...{
    boolean result = false;
    ByteArrayInputStream bis = null;
    try ...{
      bis = new ByteArrayInputStream(data);
    } catch (Exception e) ...{
      bis = null;
    }
    if (bis == null) ...{
      return false;
    }
    // 判断类型
    switch (ftype) ...{
    // MIDI
    case SoundData._L_SOUNDTYPE_MIDI:
      // 当MIDI不存在时
      if (_midi == null) ...{
        try ...{
          // 获得Sequencer
          _midi = MidiSystem.getSequencer();
          _midi.open();
        } catch (Exception ex) ...{
          _midi = null;
        }
        if (_midi != null) ...{
          _midi.addMetaEventListener(this);
        }
      }
      // 当MIDI依旧未获得时
      if (_midi != null) ...{
        // 重新创建Sequence
        Sequence sc = null;
        try ...{
          sc = MidiSystem.getSequence(bis);
        } catch (Exception e) ...{
          sc = null;
        }
        if (sc != null) ...{
          try ...{
            _midi.setSequence(sc);
            // 获得是否循环播放
            _loop = loop;
            // 获得是否载入
            result = true;
          } catch (Exception ee) ...{
          }
          // 获得声音类型
          _soundType = SoundData._L_SOUNDTYPE_MIDI;
        }
      }
      try ...{
        bis.close();
      } catch (Exception ee) ...{
      }
      break;
    // Wav
    case SoundData._L_SOUNDTYPE_WAV:
      AudioFileFormat type = null;
      // 获得Audio
      try ...{
        type = AudioSystem.getAudioFileFormat(bis);
      } catch (Exception e) ...{
        type = null;
      }
      // 关闭流
      try ...{
        bis.close();
      } catch (Exception ex) ...{
      }
      if (type == null) ...{
        return false;
      }
      // 根据指定信息构造数据行的信息对象
      DataLine.Info di = new DataLine.Info(Clip.class,type.getFormat());
      // 转为Clip
      try ...{
        _audio = (Clip) AudioSystem.getLine(di);
      } catch (Exception e) ...{
      }
      // 播放文件
      try ...{
        _audio.open(type.getFormat(),data,data.length);
        _loop = loop;
        result = true;
      } catch (Exception e) ...{
      }
      // 获得文件类型
      _soundType = SoundData._L_SOUNDTYPE_WAV;
      break;
    }
    return result;
  }
  public boolean play(SoundData data) ...{
    if (!load(data)) ...{
      return false;
    }
    return play();
  }
  public boolean play() ...{
    switch (_soundType) ...{
    case SoundData._L_SOUNDTYPE_MIDI:
      try ...{
        _midi.start();
        _playing = true;
        _soundType = SoundData._L_SOUNDTYPE_MIDI;
      } catch (Exception ee) ...{
      }
      break;
    case SoundData._L_SOUNDTYPE_WAV:
      if (_audio != null) ...{
        if (_loop) ...{
          // 设定循环
          _audio.setLoopPoints(0,-1);
          _audio.setFramePosition(0);
          _audio.loop(Clip.LOOP_CONTINUOUSLY);
        } else ...{
          // 强制设定播放位置至0
          _audio.setFramePosition(0);
          _audio.start();
        }
        _playing = true;
      }
      break;
    }
    return _playing;
  }
  /** *//**
   * 自动播放,循环停止后结束。
   * 
   * @param data
   * @return
   */
  public boolean AutoPlay(SoundData data) ...{
    if (!load(data)) ...{
      return false;
    }
    return AutoPlay();
  }
  /** *//**
   * 自动播放,循环停止后结束。
   * 
   * @return
   */
  public boolean AutoPlay() ...{
    _isRun = true;
    _thread = new Thread(this);
    _thread.start();
    return _playing;
  }
  /** *//**
   * 停止播放
   */
  public void stop() ...{
    if (_audio != null && _audio.isActive()) ...{
      try ...{
        _audio.stop();
      } catch (Exception e) ...{
      }
    }
    if (_midi != null) ...{
      _midi.stop();
    }
    _playing = false;
    _isRun = false;
  }
  /** *//**
   * 释放数据
   * 
   */
  public void reset() ...{
    stop();
    _loop = false;
    _soundType = 0;
    if (_midi != null) ...{
      _midi.close();
      _midi = null;
    }
    if (_audio != null && _audio.isOpen()) ...{
      _audio.close();
      _audio = null;
    }
    _isRun = false;
    _thread = null;
  }
  /** *//**
   * 设定MetaMessage
   */
  public void meta(MetaMessage meta) ...{
    // 判断是否循环播放MIDI
    if (_loop && _soundType == SoundData._L_SOUNDTYPE_MIDI
        && meta.getType() == 47) ...{
      if (_midi != null && _midi.isOpen()) ...{
        _midi.setMicrosecondPosition(0);
        _midi.start();
      }
    }
  }
  public void run() ...{
    while (_isRun) ...{
      play();
      // 因为播放类型唯一,所以只会返回一个_playing结果,以此判定。
      if (_midi != null) ...{
        _playing = _midi.isRunning();
      }
      if (_audio != null) ...{
        _playing = _audio.isRunning();
      }
      // 当播放停止
      if (!_playing) ...{
        // 释放
        reset();
      }
      try ...{
        Thread.sleep(_sleepTime);
      } catch (InterruptedException e) ...{
        e.printStackTrace();
      }
    }
  }
  public int getSleepTime() ...{
    return _sleepTime;
  }
  /** *//**
   * 设定AutoPlay线程循环时间。
   * 
   * @param time
   */
  public void setSleepTime(int time) ...{
    _sleepTime = time;
  }
}
这时我们需要面对的,仅是封装为实体的SoundData数据和SoundPlay操作,而不必和繁复的javax.sound再打交道。 (编辑:莱芜站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!  | 
                  
