사용자 도구

사이트 도구


모르스_부호_변환기

모르스 부호 변환기

자바스크립트, 파이썬, port 3040 으로 웹 접속

수정된 버전입니다. (2026/09/05)

수정 사항은 각 스페이스 (출력되는 내용에서 '/'로 표시) 사이에 0.3초의 공백이 추가되었습니다.

# 2026.09.05 
# morse-letter to morse ver 0.7
# written by 6K2LSV
# python3 에서 작동
# pip3 install flask jamo 
# port number 3040

from flask import Flask, request, render_template_string
import json
from jamo import h2j, j2hcj

app = Flask(__name__)

# 모르스 부호 사전
MORSE_DICT = {
    'ㄱ': '.-..', 'ㄴ': '..-.', 'ㄷ': '-...', 'ㄹ': '...-', 'ㅁ': '--', 'ㅂ': '.--.', 
    'ㅅ': '--.', 'ㅇ': '-.-', 'ㅈ': '.---', 'ㅊ': '-.-.', 'ㅋ': '-..-', 'ㅌ': '--..', 
    'ㅍ': '---.', 'ㅎ': '.---.', 'ㄺ' : '.-....-', 'ㄻ' : '.-..--', 'ㄼ' : '.-.....-', 
    'ㄿ' : '.-....-.', 'ㅀ' : '.-..--..', 'ㄽ' : '..--...', 'ㄲ': '.-...-..', 
    'ㄳ' : '--....', 'ㄵ' : '.---.', 'ㄶ' : '.--...--.', 'ㄸ' : '..--', 'ㅃ' : '..--..', 
    'ㅆ' : '......', 'ㅄ' : '---.--.-', 'ㅉ' : '..-...-.',
    'ㅏ' : '.', '야' : '..', 'ㅓ' : '-', 'ㅕ' : '...', 'ㅗ' : '.-', 'ㅛ' : '-.', 
    'ㅜ' : '....', 'ㅠ' : '.-.', 'ㅡ' : '-..', 'ㅣ' : '..-', 'ㅐ' : '.---', 'ㅔ' : '--..', 
    'ㅙ' : '.-..-', 'ㅘ' : '.-.-.-', 'ㅝ' : '..--', 'ㅚ' : '--..', 'ㅒ' : '..--.-', 
    'ㅖ' : '..--', 'ㅟ' : '----.-', 'ㅢ' : '...-.-..',
    'A': '.-', 'B': '-...', 'C': '-.-.', 'D': '-..', 'E': '.', 'F': '..-.', 
    'G': '--.', 'H': '....', 'I': '..', 'J': '.---', 'K': '-.-', 'L': '.-..', 
    'M': '--', 'N': '-.', 'O': '---', 'P': '.--.', 'Q': '--.-', 'R': '.-.', 
    'S': '...', 'T': '-', 'U': '..-', 'V': '...-', 'W': '.--', 'X': '-..-', 
    'Y': '-.--', 'Z': '--..',
    '1': '.----', '2': '..---', '3': '...--', '4': '....-', '5': '.....', 
    '6': '-....', '7': '--...', '8': '---..', '9': '----.', '0': '-----',
    ' ': '/'
}

