forked from orhun/linuxwave
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgen.zig
More file actions
72 lines (63 loc) · 2.64 KB
/
gen.zig
File metadata and controls
72 lines (63 loc) · 2.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
//! Music generator.
const std = @import("std");
/// Generator configuration.
pub const GeneratorConfig = struct {
/// Semitones from the base note in a major musical scale.
scale: []const u8,
/// Frequency of the note.
///
/// <https://pages.mtu.edu/~suits/notefreqs.html>
note: f32,
/// Volume control.
volume: u8,
};
/// Generator implementation.
pub const Generator = struct {
/// Number of calculated samples per sine curve (affects perceived frequency).
pub const sample_count: usize = 10000;
/// Configuration.
config: GeneratorConfig,
/// Creates a new instance.
pub fn init(config: GeneratorConfig) Generator {
return Generator{ .config = config };
}
/// Generates a sound from the given sample.
///
/// Returns an array that contains the amplitudes of the sound wave at a given point in time.
pub fn generate(self: Generator, allocator: std.mem.Allocator, sample: u8) ![]u8 {
var buffer: std.ArrayList(u8) = .empty;
var i: usize = 0;
while (i < sample_count) : (i += 1) {
// Calculate the frequency according to the equal temperament.
// Hertz = 440 * 2^(semitone distance / 12)
// (<http://en.wikipedia.org/wiki/Equal_temperament>)
const tone_distance: f32 = @floatFromInt(self.config.scale[sample % self.config.scale.len]);
const increment: f32 = @floatFromInt(i);
var amp = @sin(self.config.note * std.math.pi * std.math.pow(f32, 2, tone_distance / 12) * (increment * 0.0001));
// Scale the amplitude between 0 and 256.
amp = (amp * std.math.maxInt(u8) / 2) + (std.math.maxInt(u8) / 2);
// Apply the volume control.
const volume: f32 = @floatFromInt(self.config.volume);
amp = amp * volume / 100;
try buffer.append(allocator, @trunc(amp));
}
return buffer.toOwnedSlice(allocator);
}
};
test "generate music" {
const config = GeneratorConfig{
.scale = &[_]u8{ 0, 1 },
.note = 440,
.volume = 100,
};
const generator = Generator.init(config);
const allocator = std.testing.allocator;
var data1 = try generator.generate(allocator, 'a');
defer allocator.free(data1);
try std.testing.expectEqualSlices(u8, &[_]u8{ 127, 145, 163, 181, 197, 212, 225, 235 }, data1[0..8]);
try std.testing.expect(data1.len == 10000);
var data2 = try generator.generate(allocator, 'b');
defer allocator.free(data2);
try std.testing.expectEqualSlices(u8, &[_]u8{ 127, 144, 161, 178, 193, 208, 221, 232 }, data2[0..8]);
try std.testing.expect(data2.len == 10000);
}