about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--audio-extension.md39
-rw-r--r--gaudio/README.md2
-rw-r--r--gaudio/src/mp3/mod.rs39
3 files changed, 68 insertions, 12 deletions
diff --git a/audio-extension.md b/audio-extension.md
index a213a8f..dd8503c 100644
--- a/audio-extension.md
+++ b/audio-extension.md
@@ -1,11 +1,38 @@
 # Audio Extension DRAFT
 Add an MP3 audio stream to a gif. 
 
-An application extension with the identifier "GENNYBLE" and auth code "AUD". The data is simply MP3 frames.
+An application extension with the identifier "GENNYBLE" and auth code "AUD".
 
-Questions yet answered:
-- what do we do if the animation and audio length differ?
-- what if there is no graphics extension and thus no length? do we behave differently?
-- what if audio data starts before image data? do we play audio before we display?
+Rough idea:
+- we need an "Audio Control Extension", which is similar to a "Graphic Control Extension". It will provide detail on the upcoming audio data and where it appears so it may inform the decoder.
+- two version:
+	- one that's more era appropriate with MP3
+	- one with Opus which is just cuter
 
-What I'd like to do is just say "all we're doing is shoving MP3 frames in the extension, the rest is on you" and like, the decoder is just supposed to buffer and play the audio when it's received, but that seems.. not great.
\ No newline at end of file
+## Audio Control Extension
+Application Extension. Ident "GENNYBLE" auth code "ACE" *(audio control extension)*.
+
+problems:
+- a decoder may stop reading blocks after it draws an image that has a graphic control with delay. if there is supposed to be audio playing with this frame, it won't know.
+
+## ahh
+- a fixed timescale counting up from the first image every hundreth of a second. audio may not play first.
+
+The stream is driven by the gif video and assumed to be in sync from when it starts.
+
+for audio to be played, there **must** be an ACN extension before the image it's to be played with. this informs the decoder that it's to continue processing after it draws the image. directly after the image should appear the ADT extension
+
+The gif image data drives the audio. The audio **must not** extend the time of
+the file. 
+
+Because the minimal length of an MP3 frame is 1152 samples *(something about size)* the buffer **must** be able to contain a frame of MP3 data. 
+
+## Audio Data Block Extension
+Application Extension. Ident "GENNYBLE" auth code "ADT" *(audio data)*.
+
+
+## Example Data Stream
+GCE - delay 0.1
+ACE - audio after image
+IMG - image
+ADT - audio, dur 0.09, delay 0.01
\ No newline at end of file
diff --git a/gaudio/README.md b/gaudio/README.md
new file mode 100644
index 0000000..b8b16b2
--- /dev/null
+++ b/gaudio/README.md
@@ -0,0 +1,2 @@
+# gaudio
+shove mp3 into a gif.
\ No newline at end of file
diff --git a/gaudio/src/mp3/mod.rs b/gaudio/src/mp3/mod.rs
index 952e9b2..fe6433f 100644
--- a/gaudio/src/mp3/mod.rs
+++ b/gaudio/src/mp3/mod.rs
@@ -1,4 +1,7 @@
-use std::io::{BufRead, BufReader, Cursor, ErrorKind, Read};
+use std::{
+	io::{BufRead, BufReader, Cursor, ErrorKind, Read},
+	time::Duration,
+};
 
 use crate::mp3::bitrate::Bitrate;
 
@@ -49,15 +52,17 @@ impl Breaker {
 				let mut data = vec![0; dat_len];
 				reader.read_exact(&mut data)?;
 				consumed += dat_len;
+				let frame = Frame { header, data };
 
 				println!(
-					"{}kbps {}kHz {}bytes",
-					header.bitrate.kbps().unwrap(),
-					header.samplerate.freq() / 1000,
-					header.length()
+					"{}kbps {}kHz {:<4}bytes [{}ms]",
+					frame.header.bitrate.kbps().unwrap(),
+					frame.header.samplerate.freq() / 1000,
+					frame.header.length(),
+					frame.duration().as_millis()
 				);
 
-				self.frames.push(Frame { header, data });
+				self.frames.push(frame);
 			} else {
 				println!("unsynced!");
 				panic!()
@@ -114,6 +119,28 @@ pub struct Frame {
 	pub data: Vec<u8>,
 }
 
+impl Frame {
+	/// The number of moments-in-time this frame represents. This is constant
+	/// and related to the [Layer]
+	pub fn sample_count(&self) -> usize {
+		// http://www.datavoyage.com/mpgscript/mpeghdr.htm
+		// > Frame size is the number of samples contained in a frame. It is
+		// > constant and always 384 samples for Layer I and 1152 samples for
+		// > Layer II and Layer III.
+		match self.header.layer {
+			Layer::Reserved => panic!(),
+			Layer::Layer1 => 384,
+			Layer::Layer2 | Layer::Layer3 => 1152,
+		}
+	}
+
+	/// Compute the duration of this audio frame
+	pub fn duration(&self) -> Duration {
+		let millis = (self.sample_count() * 1000) / self.header.samplerate.freq();
+		Duration::from_millis(millis as u64)
+	}
+}
+
 pub struct Header {
 	// I only want to parse what i need, but we need this for writing out, so
 	pub raw: [u8; 4],