HTML_TEMPLATE = """
<!DOCTYPE html>
<html>
<head>
    <title>모르스 변환기</title>
    <style>
        body { font-family: 'Segoe UI', sans-serif; text-align: center; background-color: #eef2f3; margin-top: 50px; }
        .container { width: 70%; margin: auto; padding: 30px; background: white; border-radius: 15px; box-shadow: 0 10px 25px rgba(0,0,0,0.1); }
        textarea { width: 95%; height: 80px; padding: 15px; font-size: 16px; border: 2px solid #ddd; border-radius: 10px; margin-bottom: 15px; }
        button { background-color: #4CAF50; color: white; padding: 12px 30px; border: none; border-radius: 8px; font-size: 18px; cursor: pointer; }
        .display-area { margin-top: 25px; padding: 20px; background: #2d2d2d; border-radius: 10px; min-height: 150px; text-align: left; line-height: 1.8; }
        .unit { display: inline-block; margin-right: 15px; margin-bottom: 10px; padding: 5px 10px; border-radius: 5px; background: #444; }
        .char { display: block; color: #fff; font-size: 14px; text-align: center; border-bottom: 1px solid #666; margin-bottom: 3px; }
        .morse { display: block; color: #ffeb3b; font-family: monospace; font-size: 18px; text-align: center; }
    </style>
</head>
<body>
    <div class="container">
        <h1>📟 한글, 영어, 숫자 모르스 변환기</h1>
        <form method="POST">
            <textarea name="text" placeholder="내용을 입력하세요...">{{ input_text }}</textarea><br>
            <button type="submit">변환 시작</button>
        </form>

        {% if pair_data %}
        <div class="display-area" id="display-area"></div>
        <script>
            const pairData = {{ pair_data | tojson }};
            const displayArea = document.getElementById('display-area');
            const audioCtx = new (window.AudioContext || window.webkitAudioContext)();

            function playBeep(symbol) {
                if (symbol === ' ' || symbol === '/') return 0; // 공백일 때는 비프음 소요 시간 0 반환

                const oscillator = audioCtx.createOscillator();
                const gainNode = audioCtx.createGain();

                oscillator.connect(gainNode);
                gainNode.connect(audioCtx.destination);
                oscillator.type = 'sine';
                oscillator.frequency.setValueAtTime(600, audioCtx.currentTime);

                const duration = (symbol === '-') ? 0.2 : 0.07;
                gainNode.gain.setValueAtTime(0.1, audioCtx.currentTime);

                oscillator.start();
                oscillator.stop(audioCtx.currentTime + duration);

                return duration; // 재생된 시간 반환
            }

            async function startDisplay() {
                for (const item of pairData) {
                    // 한 글자 단위 박스 생성
                    const unit = document.createElement('div');
                    unit.className = 'unit';
                    unit.innerHTML = `<span class="char">${item.char === ' ' ? '공백' : item.char}</span><span class="morse" id="m-${item.id}"></span>`;
                    displayArea.appendChild(unit);

                    const morseSpan = document.getElementById(`m-${item.id}`);

                    // 해당 문자의 모르스 부호를 한 기호씩 출력
                    for (const symbol of item.morse) {
                        morseSpan.innerText += symbol;

                        if (symbol === '/' || symbol === ' ') {
                            // 스페이스 기호가 나오면 0.3초(300ms) 동안 대기
                            await new Promise(resolve => setTimeout(resolve, 300));
                        } else {
                            playBeep(symbol);
                            // 일반 기호는 음길이 이후 다음 기호와의 간격을 위해 기존 150ms 대기
                            await new Promise(resolve => setTimeout(resolve, 150));
                        }
                    }
                }
            }

            window.onload = startDisplay;
        </script>
        {% endif %}
    </div>
</body>
</html>
"""

@app.route('/', methods=['GET', 'POST'])
def index():
    input_text = ""
    pair_data = []
    if request.method == 'POST':
        input_text = request.form.get('text', '')
        # 자모 분리 및 대문자화
        processed_chars = list(j2hcj(h2j(input_text.upper())))

        for i, char in enumerate(processed_chars):
            pair_data.append({
                'id': i,
                'char': char,
                'morse': MORSE_DICT.get(char, char)
            })

    return render_template_string(HTML_TEMPLATE, pair_data=pair_data, input_text=input_text)

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=3040, debug=False)

원래 소스 코드는 아래에 ..

# pip3 instasll flask jamo
from flask import Flask, request, render_template_string
from jamo import h2j, j2hcj

app = Flask(__name__)

MORSE_DICT = {
    'ㄱ': '.-..', 'ㄴ': '..-.', 'ㄷ': '-...', 'ㄹ': '...-', 'ㅁ': '--',
    'ㅂ': '.--.', 'ㅅ': '--.', 'ㅇ': '-.-', 'ㅈ': '.---', 'ㅊ': '-.-.',
    'ㅋ': '-..-', 'ㅌ': '--..', 'ㅍ': '---.', 'ㅎ': '.---.',
    'ㄺ' : '.-....-', 'ㄻ' : '.-..--', 'ㄼ' : '.-.....-',
    'ㄿ' : '.-....-.', 'ㅀ' : '.-..--..', 'ㄽ' : '..--...',
    'ㄲ': '.-...-..',  'ㄳ' : '--....', 'ㄵ' : '.---.', 'ㄶ' : '.--...--.', 'ㄸ' : '..--',
    'ㅃ' : '..--..', 'ㅆ' : '......', 'ㅄ' : '---.--.-', 'ㅉ' : '..-...-.',
    
    'ㅏ' : '.', 'ㅑ' : '..',
    'ㅓ' : '-', 'ㅕ' : '...', 'ㅗ' : '.-', 'ㅛ' : '-.', 'ㅜ' : '....',
    'ㅠ' : '.-.', 'ㅡ' : '-..', 'ㅣ' : '..-', 'ㅐ' : '.---', 'ㅔ' : '--..',
    'ㅙ' : '.-..-', 'ㅝ' : '..--', 'ㅚ' : '--..', 'ㅒ' : '..--.-','ㅘ' : '.-.-.-',
    'ㅖ' : '..--', 'ㅟ' : '----.-', 'ㅢ' : '...-.-..',

    'A': '.-', 'B': '-...', 'C': '-.-.', 'D': '-..', 'E': '.', 'F': '..-.',
    'G': '--.', 'H': '....', 'I': '..', 'J': '.---', 'K': '-.-', 'L': '.-..',
    'M': '--', 'N': '-.', 'O': '---', 'P': '.--.', 'Q': '--.-', 'R': '.-.',
    'S': '...', 'T': '-', 'U': '..-', 'V': '...-', 'W': '.--', 'X': '-..-',
    'Y': '-.--', 'Z': '--..',

    '1': '.----', '2': '..---', '3': '...--', '4': '....-', '5': '.....',
    '6': '-....', '7': '--...', '8': '---..', '9': '----.', '0': '-----',
    ' ': '/'
}

HTML_TEMPLATE = """
<!DOCTYPE html>
<html>
<head>
    <title>한글/영문/숫자 모르스 변환기 (Port 3040)</title>
    <style>
        body { font-family: 'Segoe UI', sans-serif; text-align: center; background-color: #eef2f3; margin-top: 50px; }
        .container { width: 60%; margin: auto; padding: 30px; background: white; border-radius: 15px; box-shadow: 0 10px 25px rgba(0,0,0,0.1); }
        textarea { width: 95%; height: 120px; padding: 15px; font-size: 16px; border: 2px solid #ddd; border-radius: 10px; resize: none; margin-bottom: 15px; }
        button { background-color: #4CAF50; color: white; padding: 12px 30px; border: none; border-radius: 8px; font-size: 18px; cursor: pointer; transition: 0.3s; }
        button:hover { background-color: #45a049; }
        .box { text-align: left; margin-top: 25px; padding: 15px; border-radius: 8px; border-left: 5px solid #2196F3; background-color: #f9f9f9; }
        .morse-box { background: #2d2d2d; color: #ffeb3b; font-family: 'Courier New', monospace; word-break: break-all; font-size: 20px; letter-spacing: 2px; border-left: 5px solid #ff9800; min-height: 60px; }
        h4 { margin-bottom: 5px; color: #555; }
    </style>
</head>
<body>
    <div class="container">
        <h1>📟 사운드 모르스 변환기</h1>
        <form method="POST">
            <textarea name="text" placeholder="입력하세요...">{{ input_text }}</textarea><br>
            <button type="submit">변환 및 재생</button>
        </form>

        {% if result %}
                <div class="box">
            <h4>📝 입력 데이터 :</h4>
            <div style="font-size: 18px;">{{ input_text }}</div>
        </div>

        <div class="box morse-box">
            <h4>⚡ 모르스 부호 변환 결과 :</h4>
            <div id="typing-result"></div>
        </div>

        <script>
            const fullText = {{ result | tojson }};
            const displayElement = document.getElementById('typing-result');
            let index = 0;

            // Web Audio API 설정
            const audioCtx = new (window.AudioContext || window.webkitAudioContext)();

            function playBeep(isDash) {
                const oscillator = audioCtx.createOscillator();
                const gainNode = audioCtx.createGain();

                oscillator.connect(gainNode);
                gainNode.connect(audioCtx.destination);

                oscillator.type = 'sine';
                oscillator.frequency.setValueAtTime(600, audioCtx.currentTime); // 주파수 600Hz

                const duration = isDash ? 0.2 : 0.07; // 대시(-)는 길게, 도트(.)는 짧게

                gainNode.gain.setValueAtTime(0.1, audioCtx.currentTime);
                oscillator.start();
                oscillator.stop(audioCtx.currentTime + duration);
            }

            function typeAndPlay() {
                if (index < fullText.length) {
                    const char = fullText.charAt(index);
                    displayElement.innerHTML += char;

                    if (char === '.') {
                        playBeep(false);
                    } else if (char === '-') {
                        playBeep(true);
                    }

                            index++;
                    setTimeout(typeAndPlay, 100); // 0.1초 간격
                }
            }

            window.onload = () => {

                if (fullText.length > 0) {
                    typeAndPlay();
                }
            };
        </script>
        {% endif %}
    </div>
</body>
</html>
"""

@app.route('/', methods=['GET', 'POST'])
def index():
    result = ""
    input_text = ""
    if request.method == 'POST':
        input_text = request.form.get('text', '')
        processed = j2hcj(h2j(input_text.upper()))
        morse_list = [MORSE_DICT.get(char, char) for char in processed]
        result = " ".join(morse_list)
    return render_template_string(HTML_TEMPLATE, result=result, input_text=input_text)

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=3040, debug=False)
적당한 파일이름(여기서는 morse.py)으로 저장하고,
python3 morse.py 로 실행

2026.03.18 akpil

모르스_부호_변환기.txt · 마지막으로 수정됨: 저자 akpil

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki