Skip to content

Commit 77a7647

Browse files
committed
First version of website and python software completed and functional
1 parent 439029c commit 77a7647

5 files changed

Lines changed: 287 additions & 1 deletion

File tree

Python/main.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import string
2+
3+
import numpy as np
4+
import simpleaudio as sa
5+
6+
SAMPLE_RATE = 11025
7+
DURATION = 0.2 # time per letter
8+
SPACE_SILENCE = 1.0 # silence for space
9+
10+
# All letters a-z; each one rises a semitone.
11+
LETTER_SET = list(string.ascii_lowercase)
12+
BASE_FREQ = 196.0 # G3 as base
13+
14+
15+
def letter_freq(letter: str) -> float:
16+
idx = LETTER_SET.index(letter.lower())
17+
return BASE_FREQ * 2 ** (idx / 12)
18+
19+
20+
def tone(freq: float, duration: float = DURATION, sample_rate: int = SAMPLE_RATE) -> np.ndarray:
21+
t = np.linspace(0, duration, int(sample_rate * duration), False)
22+
wave = np.sign(np.sin(2 * np.pi * freq * t)) # square wave
23+
audio = (wave * 0.35 * (2**7 - 1)).astype(np.int8)
24+
return audio
25+
26+
27+
def silence(duration: float, sample_rate: int = SAMPLE_RATE) -> np.ndarray:
28+
return np.zeros(int(sample_rate * duration), dtype=np.int8)
29+
30+
31+
def play_text(text: str) -> None:
32+
chunks: list[np.ndarray] = []
33+
for ch in text:
34+
if ch == " ":
35+
chunks.append(silence(SPACE_SILENCE))
36+
elif ch.lower() in LETTER_SET:
37+
chunks.append(tone(letter_freq(ch)))
38+
chunks.append(silence(0.05))
39+
if not chunks:
40+
return
41+
audio = np.concatenate(chunks)
42+
play_obj = sa.play_buffer(audio, 1, 1, SAMPLE_RATE)
43+
play_obj.wait_done()
44+
45+
46+
if __name__ == "__main__":
47+
sample = "hello world" # example with letters a-z and spaces
48+
play_text(sample)

README.md

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,28 @@
11
# DoremiChar
2-
In Development.
2+
3+
> A website and python software that converts text into 8-bit audio tones.
4+
5+
---
6+
7+
## Needs for use
8+
9+
- PC, laptop for python software.
10+
- PC, laptop or/and smarthphone (web)
11+
- For run in python (pip install numpy simpleaudio)
12+
13+
```bash
14+
pip install numpy simpleaudio
15+
```
16+
17+
---
18+
19+
## Technollogy used
20+
21+
- Python
22+
- Web Audio API (native)
23+
24+
---
25+
26+
## URL of website
27+
28+
[https://airam21170247.github.io/DoremiChar/](https://airam21170247.github.io/DoremiChar/)

Web/index.html

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="UTF-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
<script src="script.js"></script>
8+
<link rel="stylesheet" href="style.css">
9+
<title>DoremiChar</title>
10+
</head>
11+
12+
<body>
13+
<!-- Header -->
14+
<h1>Welcome to DoremiChar</h1>
15+
<p>This website converts text into 8-bit audio tones.</p>
16+
<p style="color: red;">Note:</p>
17+
<p style="color: red;">&nbsp;&nbsp;&nbsp;Just English alphabet is supported (a-z).</p>
18+
<p style="color: red;">&nbsp;&nbsp;&nbsp;Spaces are supported and produce silence.</p>
19+
<p style="color: red;">&nbsp;&nbsp;&nbsp;Uppercase and Lowercase letters are treated the same.</p>
20+
<p></p>
21+
22+
<!-- Text Input -->
23+
<p>Put your text here</p>
24+
<input type="text" id="textInput" placeholder="Enter text...">
25+
<button onclick="playText()">Play Text</button>
26+
27+
<!-- Audio Settings -->
28+
<h2>Audio Settings</h2>
29+
<p for="duration">Duration per Letter (seconds):</p>
30+
<input type="number" id="duration" value="0.2" step="0.1" min="0.1"><br><br>
31+
<p for="spaceSilence">Silence for Space (seconds):</p>
32+
<input type="number" id="spaceSilence" value="1.0" step="0.1" min="0.1"><br><br>
33+
<p for="baseFreq">Base Frequency (Hz):</p>
34+
<input type="number" id="baseFreq" value="196.0" step="10" min="20"><br><br>
35+
</body>
36+
37+
</html>

Web/script.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Init AudioContext (lazy initialization)
2+
let audioContext = null;
3+
4+
function initAudioContext() {
5+
if (!audioContext) {
6+
audioContext = new (window.AudioContext || window.webkitAudioContext)();
7+
}
8+
// Resume context if suspended (required by some browsers)
9+
if (audioContext.state === 'suspended') {
10+
audioContext.resume();
11+
}
12+
return audioContext;
13+
}
14+
15+
const baseFreq = 196; // G3 as base frequency
16+
const letters = 'abcdefghijklmnopqrstuvwxyz';
17+
const durationPerLetter = 0.2; // seconds
18+
const durationPerSpace = 1.0; // seconds
19+
20+
function playTone(frequency, duration = durationPerLetter) {
21+
const ctx = initAudioContext();
22+
const oscillator = ctx.createOscillator();
23+
const gainNode = ctx.createGain();
24+
25+
oscillator.connect(gainNode);
26+
gainNode.connect(ctx.destination);
27+
28+
oscillator.type = 'square'; // square wave
29+
oscillator.frequency.value = frequency;
30+
31+
gainNode.gain.setValueAtTime(0.3, ctx.currentTime);
32+
gainNode.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + duration);
33+
34+
oscillator.start(ctx.currentTime);
35+
oscillator.stop(ctx.currentTime + duration);
36+
}
37+
38+
function playSilence(duration = durationPerSpace) {
39+
// Wait without playing sound
40+
return new Promise(resolve => setTimeout(resolve, duration * 1000));
41+
}
42+
43+
async function playText() {
44+
const text = document.getElementById("textInput").value;
45+
console.log("Playing text as sound:", text);
46+
47+
const durationPerLetter = parseFloat(document.getElementById("duration").value);
48+
const durationPerSpace = parseFloat(document.getElementById("spaceSilence").value);
49+
const baseFreq = parseFloat(document.getElementById("baseFreq").value);
50+
51+
for (const ch of text) {
52+
if (ch === ' ') {
53+
await playSilence(durationPerSpace);
54+
} else if (letters.includes(ch.toLowerCase())) {
55+
const idx = letters.indexOf(ch.toLowerCase());
56+
const freq = baseFreq * Math.pow(2, idx / 12);
57+
playTone(freq, durationPerLetter);
58+
await playSilence(0.05);
59+
}
60+
}
61+
}

Web/style.css

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
* {
2+
margin: 0;
3+
padding: 0;
4+
box-sizing: border-box;
5+
}
6+
7+
html, body {
8+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
9+
background-color: #ffffff;
10+
color: #000000;
11+
}
12+
13+
body {
14+
padding: 40px 20px;
15+
max-width: 600px;
16+
margin: 0 auto;
17+
}
18+
19+
h1 {
20+
font-size: 32px;
21+
font-weight: 700;
22+
margin-bottom: 10px;
23+
color: #000000;
24+
}
25+
26+
h2 {
27+
font-size: 20px;
28+
font-weight: 600;
29+
margin-top: 30px;
30+
margin-bottom: 15px;
31+
color: #000000;
32+
}
33+
34+
p {
35+
font-size: 14px;
36+
margin-bottom: 8px;
37+
color: #000000;
38+
font-weight: 500;
39+
}
40+
41+
input[type="text"],
42+
input[type="number"] {
43+
width: 100%;
44+
padding: 10px;
45+
margin-bottom: 15px;
46+
border: 1px solid #000000;
47+
background-color: #ffffff;
48+
color: #000000;
49+
font-size: 14px;
50+
font-family: inherit;
51+
}
52+
53+
input[type="text"]:focus,
54+
input[type="number"]:focus {
55+
outline: none;
56+
border: 2px solid #000000;
57+
}
58+
59+
button {
60+
width: 100%;
61+
padding: 12px;
62+
background-color: #000000;
63+
color: #ffffff;
64+
border: none;
65+
font-weight: 600;
66+
font-size: 16px;
67+
cursor: pointer;
68+
transition: background-color 0.2s ease;
69+
margin-bottom: 20px;
70+
}
71+
72+
button:hover {
73+
background-color: #333333;
74+
}
75+
76+
button:active {
77+
background-color: #000000;
78+
}
79+
80+
/* Responsive for mobile */
81+
@media (max-width: 600px) {
82+
body {
83+
padding: 20px 15px;
84+
}
85+
86+
h1 {
87+
font-size: 24px;
88+
margin-bottom: 8px;
89+
}
90+
91+
h2 {
92+
font-size: 18px;
93+
margin-top: 20px;
94+
margin-bottom: 12px;
95+
}
96+
97+
p {
98+
font-size: 13px;
99+
margin-bottom: 6px;
100+
}
101+
102+
input[type="text"],
103+
input[type="number"] {
104+
padding: 8px;
105+
margin-bottom: 12px;
106+
font-size: 16px;
107+
}
108+
109+
button {
110+
padding: 10px;
111+
font-size: 14px;
112+
margin-bottom: 15px;
113+
}
114+
}

0 commit comments

Comments
 (0)