`
w7849516230
  • 浏览: 18548 次
文章分类
社区版块
存档分类
最新评论

sipphone中播放声音文件

 
阅读更多
在这一篇里记录下在Java sip softphone的基础上添加播放音乐文件的功能。前面介绍了几款sipphone,发现Java sip softphone这款开源软件功能简单易于修改,根据自己的需要选择是否保留其GUI,然后修改少部分代码即可实现在拨号后播放指定的音乐文件。但是仍然有几个问题有待以后解决:1是对整个源码的框架流程的分析,目前我也只是删除了其GUI部分,而底层的sip rtp传输没有涉及;2是我下载的版本在挂断、远端忙等情况时有问题;3是播放声音文件目前只是使用默认支持的wav格式,其它格式可以扩展,但是我实现的方式应该还是有问题,因为获取发送声音数据的间隔还没有确定,只是自己测试的20ms,后面会看到。
要播放声音文件,首先得找到是从哪里获得数据的。在源码书中看到net.sourceforge.peers.media包,类Capture中看到buffer = soundManager.readData();,进而查看类SoundManager,可以发现:openAndStartLines()、 readData() 、writeData()等函数,正是我们需要的麦克风音频数据的捕获和写入扬声器的方法。查看readData()方法如下:
/**
     * audio read from microphone, read all available data
     * @return
     */
    public synchronized byte[] readData() {
        if (targetDataLine == null) {
            return null;
        }
        int ready = targetDataLine.available();
        while (ready == 0) {
            try {
                Thread.sleep(2);
                ready = targetDataLine.available();
            } catch (InterruptedException e) {
                return null;
            }
        }
        if (ready <= 0) {
            return null;
        }
        byte[] buffer = new byte[ready];
        targetDataLine.read(buffer, 0, buffer.length);
        if (mediaDebug) {
            try {
                microphoneOutput.write(buffer, 0, buffer.length);
            } catch (IOException e) {
                logger.error("cannot write to file", e);
                return null;
            }
        }
        return buffer;
    }
去掉调试信息,结合类Capture中的代码,发现这里仅是通过buffer将数据获取,然后通过PipedOutputStream类型的rawData将数据发送出去;因此只要在readData()中使buffer返回需要发送的声音文件数据即可。
根据以上分析,剩下的就是读取声音文件;java默认支持AU、AIFF、WAVE、MIDI 四种声音格式,如果需要更多的格式,需要下载附加包(参考1),然后先从(参考2)常用的JavaSound类图如下:

理解下从AudioSystem中获得系统的符合DataLine.info中指定的AudioFormat属性的混频器;从类SoundManager的构造函数和openAndStartLines()函数中看到如下代码:
// linear PCM 8kHz, 16 bits signed, mono-channel, little endian
        audioFormat = new AudioFormat(8000, 16, 1, true, false);
        targetInfo = new DataLine.Info(TargetDataLine.class, audioFormat);
        sourceInfo = new DataLine.Info(SourceDataLine.class, audioFormat);
public void openAndStartLines() {
        logger.debug("openAndStartLines");
        //我删除了调试部分代码
        try {
            targetDataLine = (TargetDataLine) AudioSystem.getLine(targetInfo);
 
           
            targetDataLine.open(audioFormat);
        } catch (LineUnavailableException e) {
            logger.error("target line unavailable", e);
            return;
        }
        targetDataLine.start();
        try {
            sourceDataLine = (SourceDataLine) AudioSystem.getLine(sourceInfo);
            sourceDataLine.open(audioFormat);
        } catch (LineUnavailableException e) {
            logger.error("source line unavailable", e);
            return;
        }
        sourceDataLine.start();
    }
虽然看了这么多,但是我们并不需要将数据写入扬声器,只是获得并返回,因此(参考1)我们从文件中获得输入流,代码如下:
if (testNum < 1) {
			File file = new File("test.wav");
			// AudioInputStream audioInputStream = null;// 文件流
			// AudioFormat audioFormat = null;// 文件格式
			// 取得文件输入流
			try {
				audioInputStream = AudioSystem.getAudioInputStream(file);
			} catch (UnsupportedAudioFileException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			audioFormat = audioInputStream.getFormat();
			// 这里可以加入转换文件编码的代码
			testNum++;
			System.out.println("testNum" + testNum);
		}
		testNum++;
		System.out.println("testNum" + testNum);
		int ready = 0;
		try {
			ready = audioInputStream.available();

			// while (ready == 0) {
			try {
				Thread.sleep(20);
				ready = audioInputStream.available();
			} catch (InterruptedException e) {
				return null;
			}
			// }
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		if (ready <= 0) {
			return null;
		}

		byte[] buffer = new byte[320];//readData是循环读取
		try {
			audioInputStream.read(buffer, 0, buffer.length);
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		System.out.println("ready:" + ready);
		return buffer;
然后,按照原定的采样频率、通道数等利用格式转换器将一个MP3或其它格式文件先转换成一个wav文件,就可以进行测试;特别是注意中间sleep的间隔,如果没有的话会导致数据无法及时处理而造成停顿,而且问题也在这里,这个间隔时间为多少合适,buffer读取的数据定多少呢?这个应该可以根据文件的属性来设置,但是应该有更好的方法吧?继续寻找。

最后,才想起来没有把环境的搭建过程说下,下次整理吧。

转载注明出处:http://blog.csdn.net/w7849516230/article/details/7695912

参考
1.使用Java实现MP3音乐播放器
2.javaSE播放声音原理

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics