TheAnalogKid
|
 |
«
Posted
2004-10-19 19:57:56 » |
|
Hi.
I use java sound (with VorbisSPI and Jorbis) for OGG playback using a SourceDataLine to play a byte array. The problem is that at the end of playback of any sound, there is always a kind of "pop" noise. And I can assure you that I doesn't stop or flush the line before playback ends. This noise makes sound not really good.
What's weird is that even when I test the OGG using a Clip object, the same problem occurs and you know that I can't control line feeding in this situation so it shouldn't be a buffer underrun/overflow problem.
One interesting thing is that when I test the same sound but in the wav format then the problem simply disapear.
So my guess is that the problem seems to be in either VorbisSPI or Jorbis.
Any help or ideas would be greatly appreciated otherwise the playback isn't great.
Thanks
|
|
|
|
princec
|
 |
«
Reply #1 - Posted
2004-10-20 11:51:30 » |
|
Is the sample at 0 at the end? You can fix pops with a tiny 0.1ms fadeout at the end of the sample. Cas 
|
|
|
|
TheAnalogKid
|
 |
«
Reply #2 - Posted
2004-10-20 12:44:09 » |
|
Is the sample at 0 at the end? What doyou mean by sample? Yes I could do a kind of fade out but to me it's a patch and not a real solution but thanks anyway.
|
|
|
|
Games published by our own members! Check 'em out!
|
|
princec
|
 |
«
Reply #3 - Posted
2004-10-20 14:20:36 » |
|
It's not a patch, that IS the solution! If your samples stop dead at, say a PCM level of 3234, then the next thing they have to do is to straight to zero when the sample stops - Pop! Cas 
|
|
|
|
erikd
|
 |
«
Reply #4 - Posted
2004-10-20 14:27:46 » |
|
If the sample doesn't end with 0, the result would indeed be little pop (abeit usually a small one) and it's generally better to *always* have samples end with 0 like Cas described. This is not a patch, it's just good practice. However, I guess you'd have the same problem with the wav version if a non-zero value at the end would be the cause. Did you create the ogg from the wav file or the other way around (the latter to test the wav output)? Does the ogg sound correctly in any other ogg player? Could you post the source?
|
|
|
|
TheAnalogKid
|
 |
«
Reply #5 - Posted
2004-10-20 16:57:10 » |
|
Sorry Cas! I didn't want to offense you. Actually if I play the OGG in Winamp everything is fine (no pop at the end) so to me it's not a fading issue. And I have to say that I've already faded out the OGG at the end to prevent this problem. In summary eveything seems to be ok but the problem still persists. And yes the playback of the wav is always good. Anyway here is the source code: I have an entity class (StreamedSound which extends AbstractSound) that holds the data and has the capability to render it and another class to control playback (Player). AbstractSound.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
| package sound;
import java.io.File; import java.io.IOException;
import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem;
public abstract class AbstractSound implements Sound {
private File file; private AudioInputStream sourceStream; private AudioFormat sourceFormat; private AudioInputStream decodedStream; private AudioFormat decodedFormat; private boolean decoded; private boolean cached; private String id; private boolean open; protected AbstractSound() { id = ""; open = false; } public void setFileName(String fileName) { file = new File(fileName); if (id.length() == 0) { id = fileName; } } public void setDecoded(boolean decoded) { this.decoded = decoded; } boolean isDecoded() { return decoded; } public void setCached(boolean cached) { this.cached = cached; } public boolean isCached() { return cached; }
public String getId() { return id; } public void setId(String id) { this.id = id; } protected void load() throws Exception { System.out.println("Loading audio data for sound " + id); sourceStream = AudioSystem.getAudioInputStream(file); if (sourceStream == null) { throw new Exception("Got null AudioInputStream for given sound file."); } sourceFormat = sourceStream.getFormat(); if (decoded) { decodedFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, sourceFormat.getSampleRate(), 16, sourceFormat.getChannels(), sourceFormat.getChannels() * 2, sourceFormat.getSampleRate(), false); decodedStream = AudioSystem.getAudioInputStream(decodedFormat, sourceStream); } open = true; } public boolean isOpen() { return open; } protected void setOpen(boolean open) { this.open = open; } protected AudioFormat getFormat() { if (decoded) { return decodedFormat; } else { return sourceFormat; } } void setSourceFormat(AudioFormat format) { sourceFormat = format; } void setDecodedFormat(AudioFormat format) { decodedFormat = format; } protected AudioInputStream getAudioInputStream() { if (decoded) { return decodedStream; } else { return sourceStream; } } public void dispose() { close(); } protected void close() { boolean closedStream = false; try { if (sourceStream != null) { sourceStream.close(); closedStream = true; } if (decoded && decodedStream != null) { decodedStream.close(); closedStream = true; } } catch (IOException e) { throw new RuntimeException("Unable to dispose AudioInputStream.", e); } if (closedStream) { System.out.println("Closed streams for sound " + id); } open = false; } public abstract Object clone() throws CloneNotSupportedException; protected void copy(AbstractSound sound) { sound.setId(id); sound.setFileName(file.getAbsolutePath()); sound.setDecoded(decoded); sound.setCached(cached); sound.setSourceFormat(sourceFormat); sound.setDecodedFormat(decodedFormat); sound.setOpen(open); } } |
|
|
|
|
TheAnalogKid
|
 |
«
Reply #6 - Posted
2004-10-20 16:59:31 » |
|
And here is the rest of the code: StreamedSound.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
| package sound;
import java.io.ByteArrayOutputStream; import java.io.IOException;
import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.DataLine; import javax.sound.sampled.SourceDataLine;
public class StreamedSound extends AbstractSound {
private SourceDataLine line; private Player player; private PlaybackStrategy playbackStrategy; private boolean playing; private boolean writing; private int maxLineBufferSize; public StreamedSound() { playing = false; writing = false; maxLineBufferSize = 30000; } public void open() throws Exception { player = Player.getInstance(); if (!isOpen()) { load(); openLine();
if (playbackStrategy == null) { if (isCached()) { playbackStrategy = new CachingStrategy(); } else { playbackStrategy = new StreamingStrategy(); } playbackStrategy.load(); }
player.start(); } else { openLine(); } } void openLine() throws Exception { DataLine.Info srcDataInfo = new DataLine.Info(SourceDataLine.class, getFormat()); line = (SourceDataLine) AudioSystem.getLine(srcDataInfo); line.open(getFormat()); int frameSize = getFormat().getFrameSize(); if (line == null) { throw new Exception("SourceDataLine is null."); } line.start(); } public void play() throws IOException { stop(); while (writing) { Thread.yield(); } line.flush(); line.stop(); playbackStrategy.rewind(); playing = true; line.start(); player.add(this); }
public void stop() { playing = false; }
public boolean isPlaying() { return playing; } public Object clone() throws CloneNotSupportedException { StreamedSound sound = new StreamedSound(); copy(sound); PlaybackStrategy strategy = (PlaybackStrategy) playbackStrategy.clone(); sound.setPlaybackStrategy(strategy); return sound; } void setAudioData(byte[] data) { if (isCached()) { ((CachingStrategy) playbackStrategy).setDataBuffer(data); } } public void dispose() { player.stop(); close(); } protected void close() { super.close(); line.close(); }
PlaybackStrategy getPlaybackStrategy() { return playbackStrategy; } void setPlaybackStrategy(PlaybackStrategy playbackStrategy) { this.playbackStrategy = playbackStrategy; }
abstract class PlaybackStrategy { abstract void load() throws IOException; abstract void play() throws IOException; abstract void rewind(); abstract public Object clone(); } private class CachingStrategy extends PlaybackStrategy { private byte[] buffer; private int offset; CachingStrategy() { buffer = null; offset = 0; } void load() throws IOException { AudioInputStream stream = getAudioInputStream(); ByteArrayOutputStream outStream = new ByteArrayOutputStream(); byte[] data = new byte[maxLineBufferSize]; int bytesRead = 0;
while (bytesRead != -1) { bytesRead = stream.read(data, 0, data.length); if (bytesRead != -1) { outStream.write(data, 0, bytesRead); } } buffer = outStream.toByteArray(); outStream.close(); rewind(); } void play() throws IOException { writing = true; if (offset < buffer.length) { int lineBufferSize = maxLineBufferSize; if (offset + lineBufferSize >= buffer.length) { lineBufferSize = buffer.length - offset; } line.write(buffer, offset, lineBufferSize); offset += lineBufferSize; } else { playing = false; } writing = false; } void rewind() { offset = 0; } byte[] getDataBuffer() { return buffer; } public Object clone() { CachingStrategy strategy = new CachingStrategy(); strategy.setDataBuffer(buffer); return strategy; } void setDataBuffer(byte[] data) { buffer = data; } } private class StreamingStrategy extends PlaybackStrategy {
private AudioInputStream stream; private int bytesRead; private byte[] buffer; StreamingStrategy() { stream = null; buffer = new byte[maxLineBufferSize]; bytesRead = 0; } void load() throws IOException { rewind(); } void play() throws IOException { writing = true; bytesRead = stream.read(buffer, 0, buffer.length); if (bytesRead != -1) { line.write(buffer, 0, bytesRead); } else { playing = false; } writing = false; } void rewind() { close(); try { open(); } catch (Exception e) { e.printStackTrace(); } stream = getAudioInputStream(); bytesRead = 0; }
public Object clone() { return new StreamingStrategy(); } } } |
Player 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
| package sound;
import java.io.IOException; import java.util.ArrayList; import java.util.List;
class Player {
private SoundThread thread; private boolean running; private static Player instance = null; private Player() { running = false; } static Player getInstance() { if (instance == null) { instance = new Player(); } return instance; } void start() { if (!running) { thread = new SoundThread(); thread.start(); running = true; } } void add(StreamedSound sound) { thread.add(sound); } void stop() { thread.stop(); running = false; } private class SoundThread implements Runnable {
private Thread thread; private List sounds; private int soundCount; private boolean running;
SoundThread() { sounds = new ArrayList(); soundCount = 0; running = false; } void start() { if (!running) { thread = new Thread(this); thread.start(); } } void add(StreamedSound sound) { sounds.add(sound); soundCount++; } void remove(StreamedSound sound) { if (isRunning()) { throw new IllegalStateException("Can not unregister sound while this thread running."); } sounds.remove(sound); soundCount--; } int getSoundCount() { return soundCount; } public void run() { running = true; StreamedSound sound; while (running) { for (int i = 0; i < soundCount; i++) { sound = (StreamedSound) sounds.get(i); if (sound.isPlaying()) { try { sound.getPlaybackStrategy().play(); } catch (IOException e) { throw new RuntimeException("Unable to play streamed sound.", e); } if (!sound.isPlaying()) { sounds.remove(i); soundCount--; i--; } } Thread.yield(); } if (soundCount == 0) { Thread.yield(); } } } boolean isRunning() { return running; } void stop() { running = false; while (thread.isAlive()) { Thread.yield(); } } } } |
|
|
|
|
TheAnalogKid
|
 |
«
Reply #7 - Posted
2004-10-20 17:12:18 » |
|
Me again.
Would it be possible that faded out samples is not supported by VorbisSPI or Jorbis? That would explain my problem. Maybe the question is irrelevant.
|
|
|
|
Mac_Systems
Junior Member  
I love my Java
|
 |
«
Reply #8 - Posted
2004-10-22 15:26:04 » |
|
Well, as we cannot look into winamp's sourcecode, im quite sure that winamp will do a fadeout by itself. At least winamp 0's the last byte it plays - this is not hearable even in a samplerate of 8KHZ. I do not know why you flood the forum with you code, simple write a small testcase for your needs - and judge for yourself.
Stay Tuned, Jens
|
|
|
|
TheAnalogKid
|
 |
«
Reply #9 - Posted
2004-10-22 15:41:59 » |
|
I do not know why you flood the forum with you code... Because I was asked for. Anyway, does it cause you a problem? Thanks for your answer about the Winamp explication.
|
|
|
|
Games published by our own members! Check 'em out!
|
|
dranonymous
Junior Member  
Hoping to become a Java Titan someday!
|
 |
«
Reply #10 - Posted
2004-10-22 22:02:02 » |
|
Do you have a sound editor that you can use to view the actual sound? You can get a great one from sourceforge - AudacityThis should tell you for sure if you have a problem with the audio going from some high - non zero value at the end. Dr. A>
|
|
|
|
|
erikd
|
 |
«
Reply #11 - Posted
2004-10-25 08:48:21 » |
|
Because I was asked for. Yes, I asked for the source although the source is a bit larger than what I hoped for  . Sorry for not having replied yet (I'm a bit busy these days), but as far as I could tell there's no main method in there so if you could add a little test case (preferrably with a test sound uploaded somewhere) that would help a lot given my current time constraints. BTW, does the problem also occur with a sample with only silence?
|
|
|
|
dranonymous
Junior Member  
Hoping to become a Java Titan someday!
|
 |
«
Reply #12 - Posted
2004-10-25 20:19:07 » |
|
Here's another link to an Ogg Vorbis decoder that a friend passed on to me - JOggI had a problem yesterday getting to the site, but today it was fine. You might try using this one and see if it meets your needs as well. It may not fix your pop, but if it is the decoder, you'd know. Dr. A>
|
|
|
|
|
TheAnalogKid
|
 |
«
Reply #13 - Posted
2004-10-27 13:47:03 » |
|
Thanks all for your help. I think Jogg is already used by Jorbis (the jar at least). I already use Audacity. I used it to fade out the OGG at the end. I've uploaded a zip that will let you test it and check the code just in case you could find out something. You can download it at http://www.myjavaserver.com/~theanalogkid/servlets/app/soundtest.zipYou just have to run test.bat to run the test. The java path is hardcoded to jdk1.4. I recommend running the test with Java 1.4 because in version 5 I'm not sure if the problem presists. Just a note concerning Java 5: Like many of you I noticed that the sound is offen chopped (at the begining) and the volume is higher than in 1.4. Thanks again to all of you.
|
|
|
|
dranonymous
Junior Member  
Hoping to become a Java Titan someday!
|
 |
«
Reply #14 - Posted
2004-10-28 18:40:33 » |
|
I'm not sure why JOrbis has the jogg jar in it. You can compile the jcraft sources and run them without any jogg stuff.
The code in the jogg stuff is very clean and well commented as well. It took me almost no time to get the encoder installed in my code and running.
Regards, Dr. A>
|
|
|
|
|
TheAnalogKid
|
 |
«
Reply #15 - Posted
2004-10-28 19:47:45 » |
|
Is Jogg compatible with VorbisSPI?
|
|
|
|
oNyx
|
 |
«
Reply #16 - Posted
2004-10-30 18:57:56 » |
|
 end at green line=ok end at red line=plop Just found that image, while skimming through my hdd (searched something completely different). Well, thought it's cool as visualisation thingy. The transparent line in the middle should have been black... oh well... but I guess it's still ok, since this post is going to end up on a dark stripe 
|
|
|
|
dranonymous
Junior Member  
Hoping to become a Java Titan someday!
|
 |
«
Reply #17 - Posted
2004-11-02 14:58:31 » |
|
You'll need to check. I didn't need to worry about the SPI interface, it only took about 10 lines of code to get things playing. I remember the documentation mentioning SPI, but that's about it.
HTH - Dr. A>
|
|
|
|
|
TheAnalogKid
|
 |
«
Reply #18 - Posted
2004-11-08 18:32:16 » |
|
Yes, I asked for the source although the source is a bit larger than what I hoped for  . Sorry for not having replied yet (I'm a bit busy these days), but as far as I could tell there's no main method in there so if you could add a little test case (preferrably with a test sound uploaded somewhere) that would help a lot given my current time constraints. BTW, does the problem also occur with a sample with only silence? Have you figured out something? The problem only occurs with the provided sound in the test jar ( http://www.myjavaserver.com/~theanalogkid/servlets/app/soundtest.zip)
|
|
|
|
|