Implement SoundStreamHead tags
This commit is contained in:
parent
dc075a0302
commit
b4c40ac26c
85
src/read.rs
85
src/read.rs
|
@ -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 {
|
||||||
|
|
|
@ -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![
|
||||||
|
|
23
src/types.rs
23
src/types.rs
|
@ -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,
|
||||||
}
|
}
|
74
src/write.rs
74
src/write.rs
|
@ -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.
Loading…
Reference in New Issue