diff --git a/trunk/src/com/jpexs/decompiler/flash/SWF.java b/trunk/src/com/jpexs/decompiler/flash/SWF.java index 598b32b60..ceb5fadb3 100644 --- a/trunk/src/com/jpexs/decompiler/flash/SWF.java +++ b/trunk/src/com/jpexs/decompiler/flash/SWF.java @@ -235,6 +235,10 @@ public final class SWF implements TreeItem, Timelined { private Timeline timeline; + public void updateCharacters() { + parseCharacters(new ArrayList(tags)); + } + private void parseCharacters(List list) { for (ContainerItem t : list) { if (t instanceof CharacterTag) { @@ -563,7 +567,7 @@ public final class SWF implements TreeItem, Timelined { showFrameTag.innerTags = innerTags; } } - parseCharacters(new ArrayList(tags)); + updateCharacters(); assignExportNamesToSymbols(); assignClassesToSymbols(); findFileAttributes(); diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/ImagePanel.java b/trunk/src/com/jpexs/decompiler/flash/gui/ImagePanel.java index 34305035c..e59bf7fb6 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/ImagePanel.java +++ b/trunk/src/com/jpexs/decompiler/flash/gui/ImagePanel.java @@ -308,7 +308,7 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis return 0; } if (stillFrame) { - return 1; + return 0; } return timelined.getTimeline().frames.size(); } diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/trunk/src/com/jpexs/decompiler/flash/gui/MainPanel.java index 5d4a4da24..37a9546dd 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/trunk/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -157,6 +157,7 @@ import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; @@ -282,6 +283,9 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec private File tempFile; private PlayerControls imagePlayControls; + private JPanel repPanel; + private JPanel repPanel2; + private SoundThread soundThread = null; private class SoundThread implements MediaDisplay { @@ -422,6 +426,7 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec private static final String ACTION_SELECT_BKCOLOR = "SELECTCOLOR"; private static final String ACTION_REPLACE_IMAGE = "REPLACEIMAGE"; private static final String ACTION_REPLACE_BINARY = "REPLACEBINARY"; + private static final String ACTION_REPLACE_OTHER = "REPLACEOTHER"; private static final String ACTION_EDIT_GENERIC_TAG = "EDITGENERICTAG"; private static final String ACTION_SAVE_GENERIC_TAG = "SAVEGENERICTAG"; private static final String ACTION_CANCEL_GENERIC_TAG = "CANCELGENERICTAG"; @@ -507,6 +512,11 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec replaceBinarySelectionMenuItem.addActionListener(this); contextPopupMenu.add(replaceBinarySelectionMenuItem); + final JMenuItem replaceSoundSelectionMenuItem = new JMenuItem(translate("button.replace")); + replaceSoundSelectionMenuItem.setActionCommand(ACTION_REPLACE_OTHER); + replaceSoundSelectionMenuItem.addActionListener(this); + contextPopupMenu.add(replaceSoundSelectionMenuItem); + final JMenuItem closeSelectionMenuItem = new JMenuItem(translate("contextmenu.closeSwf")); closeSelectionMenuItem.setActionCommand(ACTION_CLOSE_SWF); closeSelectionMenuItem.addActionListener(this); @@ -548,6 +558,7 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec replaceImageSelectionMenuItem.setVisible(false); replaceBinarySelectionMenuItem.setVisible(false); + replaceSoundSelectionMenuItem.setVisible(false); closeSelectionMenuItem.setVisible(false); moveTagMenu.setVisible(false); expandRecursiveMenuItem.setVisible(false); @@ -564,6 +575,10 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec replaceBinarySelectionMenuItem.setVisible(true); } + if (item instanceof DefineSoundTag) { + replaceSoundSelectionMenuItem.setVisible(true); + } + if (treeNode instanceof SWFNode) { closeSelectionMenuItem.setVisible(true); } @@ -947,6 +962,13 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec } pan.add(leftComponent, BorderLayout.CENTER); + + repPanel = new JPanel(new FlowLayout()); + JButton repButton = new JButton(translate("button.replace"), View.getIcon("edit16")); + repButton.addActionListener(this); + repButton.setActionCommand(ACTION_REPLACE_OTHER); + repPanel.add(repButton); + pan.add(repPanel, BorderLayout.SOUTH); viewerCards.add(pan, FLASH_VIEWER_CARD); previewSplitPane.setLeftComponent(viewerCards); @@ -976,9 +998,19 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec prevIntLabel.setHorizontalAlignment(SwingConstants.CENTER); //prevIntLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); previewPanel.add(prevIntLabel, BorderLayout.NORTH); + + repPanel2 = new JPanel(new FlowLayout()); + JButton repButton2 = new JButton(translate("button.replace"), View.getIcon("edit16")); + repButton2.addActionListener(this); + repButton2.setActionCommand(ACTION_REPLACE_OTHER); + repPanel2.add(repButton2); + previewPanel.add(repPanel2, BorderLayout.SOUTH); + shapesCard.add(previewPanel, BorderLayout.CENTER); displayPanel.add(shapesCard, CARDDRAWPREVIEWPANEL); + displayReplace(false); + swfPreviewPanel = new ImagePanel(); displayPanel.add(swfPreviewPanel, CARDSWFPREVIEWPANEL); @@ -2251,13 +2283,79 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec reload(true); } break; - case ACTION_REPLACE_IMAGE: { + case ACTION_REPLACE_BINARY: + case ACTION_REPLACE_IMAGE: + case ACTION_REPLACE_OTHER: { TreeNode treeNode = (TreeNode) tagTree.getLastSelectedPathComponent(); if (treeNode == null) { return; } TreeItem item = treeNode.getItem(); + if (item instanceof DefineSoundTag) { + JFileChooser fc = new JFileChooser(); + fc.setCurrentDirectory(new File(Configuration.lastOpenDir.get())); + fc.setFileFilter(new FileFilter() { + @Override + public boolean accept(File f) { + return (f.getName().toLowerCase().endsWith(".mp3")) + || (f.getName().toLowerCase().endsWith(".wav")) + || (f.isDirectory()); + } + + @Override + public String getDescription() { + return translate("filter.sounds"); + } + }); + fc.addChoosableFileFilter(new FileFilter() { + @Override + public boolean accept(File f) { + return (f.getName().toLowerCase().endsWith(".mp3")) + || (f.isDirectory()); + } + + @Override + public String getDescription() { + return translate("filter.sounds.mp3"); + } + }); + fc.addChoosableFileFilter(new FileFilter() { + @Override + public boolean accept(File f) { + return (f.getName().toLowerCase().endsWith(".wav")) + || (f.isDirectory()); + } + + @Override + public String getDescription() { + return translate("filter.sounds.wav"); + } + }); + JFrame f = new JFrame(); + View.setWindowIcon(f); + int returnVal = fc.showOpenDialog(f); + if (returnVal == JFileChooser.APPROVE_OPTION) { + Configuration.lastOpenDir.set(Helper.fixDialogFile(fc.getSelectedFile()).getParentFile().getAbsolutePath()); + File selfile = Helper.fixDialogFile(fc.getSelectedFile()); + DefineSoundTag ds = (DefineSoundTag) item; + int soundFormat = DefineSoundTag.FORMAT_UNCOMPRESSED_LITTLE_ENDIAN; + if (selfile.getName().toLowerCase().endsWith(".mp3")) { + soundFormat = DefineSoundTag.FORMAT_MP3; + } + boolean ok = false; + try { + ok = ds.setSound(new FileInputStream(selfile), soundFormat); + } catch (IOException ex) { + //ignore + } + if (!ok) { + View.showMessageDialog(null, translate("error.sound.invalid"), translate("error"), JOptionPane.ERROR_MESSAGE); + } else { + reload(true); + } + } + } if (item instanceof ImageTag) { ImageTag it = (ImageTag) item; if (it.importSupported()) { @@ -2290,12 +2388,13 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec if (it instanceof DefineBitsTag) { DefineBitsJPEG2Tag jpeg2Tag = new DefineBitsJPEG2Tag(swf, it.getOriginalData(), it.getPos(), it.getCharacterId(), data); swf.tags.set(swf.tags.indexOf(it), jpeg2Tag); + swf.updateCharacters(); refreshTree(); setTreeItem(jpeg2Tag); } else { it.setImage(data); } - swf.clearImageCache(); + SWF.clearImageCache(); } catch (IOException ex) { Logger.getLogger(MainPanel.class.getName()).log(Level.SEVERE, "Invalid image", ex); View.showMessageDialog(null, translate("error.image.invalid"), translate("error"), JOptionPane.ERROR_MESSAGE); @@ -2304,15 +2403,6 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec } } } - } - break; - case ACTION_REPLACE_BINARY: { - TreeNode treeNode = (TreeNode) tagTree.getLastSelectedPathComponent(); - if (treeNode == null) { - return; - } - - TreeItem item = treeNode.getItem(); if (item instanceof DefineBinaryDataTag) { DefineBinaryDataTag bt = (DefineBinaryDataTag) item; JFileChooser fc = new JFileChooser(); @@ -2515,8 +2605,9 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec if (soundThread != null) { soundThread.pause(); } - imagePlayControls.setMedia(imagePanel); + imagePlayControls.setMedia(previewImagePanel); stopFlashPlayer(); + displayReplace(tagObj instanceof DefineSoundTag); if (tagObj instanceof ScriptPack) { final ScriptPack scriptLeaf = (ScriptPack) tagObj; final List abcList = scriptLeaf.abc.swf.abcList; @@ -3320,4 +3411,11 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec } }; } + + public void displayReplace(boolean display) { + + repPanel.setVisible(display); + repPanel2.setVisible(display); + + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties b/trunk/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties index 58fb60cfc..e49bce558 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties +++ b/trunk/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties @@ -455,3 +455,9 @@ generictag.array.insertend = Insert %item% at the end #after version 2.0.0 contextmenu.expandAll = Expand all binaryData.truncateWarning = %count% bytes truncated. + +filter.sounds = Supported sound formats (*.wav, *.mp3) +filter.sounds.wav = Wave file format (*.wav) +filter.sounds.mp3 = MP3 compressed format (*.mp3) + +error.sound.invalid = Invalid sound. \ No newline at end of file diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties b/trunk/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties index 993057cb5..7ca259b34 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties +++ b/trunk/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties @@ -453,3 +453,10 @@ generictag.array.insertbefore = Vlo\u017eit %item% p\u0159ed generictag.array.remove = Odstranit %item% generictag.array.insertafter = Vlo\u017eit %item% za generictag.array.insertend = Vlo\u017eit %item% na konec + + +filter.sounds = Podporovan\u00e9 zvukov\u00e9 form\u00e1ty (*.wav, *.mp3) +filter.sounds.wav = Wave form\u00e1t (*.wav) +filter.sounds.mp3 = MP3 komprimovan\u00fd form\u00e1t (*.mp3) + +error.sound.invalid = Neplatn\u00fd zvuk. \ No newline at end of file diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineSoundTag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineSoundTag.java index c3e245d7f..afffeebb8 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineSoundTag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineSoundTag.java @@ -22,10 +22,20 @@ import com.jpexs.decompiler.flash.SWFOutputStream; import com.jpexs.decompiler.flash.tags.base.CharacterTag; import com.jpexs.decompiler.flash.types.BasicType; import com.jpexs.decompiler.flash.types.annotations.SWFType; +import com.jpexs.decompiler.flash.types.sound.MP3FRAME; +import com.jpexs.decompiler.flash.types.sound.MP3SOUNDDATA; +import com.jpexs.helpers.Helper; +import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; +import java.util.Arrays; +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.UnsupportedAudioFileException; /** * @@ -83,6 +93,7 @@ public class DefineSoundTag extends CharacterTag { sos.writeUB(1, soundType ? 1 : 0); sos.writeUI32(soundSampleCount); sos.write(soundData); + } catch (IOException e) { } return baos.toByteArray(); @@ -123,4 +134,153 @@ public class DefineSoundTag extends CharacterTag { } return "flv"; } + + private void loadID3v2(InputStream in) { + int size = -1; + try { + // Read ID3v2 header (10 bytes). + in.mark(10); + size = readID3v2Header(in); + } catch (IOException e) { + } finally { + try { + // Unread ID3v2 header (10 bytes). + in.reset(); + } catch (IOException e) { + } + } + // Load ID3v2 tags. + try { + if (size > 0) { + byte[] rawid3v2 = new byte[size]; + in.read(rawid3v2, 0, rawid3v2.length); + } + } catch (IOException e) { + } + } + + /** + * Parse ID3v2 tag header to find out size of ID3v2 frames. + * + * @param in MP3 InputStream + * @return size of ID3v2 frames + header + * @throws IOException + * @author JavaZOOM + */ + private int readID3v2Header(InputStream in) throws IOException { + byte[] id3header = new byte[4]; + int size = -10; + in.read(id3header, 0, 3); + // Look for ID3v2 + if ((id3header[0] == 'I') && (id3header[1] == 'D') && (id3header[2] == '3')) { + in.read(id3header, 0, 3); + int majorVersion = id3header[0]; + int revision = id3header[1]; + in.read(id3header, 0, 4); + size = (int) (id3header[0] << 21) + (id3header[1] << 14) + (id3header[2] << 7) + (id3header[3]); + } + return (size + 10); + } + + public boolean setSound(InputStream is, int newSoundFormat) throws IOException { + int newSoundRate = -1; + boolean newSoundSize = false; + boolean newSoundType = false; + long newSoundSampleCount = -1; + byte newSoundData[]; + switch (newSoundFormat) { + case FORMAT_UNCOMPRESSED_LITTLE_ENDIAN: + try (AudioInputStream audioIs = AudioSystem.getAudioInputStream(new BufferedInputStream(is))) { + AudioFormat fmt = audioIs.getFormat(); + newSoundType = fmt.getChannels() == 2; + newSoundSize = fmt.getSampleSizeInBits() == 16; + newSoundSampleCount = audioIs.getFrameLength(); + newSoundData = Helper.readStream(audioIs); + newSoundRate = (int) Math.round(fmt.getSampleRate()); + switch (newSoundRate) { + case 5512: + newSoundRate = 0; + break; + case 11025: + newSoundRate = 1; + break; + case 22050: + newSoundRate = 2; + break; + case 44100: + newSoundRate = 3; + break; + default: + return false; + } + } catch (UnsupportedAudioFileException ex) { + return false; + } + break; + case FORMAT_MP3: + BufferedInputStream bis = new BufferedInputStream(is); + loadID3v2(bis); + byte mp3data[] = Helper.readStream(bis); + + final int ID3_V1_LENTGH = 128; + final int ID3_V1_EXT_LENGTH = 227; + + if (mp3data.length > ID3_V1_LENTGH) { + //ID3v1 + if (mp3data[mp3data.length - ID3_V1_LENTGH] == 'T' && mp3data[mp3data.length - ID3_V1_LENTGH + 1] == 'A' && mp3data[mp3data.length - ID3_V1_LENTGH + 2] == 'G') { + mp3data = Arrays.copyOf(mp3data, mp3data.length - ID3_V1_LENTGH); + if (mp3data.length > ID3_V1_EXT_LENGTH) { + //ID3v1 extended + if (mp3data[mp3data.length - ID3_V1_EXT_LENGTH] == 'T' && mp3data[mp3data.length - ID3_V1_EXT_LENGTH + 1] == 'A' && mp3data[mp3data.length - ID3_V1_EXT_LENGTH + 2] == 'G' && mp3data[mp3data.length - ID3_V1_EXT_LENGTH + 3] == '+') { + mp3data = Arrays.copyOf(mp3data, mp3data.length - ID3_V1_EXT_LENGTH); + } + } + } + } + MP3SOUNDDATA snd = new MP3SOUNDDATA(new ByteArrayInputStream(mp3data), true); + if (!snd.frames.isEmpty()) { + MP3FRAME fr = snd.frames.get(0); + newSoundRate = fr.getSamplingRate(); + switch (newSoundRate) { + case 11025: + newSoundRate = 1; + break; + case 22050: + newSoundRate = 2; + break; + case 44100: + newSoundRate = 3; + break; + default: + return false; + } + newSoundSize = true; + newSoundType = fr.isStereo(); + int len = snd.sampleCount(); + if (fr.isStereo()) { + len = len / 2; + } + newSoundSampleCount = len; + + } + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + SWFOutputStream sos = new SWFOutputStream(baos, SWF.DEFAULT_VERSION); + sos.writeSI16(0); //Latency - how to calculate it? + sos.write(mp3data); + newSoundData = baos.toByteArray(); + break; + default: + return false; + } + if (newSoundData != null) { + this.soundSize = newSoundSize; + this.soundRate = newSoundRate; + this.soundSampleCount = newSoundSampleCount; + this.soundData = newSoundData; + this.soundType = newSoundType; + this.soundFormat = newSoundFormat; + return true; + } + return false; + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/sound/MP3FRAME.java b/trunk/src/com/jpexs/decompiler/flash/types/sound/MP3FRAME.java index ac7a94c62..f990a7396 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/sound/MP3FRAME.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/sound/MP3FRAME.java @@ -16,14 +16,15 @@ */ package com.jpexs.decompiler.flash.types.sound; -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFInputStream; -import com.jpexs.decompiler.flash.SWFOutputStream; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.InputStream; import java.util.logging.Level; import java.util.logging.Logger; +import javazoom.jl.decoder.Bitstream; +import javazoom.jl.decoder.BitstreamException; +import javazoom.jl.decoder.Decoder; +import javazoom.jl.decoder.DecoderException; +import javazoom.jl.decoder.Header; +import javazoom.jl.decoder.SampleBuffer; /** * @@ -31,113 +32,75 @@ import java.util.logging.Logger; */ public class MP3FRAME { - public int syncWord; - public int mpegVersion; - public int layer; - public boolean protectionBit; - public int bitRate; - public int samplingRate; - public boolean paddingBit; - public int reserved; - public int channelMode; - public int modelExtension; - public boolean copyright; - public boolean original; - public int emphasis; - public byte[] sampleData; + private Header h; + private SampleBuffer samples; - public MP3FRAME(InputStream is) throws IOException { - SWFInputStream sis = new SWFInputStream(is, SWF.DEFAULT_VERSION); - syncWord = (int) sis.readUB(11); - mpegVersion = (int) sis.readUB(2); - layer = (int) sis.readUB(2); - protectionBit = sis.readUB(1) == 1; - bitRate = (int) sis.readUB(4); - samplingRate = (int) sis.readUB(2); - paddingBit = sis.readUB(1) == 1; - reserved = (int) sis.readUB(1); - channelMode = (int) sis.readUB(2); - modelExtension = (int) sis.readUB(2); + private MP3FRAME() { - int size = getSampleDataSize(); - int sizeBytes = (int) Math.ceil((double) size / 8.0); - sampleData = sis.readBytes(sizeBytes); } - public byte[] getData() { + public static MP3FRAME readFrame(Bitstream bitstream, Decoder decoder) throws IOException { + MP3FRAME ret = new MP3FRAME(); try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - SWFOutputStream sos = new SWFOutputStream(baos, SWF.DEFAULT_VERSION); - sos.writeUB(11, syncWord); - sos.writeUB(1, mpegVersion); - sos.writeUB(2, layer); - sos.writeUB(1, protectionBit ? 1 : 0); - sos.writeUB(4, bitRate); - sos.writeUB(2, samplingRate); - sos.writeUB(1, paddingBit ? 1 : 0); - sos.writeUB(1, reserved); - sos.writeUB(2, channelMode); - sos.writeUB(2, modelExtension); - sos.write(sampleData); - return baos.toByteArray(); - } catch (IOException ex) { + ret.h = bitstream.readFrame(); + if (ret.h == null) { + return null; + } + } catch (BitstreamException ex) { Logger.getLogger(MP3FRAME.class.getName()).log(Level.SEVERE, null, ex); } - return null; - } - - private int getSampleDataSize() { - return (((isMPEG1() ? 144 : 72) * bitRate) / samplingRate) + (paddingBit ? 1 : 0) - 4; - } - - public boolean isMPEG2() { - return mpegVersion == 2; - } - - public boolean isMPEG1() { - return mpegVersion == 3; - } - - public boolean isLayerI() { - return layer == 3; - } - - public boolean isLayerII() { - return layer == 2; - } - - public boolean isLayerIII() { - return layer == 1; - } - - public int getBitrate() { - int[] v1_l1_map = {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1}; - int[] v1_l2_map = {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1}; - int[] v1_l3_map = {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1}; - int[] v2_l1_map = {0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, -1}; - int[] v2_l2l3_map = {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1}; - - int[][][] map = { - {v1_l1_map, v1_l2_map, v1_l3_map}, - {v2_l1_map, v2_l2l3_map, v2_l2l3_map} - }; - int layer = 0; - if (isLayerI()) { - layer = 0; + try { + ret.samples = (SampleBuffer) decoder.decodeFrame(ret.h, bitstream); + } catch (DecoderException ex) { + Logger.getLogger(MP3FRAME.class.getName()).log(Level.SEVERE, null, ex); } - if (isLayerII()) { - layer = 1; + bitstream.closeFrame(); + return ret; + } + + public boolean isStereo() { + return h.mode() != Header.SINGLE_CHANNEL; + } + + public SampleBuffer getSamples() { + return samples; + } + + public int getSamplingRate() { + switch (h.sample_frequency()) { + case Header.THIRTYTWO: + if (h.version() == Header.MPEG1) { + return 32000; + } else if (h.version() == Header.MPEG2_LSF) { + return 16000; + } else // SZD + { + return 8000; + } + case Header.FOURTYFOUR_POINT_ONE: + if (h.version() == Header.MPEG1) { + return 44100; + } else if (h.version() == Header.MPEG2_LSF) { + return 22050; + } else // SZD + { + return 11025; + } + case Header.FOURTYEIGHT: + if (h.version() == Header.MPEG1) { + return 48000; + } else if (h.version() == Header.MPEG2_LSF) { + return 24000; + } else // SZD + { + return 12000; + } + default: + return 0; } - if (isLayerIII()) { - layer = 2; - } - int mver = 0; - if (isMPEG1()) { - mver = 0; - } - if (isMPEG2()) { - mver = 1; - } - return map[mver][layer][bitRate]; + } + + public int getBitRate() { + return h.bitrate(); } } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/sound/MP3SOUNDDATA.java b/trunk/src/com/jpexs/decompiler/flash/types/sound/MP3SOUNDDATA.java index 65668a1c0..eb082db8f 100644 --- a/trunk/src/com/jpexs/decompiler/flash/types/sound/MP3SOUNDDATA.java +++ b/trunk/src/com/jpexs/decompiler/flash/types/sound/MP3SOUNDDATA.java @@ -22,6 +22,8 @@ import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; +import javazoom.jl.decoder.Bitstream; +import javazoom.jl.decoder.Decoder; /** * @@ -32,12 +34,25 @@ public class MP3SOUNDDATA { public int seekSamples; public List frames; - public MP3SOUNDDATA(InputStream is) throws IOException { + public MP3SOUNDDATA(InputStream is, boolean raw) throws IOException { SWFInputStream sis = new SWFInputStream(is, SWF.DEFAULT_VERSION); - seekSamples = sis.readUI16(); + if (!raw) { + seekSamples = sis.readSI16(); + } frames = new ArrayList<>(); - while (sis.available() > 0) { - frames.add(new MP3FRAME(sis)); + MP3FRAME f; + Decoder decoder = new Decoder(); + Bitstream bitstream = new Bitstream(is); + while ((f = MP3FRAME.readFrame(bitstream, decoder)) != null) { + frames.add(f); } } + + public int sampleCount() { + int r = 0; + for (MP3FRAME f : frames) { + r += f.getSamples().getBufferLength(); + } + return r; + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java b/trunk/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java index e68f2ac51..0405cd3df 100644 --- a/trunk/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java +++ b/trunk/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java @@ -91,6 +91,7 @@ import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord; import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; import com.jpexs.decompiler.flash.types.sound.MP3FRAME; +import com.jpexs.decompiler.flash.types.sound.MP3SOUNDDATA; import com.jpexs.decompiler.graph.ExportMode; import com.jpexs.helpers.SerializableImage; import com.jpexs.helpers.utf8.Utf8Helper; @@ -1441,9 +1442,11 @@ public class XFLConverter { format += 4; //quality best SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(soundData), swf.version); try { - sis.readSI16(); - MP3FRAME frame = new MP3FRAME(sis); - int bitRate = frame.getBitrate(); + MP3SOUNDDATA s = new MP3SOUNDDATA(sis, false); + //sis.readSI16(); + //MP3FRAME frame = new MP3FRAME(sis); + MP3FRAME frame = s.frames.get(0); + int bitRate = frame.getBitRate(); switch (bitRate) { case 8: diff --git a/trunk/src/com/jpexs/helpers/sound/MP3Player.java b/trunk/src/com/jpexs/helpers/sound/MP3Player.java index 360d7e36c..40a5f411c 100644 --- a/trunk/src/com/jpexs/helpers/sound/MP3Player.java +++ b/trunk/src/com/jpexs/helpers/sound/MP3Player.java @@ -46,6 +46,7 @@ public class MP3Player extends SoundPlayer { private int positionSamples = 0; private long sampleCount; private int frameRate; + private int positionFrames = 0; public MP3Player(InputStream stream, long sampleCount, int frameRate) throws JavaLayerException { this(stream, null); @@ -118,6 +119,7 @@ public class MP3Player extends SoundPlayer { } SampleBuffer output = (SampleBuffer) decoder.decodeFrame(h, bitstream); positionSamples += output.getBufferLength(); + positionFrames++; bitstream.closeFrame(); if (positionSamples >= endPosition) { break; @@ -163,6 +165,7 @@ public class MP3Player extends SoundPlayer { SampleBuffer output = (SampleBuffer) decoder.decodeFrame(h, bitstream); positionSamples += output.getBufferLength(); + positionFrames++; synchronized (this) { out = audio;