Last active
October 28, 2019 04:59
-
-
Save damncabbage/08b85afb62f30667270bf1f1026abfb4 to your computer and use it in GitHub Desktop.
Voyager Image, a crappy non-functional attempt. π
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[package] | |
name = "voyager_images" | |
version = "0.1.0" | |
authors = ["Rob Howard <[email protected]>"] | |
edition = "2018" | |
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | |
[dependencies] | |
hound = "3.4.0" | |
image = "0.22.3" | |
log = "0.4.8" | |
simple_logger = "1.0.1" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::f32; | |
use std::io; | |
use log::{error, info, debug}; | |
use simple_logger; | |
use std::convert::{AsMut, TryInto}; | |
use hound; | |
use image::bmp::*; | |
use image::ColorType; | |
use std::path::Path; | |
use std::io::{BufWriter, Write}; | |
use std::fs::File; | |
fn main() { | |
simple_logger::init().unwrap(); | |
let (wav, image) = ("./384kHzStereo.wav", "./384kHzStereo.bmp"); | |
match &wav_to_image(wav, image) { | |
Err(e) => error!("ERROR: {}", e), | |
Ok(_result) => info!("Done!"), | |
} | |
} | |
// The 'real' main | |
fn wav_to_image(wav_filename: &str, img_filename: &str) -> io::Result<()> { | |
let lines = scaled_lines_from_wav(wav_filename, (0, 255))?; | |
let image = image_from_lines(lines, img_filename)?; | |
// TODO: write_image_to_file(image, img_filename)?; | |
// TODO: JUST WRITE IT OUT AS A BIG SHITTY IMAGE | |
// and then run `convert` on it. | |
Ok(()) | |
} | |
fn scaled_lines_from_wav(filename: &str, rescale_range: (u8, u8)) -> io::Result<Vec<Vec<u8>>> { | |
let mut reader = | |
hound::WavReader::open(filename).expect(&["Failed to open WAV file: ", filename].concat()); | |
// File info from afinfo: | |
// 384kHzStereo.wav | |
// File: 384kHzStereo.wav | |
// File type ID: WAVE | |
// Num Tracks: 1 | |
// ---- | |
// Data format: 2 ch, 384000 Hz, 'lpcm' (0x00000009) 32-bit little-endian float | |
// no channel layout. | |
// estimated duration: 473.856000 sec | |
// audio bytes: 1455685632 | |
// audio packets: 181960704 | |
// bit rate: 24576000 bits per second | |
// packet size upper bound: 8 | |
// maximum packet size: 8 | |
// audio data file offset: 88 | |
// optimized | |
// source bit depth: F32 | |
// ---- | |
// File info from Hound: | |
// println!("{:?}", reader.spec()); | |
// WavSpec { channels: 2, sample_rate: 384000, bits_per_sample: 32, sample_format: Float } | |
// | |
// Extra info: | |
// num_samples = 363_921_408 | |
// start (before first 'buffer'): 00:00:15.674 sec, 6_023_017 samples | |
// length of each 'content' chunk: 0.006 sec, 2564 samples long | |
// length of each 'buffer' chunk: 0.002 sec, 649 sampless long | |
// TODO: Use a smarter 'picture frame'/buffer detection method so we don't have to fiddle with fixed | |
// number; each scan-line and buffer chunk is a different size. | |
let before_first_buffer_chunk = 6_019_836; | |
let content_chunk_length = 2_555; | |
let buffer_chunk_length = 643; | |
let before_last_buffer_chunk = 7_707_417; | |
let picture_length = before_last_buffer_chunk - before_first_buffer_chunk; // 1687581 | |
let num_samples = reader.len() as u32; | |
// - Seek to where we think the first sample is starting (hardcoded) | |
reader.seek(before_first_buffer_chunk)?; | |
// - Figure out how long we want to grab a sample for (hardcoded) | |
// - Iterate for that amount, pulling out only the Left channel (the one with the calibration picture on it). | |
let samples = reader | |
.samples::<f32>() | |
.take((picture_length as usize) * 2) | |
// .skip(2)? | |
.fold((true, Vec::new()), |(is_left, mut amps), s| { | |
if is_left { | |
amps.push(s.unwrap()); | |
} | |
(!is_left, amps) | |
}) | |
.1; | |
let mut samples_iter = samples.iter(); | |
let mut lines = Vec::new(); | |
for _idx in 0..((picture_length / (content_chunk_length + buffer_chunk_length)) as u32) { | |
let line = &mut samples_iter; | |
let (min, max, line) = line | |
.skip(buffer_chunk_length as usize) | |
.take(content_chunk_length as usize) | |
.fold( | |
(f32::INFINITY, f32::NEG_INFINITY, Vec::new()), | |
|(mut min, mut max, mut samples), sample| { | |
if sample < &min { | |
min = *sample | |
} | |
if sample > &max { | |
max = *sample | |
} | |
samples.push(*sample); | |
(min, max, samples) | |
}, | |
); | |
let rescaled_line = line.iter().map(|sample| { | |
let (rescale_from, rescale_to) = (rescale_range.0 as f32, rescale_range.1 as f32); | |
let rescaled = (sample - min) * (rescale_from - rescale_to) / (max - min) + rescale_from; | |
rescaled as u8 | |
}).collect::<Vec<u8>>(); | |
lines.push(rescaled_line); | |
} | |
println!("Samples: {} {:?} {:?}", num_samples, lines.len(), lines[0].len()); | |
Ok(lines) | |
} | |
fn image_from_lines(lines: Vec<Vec<u8>>, filename: &str) -> io::Result<()> { | |
// TODO: Actually make things the right size. | |
// TODO: Not use a scattered bunch of constants everywhere I need to manually do the arithmetic on | |
// to get the array size for (ie. compile-time arithmetic). | |
// 525 high x 2564 wide, ie. picture_length high x content_chunk_length wide | |
let height: u32 = 528; | |
let width: u32 = 2564; | |
let mut picture = [0; 1353792]; | |
for row_idx in 0..lines.len() { | |
let row_idx_base = row_idx * (width as usize); | |
let v_row = &lines[row_idx]; | |
debug!("{}, {}, {}: {}", row_idx, row_idx_base, row_idx_base + v_row.len(), v_row.len()); | |
for col_idx in 0..v_row.len() { | |
picture[row_idx_base + col_idx] = v_row[col_idx]; | |
} | |
} | |
let path = Path::new(filename); | |
let file = &mut BufWriter::new(File::create(path)?); | |
let mut encoder = BMPEncoder::new(file); | |
encoder.encode( | |
&picture, | |
width, | |
height, | |
ColorType::Gray(8), | |
)?; | |
Ok(()) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment