Maus als Morsetaste mit dem Browser (BETA)

Dieses ist eine frühe BETA Version welche dazu gedacht ist das Projekt weiter zu verbessern.

Ich wollte schon länger mal das Projekt umsetzen, welches die Maus als Morsetaste missbraucht, um einen Morsetutor im Browser zu verwirklichen.

Dabei kann die Maus selbst als Paddle Taste missbraucht werden, oder man lötet einen Anschluss an die beiden Tasten an um sie direkt mit einer Morsetaste zu verbinden.

Bedienung: Stelle die gewünschte Sendegeschwindigkeit und Tonhöhe ein.
Bewege den Mauszeiger in den hellgrünen Kasten – Fertig – jetzt kannst du die beiden Maustasten als Paddle benutzen.

Testbereich

15
800


Maus in dieses Feld bewegen.
Klicken und halten Sie die Maustasten

Technik

Zum Testen braucht ihr erst mal nichts außer der am PC angeschlossenen Maus. Diese einfach in das grüne Feld ziehen und schon kann gemorst werden. Wer seine Paddle Taste anschließen will kann das sehr einfach und kostengünstig.

Nehmt eine (alte) Maus. Öffnet Sie und lötet einfach an den beiden Tastern die Drähte an, welche Ihr an die Paddle Taste anschließt.

Es können mehrere Mäuse parallel betrieben werden. Nur die Maus mit welcher ihr morsen wollt muss in das grüne Feld bewegt werden.

Code

<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="UTF-8">
    <title>Morse-Eingabe mit WPM-Einstellung</title>
    <style>
        body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
        #input-area {
            width: 300px;
            height: 100px;
            border: 2px solid #000;
            margin: 20px 0;
            display: flex;
            justify-content: center;
            align-items: center;
            font-size: 18px;
            cursor: pointer;
            padding: 15px;
            background: lightgreen;
        }
        input[type=range]:focus {
            outline: none;
        }
  
        input[type=range] {
	  appearance: none;
	  width: 100%;
	  border-radius: 6px;
	  height: 12px;
	  border: 1px solid blue;
	  background-color: cornflowerblue; 
        }
        #visual-feedback {
            width: 50px;
            height: 50px;
            border-radius: 50%;
            background-color: #ccc;
            margin: 20px auto;
        }
        #output { font-size: 18px; margin-top: 20px; }
        .slider-container { margin-bottom: 10px; }
        .slider-container label { display: inline-block; width: 200px; }
        .slider-container input { width: 300px; }
        .slider-value { display: inline-block; width: 50px; text-align: right; }
    </style>
