mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-06-30 16:01:23 +00:00
Fixed #2143 FLA Export / Sound playback - taking MP3 initial latency into account
This commit is contained in:
@@ -127,7 +127,7 @@ public class CallMethodActionItem extends ActionItem {
|
||||
scriptObject.toString(writer, localData);
|
||||
}
|
||||
if (
|
||||
!(((DirectValueActionItem)methodName).value instanceof RegisterNumber)
|
||||
!(((DirectValueActionItem) methodName).value instanceof RegisterNumber)
|
||||
&& IdentifiersDeobfuscation.isValidName(false, methodName.toStringNoQuotes(localData))
|
||||
) {
|
||||
writer.append(".");
|
||||
|
||||
@@ -194,7 +194,7 @@ public class SoundExporter {
|
||||
}
|
||||
} else {
|
||||
List<ByteArrayRange> soundData = st.getRawSoundData();
|
||||
fmt.createWav(null, soundData, fos);
|
||||
fmt.createWav(null, soundData, fos, st.getInitialLatency());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ import com.jpexs.helpers.ByteArrayRange;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -137,6 +139,9 @@ public class DefineSoundTag extends CharacterTag implements SoundTag {
|
||||
@Override
|
||||
public SoundExportFormat getExportFormat() {
|
||||
if (soundFormat == SoundFormat.FORMAT_MP3) {
|
||||
if (getInitialLatency() > 0) {
|
||||
return SoundExportFormat.WAV;
|
||||
}
|
||||
return SoundExportFormat.MP3;
|
||||
}
|
||||
if (soundFormat == SoundFormat.FORMAT_ADPCM) {
|
||||
@@ -241,5 +246,19 @@ public class DefineSoundTag extends CharacterTag implements SoundTag {
|
||||
@Override
|
||||
public String getFlaExportName() {
|
||||
return "sound" + getCharacterId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInitialLatency() {
|
||||
if (soundFormat == SoundFormat.FORMAT_MP3) {
|
||||
SWFInputStream sis;
|
||||
try {
|
||||
sis = new SWFInputStream(null, soundData.getRangeData(0, 2));
|
||||
return sis.readSI16("seekSamples");
|
||||
} catch (IOException ex) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,4 +290,9 @@ public class SoundStreamHead2Tag extends SoundStreamHeadTypeTag {
|
||||
public String getFlaExportName() {
|
||||
return "sound" + getCharacterId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInitialLatency() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,4 +299,9 @@ public class SoundStreamHeadTag extends SoundStreamHeadTypeTag {
|
||||
public String getFlaExportName() {
|
||||
return "sound" + getCharacterId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInitialLatency() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,4 +65,6 @@ public interface SoundTag extends TreeItem {
|
||||
public String getName();
|
||||
|
||||
public String getFlaExportName();
|
||||
|
||||
public int getInitialLatency();
|
||||
}
|
||||
|
||||
@@ -247,5 +247,10 @@ public class DefineExternalSound extends CharacterTag implements SoundTag {
|
||||
@Override
|
||||
public String getFlaExportName() {
|
||||
return "sound" + getCharacterId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInitialLatency() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,4 +260,9 @@ public class DefineExternalStreamSound extends Tag implements CharacterIdTag, So
|
||||
public String getFlaExportName() {
|
||||
return "sound" + getCharacterId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInitialLatency() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,4 +164,9 @@ public class SoundStreamFrameRange implements TreeItem, SoundTag {
|
||||
public String getFlaExportName() {
|
||||
return head.getFlaExportName() + "_" + (startFrame + 1) + "-" + (endFrame + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInitialLatency() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.jpexs.helpers.utf8.Utf8Helper;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import javax.sound.sampled.AudioFormat;
|
||||
import javax.sound.sampled.AudioSystem;
|
||||
@@ -86,28 +87,11 @@ public class SoundFormat {
|
||||
this.samplingRate = samplingRate;
|
||||
this.stereo = stereo;
|
||||
ensureFormat();
|
||||
}
|
||||
|
||||
public byte[] decode(SWFInputStream sis) {
|
||||
try {
|
||||
return getDecoder().decode(sis);
|
||||
} catch (IOException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean decode(SWFInputStream sis, OutputStream os) {
|
||||
try {
|
||||
getDecoder().decode(sis, os);
|
||||
return true;
|
||||
} catch (IOException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean play(SWFInputStream sis) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
if (!decode(sis, baos)) {
|
||||
if (!SoundFormat.this.decode(sis, baos)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -200,7 +184,24 @@ public class SoundFormat {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean createWav(SOUNDINFO soundInfo, List<ByteArrayRange> dataRanges, OutputStream os) throws IOException {
|
||||
public byte[] decode(SWFInputStream sis) {
|
||||
try {
|
||||
return getDecoder().decode(sis);
|
||||
} catch (IOException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean decode(SWFInputStream sis, OutputStream os) {
|
||||
try {
|
||||
getDecoder().decode(sis, os);
|
||||
return true;
|
||||
} catch (IOException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] decode(SOUNDINFO soundInfo, List<ByteArrayRange> dataRanges, int skipSamples) throws IOException {
|
||||
ensureFormat();
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
SoundDecoder decoder = getDecoder();
|
||||
@@ -209,34 +210,48 @@ public class SoundFormat {
|
||||
sis.seek(dataRange.getPos());
|
||||
decoder.decode(sis, baos);
|
||||
}
|
||||
|
||||
/*
|
||||
System.err.println("sampling rate:" + samplingRate);
|
||||
System.err.println("len:" + baos.toByteArray().length);
|
||||
*/
|
||||
byte[] decodedData = baos.toByteArray();
|
||||
if (skipSamples > 0) {
|
||||
byte[] data = decodedData;
|
||||
data = Arrays.copyOfRange(
|
||||
data,
|
||||
skipSamples * 2 * (stereo ? 2 : 1),
|
||||
data.length
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
return decodedData;
|
||||
}
|
||||
|
||||
public boolean createWav(SOUNDINFO soundInfo, List<ByteArrayRange> dataRanges, OutputStream os, int skipSamples) throws IOException {
|
||||
|
||||
byte[] decodedData = decode(soundInfo, dataRanges, skipSamples);
|
||||
boolean convertedStereo = stereo;
|
||||
|
||||
ByteArrayOutputStream baosFiltered;
|
||||
if (soundInfo == null) {
|
||||
baosFiltered = baos;
|
||||
baosFiltered = new ByteArrayOutputStream();
|
||||
baosFiltered.write(decodedData);
|
||||
} else {
|
||||
int inPoint = (soundInfo.hasInPoint ? (int) Math.round(soundInfo.inPoint * samplingRate / 44100.0) : 0);
|
||||
int outPoint = (soundInfo.hasOutPoint ? (int) Math.round(soundInfo.outPoint * samplingRate / 44100.0) : Integer.MAX_VALUE);
|
||||
byte[] data = baos.toByteArray();
|
||||
baosFiltered = new ByteArrayOutputStream();
|
||||
int inPointBytes = inPoint * 2 /*16bit*/ * (stereo ? 2 : 1);
|
||||
int outPointBytes = soundInfo.hasOutPoint ? outPoint * 2 /*16bit*/ * (stereo ? 2 : 1) : data.length;
|
||||
//Q: Use skipSamples value?
|
||||
|
||||
int outPointBytes = soundInfo.hasOutPoint ? outPoint * 2 /*16bit*/ * (stereo ? 2 : 1) : decodedData.length;
|
||||
for (int i = inPointBytes; i < outPointBytes; i += (stereo ? 4 : 2)) {
|
||||
if (i + 1 >= data.length) {
|
||||
if (i + 1 >= decodedData.length) {
|
||||
break;
|
||||
}
|
||||
int left = ((data[i] & 0xff) + ((data[i + 1] & 0xff) << 8)) << 16 >> 16;
|
||||
int left = ((decodedData[i] & 0xff) + ((decodedData[i + 1] & 0xff) << 8)) << 16 >> 16;
|
||||
int right = left;
|
||||
if (stereo) {
|
||||
if (i + 3 >= data.length) {
|
||||
if (i + 3 >= decodedData.length) {
|
||||
break;
|
||||
}
|
||||
right = ((data[i + 2] & 0xff) + ((data[i + 3] & 0xff) << 8)) << 16 >> 16;
|
||||
right = ((decodedData[i + 2] & 0xff) + ((decodedData[i + 3] & 0xff) << 8)) << 16 >> 16;
|
||||
}
|
||||
|
||||
if (soundInfo.hasEnvelope) {
|
||||
|
||||
@@ -162,6 +162,7 @@ import java.awt.Font;
|
||||
import java.awt.Point;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
@@ -1961,7 +1962,7 @@ public class XFLConverter {
|
||||
writer.writeEndElement();
|
||||
}
|
||||
|
||||
private void convertSoundMedia(SWF swf, ReadOnlyTagList tags, SoundTag symbol, XFLXmlWriter writer, HashMap<String, byte[]> files) throws XMLStreamException {
|
||||
private void convertSoundMedia(SWF swf, ReadOnlyTagList tags, SoundTag symbol, XFLXmlWriter writer, HashMap<String, byte[]> files, HashMap<String, byte[]> datfiles) throws XMLStreamException {
|
||||
int soundFormat = 0;
|
||||
int soundRate = 0;
|
||||
boolean soundType = false;
|
||||
@@ -2034,6 +2035,7 @@ public class XFLConverter {
|
||||
logger.log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
int seekSamples = 0;
|
||||
if (soundFormat == SoundFormat.FORMAT_MP3) {
|
||||
exportFormat = "mp3";
|
||||
if (!soundType) { //mono
|
||||
@@ -2043,9 +2045,13 @@ public class XFLConverter {
|
||||
try {
|
||||
SWFInputStream sis = new SWFInputStream(swf, soundData);
|
||||
MP3SOUNDDATA s = new MP3SOUNDDATA(sis, false);
|
||||
if (s.seekSamples > 0) {
|
||||
seekSamples = s.seekSamples;
|
||||
exportFormat = "wav";
|
||||
}
|
||||
if (!s.frames.isEmpty()) {
|
||||
MP3FRAME frame = s.frames.get(0);
|
||||
int bitRate = frame.getBitRate();
|
||||
int bitRate = frame.getBitRate() / 1000;
|
||||
|
||||
switch (bitRate) {
|
||||
case 8:
|
||||
@@ -2084,8 +2090,7 @@ public class XFLConverter {
|
||||
case 160:
|
||||
bits = 17;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException | IndexOutOfBoundsException ex) {
|
||||
logger.log(Level.SEVERE, null, ex);
|
||||
@@ -2095,10 +2100,22 @@ public class XFLConverter {
|
||||
SoundFormat fmt = st.getSoundFormat();
|
||||
byte[] data = SWFInputStream.BYTE_ARRAY_EMPTY;
|
||||
try {
|
||||
data = new SoundExporter().exportSound(st, SoundExportMode.MP3_WAV);
|
||||
data = new SoundExporter().exportSound(st, seekSamples > 0 ? SoundExportMode.WAV : SoundExportMode.MP3_WAV);
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.SEVERE, null, ex);
|
||||
}
|
||||
|
||||
String datFileName = null;
|
||||
if (seekSamples > 0) {
|
||||
long ts = getTimestamp(swf);
|
||||
datFileName = "M " + (datfiles.size() + 1) + " " + ts + ".dat";
|
||||
try {
|
||||
byte[] decodedData = st.getSoundFormat().decode(null, st.getRawSoundData(), seekSamples);
|
||||
datfiles.put(datFileName, decodedData);
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
|
||||
String symbolFile = symbol.getFlaExportName() + "." + exportFormat;
|
||||
files.put(symbolFile, data);
|
||||
@@ -2107,6 +2124,9 @@ public class XFLConverter {
|
||||
"sourceLastImported", Long.toString(getTimestamp(swf)),
|
||||
"externalFileSize", Integer.toString(data.length)});
|
||||
writer.writeAttribute("href", symbolFile);
|
||||
if (datFileName != null) {
|
||||
writer.writeAttribute("soundDataHRef", datFileName);
|
||||
}
|
||||
writer.writeAttribute("format", rateMap[soundRate] + "kHz" + " " + (soundSize ? "16bit" : "8bit") + " " + (soundType ? "Stereo" : "Mono"));
|
||||
writer.writeAttribute("exportFormat", format);
|
||||
writer.writeAttribute("exportBits", bits);
|
||||
@@ -2227,7 +2247,7 @@ public class XFLConverter {
|
||||
statusStack.popStatus();
|
||||
} else if (symbol instanceof DefineSoundTag) {
|
||||
statusStack.pushStatus(symbol.toString());
|
||||
convertSoundMedia(swf, tags, (DefineSoundTag) symbol, writer, files);
|
||||
convertSoundMedia(swf, tags, (DefineSoundTag) symbol, writer, files, datfiles);
|
||||
|
||||
boolean linkageExportForAS = false;
|
||||
if (characterClasses.containsKey(symbol.getCharacterId())) {
|
||||
@@ -2336,7 +2356,7 @@ public class XFLConverter {
|
||||
SoundStreamHeadTypeTag head = (SoundStreamHeadTypeTag) t;
|
||||
for (SoundStreamFrameRange range : head.getRanges()) {
|
||||
statusStack.pushStatus(range.toString());
|
||||
convertSoundMedia(swf, tags, range, writer, files);
|
||||
convertSoundMedia(swf, tags, range, writer, files, datfiles);
|
||||
writer.writeEndElement();
|
||||
mediaCount++;
|
||||
statusStack.popStatus();
|
||||
@@ -2349,7 +2369,7 @@ public class XFLConverter {
|
||||
SoundStreamHeadTypeTag head = (SoundStreamHeadTypeTag) st;
|
||||
for (SoundStreamFrameRange range : head.getRanges()) {
|
||||
statusStack.pushStatus(range.toString());
|
||||
convertSoundMedia(swf, sprite.getTags(), range, writer, files);
|
||||
convertSoundMedia(swf, sprite.getTags(), range, writer, files, datfiles);
|
||||
writer.writeEndElement();
|
||||
mediaCount++;
|
||||
statusStack.popStatus();
|
||||
@@ -3195,7 +3215,7 @@ public class XFLConverter {
|
||||
SWF swf = startSound.getSwf();
|
||||
DefineSoundTag s = swf.getSound(startSound.soundId);
|
||||
if (s == null) {
|
||||
logger.log(Level.WARNING, "Sount tag (ID={0}) was not found", startSound.soundId);
|
||||
logger.log(Level.WARNING, "Sound tag (ID={0}) was not found", startSound.soundId);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user