Implement SoundStreamHead tags

This commit is contained in:
Mike Welsh 2016-09-13 17:29:59 -07:00
parent dc075a0302
commit b4c40ac26c
6 changed files with 160 additions and 53 deletions

View File

@ -391,6 +391,15 @@ impl<R: Read> Reader<R> {
Tag::SetBackgroundColor(try!(tag_reader.read_rgb())) Tag::SetBackgroundColor(try!(tag_reader.read_rgb()))
}, },
Some(TagCode::SoundStreamHead) => Tag::SoundStreamHead(
// TODO: Disallow certain compressions.
Box::new(try!(tag_reader.read_sound_stream_info()))
),
Some(TagCode::SoundStreamHead2) => Tag::SoundStreamHead2(
Box::new(try!(tag_reader.read_sound_stream_info()))
),
Some(TagCode::StartSound) => Tag::StartSound { Some(TagCode::StartSound) => Tag::StartSound {
id: try!(tag_reader.read_u16()), id: try!(tag_reader.read_u16()),
sound_info: Box::new(try!(tag_reader.read_sound_info())), sound_info: Box::new(try!(tag_reader.read_sound_info())),
@ -621,42 +630,35 @@ impl<R: Read> Reader<R> {
fn read_define_sound(&mut self) -> Result<Tag> { fn read_define_sound(&mut self) -> Result<Tag> {
let id = try!(self.read_u16()); let id = try!(self.read_u16());
let flags = try!(self.read_u8()); let format = try!(self.read_sound_format());
let compression = match flags >> 4 {
0 => AudioCompression::UncompressedUnknownEndian,
1 => AudioCompression::Adpcm,
2 => AudioCompression::Mp3,
3 => AudioCompression::Uncompressed,
4 => AudioCompression::Nellymoser16Khz,
5 => AudioCompression::Nellymoser8Khz,
6 => AudioCompression::Nellymoser,
11 => AudioCompression::Speex,
_ => return Err(Error::new(ErrorKind::InvalidData,
"Invalid audio format.")),
};
let sample_rate = match (flags & 0b11_00) >> 2 {
0 => 5512,
1 => 11025,
2 => 22050,
3 => 44100,
_ => unreachable!(),
};
let is_16_bit = (flags & 0b10) != 0;
let is_stereo = (flags & 0b1) != 0;
let num_samples = try!(self.read_u32()); let num_samples = try!(self.read_u32());
let mut data = Vec::new(); let mut data = Vec::new();
try!(self.input.read_to_end(&mut data)); try!(self.input.read_to_end(&mut data));
Ok(Tag::DefineSound(Box::new(Sound { Ok(Tag::DefineSound(Box::new(Sound {
id: id, id: id,
compression: compression, format: format,
sample_rate: sample_rate,
is_16_bit: is_16_bit,
is_stereo: is_stereo,
num_samples: num_samples, num_samples: num_samples,
data: data data: data
}))) })))
} }
fn read_sound_stream_info(&mut self) -> Result<SoundStreamInfo> {
// TODO: Verify version requirements.
let playback_format = try!(self.read_sound_format());
let stream_format = try!(self.read_sound_format());
let num_samples_per_block = try!(self.read_u16());
let latency_seek = if stream_format.compression == AudioCompression::Mp3 {
// Specs say this is i16, not u16. How are negative values used?
try!(self.read_i16())
} else { 0 };
Ok(SoundStreamInfo {
stream_format: stream_format,
playback_format: playback_format,
num_samples_per_block: num_samples_per_block,
latency_seek: latency_seek,
})
}
fn read_shape_styles(&mut self, shape_version: u8) -> Result<ShapeStyles> { fn read_shape_styles(&mut self, shape_version: u8) -> Result<ShapeStyles> {
let num_fill_styles = match try!(self.read_u8()) { let num_fill_styles = match try!(self.read_u8()) {
0xff if shape_version >= 2 => try!(self.read_u16()) as usize, 0xff if shape_version >= 2 => try!(self.read_u16()) as usize,
@ -1228,6 +1230,37 @@ impl<R: Read> Reader<R> {
Ok(filter) Ok(filter)
} }
fn read_sound_format(&mut self) -> Result<SoundFormat> {
let flags = try!(self.read_u8());
let compression = match flags >> 4 {
0 => AudioCompression::UncompressedUnknownEndian,
1 => AudioCompression::Adpcm,
2 => AudioCompression::Mp3,
3 => AudioCompression::Uncompressed,
4 => AudioCompression::Nellymoser16Khz,
5 => AudioCompression::Nellymoser8Khz,
6 => AudioCompression::Nellymoser,
11 => AudioCompression::Speex,
_ => return Err(Error::new(ErrorKind::InvalidData,
"Invalid audio format.")),
};
let sample_rate = match (flags & 0b11_00) >> 2 {
0 => 5512,
1 => 11025,
2 => 22050,
3 => 44100,
_ => unreachable!(),
};
let is_16_bit = (flags & 0b10) != 0;
let is_stereo = (flags & 0b1) != 0;
Ok(SoundFormat {
compression: compression,
sample_rate: sample_rate,
is_16_bit: is_16_bit,
is_stereo: is_stereo,
})
}
fn read_sound_info(&mut self) -> Result<SoundInfo> { fn read_sound_info(&mut self) -> Result<SoundInfo> {
let flags = try!(self.read_u8()); let flags = try!(self.read_u8());
let event = match (flags >> 4) & 0b11 { let event = match (flags >> 4) & 0b11 {

View File

@ -272,10 +272,12 @@ pub fn tag_tests() -> Vec<TagTestData> { vec![
4, 4,
Tag::DefineSound(Box::new(Sound { Tag::DefineSound(Box::new(Sound {
id: 1, id: 1,
compression: AudioCompression::Uncompressed, format: SoundFormat {
sample_rate: 44100, compression: AudioCompression::Uncompressed,
is_16_bit: true, sample_rate: 44100,
is_stereo: false, is_16_bit: true,
is_stereo: false,
},
num_samples: 10, num_samples: 10,
data: vec![255, 127, 0, 128, 255, 127, 0, 128, 255, 127, 0, 128, 255, 127, 0, 128, 255, 127, 0, 128], data: vec![255, 127, 0, 128, 255, 127, 0, 128, 255, 127, 0, 128, 255, 127, 0, 128, 255, 127, 0, 128],
})), })),
@ -574,6 +576,27 @@ pub fn tag_tests() -> Vec<TagTestData> { vec![
(1, Tag::ShowFrame, vec![0b01_000000, 0]), (1, Tag::ShowFrame, vec![0b01_000000, 0]),
(
3,
Tag::SoundStreamHead2(Box::new(SoundStreamInfo {
stream_format: SoundFormat {
compression: AudioCompression::Uncompressed,
sample_rate: 5512,
is_16_bit: true,
is_stereo: false,
},
playback_format: SoundFormat {
compression: AudioCompression::UncompressedUnknownEndian,
sample_rate: 5512,
is_16_bit: true,
is_stereo: false,
},
num_samples_per_block: 229,
latency_seek: 0,
})),
read_tag_bytes_from_file("tests/swfs/soundstreamhead2.swf", TagCode::SoundStreamHead2)
),
( (
9, 9,
Tag::SymbolClass(vec![ Tag::SymbolClass(vec![

View File

@ -291,6 +291,8 @@ pub enum Tag {
ImportAssets { url: String, imports: Vec<ExportedAsset> }, ImportAssets { url: String, imports: Vec<ExportedAsset> },
SetBackgroundColor(Color), SetBackgroundColor(Color),
SetTabIndex { depth: Depth, tab_index: u16 }, SetTabIndex { depth: Depth, tab_index: u16 },
SoundStreamHead(Box<SoundStreamInfo>),
SoundStreamHead2(Box<SoundStreamInfo>),
StartSound { id: CharacterId, sound_info: Box<SoundInfo> }, StartSound { id: CharacterId, sound_info: Box<SoundInfo> },
StartSound2 { class_name: String, sound_info: Box<SoundInfo> }, StartSound2 { class_name: String, sound_info: Box<SoundInfo> },
SymbolClass(Vec<SymbolClassLink>), SymbolClass(Vec<SymbolClassLink>),
@ -336,10 +338,7 @@ pub struct Shape {
#[derive(Debug,PartialEq,Clone)] #[derive(Debug,PartialEq,Clone)]
pub struct Sound { pub struct Sound {
pub id: CharacterId, pub id: CharacterId,
pub compression: AudioCompression, pub format: SoundFormat,
pub sample_rate: u16,
pub is_16_bit: bool,
pub is_stereo: bool,
pub num_samples: u32, pub num_samples: u32,
pub data: Vec<u8>, pub data: Vec<u8>,
} }
@ -506,4 +505,20 @@ pub enum AudioCompression {
Nellymoser8Khz, Nellymoser8Khz,
Nellymoser, Nellymoser,
Speex, Speex,
}
#[derive(Debug,PartialEq,Clone)]
pub struct SoundFormat {
pub compression: AudioCompression,
pub sample_rate: u16,
pub is_stereo: bool,
pub is_16_bit: bool,
}
#[derive(Debug,PartialEq,Clone)]
pub struct SoundStreamInfo {
pub stream_format: SoundFormat,
pub playback_format: SoundFormat,
pub num_samples_per_block: u16,
pub latency_seek: i16,
} }

View File

@ -454,6 +454,14 @@ impl<W: Write> Writer<W> {
try!(self.write_i16(depth)); try!(self.write_i16(depth));
}, },
&Tag::SoundStreamHead(ref sound_stream_info) => {
try!(self.write_sound_stream_head(sound_stream_info, 1));
}
&Tag::SoundStreamHead2(ref sound_stream_info) => {
try!(self.write_sound_stream_head(sound_stream_info, 2));
}
&Tag::StartSound { id, ref sound_info } => { &Tag::StartSound { id, ref sound_info } => {
let length = 3 let length = 3
+ if let Some(_) = sound_info.in_sample { 4 } else { 0 } + if let Some(_) = sound_info.in_sample { 4 } else { 0 }
@ -599,25 +607,7 @@ impl<W: Write> Writer<W> {
7 + sound.data.len() as u32 7 + sound.data.len() as u32
)); ));
try!(self.write_u16(sound.id)); try!(self.write_u16(sound.id));
try!(self.write_ubits(4, match sound.compression { try!(self.write_sound_format(&sound.format));
AudioCompression::UncompressedUnknownEndian => 0,
AudioCompression::Adpcm => 1,
AudioCompression::Mp3 => 2,
AudioCompression::Uncompressed => 3,
AudioCompression::Nellymoser16Khz => 4,
AudioCompression::Nellymoser8Khz => 5,
AudioCompression::Nellymoser => 6,
AudioCompression::Speex => 11,
}));
try!(self.write_ubits(2, match sound.sample_rate {
5512 => 0,
11025 => 1,
22050 => 2,
44100 => 3,
_ => return Err(Error::new(ErrorKind::InvalidData, "Invalid sample rate.")),
}));
try!(self.write_bit(sound.is_16_bit));
try!(self.write_bit(sound.is_stereo));
try!(self.write_u32(sound.num_samples)); try!(self.write_u32(sound.num_samples));
try!(self.output.write_all(&sound.data)); try!(self.output.write_all(&sound.data));
Ok(()) Ok(())
@ -1186,6 +1176,52 @@ impl<W: Write> Writer<W> {
Ok(()) Ok(())
} }
fn write_sound_stream_head(&mut self, stream_info: &SoundStreamInfo, version: u8) -> Result<()> {
let tag_code = if version >= 2 {
TagCode::SoundStreamHead2
} else {
TagCode::SoundStreamHead
};
// MP3 compression has added latency seek field.
let length = if stream_info.stream_format.compression == AudioCompression::Mp3 {
6
} else {
4
};
try!(self.write_tag_header(tag_code, length));
try!(self.write_sound_format(&stream_info.playback_format));
try!(self.write_sound_format(&stream_info.stream_format));
try!(self.write_u16(stream_info.num_samples_per_block));
if stream_info.stream_format.compression == AudioCompression::Mp3 {
try!(self.write_i16(stream_info.latency_seek));
}
Ok(())
}
fn write_sound_format(&mut self, sound_format: &SoundFormat) -> Result<()> {
try!(self.write_ubits(4, match sound_format.compression {
AudioCompression::UncompressedUnknownEndian => 0,
AudioCompression::Adpcm => 1,
AudioCompression::Mp3 => 2,
AudioCompression::Uncompressed => 3,
AudioCompression::Nellymoser16Khz => 4,
AudioCompression::Nellymoser8Khz => 5,
AudioCompression::Nellymoser => 6,
AudioCompression::Speex => 11,
}));
try!(self.write_ubits(2, match sound_format.sample_rate {
5512 => 0,
11025 => 1,
22050 => 2,
44100 => 3,
_ => return Err(Error::new(ErrorKind::InvalidData, "Invalid sample rate.")),
}));
try!(self.write_bit(sound_format.is_16_bit));
try!(self.write_bit(sound_format.is_stereo));
try!(self.flush_bits());
Ok(())
}
fn write_sound_info(&mut self, sound_info: &SoundInfo) -> Result<()> { fn write_sound_info(&mut self, sound_info: &SoundInfo) -> Result<()> {
let flags = let flags =
match sound_info.event { match sound_info.event {

Binary file not shown.

Binary file not shown.