</head>
<body>
    <h1>Morse-Eingabe mit WPM-Einstellung</h1>
    
    <div class="slider-container">
        <label for="wpm">Geschwindigkeit (WPM):</label>
        <input type="range" id="wpm" min="5" max="30" value="15">
        <span class="slider-value" id="wpm-value">15</span>
    </div>
    <div class="slider-container">
        <label for="tone-frequency">Tonfrequenz (Hz):</label>
        <input type="range" id="tone-frequency" min="400" max="1200" value="800">
        <span class="slider-value" id="tone-frequency-value">800</span>
    </div>
    <br><br>
    <div id="input-area" tabindex="0">Klicken und halten Sie die Maustasten</div>
    <div id="visual-feedback"></div>
    <div id="output"></div>

    <script>
    const inputArea = document.getElementById('input-area');
    const output = document.getElementById('output');
    const visualFeedback = document.getElementById('visual-feedback');
    let morseCode = '';
    let currentChar = '';
    let plainText = '';
    let isPressed = false;
    let repeatInterval;
    let pauseTimeout;
    let lastInputTime = 0;

    // Einstellungen
    const wpmSlider = document.getElementById('wpm');
    const toneFrequencySlider = document.getElementById('tone-frequency');

    const morseDict = {
        '.-': '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', '-----': '0', '.----': '1', '..---': '2', '...--': '3',
        '....-': '4', '.....': '5', '-....': '6', '--...': '7', '---..': '8',
        '----.': '9'
    };

    const audioContext = new (window.AudioContext || window.webkitAudioContext)();

    function playTone(duration, frequency) {
        const oscillator = audioContext.createOscillator();
        const gainNode = audioContext.createGain();
        
        oscillator.type = 'sine';
        oscillator.frequency.setValueAtTime(frequency, audioContext.currentTime);
        
        gainNode.gain.setValueAtTime(1, audioContext.currentTime);
        gainNode.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + duration);
        
        oscillator.connect(gainNode);
        gainNode.connect(audioContext.destination);
        
        oscillator.start();
        oscillator.stop(audioContext.currentTime + duration);
    }
	  

    function playTone(duration,frequency) {

        // GainNodes erstellen
        const gainNode = audioContext.createGain();
        const gainNodePlay = audioContext.createGain();
        const gainNodeLimiter = audioContext.createGain();

        // Oszillator erstellen
        const oscillator = audioContext.createOscillator();
        oscillator.type = 'sine'; // Typ des Oszillators (z.B. 'sine', 'square', 'triangle', 'sawtooth')
        oscillator.frequency.setValueAtTime(frequency, audioContext.currentTime); // Frequenz in Hz

        // BiquadFilter erstellen und konfigurieren
        const biquadFilter = audioContext.createBiquadFilter();
        biquadFilter.type = 'lowpass';
        biquadFilter.frequency.setValueAtTime(500, audioContext.currentTime);
        biquadFilter.Q.setValueAtTime(10, audioContext.currentTime); // Beispiel-Q-Wert

        // Verbindungen herstellen
        oscillator.connect(gainNode);
        gainNode.connect(gainNodeLimiter);
        gainNodeLimiter.connect(biquadFilter);
        biquadFilter.connect(gainNodePlay);
        gainNodePlay.connect(audioContext.destination);

        // Gain-Werte setzen
        gainNode.gain.value = 1; // Volle Lautstärke
        gainNodePlay.gain.value = 1; // Volle Lautstärke
        gainNodeLimiter.gain.value = 0.55; // Limiter-Gain

        // Oszillator starten und nach der angegebenen Dauer stoppen
        oscillator.start();
        oscillator.stop(audioContext.currentTime + duration);

        // Gain reduzieren, um den Ton langsam zu beenden
        gainNode.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + duration);
    }

    function getDotDuration() {
        // PARIS-Methode: 50 Punkte pro "PARIS" bei Standard-WPM
         return 60000 / (50 * parseInt(wpmSlider.value));
    }

    function addMorseCode(code) {
        const now = Date.now();
        const dotDuration = getDotDuration();
        const toneFrequency = parseInt(toneFrequencySlider.value);

        // Überprüfen Sie die Pause seit der letzten Eingabe
        if (currentChar && (now - lastInputTime > dotDuration * 3)) {
            // Pause zwischen Zeichen (3 Punkte lang)
            evaluateChar();
        } else if (morseCode && (now - lastInputTime > dotDuration * 7)) {
            // Pause zwischen Wörtern (7 Punkte lang)
            evaluateChar();
            morseCode += ' / ';
            plainText += ' ';
        }

        currentChar += code;
        if (code === '.') {
            playTone(dotDuration / 1000, toneFrequency);
            visualFeedback.style.backgroundColor = 'green';
        } else if (code === '-') {
            playTone((dotDuration * 3) / 1000, toneFrequency);
            visualFeedback.style.backgroundColor = 'blue';
        }
        updateOutput();
        lastInputTime = now;
        resetPauseTimer();
    }

    function resetPauseTimer() {
        clearTimeout(pauseTimeout);
        pauseTimeout = setTimeout(evaluateChar, getDotDuration() * 3);
    }

    function evaluateChar() {
        if (currentChar) {
            const letter = morseDict[currentChar] || '?';
            morseCode += currentChar + ' ';
            plainText += letter;
            currentChar = '';
            updateOutput();
        }
    }

    function startRepeating(code) {
        isPressed = true;
        addMorseCode(code);
        const repeatDuration = code === '.' ? getDotDuration() : getDotDuration() * 3;
        repeatInterval = setInterval(() => {
            if (isPressed) {
                addMorseCode(code);
            }
        }, repeatDuration);
    }

    function stopRepeating() {
        isPressed = false;
        clearInterval(repeatInterval);
        visualFeedback.style.backgroundColor = '#ccc';
        resetPauseTimer();
    }

    inputArea.addEventListener('mousedown', (e) => {
        e.preventDefault();
        if (e.button === 0) {
            startRepeating('.');
        } else if (e.button === 2) {
            startRepeating('-');
        }
    });

    inputArea.addEventListener('mouseup', stopRepeating);
    inputArea.addEventListener('mouseleave', stopRepeating);

    inputArea.addEventListener('contextmenu', (e) => {
        e.preventDefault();
    });

    document.addEventListener('keydown', (e) => {
        if (e.code === 'Space') {
            evaluateChar();
            morseCode += '/ ';
            plainText += ' ';
            updateOutput();
        }
    });

    function updateOutput() {
        output.textContent = `Aktuell: ${currentChar}\nMorse: ${morseCode}\nKlartext: ${plainText}`;
    }

    // Slider-Ereignisse
    function updateSliderValue(slider, valueSpan) {
        valueSpan.textContent = slider.value;
    }

    wpmSlider.addEventListener('input', () => {
        updateSliderValue(wpmSlider, document.getElementById('wpm-value'));
        if (isPressed) {
            stopRepeating();
            startRepeating(currentChar.slice(-1));
        }
    });
    toneFrequencySlider.addEventListener('input', () => updateSliderValue(toneFrequencySlider, document.getElementById('tone-frequency-value')));

    // Initialisierung der Slider-Werte
    updateSliderValue(wpmSlider, document.getElementById('wpm-value'));
    updateSliderValue(toneFrequencySlider, document.getElementById('tone-frequency-value'));
    </script>
