From 5f4214dffd2ae304e34db0a1c7424722935d0341 Mon Sep 17 00:00:00 2001 From: "honfika@gmail.com" Date: Mon, 15 Dec 2014 22:33:13 +0100 Subject: [PATCH] #752 Sound is not stopped when you change the selectin in he tag tree from SWF to e.g any shape fixed --- .../src/com/jpexs/helpers/SoundPlayer.java | 104 -------- .../flash/gui/FolderPreviewPanel.java | 7 +- .../decompiler/flash/gui/ImagePanel.java | 235 ++++++++++-------- .../decompiler/flash/gui/SoundTagPlayer.java | 197 +++++++-------- .../flash/gui/player/PlayerControls.java | 3 + 5 files changed, 228 insertions(+), 318 deletions(-) delete mode 100644 libsrc/ffdec_lib/src/com/jpexs/helpers/SoundPlayer.java diff --git a/libsrc/ffdec_lib/src/com/jpexs/helpers/SoundPlayer.java b/libsrc/ffdec_lib/src/com/jpexs/helpers/SoundPlayer.java deleted file mode 100644 index 647573be7..000000000 --- a/libsrc/ffdec_lib/src/com/jpexs/helpers/SoundPlayer.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2010-2014 JPEXS, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -package com.jpexs.helpers; - -import java.io.BufferedInputStream; -import java.io.IOException; -import java.io.InputStream; -import javax.sound.sampled.AudioSystem; -import javax.sound.sampled.Clip; -import javax.sound.sampled.Line; -import javax.sound.sampled.LineEvent; -import javax.sound.sampled.LineListener; -import javax.sound.sampled.LineUnavailableException; -import javax.sound.sampled.UnsupportedAudioFileException; - -/** - * - * @author JPEXS - */ -public class SoundPlayer { - - private final Clip clip; - - public SoundPlayer(InputStream is) throws LineUnavailableException, IOException, UnsupportedAudioFileException { - clip = (Clip) AudioSystem.getLine(new Line.Info(Clip.class)); - clip.open(AudioSystem.getAudioInputStream(new BufferedInputStream(is))); - } - - public long samplesCount() { - return clip.getMicrosecondLength(); - } - - public void play() { - - final SoundPlayer t = this; - clip.addLineListener(new LineListener() { - - @Override - public void update(LineEvent event) { - if (event.getType() == LineEvent.Type.STOP) { - //clip.close(); - - synchronized (t) { - t.notifyAll(); - } - } - } - }); - clip.start(); - try { - synchronized (this) { - wait(); - } - } catch (InterruptedException ex) { - // Ignore - } - } - - public long getSamplePosition() { - return clip.getMicrosecondPosition(); - } - - public void setPosition(long frames) { - clip.setMicrosecondPosition(frames); - } - - public void stop() { - clip.stop(); - } - - public boolean isPlaying() { - return clip.isActive(); - } - - public long getFrameRate() { - return 1000000L; - } - - @Override - protected void finalize() throws Throwable { - try { - if (clip != null) { - clip.close(); - } - } finally { - super.finalize(); - } - } - -} diff --git a/src/com/jpexs/decompiler/flash/gui/FolderPreviewPanel.java b/src/com/jpexs/decompiler/flash/gui/FolderPreviewPanel.java index 993619203..db1f49586 100644 --- a/src/com/jpexs/decompiler/flash/gui/FolderPreviewPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/FolderPreviewPanel.java @@ -61,6 +61,8 @@ public class FolderPreviewPanel extends JPanel { private List items; private int selectedIndex = -1; private boolean repaintQueued; + private int lastWidth; + private int lastHeight; public Map selectedItems = new HashMap<>(); @@ -217,8 +219,9 @@ public class FolderPreviewPanel extends JPanel { } } - Dimension size = getSize(); - if (size.width != width || size.height != height) { + if (lastWidth != width || lastHeight != height) { + lastWidth = width; + lastHeight = height; setSize(new Dimension(width, height)); } } diff --git a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java index 61ed0d2ea..02497e368 100644 --- a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java +++ b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java @@ -214,71 +214,66 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis if (e == null) { return; } - boolean draw2 = false; - synchronized (ImagePanel.class) { - lastMouseEvent = e; - boolean handCursor = false; - DepthState newStateUnderCursor = null; - if (timelined != null) { - Timeline tim = ((Timelined) timelined).getTimeline(); - BoundedTag bounded = (BoundedTag) timelined; - RECT rect = bounded.getRect(new HashSet()); - int width = rect.getWidth(); - double scale = 1.0; - /*if (width > swf.displayRect.getWidth()) { - scale = (double) swf.displayRect.getWidth() / (double) width; - }*/ - Matrix m = new Matrix(); - m.translate(-rect.Xmin, -rect.Ymin); - m.scale(scale); - Point p = e.getPoint(); - p = iconPanel.toImagePoint(p); - List objs = new ArrayList<>(); - String ret = ""; - if (p != null) { - int x = p.x; - int y = p.y; - objs = iconPanel.getObjectsUnderPoint(p); + lastMouseEvent = e; + boolean handCursor = false; + DepthState newStateUnderCursor = null; + if (timelined != null) { - ret += " [" + x + "," + y + "] : "; - } + Timeline tim = ((Timelined) timelined).getTimeline(); + BoundedTag bounded = (BoundedTag) timelined; + RECT rect = bounded.getRect(new HashSet()); + int width = rect.getWidth(); + double scale = 1.0; + /*if (width > swf.displayRect.getWidth()) { + scale = (double) swf.displayRect.getWidth() / (double) width; + }*/ + Matrix m = new Matrix(); + m.translate(-rect.Xmin, -rect.Ymin); + m.scale(scale); + Point p = e.getPoint(); + p = iconPanel.toImagePoint(p); + List objs = new ArrayList<>(); + String ret = ""; + if (p != null) { + int x = p.x; + int y = p.y; + objs = iconPanel.getObjectsUnderPoint(p); - boolean first = true; - for (int i = 0; i < objs.size(); i++) { - DepthState ds = objs.get(i); - if (!first) { - ret += ", "; - } - first = false; - CharacterTag c = tim.swf.characters.get(ds.characterId); - if (c instanceof ButtonTag) { - newStateUnderCursor = ds; - handCursor = true; - } - ret += c.toString(); - if (timelined instanceof ButtonTag) { - handCursor = true; - } - } - if (first) { - ret += " - "; - } - debugLabel.setText(ret); + ret += " [" + x + "," + y + "] : "; + } - if (handCursor) { - iconPanel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - } else { - iconPanel.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + boolean first = true; + for (int i = 0; i < objs.size(); i++) { + DepthState ds = objs.get(i); + if (!first) { + ret += ", "; } - if (newStateUnderCursor != stateUnderCursor) { - stateUnderCursor = newStateUnderCursor; - draw2 = true; + first = false; + CharacterTag c = tim.swf.characters.get(ds.characterId); + if (c instanceof ButtonTag) { + newStateUnderCursor = ds; + handCursor = true; + } + ret += c.toString(); + if (timelined instanceof ButtonTag) { + handCursor = true; } } - } - if (draw2) { - drawFrame(); + if (first) { + ret += " - "; + } + debugLabel.setText(ret); + + if (handCursor) { + iconPanel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + } else { + iconPanel.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + if (newStateUnderCursor != stateUnderCursor) { + stateUnderCursor = newStateUnderCursor; + drawFrame(); + } } } @@ -316,27 +311,33 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis @Override public void mouseEntered(MouseEvent e) { - drawFrame(); + synchronized (ImagePanel.class) { + drawFrame(); + } } @Override public void mouseExited(MouseEvent e) { stateUnderCursor = null; lastMouseEvent = null; - drawFrame(); + synchronized (ImagePanel.class) { + drawFrame(); + } hideMouseSelection(); } @Override public void mousePressed(MouseEvent e) { mouseButton = e.getButton(); - updatePos(e, true); - drawFrame(); - if (stateUnderCursor != null) { - ButtonTag b = (ButtonTag) swf.characters.get(stateUnderCursor.characterId); - DefineButtonSoundTag sounds = b.getSounds(); - if (sounds != null && sounds.buttonSoundChar2 != 0) { //OverUpToOverDown - playSound((SoundTag) swf.characters.get(sounds.buttonSoundChar2)); + synchronized (ImagePanel.class) { + updatePos(e, true); + drawFrame(); + if (stateUnderCursor != null) { + ButtonTag b = (ButtonTag) swf.characters.get(stateUnderCursor.characterId); + DefineButtonSoundTag sounds = b.getSounds(); + if (sounds != null && sounds.buttonSoundChar2 != 0) { //OverUpToOverDown + playSound((SoundTag) swf.characters.get(sounds.buttonSoundChar2)); + } } } } @@ -344,13 +345,15 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis @Override public void mouseReleased(MouseEvent e) { mouseButton = 0; - updatePos(e, true); - drawFrame(); - if (stateUnderCursor != null) { - ButtonTag b = (ButtonTag) swf.characters.get(stateUnderCursor.characterId); - DefineButtonSoundTag sounds = b.getSounds(); - if (sounds != null && sounds.buttonSoundChar3 != 0) { //OverDownToOverUp - playSound((SoundTag) swf.characters.get(sounds.buttonSoundChar3)); + synchronized (ImagePanel.class) { + updatePos(e, true); + drawFrame(); + if (stateUnderCursor != null) { + ButtonTag b = (ButtonTag) swf.characters.get(stateUnderCursor.characterId); + DefineButtonSoundTag sounds = b.getSounds(); + if (sounds != null && sounds.buttonSoundChar3 != 0) { //OverDownToOverUp + playSound((SoundTag) swf.characters.get(sounds.buttonSoundChar3)); + } } } } @@ -360,24 +363,26 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis @Override public void mouseMoved(MouseEvent e) { DepthState lastUnderCur = stateUnderCursor; - updatePos(e, true); - if (stateUnderCursor != null) { - if (lastUnderCur == null || lastUnderCur.instanceId != stateUnderCursor.instanceId) { - //New mouse entered - ButtonTag b = (ButtonTag) swf.characters.get(stateUnderCursor.characterId); - DefineButtonSoundTag sounds = b.getSounds(); - if (sounds != null && sounds.buttonSoundChar1 != 0) { //IddleToOverUp - playSound((SoundTag) swf.characters.get(sounds.buttonSoundChar1)); + synchronized (ImagePanel.class) { + updatePos(e, true); + if (stateUnderCursor != null) { + if (lastUnderCur == null || lastUnderCur.instanceId != stateUnderCursor.instanceId) { + //New mouse entered + ButtonTag b = (ButtonTag) swf.characters.get(stateUnderCursor.characterId); + DefineButtonSoundTag sounds = b.getSounds(); + if (sounds != null && sounds.buttonSoundChar1 != 0) { //IddleToOverUp + playSound((SoundTag) swf.characters.get(sounds.buttonSoundChar1)); + } } } - } - if (lastUnderCur != null) { - if (stateUnderCursor == null || stateUnderCursor.instanceId != lastUnderCur.instanceId) { - //Old mouse leave - ButtonTag b = (ButtonTag) swf.characters.get(lastUnderCur.characterId); - DefineButtonSoundTag sounds = b.getSounds(); - if (sounds != null && sounds.buttonSoundChar0 != 0) { //OverUpToIddle - playSound((SoundTag) swf.characters.get(sounds.buttonSoundChar0)); + if (lastUnderCur != null) { + if (stateUnderCursor == null || stateUnderCursor.instanceId != lastUnderCur.instanceId) { + //Old mouse leave + ButtonTag b = (ButtonTag) swf.characters.get(lastUnderCur.characterId); + DefineButtonSoundTag sounds = b.getSounds(); + if (sounds != null && sounds.buttonSoundChar0 != 0) { //OverUpToIddle + playSound((SoundTag) swf.characters.get(sounds.buttonSoundChar0)); + } } } } @@ -385,7 +390,9 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis @Override public void mouseDragged(MouseEvent e) { - updatePos(e, true); + synchronized (ImagePanel.class) { + updatePos(e, true); + } } }); @@ -394,7 +401,9 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis @Override public void zoom(double zoom) { this.zoom = zoom; - drawFrame(); + synchronized (ImagePanel.class) { + drawFrame(); + } } @Override @@ -510,7 +519,10 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis timer.cancel(); timer = null; } - stopAllSounds(); + + synchronized (ImagePanel.class) { + stopAllSounds(); + } } private void stopAllSounds() { @@ -601,7 +613,7 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis return img; } - private synchronized void drawFrame() { + private void drawFrame() { if (timelined == null) { return; } @@ -627,6 +639,7 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis } } } + for (int sndId : sounds) { CharacterTag c = swf.characters.get(sndId); if (c instanceof SoundTag) { @@ -681,6 +694,10 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis timer.cancel(); timer = null; } + + synchronized (ImagePanel.class) { + stopAllSounds(); + } } @Override @@ -694,26 +711,32 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis @Override public void run() { - Timeline timeline = timelined.getTimeline(); - if (timeline.getFrameCount() <= 1 && timeline.isSingleFrame()) { - if (first) { - drawFrame(); - first = false; + synchronized (ImagePanel.class) { + if (timer == null) { + return; + } + + Timeline timeline = timelined.getTimeline(); + if (timeline.getFrameCount() <= 1 && timeline.isSingleFrame()) { + if (first) { + drawFrame(); + first = false; + } + } else { + nextFrame(); } - } else { - nextFrame(); } } }, 0, frameRate == 0 ? Integer.MAX_VALUE : frameRate); - } else { - drawFrame(); } } @Override public void rewind() { frame = 0; - drawFrame(); + synchronized (ImagePanel.class) { + drawFrame(); + } } @Override @@ -740,7 +763,9 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis return; } this.frame = frame; - drawFrame(); + synchronized (ImagePanel.class) { + drawFrame(); + } } @Override diff --git a/src/com/jpexs/decompiler/flash/gui/SoundTagPlayer.java b/src/com/jpexs/decompiler/flash/gui/SoundTagPlayer.java index 9a2040815..3ac08a775 100644 --- a/src/com/jpexs/decompiler/flash/gui/SoundTagPlayer.java +++ b/src/com/jpexs/decompiler/flash/gui/SoundTagPlayer.java @@ -21,7 +21,6 @@ import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.gui.player.MediaDisplay; import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.tags.base.SoundTag; -import com.jpexs.helpers.SoundPlayer; import java.awt.Color; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; @@ -29,8 +28,11 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.Clip; +import javax.sound.sampled.Line; +import javax.sound.sampled.LineEvent; +import javax.sound.sampled.LineListener; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.UnsupportedAudioFileException; @@ -40,10 +42,11 @@ import javax.sound.sampled.UnsupportedAudioFileException; */ public class SoundTagPlayer implements MediaDisplay { - private final SoundPlayer player; + private final Clip clip; - private Thread thr; - private int actualPos = 0; + private int loopCount; + private boolean paused = true; + private final Object playLock = new Object(); private final SoundTag tag; private final List listeners = new ArrayList<>(); @@ -63,13 +66,9 @@ public class SoundTagPlayer implements MediaDisplay { private static final int FRAME_DIVISOR = 8000; - private int loops; - private boolean paused = true; - private final Object playLock = new Object(); - public SoundTagPlayer(SoundTag tag, int loops) throws LineUnavailableException, IOException, UnsupportedAudioFileException { this.tag = tag; - this.loops = loops; + this.loopCount = loops; ByteArrayOutputStream baos = new ByteArrayOutputStream(); List soundData = tag.getRawSoundData(); SWF swf = ((Tag) tag).getSwf(); @@ -78,107 +77,70 @@ public class SoundTagPlayer implements MediaDisplay { siss.add(new SWFInputStream(swf, data)); } tag.getSoundFormat().createWav(siss, baos); - player = new SoundPlayer(new ByteArrayInputStream(baos.toByteArray())); - } + clip = (Clip) AudioSystem.getLine(new Line.Info(Clip.class)); + clip.open(AudioSystem.getAudioInputStream(new ByteArrayInputStream(baos.toByteArray()))); - @Override - public synchronized int getCurrentFrame() { - - synchronized (playLock) { - if (isPlaying()) { - actualPos = (int) (player.getSamplePosition() / FRAME_DIVISOR); - } - return actualPos; - } - } - - @Override - public synchronized int getTotalFrames() { - - int ret = (int) (player.samplesCount() / FRAME_DIVISOR); - return ret; - } - - @Override - public synchronized void pause() { - if (!isPlaying()) { - paused = true; - return; - } - - synchronized (playLock) { - actualPos = (int) (player.getSamplePosition() / FRAME_DIVISOR); - } - - waitStop(); - } - - private void waitStop() { - synchronized (playLock) { - if (!paused) { - paused = true; - player.stop(); - try { - playLock.wait(); - } catch (InterruptedException ex) { - Logger.getLogger(SoundTagPlayer.class.getName()).log(Level.SEVERE, null, ex); - } - } - } - } - - public void play(boolean async) { - - waitStop(); - Runnable r = new Runnable() { + clip.addLineListener(new LineListener() { @Override - public void run() { - boolean playAgain = true; - while (playAgain) { - int startPos; + public void update(LineEvent event) { + if (event.getType() == LineEvent.Type.STOP) { + //clip.close(); synchronized (playLock) { - startPos = actualPos * FRAME_DIVISOR; - } - player.setPosition(startPos); - player.play(); - - synchronized (playLock) { - playAgain = !paused && loops > 0; if (!paused) { - if (loops == 0) { - paused = true; - } else if (loops != Integer.MAX_VALUE) { - loops--; + decreaseLoopCount(); + + if (loopCount > 0) { + clip.setFramePosition(0); + clip.start(); + } else { + fireFinished(); } } - if (playAgain) { - actualPos = 0; - } } } - - fireFinished(); - synchronized (playLock) { - playLock.notifyAll(); - } } - }; - synchronized (playLock) { - paused = false; - } - if (async) { - thr = new Thread(r); - thr.start(); - } else { - r.run(); - } - + }); } @Override - public synchronized void play() { - play(true); + public int getCurrentFrame() { + + synchronized (playLock) { + return (int) (clip.getMicrosecondPosition() / FRAME_DIVISOR); + } + } + + @Override + public int getTotalFrames() { + + synchronized (playLock) { + return (int) (clip.getMicrosecondLength() / FRAME_DIVISOR); + } + } + + @Override + public void pause() { + + synchronized (playLock) { + paused = true; + clip.stop(); + } + } + + @Override + public void play() { + synchronized (playLock) { + paused = false; + if (!clip.isActive()) { + if (clip.getMicrosecondLength() == clip.getMicrosecondPosition()) { + decreaseLoopCount(); + clip.setFramePosition(0); + } + + clip.start(); + } + } } @Override @@ -188,7 +150,7 @@ public class SoundTagPlayer implements MediaDisplay { @Override public boolean isPlaying() { - return player.isPlaying(); + return clip.isActive(); } @Override @@ -217,10 +179,17 @@ public class SoundTagPlayer implements MediaDisplay { } @Override - public synchronized void gotoFrame(int frame) { - pause(); + public void gotoFrame(int frame) { synchronized (playLock) { - actualPos = frame; + boolean active = clip.isActive(); + if (active) { + clip.stop(); + } + clip.setMicrosecondPosition(frame * FRAME_DIVISOR); + + if (active) { + clip.start(); + } } } @@ -231,10 +200,7 @@ public class SoundTagPlayer implements MediaDisplay { @Override public int getFrameRate() { - if (player == null) { - return 1; - } - return (int) (player.getFrameRate() / FRAME_DIVISOR); + return (int) (1000000L / FRAME_DIVISOR); } @Override @@ -247,4 +213,21 @@ public class SoundTagPlayer implements MediaDisplay { return null; } + @Override + protected void finalize() throws Throwable { + try { + if (clip != null) { + clip.close(); + } + } finally { + super.finalize(); + } + } + + private void decreaseLoopCount() { + // this method should be called from synchronized (playLock) block + if (loopCount > 0 && loopCount != Integer.MAX_VALUE) { + loopCount--; + } + } } diff --git a/src/com/jpexs/decompiler/flash/gui/player/PlayerControls.java b/src/com/jpexs/decompiler/flash/gui/player/PlayerControls.java index 0e6169480..51531a022 100644 --- a/src/com/jpexs/decompiler/flash/gui/player/PlayerControls.java +++ b/src/com/jpexs/decompiler/flash/gui/player/PlayerControls.java @@ -334,6 +334,9 @@ public class PlayerControls extends JPanel implements ActionListener { frameControls.setVisible(display.screenAvailable()); int totalFrames = display.getTotalFrames(); int currentFrame = display.getCurrentFrame(); + if (currentFrame >= totalFrames) { + currentFrame = totalFrames - 1; + } int frameRate = display.getFrameRate(); if (totalFrames == 0) { progress.setIndeterminate(true);