hilbert_image_to_sound/
lib.rs

1#![deny(missing_docs)]
2
3//! # hilbert_image_to_sound
4//! A library for turning images into sound using Hilbert space-filling curves
5//!
6//! [Links to other related research projects](https://github.com/advancedresearch/hilbert_image_to_sound/issues/3)
7//!
8//! *Notice: This library is in rough shape now,
9//! just to demonstrating the concept. PRs are welcome!*
10
11/// Plays an image file as sound.
12pub fn play(file: &str) {
13    use cpal::traits::{DeviceTrait, HostTrait, EventLoopTrait};
14
15    let image = image::open(file).unwrap().to_rgba();
16
17    // Controls the resolution of transformed image dimensions.
18    let n = 16;
19    // Stores the Hilbert curve 1d coordinates for each pixel position.
20    let mut hilbert: Vec<Vec<usize>> = vec![vec![0; n]; n];
21    // Create an inverse map.
22    let n2 = n as usize * n as usize;
23    for i in 0..n2 {
24        let (x, y) = hilbert_curve::convert_1d_to_2d(i, n2);
25        hilbert[y][x] = i;
26    }
27
28    // Stores sound frequency amplitudes.
29    let mut amplitudes: Vec<f64> = vec![0.0; n2];
30    let (w, h) = image.dimensions();
31    let cell = w.min(h) / n as u32;
32    for (x, y, pixel) in image.enumerate_pixels() {
33        let x = x / cell;
34        let y = y / cell;
35        if x >= n as u32 || y >= n as u32 {continue}
36        let image::Rgba([r, g, b, _]) = pixel;
37        let frequency = hilbert[y as usize][x as usize];
38        let amplitude = (*r as f64 + *g as f64 + *b as f64) / 255.0 / 3.0;
39        amplitudes[frequency] += amplitude / (cell as f64 * cell as f64);
40    }
41
42    let host = cpal::default_host();
43    let event_loop = host.event_loop();
44    let device = host.default_output_device().expect("no output device available");
45
46    let mut supported_formats_range = device.supported_output_formats()
47        .expect("error while querying formats");
48    let format = supported_formats_range.next()
49        .expect("no supported format?!")
50        .with_max_sample_rate();
51
52    let stream_id = event_loop.build_output_stream(&device, &format).unwrap();
53    event_loop.play_stream(stream_id).expect("failed to play_stream");
54
55    let tau: f64 = 6.283185307179586;
56    let mut t: f64 = 0.0;
57    let volume = 0.2;
58    std::thread::spawn(move || {
59
60        event_loop.run(move |stream_id, stream_result| {
61            use cpal::{StreamData, UnknownTypeOutputBuffer};
62
63            let stream_data = match stream_result {
64                Ok(data) => data,
65                Err(err) => {
66                    eprintln!("an error occurred on stream {:?}: {}", stream_id, err);
67                    return;
68                }
69            };
70
71            match stream_data {
72                StreamData::Output { buffer: UnknownTypeOutputBuffer::F32(mut buffer) } => {
73                    for elem in buffer.iter_mut() {
74                        let mut s: f64 = 0.0;
75                        for (i, amp) in amplitudes.iter().enumerate() {
76                            let fi = i as f64 / n2 as f64;
77                            let f = 4.5 * fi + 0.25;
78                            s += *amp * (t * f * tau + i as f64).sin();
79                        }
80                        *elem = volume * s as f32;
81
82                        t += 0.005;
83                    }
84                },
85                _ => (),
86            }
87        });
88    });
89
90    std::thread::sleep(std::time::Duration::from_secs_f64(2.0))
91}