Added GFX - DefineExternalSound and DefineExternalStreamSound playback

This commit is contained in:
Jindra Petřík
2023-01-07 16:48:25 +01:00
parent 1447e92289
commit 52c17de7b2
13 changed files with 299 additions and 57 deletions

View File

@@ -30,6 +30,8 @@ import com.jpexs.decompiler.flash.tags.SoundStreamBlockTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag;
import com.jpexs.decompiler.flash.tags.base.SoundTag;
import com.jpexs.decompiler.flash.tags.gfx.DefineExternalSound;
import com.jpexs.decompiler.flash.tags.gfx.DefineExternalStreamSound;
import com.jpexs.decompiler.flash.types.sound.SoundExportFormat;
import com.jpexs.decompiler.flash.types.sound.SoundFormat;
import com.jpexs.helpers.ByteArrayRange;
@@ -141,7 +143,7 @@ public class SoundExporter {
fos.write(data.getRangeData());
}
} else if ((nativeFormat == SoundExportFormat.FLV && mode.hasFlv()) || mode == SoundExportMode.FLV) {
if (st instanceof DefineSoundTag) {
if ((st instanceof DefineSoundTag)||(st instanceof DefineExternalSound)||(st instanceof DefineExternalStreamSound)){
FLVOutputStream flv = new FLVOutputStream(fos);
flv.writeHeader(true, false);
List<ByteArrayRange> datas = st.getRawSoundData();

View File

@@ -31,6 +31,7 @@ import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag;
import com.jpexs.decompiler.flash.tags.base.RemoveTag;
import com.jpexs.decompiler.flash.tags.base.RenderContext;
import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag;
import com.jpexs.decompiler.flash.tags.gfx.DefineExternalStreamSound;
import com.jpexs.decompiler.flash.timeline.Timeline;
import com.jpexs.decompiler.flash.timeline.Timelined;
import com.jpexs.decompiler.flash.types.BasicType;
@@ -369,7 +370,7 @@ public class DefineSpriteTag extends DrawableTag implements Timelined {
@Override
public void getNeededCharacters(Set<Integer> needed) {
for (Tag t : getTags()) {
if ((t instanceof CharacterIdTag) && !(t instanceof SoundStreamHeadTypeTag)) {
if ((t instanceof CharacterIdTag) && !(t instanceof SoundStreamHeadTypeTag) && !(t instanceof DefineExternalStreamSound)) {
needed.add(((CharacterIdTag) t).getCharacterId());
}
}

View File

@@ -171,11 +171,6 @@ public class SoundStreamHead2Tag extends SoundStreamHeadTypeTag {
return streamSoundSampleCount;
}
@Override
public void setVirtualCharacterId(int ch) {
virtualCharacterId = ch;
}
@Override
public int getSoundFormatId() {
return streamSoundCompression;
@@ -206,7 +201,7 @@ public class SoundStreamHead2Tag extends SoundStreamHeadTypeTag {
@Override
public boolean importSupported() {
return false;
return true;
}
@Override

View File

@@ -176,11 +176,6 @@ public class SoundStreamHeadTag extends SoundStreamHeadTypeTag {
virtualCharacterId = characterId;
}
@Override
public void setVirtualCharacterId(int ch) {
virtualCharacterId = ch;
}
@Override
public long getSoundSampleCount() {
return streamSoundSampleCount;
@@ -216,7 +211,7 @@ public class SoundStreamHeadTag extends SoundStreamHeadTypeTag {
@Override
public boolean importSupported() {
return false;
return true;
}
@Override

View File

@@ -16,35 +16,11 @@
*/
package com.jpexs.decompiler.flash.tags.base;
import com.jpexs.decompiler.flash.ReadOnlyTagList;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.SWFOutputStream;
import com.jpexs.decompiler.flash.tags.ShowFrameTag;
import com.jpexs.decompiler.flash.tags.SoundStreamBlockTag;
import com.jpexs.decompiler.flash.tags.SoundStreamHeadTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.timeline.Timelined;
import com.jpexs.decompiler.flash.types.sound.MP3FRAME;
import com.jpexs.decompiler.flash.types.sound.MP3SOUNDDATA;
import com.jpexs.decompiler.flash.types.sound.SoundFormat;
import com.jpexs.helpers.ByteArrayRange;
import com.jpexs.helpers.Helper;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;
/**
*
@@ -61,8 +37,6 @@ public abstract class SoundStreamHeadTypeTag extends Tag implements CharacterIdT
public abstract long getSoundSampleCount();
public abstract void setVirtualCharacterId(int ch);
public abstract List<SoundStreamBlockTag> getBlocks();
}

View File

@@ -19,16 +19,31 @@ package com.jpexs.decompiler.flash.tags.gfx;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.SWFOutputStream;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.CharacterTag;
import com.jpexs.decompiler.flash.tags.base.SoundTag;
import com.jpexs.decompiler.flash.types.sound.SoundExportFormat;
import com.jpexs.decompiler.flash.types.sound.SoundFormat;
import com.jpexs.helpers.ByteArrayRange;
import com.jpexs.helpers.Helper;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;
/**
*
* @author JPEXS
*/
public class DefineExternalSound extends CharacterTag {
public class DefineExternalSound extends CharacterTag implements SoundTag {
public static final int ID = 1006;
@@ -84,7 +99,7 @@ public class DefineExternalSound extends CharacterTag {
super(sis.getSwf(), ID, NAME, data);
readData(sis, data, 0, false, false, false);
}
public DefineExternalSound(SWF swf) {
super(swf, ID, NAME, null);
exportName = "";
@@ -114,4 +129,107 @@ public class DefineExternalSound extends CharacterTag {
public void setCharacterId(int characterId) {
this.characterId = characterId;
}
@Override
public SoundExportFormat getExportFormat() {
return SoundExportFormat.WAV; //?
}
@Override
public boolean importSupported() {
return false;
}
@Override
public int getSoundRate() {
switch ((int) sampleRate) {
case 5512:
return 0;
case 11025:
return 1;
case 22050:
return 2;
case 44100:
return 3;
}
return -1; //?
}
@Override
public boolean getSoundType() {
return channels == 2;
}
@Override
public List<ByteArrayRange> getRawSoundData() {
List<ByteArrayRange> ret = new ArrayList<>();
Path soundPath = getSwf().getFile() == null ? null : Paths.get(getSwf().getFile()).getParent().resolve(Paths.get(fileName));
if (soundPath == null || !soundPath.toFile().exists()) {
ret.add(new ByteArrayRange(new byte[]{}));
return ret;
}
try (FileInputStream fis = new FileInputStream(soundPath.toFile()); AudioInputStream audioIs = AudioSystem.getAudioInputStream(new BufferedInputStream(fis))) {
ret.add(new ByteArrayRange(Helper.readStream(audioIs)));
return ret;
} catch (IOException | UnsupportedAudioFileException iex) {
ret.add(new ByteArrayRange(new byte[]{}));
return ret;
}
}
@Override
public int getSoundFormatId() {
return SoundFormat.FORMAT_UNCOMPRESSED_LITTLE_ENDIAN;
}
@Override
public long getTotalSoundSampleCount() {
return sampleCount;
}
@Override
public boolean getSoundSize() {
return bits == 16;
}
@Override
public SoundFormat getSoundFormat() {
final int[] rateMap = {5512, 11025, 22050, 44100};
return new SoundFormat(getSoundFormatId(), rateMap[getSoundRate()], getSoundType());
}
@Override
public void setSoundSize(boolean soundSize) {
if (soundSize) {
bits = 16;
} else {
bits = 8;
}
}
@Override
public void setSoundType(boolean soundType) {
if (soundType) {
channels = 2;
} else {
channels = 1;
}
}
@Override
public void setSoundSampleCount(long soundSampleCount) {
this.sampleCount = soundSampleCount;
}
@Override
public void setSoundCompression(int soundCompression) {
//unsupported
}
@Override
public void setSoundRate(int soundRate) {
final int[] rateMap = {5512, 11025, 22050, 44100};
this.sampleRate = rateMap[soundRate];
}
}

View File

@@ -20,14 +20,29 @@ import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.SWFOutputStream;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.CharacterIdTag;
import com.jpexs.decompiler.flash.tags.base.SoundTag;
import com.jpexs.decompiler.flash.types.annotations.Internal;
import com.jpexs.decompiler.flash.types.sound.SoundExportFormat;
import com.jpexs.decompiler.flash.types.sound.SoundFormat;
import com.jpexs.helpers.ByteArrayRange;
import com.jpexs.helpers.Helper;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;
/**
*
* @author JPEXS
*/
public class DefineExternalStreamSound extends Tag {
public class DefineExternalStreamSound extends Tag implements CharacterIdTag, SoundTag {
public static final int ID = 1007;
@@ -52,6 +67,9 @@ public class DefineExternalStreamSound extends Tag {
public String fileName;
public static final int SOUND_FORMAT_WAV = 0;
@Internal
private int virtualCharacterId = 0;
/**
* Gets data bytes
@@ -103,4 +121,127 @@ public class DefineExternalStreamSound extends Tag {
lastFrame = sis.readUI32("lastFrame");
fileName = sis.readNetString("fileName");
}
@Override
public SoundExportFormat getExportFormat() {
return SoundExportFormat.WAV; //?
}
@Override
public boolean importSupported() {
return false;
}
@Override
public int getSoundRate() {
switch ((int) sampleRate) {
case 5512:
return 0;
case 11025:
return 1;
case 22050:
return 2;
case 44100:
return 3;
}
return -1; //?
}
@Override
public boolean getSoundType() {
return channels == 2;
}
@Override
public List<ByteArrayRange> getRawSoundData() {
List<ByteArrayRange> ret = new ArrayList<>();
Path soundPath = getSwf().getFile() == null ? null : Paths.get(getSwf().getFile()).getParent().resolve(Paths.get(fileName));
if (soundPath == null || !soundPath.toFile().exists()) {
ret.add(new ByteArrayRange(new byte[]{}));
return ret;
}
try (FileInputStream fis = new FileInputStream(soundPath.toFile()); AudioInputStream audioIs = AudioSystem.getAudioInputStream(new BufferedInputStream(fis))) {
ret.add(new ByteArrayRange(Helper.readStream(audioIs)));
return ret;
} catch (IOException | UnsupportedAudioFileException iex) {
ret.add(new ByteArrayRange(new byte[]{}));
return ret;
}
}
@Override
public int getSoundFormatId() {
return SoundFormat.FORMAT_UNCOMPRESSED_LITTLE_ENDIAN;
}
@Override
public long getTotalSoundSampleCount() {
return sampleCount;
}
@Override
public boolean getSoundSize() {
return bits == 16;
}
@Override
public SoundFormat getSoundFormat() {
final int[] rateMap = {5512, 11025, 22050, 44100};
return new SoundFormat(getSoundFormatId(), rateMap[getSoundRate()], getSoundType());
}
@Override
public void setSoundSize(boolean soundSize) {
if (soundSize) {
bits = 16;
} else {
bits = 8;
}
}
@Override
public void setSoundType(boolean soundType) {
if (soundType) {
channels = 2;
} else {
channels = 1;
}
}
@Override
public void setSoundSampleCount(long soundSampleCount) {
this.sampleCount = soundSampleCount;
}
@Override
public void setSoundCompression(int soundCompression) {
//unsupported
}
@Override
public void setSoundRate(int soundRate) {
final int[] rateMap = {5512, 11025, 22050, 44100};
this.sampleRate = rateMap[soundRate];
}
@Override
public int getCharacterId() {
return this.virtualCharacterId;
}
@Override
public void setCharacterId(int characterId) {
this.virtualCharacterId = characterId;
}
@Override
public String getCharacterExportFileName() {
return "" + getCharacterId();
}
@Override
public String toString() {
return getName() + (virtualCharacterId > 0 ? " (" + virtualCharacterId + ")" : "");
}
}

View File

@@ -52,6 +52,7 @@ import com.jpexs.decompiler.flash.tags.base.RenderContext;
import com.jpexs.decompiler.flash.tags.base.ShapeTag;
import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag;
import com.jpexs.decompiler.flash.tags.base.TextTag;
import com.jpexs.decompiler.flash.tags.gfx.DefineExternalStreamSound;
import com.jpexs.decompiler.flash.types.BlendMode;
import com.jpexs.decompiler.flash.types.CLIPACTIONS;
import com.jpexs.decompiler.flash.types.CXFORMWITHALPHA;
@@ -565,11 +566,16 @@ public class Timeline {
for (Tag t : tags) {
if (t instanceof SoundStreamHeadTypeTag) {
SoundStreamHeadTypeTag head = (SoundStreamHeadTypeTag) t;
head.setVirtualCharacterId(containerId);
head.setCharacterId(containerId);
blocks = new ArrayList<>();
soundStramBlocks.put(containerId, blocks);
continue;
}
if (t instanceof DefineExternalStreamSound) {
DefineExternalStreamSound externalStream = (DefineExternalStreamSound) t;
externalStream.setCharacterId(containerId);
continue;
}
if (t instanceof DefineSpriteTag) {
DefineSpriteTag sprite = (DefineSpriteTag) t;

View File

@@ -1118,7 +1118,7 @@ public class XFLConverter {
private static HashMap<Integer, CharacterTag> getCharacters(ReadOnlyTagList tags) {
HashMap<Integer, CharacterTag> ret = new HashMap<>();
int maxId = 0;
/*int maxId = 0;
for (Tag t : tags) {
if (t instanceof CharacterTag) {
CharacterTag ct = (CharacterTag) t;
@@ -1126,12 +1126,12 @@ public class XFLConverter {
maxId = ct.getCharacterId();
}
}
}
}*/
for (Tag t : tags) {
if (t instanceof SoundStreamHeadTypeTag) {
/*if (t instanceof SoundStreamHeadTypeTag) {
SoundStreamHeadTypeTag ssh = (SoundStreamHeadTypeTag) t;
ssh.setVirtualCharacterId(++maxId);
}
ssh.setCharacterId(++maxId);
}*/
if (t instanceof CharacterTag) {
CharacterTag ct = (CharacterTag) t;
ret.put(ct.getCharacterId(), ct);