Added: Importing sound stream block ranges

Added: Commandline replacing sound stream block ranges
This commit is contained in:
Jindra Petřík
2025-06-06 20:07:42 +02:00
parent e65ac557fb
commit 549c6dd9a3
6 changed files with 85 additions and 21 deletions
+2
View File
@@ -28,6 +28,8 @@ All notable changes to this project will be documented in this file.
- [#2463] Export subsprites animation context menu on frames - [#2463] Export subsprites animation context menu on frames
- Open in the Flash Player context menu on graphic/sound tags and frames - Open in the Flash Player context menu on graphic/sound tags and frames
- [#2451] Replacing sound stream block ranges - [#2451] Replacing sound stream block ranges
- Importing sound stream block ranges
- Commandline replacing sound stream block ranges
### Changed ### Changed
- AS1/2 - Single DoAction tag inside frame is now displayed directly as frame node - AS1/2 - Single DoAction tag inside frame is now displayed directly as frame node
@@ -370,9 +370,9 @@ public class SoundImporter {
|| newSoundType != streamHead.getSoundType() || newSoundType != streamHead.getSoundType()
|| newSoundRate != streamHead.getSoundRate()) { || newSoundRate != streamHead.getSoundRate()) {
throw new SoundParametersMismatchException( throw new SoundParametersMismatchException(
streamHead.getSoundType(), streamHead.getSoundType(),
streamHead.getSoundSize(), streamHead.getSoundSize(),
streamHead.getSoundRate(), streamHead.getSoundRate(),
streamHead.getSoundFormatId(), streamHead.getSoundFormatId(),
newSoundType, newSoundType,
newSoundSize, newSoundSize,
@@ -405,8 +405,8 @@ public class SoundImporter {
break; break;
} }
} }
} }
} else { } else {
for (SoundStreamFrameRange range : ranges) { for (SoundStreamFrameRange range : ranges) {
if (range.startFrame == startFrame) { if (range.startFrame == startFrame) {
existingBlocks.addAll(range.blocks); existingBlocks.addAll(range.blocks);
@@ -414,7 +414,7 @@ public class SoundImporter {
} }
} }
} }
for (SoundStreamBlockTag block : existingBlocks) { for (SoundStreamBlockTag block : existingBlocks) {
timelined.removeTag(block); timelined.removeTag(block);
} }
@@ -552,15 +552,16 @@ public class SoundImporter {
* @param soundTag Sound tag * @param soundTag Sound tag
* @param is Input stream * @param is Input stream
* @param newSoundFormat New sound format * @param newSoundFormat New sound format
* @param startFrame Starting frame. null = autodetect, replace all
* @return True if sound was imported successfully * @return True if sound was imported successfully
* @throws SoundImportException On sound import error * @throws SoundImportException On sound import error
*/ */
public boolean importSound(SoundTag soundTag, InputStream is, int newSoundFormat) throws SoundImportException { public boolean importSound(SoundTag soundTag, InputStream is, int newSoundFormat, Integer startFrame) throws SoundImportException {
if (soundTag instanceof DefineSoundTag) { if (soundTag instanceof DefineSoundTag) {
return importDefineSound((DefineSoundTag) soundTag, is, newSoundFormat); return importDefineSound((DefineSoundTag) soundTag, is, newSoundFormat);
} }
if (soundTag instanceof SoundStreamHeadTypeTag) { if (soundTag instanceof SoundStreamHeadTypeTag) {
return importSoundStream((SoundStreamHeadTypeTag) soundTag, is, newSoundFormat); return importSoundStreamAtFrame((SoundStreamHeadTypeTag) soundTag, is, newSoundFormat, startFrame);
} }
if (soundTag instanceof SoundStreamFrameRange) { if (soundTag instanceof SoundStreamFrameRange) {
return importSoundStreamAtFrame(((SoundStreamFrameRange) soundTag).getHead(), is, newSoundFormat, ((SoundStreamFrameRange) soundTag).startFrame); return importSoundStreamAtFrame(((SoundStreamFrameRange) soundTag).getHead(), is, newSoundFormat, ((SoundStreamFrameRange) soundTag).startFrame);
@@ -595,16 +596,21 @@ public class SoundImporter {
}); });
List<SoundTag> soundTags = new ArrayList<>(); List<SoundTag> soundTags = new ArrayList<>();
List<List<SoundStreamFrameRange>> ranges = new ArrayList<>();
for (int characterId : characters.keySet()) { for (int characterId : characters.keySet()) {
CharacterTag tag = characters.get(characterId); CharacterTag tag = characters.get(characterId);
if (tag instanceof DefineSoundTag) { if (tag instanceof DefineSoundTag) {
soundTags.add((DefineSoundTag) tag); soundTags.add((DefineSoundTag) tag);
ranges.add(new ArrayList<>());
} }
if (tag instanceof DefineSpriteTag) { if (tag instanceof DefineSpriteTag) {
DefineSpriteTag sprite = (DefineSpriteTag) tag; DefineSpriteTag sprite = (DefineSpriteTag) tag;
for (Tag subTag : sprite.getTags()) { for (Tag subTag : sprite.getTags()) {
if (subTag instanceof SoundStreamHeadTypeTag) { if (subTag instanceof SoundStreamHeadTypeTag) {
soundTags.add((SoundStreamHeadTypeTag) subTag); soundTags.add((SoundStreamHeadTypeTag) subTag);
ranges.add(sprite.getTimeline().getSoundStreamBlocks((SoundStreamHeadTypeTag) subTag));
break;
} }
} }
} }
@@ -612,10 +618,14 @@ public class SoundImporter {
for (Tag tag : swf.getTags()) { for (Tag tag : swf.getTags()) {
if (tag instanceof SoundStreamHeadTypeTag) { if (tag instanceof SoundStreamHeadTypeTag) {
soundTags.add((SoundStreamHeadTypeTag) tag); soundTags.add((SoundStreamHeadTypeTag) tag);
ranges.add(swf.getTimeline().getSoundStreamBlocks((SoundStreamHeadTypeTag) tag));
break;
} }
} }
for (SoundTag tag : soundTags) { int pos = -1;
loopChars: for (SoundTag tag : soundTags) {
pos++;
int characterId = tag.getCharacterId(); int characterId = tag.getCharacterId();
List<File> existingFilesForSoundTag = new ArrayList<>(); List<File> existingFilesForSoundTag = new ArrayList<>();
@@ -656,8 +666,38 @@ public class SoundImporter {
continue; continue;
} }
if (!ranges.get(pos).isEmpty()) {
for (SoundStreamFrameRange r : ranges.get(pos)) {
for (File sourceFile : existingFilesForSoundTag) {
if (sourceFile.getName().startsWith("" + characterId + "_" + (r.startFrame + 1) + "-")) {
try {
if (printOut) {
System.out.println("Importing character " + characterId + ", start frame " + r.startFrame + " from file " + sourceFile.getName());
}
int soundFormat = SoundFormat.FORMAT_UNCOMPRESSED_LITTLE_ENDIAN;
if (sourceFile.getAbsolutePath().toLowerCase(Locale.ENGLISH).endsWith(".mp3")) {
soundFormat = SoundFormat.FORMAT_MP3;
}
try (FileInputStream fis = new FileInputStream(sourceFile)) {
importSound(tag, fis, soundFormat, r.startFrame);
soundCount++;
}
} catch (IOException | SoundImportException ex) {
Logger.getLogger(ShapeImporter.class.getName()).log(Level.WARNING, "Cannot import sound " + characterId + " from file " + sourceFile.getName(), ex);
}
if (CancellableWorker.isInterrupted()) {
break loopChars;
}
break;
}
}
}
continue;
}
if (existingFilesForSoundTag.size() > 1) { if (existingFilesForSoundTag.size() > 1) {
Logger.getLogger(ShapeImporter.class.getName()).log(Level.WARNING, "Multiple matching files for sound tag {0} exists, {1} selected", new Object[]{characterId, existingFilesForSoundTag.get(0).getName()}); Logger.getLogger(SoundImporter.class.getName()).log(Level.WARNING, "Multiple matching files for sound tag {0} exists, {1} selected", new Object[]{characterId, existingFilesForSoundTag.get(0).getName()});
} }
File sourceFile = existingFilesForSoundTag.get(0); File sourceFile = existingFilesForSoundTag.get(0);
@@ -670,7 +710,7 @@ public class SoundImporter {
soundFormat = SoundFormat.FORMAT_MP3; soundFormat = SoundFormat.FORMAT_MP3;
} }
try (FileInputStream fis = new FileInputStream(sourceFile)) { try (FileInputStream fis = new FileInputStream(sourceFile)) {
importSound(tag, fis, soundFormat); importSound(tag, fis, soundFormat, null);
soundCount++; soundCount++;
} }
} catch (IOException | SoundImportException ex) { } catch (IOException | SoundImportException ex) {
@@ -80,7 +80,9 @@ public class SoundStreamFrameRange implements TreeItem, SoundTag {
@Override @Override
public boolean importSupported() { public boolean importSupported() {
return true; return head.getSoundFormatId() == SoundFormat.FORMAT_MP3
|| head.getSoundFormatId() == SoundFormat.FORMAT_UNCOMPRESSED_LITTLE_ENDIAN
|| head.getSoundFormatId() == SoundFormat.FORMAT_UNCOMPRESSED_NATIVE_ENDIAN;
} }
@Override @Override
@@ -132,7 +134,6 @@ public class SoundStreamFrameRange implements TreeItem, SoundTag {
return "SoundStreamBlocks"; return "SoundStreamBlocks";
} }
@Override @Override
public SoundFormat getSoundFormat() { public SoundFormat getSoundFormat() {
return head.getSoundFormat(); return head.getSoundFormat();
@@ -177,7 +178,6 @@ public class SoundStreamFrameRange implements TreeItem, SoundTag {
return head; return head;
} }
@Override @Override
public String toString() { public String toString() {
return "SoundStreamBlocks (frame " + (startFrame + 1) + " - " + (endFrame + 1) + ")"; return "SoundStreamBlocks (frame " + (startFrame + 1) + " - " + (endFrame + 1) + ")";
@@ -3001,7 +3001,7 @@ public class CommandLineArgumentParser {
try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) { try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) {
SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset); SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset);
while (true) { while (true) {
String objectToReplace = args.pop(); String objectToReplace = args.pop();
if (objectToReplace.matches("\\d+")) { if (objectToReplace.matches("\\d+")) {
// replace character tag // replace character tag
@@ -3104,10 +3104,29 @@ public class CommandLineArgumentParser {
}).importText(textTag, new String(data, Utf8Helper.charset)); }).importText(textTag, new String(data, Utf8Helper.charset));
} else if (characterTag instanceof SoundTag) { } else if (characterTag instanceof SoundTag) {
SoundTag st = (SoundTag) characterTag; SoundTag st = (SoundTag) characterTag;
Integer startFrame = null;
if (!args.isEmpty()) {
if (args.peek().toLowerCase(Locale.ENGLISH).equals("-startframe")) {
args.pop();
if (args.isEmpty()) {
System.err.println("Frame number must be specified");
badArguments("replace");
}
try {
startFrame = Integer.parseInt(args.pop());
} catch (NumberFormatException nfe) {
System.err.println("Frame number should be integer");
badArguments("replace");
}
}
}
boolean ok = false; boolean ok = false;
SoundImporter soundImporter = new SoundImporter(); SoundImporter soundImporter = new SoundImporter();
try { try {
ok = soundImporter.importSound(st, new ByteArrayInputStream(data), soundFormat); ok = soundImporter.importSound(st, new ByteArrayInputStream(data), soundFormat, startFrame);
} catch (SoundParametersMismatchException spm) {
System.err.println("Import FAILED. " + spm.getMessage());
System.exit(3);
} catch (UnsupportedSamplingRateException usre) { } catch (UnsupportedSamplingRateException usre) {
List<String> supportedRatesStr = new ArrayList<>(); List<String> supportedRatesStr = new ArrayList<>();
for (int i : usre.getSupportedRates()) { for (int i : usre.getSupportedRates()) {
@@ -98,18 +98,21 @@ alias /?
Convert FlashPaper SWF file <infile> to PDF <outfile>. Convert FlashPaper SWF file <infile> to PDF <outfile>.
Use -zoom parameter to specify image quality. Use -zoom parameter to specify image quality.
-replace <infile> <outfile> (<characterId1>|<scriptName1>) <importDataFile1> \ -replace <infile> <outfile> \
[nofill] ([<format1>][<methodBodyIndex1>]) \ (<characterId1>|<scriptName1>) <importDataFile1> \
[(<characterId2>|<scriptName2>) <importDataFile2> \ [nofill] [-startFrame <startFrame>] ([<format1>][<methodBodyIndex1>]) \
[nofill] ([<format2>][<methodBodyIndex2>])]... [(<characterId2>|<scriptName2>) <importDataFile2> \
[nofill] [-startFrame <startFrame>] ([<format2>][<methodBodyIndex2>])]...
Replace data. Replace data.
Replaces the data of the specified BinaryData, Image, Shape, Text, Replaces the data of the specified BinaryData, Image, Shape, Text,
Sound tag or Script. Sound tag or Script.
nofill parameter can be specified only for shape replace. nofill parameter can be specified only for shape replace.
<startFrame> parameter sets sound range start frame (for sound streams)
<format> parameter can be specified for Image and Shape tags. <format> parameter can be specified for Image and Shape tags.
valid formats: lossless, lossless2, jpeg2, jpeg3, jpeg4. valid formats: lossless, lossless2, jpeg2, jpeg3, jpeg4.
<methodBodyIndexN> parameter should be specified if and only if the imported entity is an AS3 P-Code. <methodBodyIndexN> parameter should be specified if and only if the imported entity is an AS3 P-Code.
Use -1 as characterId to replace main timeline SoundStreamHead. Use -1 as characterId to replace main timeline SoundStreamHead.
-replace <infile> <outfile> <argsfile> -replace <infile> <outfile> <argsfile>
Replace data using argsfile. Replace data using argsfile.
Same as -replace command, but the rest of arguments is read as lines from Same as -replace command, but the rest of arguments is read as lines from
@@ -5175,7 +5175,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
boolean ok = false; boolean ok = false;
try { try {
ok = soundImporter.importSound(st, new FileInputStream(selfile), soundFormat); ok = soundImporter.importSound(st, new FileInputStream(selfile), soundFormat, null);
((SWF) ((TreeItem) st).getOpenable()).clearSoundCache(); ((SWF) ((TreeItem) st).getOpenable()).clearSoundCache();
} catch (IOException ex) { } catch (IOException ex) {
//ignore //ignore