├── .gitignore
├── LICENSE.md
├── README.md
├── face.png
├── spectrophoto.c
└── stb_image.h
/.gitignore:
--------------------------------------------------------------------------------
1 | out.pcm
2 | spectrogram.png
3 | spectrophoto
4 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Kyle Swanson
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # spectrophoto
2 | Turns an image into an audio file. Image is visible on the spectrogram.
3 |
4 | ## Building
5 | ```bash
6 | git clone https://github.com/kylophone/spectrophoto
7 | cd spectrophoto
8 | gcc ./spectrophoto.c -o spectrophoto -std=c99 -lm -O3 -Wall
9 | ```
10 | ## Usage
11 | ```
12 | spectrophoto in.jpg out.pcm 10
13 | ```
14 | ## Output
15 | This will generate a 48k/32-bit floating point raw PCM file. You can listen to it with SoX like this:
16 |
17 | `play -c 1 -r 48000 -b 32 -e float -t raw out.pcm`
18 |
19 | Generating a spectrogram with SoX looks like this:
20 |
21 | `sox -c 1 -r 48000 -b 32 -e float -t raw out.pcm -n spectrogram`
22 |
--------------------------------------------------------------------------------
/face.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kylophone/spectrophoto/de54397502991b824a59c4ab1e4558362a9a0764/face.png
--------------------------------------------------------------------------------
/spectrophoto.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #define STB_IMAGE_IMPLEMENTATION
6 | #include "stb_image.h"
7 |
8 | #ifndef M_PI
9 | #define M_PI 3.14159265358979323846
10 | #endif
11 |
12 | #define SAMPLE_RATE 48000
13 |
14 | void column_to_PCM(float *buf, float *column, int nb_samples, int height, float **sin_lut)
15 | {
16 | float sample = 0.;
17 |
18 | for (int i = 0; i < nb_samples; i++) {
19 | for (int j = 0; j < height; j++) {
20 | sample += (column[j] * sin_lut[j][i]) / height;
21 | }
22 | sample *= .90;
23 | buf[i] = sample;
24 | }
25 | }
26 |
27 | int get_point_index(int i, int j, int x, int n)
28 | {
29 | return (j * (x * n)) + i * n;
30 | }
31 |
32 | float get_pixel_intensity(unsigned char *img, int n)
33 | {
34 | int RGB_sum = 0;
35 | for (int i = 0; i < n; i++) {
36 | RGB_sum += img[i];
37 | }
38 | float intensity = ((float) RGB_sum / n) / 255;
39 | return intensity;
40 | }
41 |
42 | int main(int argc, char **argv)
43 | {
44 | if (argc != 4) {
45 | fprintf(stderr, "Usage: %s \n", argv[0]);
46 | return 1;
47 | }
48 |
49 | int x,y,n;
50 | const char *input_file = argv[1];
51 | unsigned char *img = stbi_load(input_file, &x, &y, &n, 1);
52 | if (!img) {
53 | fprintf(stderr, "Couldn't load image: %s\n", input_file);
54 | return 1;
55 | }
56 |
57 | int duration = atoi(argv[3]);
58 | int nb_samples = (duration * SAMPLE_RATE) / x;
59 | if (nb_samples <= 0) {
60 | fprintf(stderr, "Duration is too low: %d\n", duration);
61 | return 1;
62 | }
63 |
64 | const char *output_file = argv[2];
65 | FILE *pcm = fopen(output_file, "wb");
66 | if (!pcm) {
67 | fprintf(stderr, "Couldn't open file: %s\n", output_file);
68 | return 1;
69 | }
70 |
71 | float *column = malloc(sizeof(float) * y);
72 | if (!column) {
73 | fprintf(stderr, "Couldn't allocate buffer.\n");
74 | return 1;
75 | }
76 |
77 | float *buf = malloc(sizeof(float) * nb_samples);
78 | if (!buf) {
79 | fprintf(stderr, "Couldn't allocate buffer.\n");
80 | return 1;
81 | }
82 |
83 | float **sin_lut = malloc(sizeof(float*) * y);
84 | if (!sin_lut) {
85 | fprintf(stderr, "Couldn't allocate buffer.\n");
86 | return 1;
87 | }
88 |
89 | for (int i = 0; i < y; i++) {
90 | sin_lut[i] = malloc(sizeof(float) * nb_samples);
91 | if (!sin_lut[i]) {
92 | fprintf(stderr, "Couldn't allocate buffer.\n");
93 | return 1;
94 | }
95 | }
96 |
97 | float nyquist = (float) SAMPLE_RATE / 2;
98 | float hz_step = nyquist / y;
99 | for (int i = 0; i < y; i++) {
100 | for (int j = 0; j < nb_samples; j++) {
101 | float freq = nyquist - (hz_step * i);
102 | float env = sin(2 * M_PI * ((float) j / nb_samples));
103 | sin_lut[i][j] = env * sin(2 * M_PI * freq * ((float) j / SAMPLE_RATE));
104 | }
105 | }
106 |
107 | for (int i = 0; i < x; i++) {
108 | for (int j = 0; j < y; j++) {
109 | int index = get_point_index(i, j, x, 1);
110 | float intensity = get_pixel_intensity(&img[index], 1);
111 | column[j] = intensity;
112 | }
113 | column_to_PCM(buf, column, nb_samples, y, sin_lut);
114 | fwrite(buf, sizeof(float) * nb_samples, 1, pcm);
115 | }
116 |
117 | for (int i = 0; i < y; i++)
118 | free(sin_lut[i]);
119 | free(sin_lut);
120 |
121 | free(buf);
122 | free(column);
123 | stbi_image_free(img);
124 |
125 | fclose(pcm);
126 | return 0;
127 | }
128 |
--------------------------------------------------------------------------------