diff --git a/CHANGELOG.md b/CHANGELOG.md index 951fb24c2..00f390067 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ All notable changes to this project will be documented in this file. - Linux ffdec script without extension - [PR190] Collect depth as sprites - Updated Dutch translation +- [#2259] Optional resampling sound to 44kHz on playback and on export ### Fixed - Debugger - getting children of top level variables @@ -3396,6 +3397,7 @@ Major version of SWF to XML export changed to 2. [#2176]: https://www.free-decompiler.com/flash/issues/2176 [#2179]: https://www.free-decompiler.com/flash/issues/2179 [#2185]: https://www.free-decompiler.com/flash/issues/2185 +[#2259]: https://www.free-decompiler.com/flash/issues/2259 [#2149]: https://www.free-decompiler.com/flash/issues/2149 [#2172]: https://www.free-decompiler.com/flash/issues/2172 [#2174]: https://www.free-decompiler.com/flash/issues/2174 diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java index a5320a4ac..57777e12c 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java @@ -3251,8 +3251,8 @@ public final class SWF implements SWFContainerItem, Timelined, Openable { } } - public void putToCache(SOUNDINFO soundInfo, SoundTag soundTag, byte[] data) { - SoundInfoSoundCacheEntry key = new SoundInfoSoundCacheEntry(soundInfo, soundTag); + public void putToCache(SOUNDINFO soundInfo, SoundTag soundTag, boolean resample, byte[] data) { + SoundInfoSoundCacheEntry key = new SoundInfoSoundCacheEntry(soundInfo, soundTag, resample); soundCache.put(key, data); } @@ -3399,8 +3399,8 @@ public final class SWF implements SWFContainerItem, Timelined, Openable { return null; } - public byte[] getFromCache(SOUNDINFO soundInfo, SoundTag soundTag) { - SoundInfoSoundCacheEntry key = new SoundInfoSoundCacheEntry(soundInfo, soundTag); + public byte[] getFromCache(SOUNDINFO soundInfo, SoundTag soundTag, boolean resample) { + SoundInfoSoundCacheEntry key = new SoundInfoSoundCacheEntry(soundInfo, soundTag, resample); if (soundCache.contains(key)) { return soundCache.get(key); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java index be5786603..4c7b5eb9e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java @@ -993,6 +993,14 @@ public final class Configuration { @ConfigurationCategory("export") public static ConfigurationItem flaExportFixShapes = null; + @ConfigurationDefaultBoolean(false) + @ConfigurationCategory("export") + public static ConfigurationItem lastExportResampleWav = null; + + @ConfigurationDefaultBoolean(true) + @ConfigurationCategory("display") + public static ConfigurationItem previewResampleSound = null; + private enum OSId { WINDOWS, OSX, UNIX } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/SoundExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/SoundExporter.java index c6a4182ae..be4de5671 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/SoundExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/SoundExporter.java @@ -113,7 +113,7 @@ public class SoundExporter { final File file = new File(outdir + File.separator + Helper.makeFileName(st.getCharacterExportFileName()) + ext); new RetryTask(() -> { try (OutputStream os = new BufferedOutputStream(new FileOutputStream(file))) { - exportSound(os, st, settings.mode); + exportSound(os, st, settings.mode, settings.resampleWav); } }, handler).run(); @@ -144,13 +144,13 @@ public class SoundExporter { return ret; } - public byte[] exportSound(SoundTag t, SoundExportMode mode) throws IOException { + public byte[] exportSound(SoundTag t, SoundExportMode mode, boolean resampleWav) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); - exportSound(baos, t, mode); + exportSound(baos, t, mode, resampleWav); return baos.toByteArray(); } - public void exportSound(OutputStream fos, SoundTag st, SoundExportMode mode) throws IOException { + public void exportSound(OutputStream fos, SoundTag st, SoundExportMode mode, boolean resampleWav) throws IOException { SoundFormat fmt = st.getSoundFormat(); SoundExportFormat nativeFormat = fmt.getNativeExportFormat(); @@ -194,7 +194,7 @@ public class SoundExporter { } } else { List soundData = st.getRawSoundData(); - fmt.createWav(null, soundData, fos, st.getInitialLatency()); + fmt.createWav(null, soundData, fos, st.getInitialLatency(), resampleWav); } } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/AS3ScriptExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/AS3ScriptExporter.java index 9ca374883..40d56e3b9 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/AS3ScriptExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/AS3ScriptExporter.java @@ -536,7 +536,7 @@ public class AS3ScriptExporter { return ret; } SoundExporter se = new SoundExporter(); - se.exportSounds(handler, ASSETS_DIR, rttl, new SoundExportSettings(SoundExportMode.MP3_WAV), evl); + se.exportSounds(handler, ASSETS_DIR, rttl, new SoundExportSettings(SoundExportMode.MP3_WAV, exportSettings.resampleWav), evl); if (Thread.currentThread().isInterrupted()) { return ret; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/settings/ScriptExportSettings.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/settings/ScriptExportSettings.java index de3228ee9..403e45bd8 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/settings/ScriptExportSettings.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/settings/ScriptExportSettings.java @@ -38,19 +38,23 @@ public class ScriptExportSettings { public boolean exportEmbed; public boolean exportEmbedFlaMode; + + public boolean resampleWav; public ScriptExportSettings( ScriptExportMode mode, boolean singleFile, boolean ignoreFrameScripts, boolean exportEmbed, - boolean exportEmbedFlaMode + boolean exportEmbedFlaMode, + boolean resampleWav ) { this.mode = mode; this.singleFile = singleFile; this.ignoreFrameScripts = ignoreFrameScripts; this.exportEmbed = exportEmbed; this.exportEmbedFlaMode = exportEmbedFlaMode; + this.resampleWav = resampleWav; } public String getFileExtension() { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/settings/SoundExportSettings.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/settings/SoundExportSettings.java index 656aa7500..9e7708bd9 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/settings/SoundExportSettings.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/settings/SoundExportSettings.java @@ -27,8 +27,11 @@ public class SoundExportSettings { public static final String EXPORT_FOLDER_NAME = "sounds"; public SoundExportMode mode; + + public boolean resampleWav; - public SoundExportSettings(SoundExportMode mode) { + public SoundExportSettings(SoundExportMode mode, boolean resampleWav) { this.mode = mode; + this.resampleWav = resampleWav; } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/flexsdk/MxmlcAs3ScriptReplacer.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/flexsdk/MxmlcAs3ScriptReplacer.java index 721925264..0f9e93b8d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/flexsdk/MxmlcAs3ScriptReplacer.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/flexsdk/MxmlcAs3ScriptReplacer.java @@ -322,7 +322,7 @@ public class MxmlcAs3ScriptReplacer extends MxmlcRunner implements As3ScriptRepl //This compiled code won't be used at all in original SWF, //it is used only by Flex to properly compile current script AS3ScriptExporter ex = new AS3ScriptExporter(); - ex.exportActionScript3(swfCopy, null, tempDir.getAbsolutePath(), removedPacks, new ScriptExportSettings(ScriptExportMode.AS_METHOD_STUBS, false, false, false /* ??? FIXME */, false), false, null); + ex.exportActionScript3(swfCopy, null, tempDir.getAbsolutePath(), removedPacks, new ScriptExportSettings(ScriptExportMode.AS_METHOD_STUBS, false, false, false /* ??? FIXME */, false, true), false, null); //now really remove the classes from SWF copy for (ABC a : modAbcs) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/AS3ScriptImporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/AS3ScriptImporter.java index 0adc0d836..122c5b819 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/AS3ScriptImporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/AS3ScriptImporter.java @@ -55,7 +55,7 @@ public class AS3ScriptImporter { continue; } try { - File file = pack.getExportFile(scriptsFolder, new ScriptExportSettings(ScriptExportMode.AS, false, false, false, false)); + File file = pack.getExportFile(scriptsFolder, new ScriptExportSettings(ScriptExportMode.AS, false, false, false, false, true)); if (file.exists()) { Openable openable = pack.getOpenable(); SWF swf = (openable instanceof SWF) ? (SWF) openable : ((ABC) openable).getSwf(); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/sound/SoundFormat.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/sound/SoundFormat.java index 1d8383672..6c9fc2594 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/sound/SoundFormat.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/sound/SoundFormat.java @@ -224,7 +224,78 @@ public class SoundFormat { return decodedData; } - public boolean createWav(SOUNDINFO soundInfo, List dataRanges, OutputStream os, int skipSamples) throws IOException { + private byte[] resample(byte[] decodedData) throws IOException { + if (samplingRate == 44100) { + return decodedData; + } + boolean resamplingFromStereo = true; + + ByteArrayOutputStream baosResampled = new ByteArrayOutputStream(); + for (int i = 0; i < decodedData.length; i += (resamplingFromStereo ? 4 : 2)) { + if (i + 1 >= decodedData.length) { + break; + } + int left = ((decodedData[i] & 0xff) + ((decodedData[i + 1] & 0xff) << 8)) << 16 >> 16; + int right = left; + if (resamplingFromStereo) + { + if (i + 3 >= decodedData.length) { + break; + } + right = ((decodedData[i + 2] & 0xff) + ((decodedData[i + 3] & 0xff) << 8)) << 16 >> 16; + } + + int nextLeft = left; + int nextRight = right; + int nextI = i + (resamplingFromStereo ? 4 : 2); + if (nextI < decodedData.length) { + nextLeft = ((decodedData[nextI] & 0xff) + ((decodedData[nextI + 1] & 0xff) << 8)) << 16 >> 16; + nextRight = nextLeft; + if (resamplingFromStereo) { + if (nextI + 3 >= decodedData.length) { + //ignore + } else { + nextRight = ((decodedData[nextI + 2] & 0xff) + ((decodedData[nextI + 3] & 0xff) << 8)) << 16 >> 16; + } + } + } + + writeLE(baosResampled, left, 2); + writeLE(baosResampled, right, 2); + if (samplingRate == 5512) { + writeLE(baosResampled, left + (nextLeft - left) / 8, 2); + writeLE(baosResampled, right + (nextRight - right) / 8, 2); + writeLE(baosResampled, left + (nextLeft - left) * 2 / 8, 2); + writeLE(baosResampled, right + (nextRight - right) * 2 / 8, 2); + writeLE(baosResampled, left + (nextLeft - left) * 3 / 8, 2); + writeLE(baosResampled, right + (nextRight - right) * 3 / 8, 2); + writeLE(baosResampled, left + (nextLeft - left) * 4 / 8, 2); + writeLE(baosResampled, right + (nextRight - right) * 4 / 8, 2); + writeLE(baosResampled, left + (nextLeft - left) * 5 / 8, 2); + writeLE(baosResampled, right + (nextRight - right) * 5 / 8, 2); + writeLE(baosResampled, left + (nextLeft - left) * 6 / 8, 2); + writeLE(baosResampled, right + (nextRight - right) * 6 / 8, 2); + writeLE(baosResampled, left + (nextLeft - left) * 7 / 8, 2); + writeLE(baosResampled, right + (nextRight - right) * 7 / 8, 2); + } + if (samplingRate == 11025) { + writeLE(baosResampled, left + (nextLeft - left) / 4, 2); + writeLE(baosResampled, right + (nextRight - right) / 4, 2); + writeLE(baosResampled, left + (nextLeft - left) * 2 / 4, 2); + writeLE(baosResampled, right + (nextRight - right) * 2 / 4, 2); + writeLE(baosResampled, left + (nextLeft - left) * 3 / 4, 2); + writeLE(baosResampled, right + (nextRight - right) * 3 / 4, 2); + } + if (samplingRate == 22050) + { + writeLE(baosResampled, (left + nextLeft) / 2, 2); + writeLE(baosResampled, (right + nextRight) / 2, 2); + } + } + return baosResampled.toByteArray(); + } + + public boolean createWav(SOUNDINFO soundInfo, List dataRanges, OutputStream os, int skipSamples, boolean resample) throws IOException { byte[] decodedData = decode(soundInfo, dataRanges, skipSamples); boolean convertedStereo = stereo; @@ -252,8 +323,8 @@ public class SoundFormat { break; } right = ((decodedData[i + 2] & 0xff) + ((decodedData[i + 3] & 0xff) << 8)) << 16 >> 16; - } - + } + if (soundInfo.hasEnvelope) { for (int e = 0; e < soundInfo.envelopeRecords.length - 1; e++) { int envPosBytes = inPointBytes + (int) (soundInfo.envelopeRecords[e].pos44 * samplingRate / 44100.0 * 2 * (stereo ? 2 : 1)); @@ -274,13 +345,16 @@ public class SoundFormat { } writeLE(baosFiltered, left, 2); - writeLE(baosFiltered, right, 2); + writeLE(baosFiltered, right, 2); } convertedStereo = true; } + + + byte[] resampled = resample ? resample(baosFiltered.toByteArray()) : baosFiltered.toByteArray(); try { - createWavFromPcmData(os, samplingRate, true, convertedStereo, baosFiltered.toByteArray()); + createWavFromPcmData(os, resample ? 44100 : samplingRate, true, convertedStereo, resampled); return true; } catch (IOException ex) { return false; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/sound/SoundInfoSoundCacheEntry.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/sound/SoundInfoSoundCacheEntry.java index b4bc564b6..e25813c11 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/sound/SoundInfoSoundCacheEntry.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/sound/SoundInfoSoundCacheEntry.java @@ -28,17 +28,20 @@ public class SoundInfoSoundCacheEntry { public SOUNDINFO soundInfo; public SoundTag soundTag; + public boolean resample; - public SoundInfoSoundCacheEntry(SOUNDINFO soundInfo, SoundTag soundTag) { + public SoundInfoSoundCacheEntry(SOUNDINFO soundInfo, SoundTag soundTag, boolean resample) { this.soundInfo = soundInfo; this.soundTag = soundTag; + this.resample = resample; } @Override public int hashCode() { int hash = 7; - hash = 47 * hash + Objects.hashCode(this.soundInfo); - hash = 47 * hash + Objects.hashCode(this.soundTag); + hash = 97 * hash + Objects.hashCode(this.soundInfo); + hash = 97 * hash + Objects.hashCode(this.soundTag); + hash = 97 * hash + (this.resample ? 1 : 0); return hash; } @@ -54,13 +57,13 @@ public class SoundInfoSoundCacheEntry { return false; } final SoundInfoSoundCacheEntry other = (SoundInfoSoundCacheEntry) obj; + if (this.resample != other.resample) { + return false; + } if (!Objects.equals(this.soundInfo, other.soundInfo)) { return false; } - if (!Objects.equals(this.soundTag, other.soundTag)) { - return false; - } - return true; + return Objects.equals(this.soundTag, other.soundTag); } - + } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java index 9bfe49e1a..e54d31cf1 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java @@ -2139,7 +2139,7 @@ public class XFLConverter { SoundFormat fmt = st.getSoundFormat(); byte[] data = SWFInputStream.BYTE_ARRAY_EMPTY; try { - data = new SoundExporter().exportSound(st, convertMp3ToWav ? SoundExportMode.WAV : SoundExportMode.MP3_WAV); + data = new SoundExporter().exportSound(st, convertMp3ToWav ? SoundExportMode.WAV : SoundExportMode.MP3_WAV, true); } catch (IOException ex) { logger.log(Level.SEVERE, null, ex); } @@ -5345,7 +5345,7 @@ public class XFLConverter { } if (useAS3 && settings.exportScript) { try { - ScriptExportSettings scriptExportSettings = new ScriptExportSettings(ScriptExportMode.AS, false, true, false, true); + ScriptExportSettings scriptExportSettings = new ScriptExportSettings(ScriptExportMode.AS, false, true, false, true, true); swf.exportActionScript(handler, scriptsDir.getAbsolutePath(), scriptExportSettings, parallel, null); } catch (Exception ex) { logger.log(Level.SEVERE, "Error during ActionScript3 export", ex); diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ExportTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ExportTest.java index 3e2b18c15..c8caa2863 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ExportTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ExportTest.java @@ -108,7 +108,7 @@ public class ExportTest extends FileTestBase { return this; } - }, fdir.getAbsolutePath(), new ScriptExportSettings(exportMode, false, false, false, false), false, null); + }, fdir.getAbsolutePath(), new ScriptExportSettings(exportMode, false, false, false, false, true), false, null); } catch (Exception ex) { fail("Exception during decompilation: " + filePath + " " + ex.getMessage()); } diff --git a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java index c76261ad1..c133a3918 100644 --- a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java +++ b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java @@ -502,6 +502,7 @@ public class CommandLineArgumentParser { boolean cliMode = false; boolean air = false; boolean exportEmbed = false; + boolean resampleWav = false; Selection selection = new Selection(); Selection selectionIds = new Selection(); List selectionClasses = null; @@ -535,6 +536,9 @@ public class CommandLineArgumentParser { case "-exportembed": exportEmbed = true; break; + case "-resamplewav": + resampleWav = true; + break; case "-zoom": zoom = parseZoom(args); break; @@ -649,7 +653,7 @@ public class CommandLineArgumentParser { } else if (command.equals("proxy")) { parseProxy(args); } else if (command.equals("export")) { - parseExport(selectionClasses, selection, selectionIds, args, handler, traceLevel, format, zoom, charset, exportEmbed); + parseExport(selectionClasses, selection, selectionIds, args, handler, traceLevel, format, zoom, charset, exportEmbed, resampleWav); System.exit(0); } else if (command.equals("compress")) { parseCompress(args); @@ -1914,7 +1918,7 @@ public class CommandLineArgumentParser { } - private static void parseExport(List selectionClasses, Selection selection, Selection selectionIds, Stack args, AbortRetryIgnoreHandler handler, Level traceLevel, Map formats, double zoom, String charset, boolean exportEmbed) { + private static void parseExport(List selectionClasses, Selection selection, Selection selectionIds, Stack args, AbortRetryIgnoreHandler handler, Level traceLevel, Map formats, double zoom, String charset, boolean exportEmbed, boolean resampleWav) { if (args.size() < 3) { badArguments("export"); } @@ -2111,7 +2115,7 @@ public class CommandLineArgumentParser { if (exportAll || exportFormats.contains("sound")) { System.out.println("Exporting sounds..."); - new SoundExporter().exportSounds(handler, outDir + (multipleExportTypes ? File.separator + SoundExportSettings.EXPORT_FOLDER_NAME : ""), new ReadOnlyTagList(extags), new SoundExportSettings(enumFromStr(formats.get("sound"), SoundExportMode.class)), evl); + new SoundExporter().exportSounds(handler, outDir + (multipleExportTypes ? File.separator + SoundExportSettings.EXPORT_FOLDER_NAME : ""), new ReadOnlyTagList(extags), new SoundExportSettings(enumFromStr(formats.get("sound"), SoundExportMode.class), resampleWav), evl); } if (exportAll || exportFormats.contains("binarydata")) { @@ -2178,7 +2182,7 @@ public class CommandLineArgumentParser { singleScriptFile = false; } - ScriptExportSettings scriptExportSettings = new ScriptExportSettings(enumFromStr(formats.get("script"), ScriptExportMode.class), singleScriptFile, false, exportEmbed, false); + ScriptExportSettings scriptExportSettings = new ScriptExportSettings(enumFromStr(formats.get("script"), ScriptExportMode.class), singleScriptFile, false, exportEmbed, false, resampleWav); boolean exportAllScript = exportAll || exportFormats.contains("script"); boolean exportAs2Script = exportAllScript || exportFormats.contains("script_as2"); boolean exportAs3Script = exportAllScript || exportFormats.contains("script_as3"); diff --git a/src/com/jpexs/decompiler/flash/console/help.txt b/src/com/jpexs/decompiler/flash/console/help.txt index 417ff6b00..a16e83851 100644 --- a/src/com/jpexs/decompiler/flash/console/help.txt +++ b/src/com/jpexs/decompiler/flash/console/help.txt @@ -426,4 +426,9 @@ Pre-options: -air Applies to: -replace, -importScript - Use AIR ("airglobal.swc") for AS3 compilation instead of "playerglobal.swc". \ No newline at end of file + Use AIR ("airglobal.swc") for AS3 compilation instead of "playerglobal.swc". + +-resamplewav + Applies to: -export + Enables resampling exported WAV sound files to 44kHz. + diff --git a/src/com/jpexs/decompiler/flash/gui/ExportDialog.java b/src/com/jpexs/decompiler/flash/gui/ExportDialog.java index 9c4d65b28..8ad0def3e 100644 --- a/src/com/jpexs/decompiler/flash/gui/ExportDialog.java +++ b/src/com/jpexs/decompiler/flash/gui/ExportDialog.java @@ -33,7 +33,6 @@ import com.jpexs.decompiler.flash.exporters.modes.SpriteExportMode; import com.jpexs.decompiler.flash.exporters.modes.SymbolClassExportMode; import com.jpexs.decompiler.flash.exporters.modes.TextExportMode; import com.jpexs.decompiler.flash.gui.tagtree.TagTreeModel; -import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; import com.jpexs.decompiler.flash.tags.DefineFont4Tag; import com.jpexs.decompiler.flash.tags.DefineSpriteTag; import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag; @@ -57,6 +56,7 @@ import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Window; import java.awt.event.ActionEvent; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Locale; @@ -147,6 +147,8 @@ public class ExportDialog extends AppDialog { private JTextField zoomTextField = new JTextField(); private JCheckBox embedCheckBox; + + private JCheckBox resampleWavCheckBox; public E getValue(Class option) { for (int i = 0; i < optionClasses.length; i++) { @@ -172,6 +174,10 @@ public class ExportDialog extends AppDialog { public boolean isEmbedEnabled() { return embedCheckBox.isSelected(); } + + public boolean isResampleWavEnabled() { + return resampleWavCheckBox.isSelected(); + } public double getZoom() { return Double.parseDouble(zoomTextField.getText()) / 100; @@ -192,6 +198,12 @@ public class ExportDialog extends AppDialog { Configuration.lastSelectedExportZoom.set(Double.parseDouble(zoomTextField.getText()) / 100); Configuration.lastSelectedExportFormats.set(cfg.toString()); + if (embedCheckBox.isVisible()) { + Configuration.lastExportEnableEmbed.set(embedCheckBox.isSelected()); + } + if (resampleWavCheckBox.isVisible()) { + Configuration.lastExportResampleWav.set(resampleWavCheckBox.isSelected()); + } } private boolean optionCanHandle(int optionIndex, Object e) { @@ -284,6 +296,8 @@ public class ExportDialog extends AppDialog { comboPanel.add(selectAllCheckBox); top += selectAllCheckBox.getHeight(); + List visibleOptionClasses = new ArrayList<>(); + boolean zoomable = false; for (int i = 0; i < optionNames.length; i++) { Class c = optionClasses[i]; @@ -317,6 +331,8 @@ public class ExportDialog extends AppDialog { if (Arrays.asList(zoomClasses).contains(c)) { zoomable = true; } + + visibleOptionClasses.add(c); JLabel lab = new JLabel(translate(optionNames[i])); lab.setBounds(10, top, lab.getPreferredSize().width, lab.getPreferredSize().height); @@ -327,6 +343,7 @@ public class ExportDialog extends AppDialog { } embedCheckBox = new JCheckBox(translate("embed")); + embedCheckBox.setVisible(false); boolean hasAs3 = false; if (exportables == null) { @@ -342,7 +359,8 @@ public class ExportDialog extends AppDialog { int w = 10 + labWidth + 10 + checkBoxWidth + 10 + comboWidth + 10; - if (hasAs3 && Arrays.asList(optionClasses).contains(ScriptExportMode.class)) { + if (hasAs3 && visibleOptionClasses.contains(ScriptExportMode.class)) { + embedCheckBox.setVisible(true); top += 2; embedCheckBox.setBounds(10, top, embedCheckBox.getPreferredSize().width, embedCheckBox.getPreferredSize().height); comboPanel.add(embedCheckBox); @@ -353,8 +371,28 @@ public class ExportDialog extends AppDialog { } if (Configuration.lastExportEnableEmbed.get()) { embedCheckBox.setSelected(true); - } + } } + + resampleWavCheckBox = new JCheckBox(translate("resampleWav")); + resampleWavCheckBox.setVisible(false); + + + if (embedCheckBox.isVisible() ||visibleOptionClasses.contains(SoundExportMode.class)) { + top += 2; + resampleWavCheckBox.setVisible(true); + comboPanel.add(resampleWavCheckBox); + if (Configuration.lastExportResampleWav.get()) { + resampleWavCheckBox.setSelected(true); + } + + resampleWavCheckBox.setBounds(10, top, resampleWavCheckBox.getPreferredSize().width, resampleWavCheckBox.getPreferredSize().height); + top += resampleWavCheckBox.getHeight(); + + if (resampleWavCheckBox.getWidth() + 10 > w) { + w = resampleWavCheckBox.getWidth() + 10; + } + } int zoomWidth = 50; if (zoomable) { diff --git a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java index 1793c85cb..21d5da832 100644 --- a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java +++ b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java @@ -262,6 +262,8 @@ public final class ImagePanel extends JPanel implements MediaDisplay { private boolean frozen = false; private boolean muted = false; + + private boolean resample = false; private boolean mutable = false; @@ -734,6 +736,11 @@ public final class ImagePanel extends JPanel implements MediaDisplay { } } + @Override + public void setResample(boolean resample) { + this.resample = resample; + } + private class IconPanel extends JPanel { private SerializableImage _img; @@ -2723,6 +2730,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { this.alwaysDisplay = alwaysDisplay; this.frozen = frozen; this.muted = muted; + this.resample = Configuration.previewResampleSound.get(); this.mutable = mutable; depthStateUnderCursor = null; hilightedEdge = null; @@ -3528,7 +3536,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { loopCount = Math.max(1, soundInfo.loopCount); } - sp = new SoundTagPlayer(soundInfo, st, loopCount, false); + sp = new SoundTagPlayer(soundInfo, st, loopCount, false, resample); sp.addEventListener(new MediaDisplayListener() { @Override public void mediaDisplayStateChanged(MediaDisplay source) { diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index c51043562..023b1270b 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -2194,7 +2194,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se if (export.isOptionEnabled(SoundExportMode.class)) { ret.addAll(new SoundExporter().exportSounds(handler, selFile2 + File.separator + SoundExportSettings.EXPORT_FOLDER_NAME, sounds, - new SoundExportSettings(export.getValue(SoundExportMode.class)), evl)); + new SoundExportSettings(export.getValue(SoundExportMode.class), export.isResampleWavEnabled()), evl)); } if (export.isOptionEnabled(BinaryDataExportMode.class)) { @@ -2258,7 +2258,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se singleScriptFile = false; } - ScriptExportSettings scriptExportSettings = new ScriptExportSettings(export.getValue(ScriptExportMode.class), singleScriptFile, false, export.isEmbedEnabled(), false); + ScriptExportSettings scriptExportSettings = new ScriptExportSettings(export.getValue(ScriptExportMode.class), singleScriptFile, false, export.isEmbedEnabled(), false, export.isResampleWavEnabled()); String singleFileName = Path.combine(scriptsFolder, openable.getShortFileName() + scriptExportSettings.getFileExtension()); try (FileTextWriter writer = scriptExportSettings.singleFile ? new FileTextWriter(Configuration.getCodeFormatting(), new FileOutputStream(singleFileName)) : null) { scriptExportSettings.singleFileWriter = writer; @@ -2312,7 +2312,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se if (export.isOptionEnabled(SoundExportMode.class)) { new SoundExporter().exportSounds(handler, Path.combine(selFile, SoundExportSettings.EXPORT_FOLDER_NAME), swf.getTags(), - new SoundExportSettings(export.getValue(SoundExportMode.class)), evl); + new SoundExportSettings(export.getValue(SoundExportMode.class), export.isResampleWavEnabled()), evl); } if (export.isOptionEnabled(BinaryDataExportMode.class)) { @@ -2365,7 +2365,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se singleScriptFile = false; } - ScriptExportSettings scriptExportSettings = new ScriptExportSettings(export.getValue(ScriptExportMode.class), singleScriptFile, false, export.isEmbedEnabled(), false); + ScriptExportSettings scriptExportSettings = new ScriptExportSettings(export.getValue(ScriptExportMode.class), singleScriptFile, false, export.isEmbedEnabled(), false, export.isResampleWavEnabled()); String singleFileName = Path.combine(scriptsFolder, swf.getShortFileName() + scriptExportSettings.getFileExtension()); try (FileTextWriter writer = scriptExportSettings.singleFile ? new FileTextWriter(Configuration.getCodeFormatting(), new FileOutputStream(singleFileName)) : null) { scriptExportSettings.singleFileWriter = writer; @@ -2415,7 +2415,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se if (export.isOptionEnabled(SoundExportMode.class)) { for (SoundExportMode exportMode : SoundExportMode.values()) { new SoundExporter().exportSounds(handler, Path.combine(selFile, SoundExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.getTags(), - new SoundExportSettings(exportMode), evl); + new SoundExportSettings(exportMode, export.isResampleWavEnabled()), evl); } } @@ -2482,7 +2482,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se singleScriptFile = false; } - ScriptExportSettings scriptExportSettings = new ScriptExportSettings(exportMode, singleScriptFile, false, export.isEmbedEnabled(), false); + ScriptExportSettings scriptExportSettings = new ScriptExportSettings(exportMode, singleScriptFile, false, export.isEmbedEnabled(), false, export.isResampleWavEnabled()); String singleFileName = Path.combine(scriptsFolder, swf.getShortFileName() + scriptExportSettings.getFileExtension()); try (FileTextWriter writer = scriptExportSettings.singleFile ? new FileTextWriter(Configuration.getCodeFormatting(), new FileOutputStream(singleFileName)) : null) { scriptExportSettings.singleFileWriter = writer; @@ -5574,7 +5574,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se previewPanel.setImageReplaceButtonVisible(false, false, false, !((SoundTag) treeItem).isReadOnly() && ((SoundTag) treeItem).importSupported(), false, false, false); if (!(treeItem instanceof SoundStreamHeadTypeTag)) { try { - SoundTagPlayer soundThread = new SoundTagPlayer(null, (SoundTag) treeItem, Configuration.loopMedia.get() ? Integer.MAX_VALUE : 1, true); + SoundTagPlayer soundThread = new SoundTagPlayer(null, (SoundTag) treeItem, Configuration.loopMedia.get() ? Integer.MAX_VALUE : 1, true, Configuration.previewResampleSound.get()); if (!Configuration.autoPlaySounds.get()) { soundThread.pause(); } diff --git a/src/com/jpexs/decompiler/flash/gui/SoundTagPlayer.java b/src/com/jpexs/decompiler/flash/gui/SoundTagPlayer.java index d2be680dd..a58de23fe 100644 --- a/src/com/jpexs/decompiler/flash/gui/SoundTagPlayer.java +++ b/src/com/jpexs/decompiler/flash/gui/SoundTagPlayer.java @@ -55,6 +55,8 @@ public class SoundTagPlayer implements MediaDisplay { private boolean paused = false; private boolean closed = false; + + private boolean resample = false; private final Object playLock = new Object(); @@ -75,12 +77,16 @@ public class SoundTagPlayer implements MediaDisplay { private long lengthInMicroSec = 0; private double microsecPerByte = 0; + + private double microsecPerByteResampled = 0; private double positionMicrosec = 0; private Long newPositionMicrosec = null; private byte[] wavData = null; + + private byte[] wavDataResampled = null; private boolean active = false; @@ -144,7 +150,7 @@ public class SoundTagPlayer implements MediaDisplay { private static final int FRAME_DIVISOR = 8000; - public SoundTagPlayer(final SOUNDINFO soundInfo, final SoundTag tag, int loops, boolean async) throws LineUnavailableException, IOException, UnsupportedAudioFileException { + public SoundTagPlayer(final SOUNDINFO soundInfo, final SoundTag tag, int loops, boolean async, boolean resample) throws LineUnavailableException, IOException, UnsupportedAudioFileException { this.tag = tag; this.loopCount = loops; @@ -163,14 +169,16 @@ public class SoundTagPlayer implements MediaDisplay { }, 100, 100); if (!async) { - setPausedFlag(true); + setPausedFlag(true); } + + this.resample = resample; thread = new Thread("Sound tag player") { @Override public void run() { try { - openSound(soundInfo, tag); + openSound(soundInfo, tag, resample); } catch (IOException | LineUnavailableException | UnsupportedAudioFileException ex) { Logger.getLogger(SoundTagPlayer.class.getName()).log(Level.SEVERE, null, ex); } @@ -180,21 +188,30 @@ public class SoundTagPlayer implements MediaDisplay { } } this.setPriority(MIN_PRIORITY); - playLoop(); + try { + playLoop(); + } catch (LineUnavailableException ex) { + Logger.getLogger(SoundTagPlayer.class.getName()).log(Level.SEVERE, null, ex); + } } }; thread.start(); - } + } - private void openSound(SOUNDINFO soundInfo, SoundTag tag) throws IOException, LineUnavailableException, UnsupportedAudioFileException { + private void openSound(SOUNDINFO soundInfo, SoundTag tag, boolean resample) throws IOException, LineUnavailableException, UnsupportedAudioFileException { SWF swf = (SWF) tag.getOpenable(); - wavData = swf.getFromCache(soundInfo, tag); - if (wavData == null) { + wavData = swf.getFromCache(soundInfo, tag, false); + wavDataResampled = swf.getFromCache(soundInfo, tag, true); + if (wavData == null || wavDataResampled == null) { List soundData = tag.getRawSoundData(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); - tag.getSoundFormat().createWav(soundInfo, soundData, baos, tag.getInitialLatency()); - wavData = baos.toByteArray(); - swf.putToCache(soundInfo, tag, wavData); + tag.getSoundFormat().createWav(soundInfo, soundData, baos, tag.getInitialLatency(), false); + wavData = baos.toByteArray(); + swf.putToCache(soundInfo, tag, false, wavData); + baos = new ByteArrayOutputStream(); + tag.getSoundFormat().createWav(soundInfo, soundData, baos, tag.getInitialLatency(), true); + wavDataResampled = baos.toByteArray(); + swf.putToCache(soundInfo, tag, true, wavDataResampled); } long soundLength44 = 0; @@ -203,7 +220,7 @@ public class SoundTagPlayer implements MediaDisplay { int sampleLen = (isUnCompressed ? (tag.getSoundSize() ? 2 : 1) : 2) * (tag.getSoundFormat().stereo ? 2 : 1); switch (tag.getSoundRate()) { case 0: //5.5kHz - soundLength44 = 8 * tag.getTotalSoundSampleCount(); + soundLength44 = 8 * tag.getTotalSoundSampleCount(); microsecPerByte = 1000000d / (5512.5d * sampleLen); break; case 1: //11kHz @@ -215,18 +232,16 @@ public class SoundTagPlayer implements MediaDisplay { microsecPerByte = 1000000d / (22050d * sampleLen); break; case 3: //44kHz - soundLength44 = tag.getTotalSoundSampleCount(); + soundLength44 = tag.getTotalSoundSampleCount(); microsecPerByte = 1000000d / (44100d * sampleLen); break; } + + microsecPerByteResampled = 1000000d / (44100d * sampleLen); lengthInMicroSec = soundLength44 * 1000000 / 44100; synchronized (playLock) { - audioStream = AudioSystem.getAudioInputStream(new ByteArrayInputStream(wavData)); - DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioStream.getFormat()); - sourceLine = (SourceDataLine) AudioSystem.getLine(info); - sourceLine.open(audioStream.getFormat()); - sourceLine.start(); + reloadAudioStream(); } } @@ -266,11 +281,21 @@ public class SoundTagPlayer implements MediaDisplay { } } - private void reloadAudioStream() throws IOException, UnsupportedAudioFileException { - audioStream = AudioSystem.getAudioInputStream(new ByteArrayInputStream(wavData)); + private void reloadAudioStream() throws IOException, UnsupportedAudioFileException, LineUnavailableException { + audioStream = AudioSystem.getAudioInputStream(new ByteArrayInputStream(resample ? wavDataResampled : wavData)); + + if (sourceLine != null) { + sourceLine.drain(); + sourceLine.stop(); + sourceLine.close(); + } + DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioStream.getFormat()); + sourceLine = (SourceDataLine) AudioSystem.getLine(info); + sourceLine.open(audioStream.getFormat()); + sourceLine.start(); } - private void playLoop() { + private void playLoop() throws LineUnavailableException { loop: while (true) { @@ -287,7 +312,7 @@ public class SoundTagPlayer implements MediaDisplay { } if (!getPausedFlag()) { if (newPositionMicrosec != null) { - long newPosBytes = (long) (newPositionMicrosec / microsecPerByte); + long newPosBytes = (long) (newPositionMicrosec / (resample ? microsecPerByteResampled : microsecPerByte)); audioStream.close(); reloadAudioStream(); audioStream.skip(newPosBytes); @@ -303,7 +328,7 @@ public class SoundTagPlayer implements MediaDisplay { } sourceLine.write(data, 0, numReadBytes); synchronized (playLock) { - positionMicrosec = microsecPerByte * posBytes; + positionMicrosec = (resample ? microsecPerByteResampled : microsecPerByte) * posBytes; } } } @@ -438,6 +463,11 @@ public class SoundTagPlayer implements MediaDisplay { loopCount = loop ? Integer.MAX_VALUE : 1; } } + + public void setResample(boolean resample) { + this.resample = resample; + rewind(); + } @Override public void gotoFrame(int frame) { @@ -525,5 +555,5 @@ public class SoundTagPlayer implements MediaDisplay { @Override public boolean isMutable() { return true; - } + } } diff --git a/src/com/jpexs/decompiler/flash/gui/graphics/resample16.png b/src/com/jpexs/decompiler/flash/gui/graphics/resample16.png new file mode 100644 index 000000000..39807d240 Binary files /dev/null and b/src/com/jpexs/decompiler/flash/gui/graphics/resample16.png differ diff --git a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties index 2eabea563..80e2b68f6 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties @@ -760,3 +760,10 @@ config.description.jnaTempDirectory = Temporary directory path for JNA DLLs, etc config.name.flaExportFixShapes = FLA export - fix shapes (slow) config.description.flaExportFixShapes = Apply procedure of splitting overlapping edges to try fix missing fills on some kind of shapes. This may be very slow on some complex shapes. + +config.name.lastExportResampleWav = Last setting of resampling wav +config.description.lastExportResampleWav = Last setting of resampling wav to 44kHz + +config.name.previewResampleSound = Resample in sound previews +config.description.previewResampleSound = Resample to 44kHz in sound previews + diff --git a/src/com/jpexs/decompiler/flash/gui/locales/CollectDepthAsSpritesDialog_cs.properties b/src/com/jpexs/decompiler/flash/gui/locales/CollectDepthAsSpritesDialog_cs.properties new file mode 100644 index 000000000..782124e67 --- /dev/null +++ b/src/com/jpexs/decompiler/flash/gui/locales/CollectDepthAsSpritesDialog_cs.properties @@ -0,0 +1,23 @@ +# Copyright (C) 2010-2016 JPEXS +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +dialog.title = Nastaven\u00ed sb\u011bru +collect.depths = Hloubky pro sb\u011br: +collect.replace = Nahradit p\u016fvodn\u00ed tagy sprity +collect.offset = Posun lev\u00e9ho horn\u00edho rohu k (0,0) +collect.matrix = Pou\u017e\u00edt matici p\u0159edchoz\u00edho sn\u00edmku ako prvn\u00ed matici pro PlaceObject + +button.ok = OK +button.cancel = Storno diff --git a/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties b/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties index 906946db7..744f6bf72 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties @@ -110,3 +110,6 @@ fonts4 = DefineFont4 fonts4.cff = CFF embed = Export embedded assets via [Embed] + +#after 20.1.0 +resampleWav = Resample Wav to 44kHz \ No newline at end of file diff --git a/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog_cs.properties b/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog_cs.properties index 8cba2c77e..702046ac7 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog_cs.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog_cs.properties @@ -104,4 +104,7 @@ symbolclass.csv = CSV #after 18.0.0 images.png_gif_jpeg_alpha = PNG/GIF/JPEG+alpha -embed = Exportovat vlo\u017een\u00e9 zdroje skrze [Embed] \ No newline at end of file +embed = Exportovat vlo\u017een\u00e9 zdroje skrze [Embed] + +#after 20.1.0 +resampleWav = P\u0159evzorkovat Wav na 44kHz \ No newline at end of file diff --git a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties index 89fd7c87d..58a72d460 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties @@ -1257,4 +1257,6 @@ contextmenu.showInFramesFolder = Show in frames folder #after 20.1.0 contextmenu.collapseAll = Collapse all -contextmenu.collectDepthAsSprites = Collect tags at same depth as sprites \ No newline at end of file +contextmenu.collectDepthAsSprites = Collect tags at same depth as sprites + +preview.resample = Resample sound to 44kHz \ No newline at end of file diff --git a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties index 4a92704f3..7c35eb9b2 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties @@ -1232,3 +1232,6 @@ contextmenu.showInFramesFolder = Zobrazit ve slo\u017ece sn\u00edmk\u016f #after 20.1.0 contextmenu.collapseAll = Sbalit v\u0161e +contextmenu.collectDepthAsSprites = Posb\u00edrat tagy ve stejn\u00e9 hloubce do sprit\u016f + +preview.resample = Resample sound to 44kHz \ No newline at end of file diff --git a/src/com/jpexs/decompiler/flash/gui/player/FlashPlayerPanel.java b/src/com/jpexs/decompiler/flash/gui/player/FlashPlayerPanel.java index fbb7d2632..7120576e0 100644 --- a/src/com/jpexs/decompiler/flash/gui/player/FlashPlayerPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/player/FlashPlayerPanel.java @@ -475,4 +475,9 @@ public final class FlashPlayerPanel extends Panel implements Closeable, MediaDis public boolean isMutable() { return false; } + + @Override + public void setResample(boolean resample) { + + } } diff --git a/src/com/jpexs/decompiler/flash/gui/player/MediaDisplay.java b/src/com/jpexs/decompiler/flash/gui/player/MediaDisplay.java index c5c21f343..057993a90 100644 --- a/src/com/jpexs/decompiler/flash/gui/player/MediaDisplay.java +++ b/src/com/jpexs/decompiler/flash/gui/player/MediaDisplay.java @@ -43,6 +43,8 @@ public interface MediaDisplay extends Closeable { public boolean isPlaying(); public void setLoop(boolean loop); + + public void setResample(boolean resample); public void gotoFrame(int frame); diff --git a/src/com/jpexs/decompiler/flash/gui/player/PlayerControls.java b/src/com/jpexs/decompiler/flash/gui/player/PlayerControls.java index 556988a70..59e0bd251 100644 --- a/src/com/jpexs/decompiler/flash/gui/player/PlayerControls.java +++ b/src/com/jpexs/decompiler/flash/gui/player/PlayerControls.java @@ -72,6 +72,8 @@ public class PlayerControls extends JPanel implements MediaDisplayListener { private final JButton pauseButton; private final JButton loopButton; + + private final JToggleButton resampleButton; private MediaDisplay display; @@ -92,6 +94,8 @@ public class PlayerControls extends JPanel implements MediaDisplayListener { private static final Icon loopIcon = View.getIcon("loopon16"); private static final Icon noLoopIcon = View.getIcon("loopoff16"); + + private static final Icon resampleIcon = View.getIcon("resample16"); private final JLabel percentLabel = new JLabel("100%"); @@ -317,9 +321,17 @@ public class PlayerControls extends JPanel implements MediaDisplayListener { loopButton.addActionListener(this::loopButtonActionPerformed); boolean loop = Configuration.loopMedia.get(); loopButton.setIcon(loop ? loopIcon : noLoopIcon); + + resampleButton = new JToggleButton(resampleIcon); + resampleButton.setToolTipText(AppStrings.translate("preview.resample")); + resampleButton.setMargin(new Insets(4, 2, 2, 2)); + resampleButton.addActionListener(this::resampleButtonActionPerformed); + resampleButton.setSelected(Configuration.previewResampleSound.get()); + buttonsPanel.add(pauseButton); buttonsPanel.add(stopButton); buttonsPanel.add(loopButton); + buttonsPanel.add(resampleButton); controlPanel.add(buttonsPanel, BorderLayout.CENTER); progress = new JProgressBar(); @@ -506,6 +518,12 @@ public class PlayerControls extends JPanel implements MediaDisplayListener { loopButton.setIcon(loop ? loopIcon : noLoopIcon); display.setLoop(loop); } + + private void resampleButtonActionPerformed(ActionEvent evt) { + boolean resample = resampleButton.isSelected(); + Configuration.previewResampleSound.set(resample); + display.setResample(resample); + } private void gotoFrameButtonActionPerformed(ActionEvent evt) { final JPanel gotoPanel = new JPanel(new BorderLayout());