</body>
</html>

2 Kommentare

  1. Das hat ChatGPT draus gemacht, du kannst es gern testen und weiterentwickeln 🙂

    Morse-Eingabe mit WPM-Einstellung

    body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
    #input-area {
    width: 300px;
    height: 100px;
    border: 2px solid #000;
    margin: 20px 0;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 18px;
    cursor: pointer;
    padding: 15px;
    background: lightgreen;
    }
    input[type=range]:focus {
    outline: none;
    }
    input[type=range] {
    appearance: none;
    width: 100%;
    border-radius: 6px;
    height: 12px;
    border: 1px solid blue;
    background-color: cornflowerblue;
    }
    #visual-feedback {
    width: 50px;
    height: 50px;
    border-radius: 50%;
    background-color: #ccc;
    margin: 20px auto;
    }
    #output { font-size: 18px; margin-top: 20px; }
    .slider-container { margin-bottom: 10px; }
    .slider-container label { display: inline-block; width: 200px; }
    .slider-container input { width: 300px; }
    .slider-value { display: inline-block; width: 50px; text-align: right; }

    Morse-Eingabe mit WPM-Einstellung

    Geschwindigkeit (WPM):

    15

    Tonfrequenz (Hz):

    800

    Klicken und halten Sie die Maustasten

    const inputArea = document.getElementById(‚input-area‘);
    const output = document.getElementById(‚output‘);
    const visualFeedback = document.getElementById(‚visual-feedback‘);
    let morseCode = “;
    let currentChar = “;
    let plainText = “;
    let isPressed = false;
    let repeatInterval;
    let pauseTimeout;
    let lastInputTime = 0;
    // Einstellungen
    const wpmSlider = document.getElementById(‚wpm‘);
    const toneFrequencySlider = document.getElementById(‚tone-frequency‘);
    const morseDict = {
    ‚.-‚: ‚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‘, ‚—–‚: ‚0‘, ‚.—-‚: ‚1‘, ‚..—‚: ‚2‘, ‚…–‚: ‚3‘,
    ‚….-‚: ‚4‘, ‚…..‘: ‚5‘, ‚-….‘: ‚6‘, ‚–…‘: ‚7‘, ‚—..‘: ‚8‘,
    ‚—-.‘: ‚9‘
    };
    const audioContext = new (window.AudioContext || window.webkitAudioContext)();

    function playTone(duration, frequency) {
    const gainNode = audioContext.createGain();
    const oscillator = audioContext.createOscillator();
    oscillator.type = ’sine‘;
    oscillator.frequency.setValueAtTime(frequency, audioContext.currentTime);
    oscillator.connect(gainNode);
    gainNode.connect(audioContext.destination);
    gainNode.gain.setValueAtTime(1, audioContext.currentTime);
    gainNode.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + duration);
    oscillator.start();
    oscillator.stop(audioContext.currentTime + duration);
    }

    function getDotDuration() {
    return 60000 / (50 * parseInt(wpmSlider.value));
    }

    function addMorseCode(code) {
    const now = Date.now();
    const dotDuration = getDotDuration();
    const toneFrequency = parseInt(toneFrequencySlider.value);
    if (currentChar && (now – lastInputTime > dotDuration * 3)) {
    evaluateChar();
    } else if (morseCode && (now – lastInputTime > dotDuration * 7)) {
    evaluateChar();
    morseCode += ‚ / ‚;
    plainText += ‚ ‚;
    }
    currentChar += code;
    if (code === ‚.‘) {
    playTone(dotDuration / 1000, toneFrequency);
    visualFeedback.style.backgroundColor = ‚green‘;
    } else if (code === ‚-‚) {
    playTone((dotDuration * 3) / 1000, toneFrequency);
    visualFeedback.style.backgroundColor = ‚blue‘;
    }
    updateOutput();
    lastInputTime = now;
    resetPauseTimer();
    }

    function resetPauseTimer() {
    clearTimeout(pauseTimeout);
    pauseTimeout = setTimeout(evaluateChar, getDotDuration() * 3);
    }

    function evaluateChar() {
    if (currentChar) {
    const letter = morseDict[currentChar] || ‚?‘;
    morseCode += currentChar + ‚ ‚;
    plainText += letter;
    currentChar = “;
    updateOutput();
    }
    }

    function startRepeating(code) {
    isPressed = true;
    addMorseCode(code);
    const repeatDuration = code === ‚.‘ ? getDotDuration() : getDotDuration() * 3;
    repeatInterval = setInterval(() => {
    if (isPressed) {
    addMorseCode(code);
    }
    }, repeatDuration);
    }

    function stopRepeating() {
    isPressed = false;
    clearInterval(repeatInterval);
    visualFeedback.style.backgroundColor = ‚#ccc‘;
    resetPauseTimer();
    }

    inputArea.addEventListener(‚mousedown‘, (e) => {
    e.preventDefault();
    if (e.button === 0) {
    startRepeating(‚.‘);
    } else if (e.button === 2) {
    startRepeating(‚-‚);
    }
    });

    inputArea.addEventListener(‚mouseup‘, stopRepeating);
    inputArea.addEventListener(‚mouseleave‘, stopRepeating);
    inputArea.addEventListener(‚contextmenu‘, (e) => {
    e.preventDefault();
    });

    document.addEventListener(‚keydown‘, (e) => {
    if (e.code === ‚Space‘) {
    evaluateChar();
    morseCode += ‚/ ‚;
    plainText += ‚ ‚;
    updateOutput();
    }
    });

    function updateOutput() {
    output.textContent = `Aktuell: ${currentChar}\nMorse: ${morseCode}\nKlartext: ${plainText}`;
    }

    function updateSliderValue(slider, valueSpan) {
    valueSpan.textContent = slider.value;
    }

    wpmSlider.addEventListener(‚input‘, () => {
    updateSliderValue(wpmSlider, document.getElementById(‚wpm-value‘));
    if (isPressed) {
    stopRepeating();
    startRepeating(currentChar.slice(-1));
    }
    });

    toneFrequencySlider.addEventListener(‚input‘, () => updateSliderValue(toneFrequencySlider, document.getElementById(‚tone-frequency-value‘)));

    updateSliderValue(wpmSlider, document.getElementById(‚wpm-value‘));
    updateSliderValue(toneFrequencySlider, document.getElementById(‚tone-frequency-value‘));

    </

    Viele ♥♥♥-liche Grüße aus Sachsen !!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert