Cthulhu is no longer a fork. Now it is a screen reader written in Golang.
This commit is contained in:
1 parent
a998fe67de
commit
069fc674dc
708 files changed
+112482
-532
No files matched your search
@@ -0,0 +1,64 @@
|
||||
// Copyright (c) 2026 Storm Dragon
|
||||
// Authors: Storm Dragon, Angus Kola
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package sound
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const progressCuePrefix = "generated-progress-tone:"
|
||||
|
||||
func ProgressCueName(percentage float64) string {
|
||||
return progressCuePrefix + strconv.FormatFloat(percentage, 'g', -1, 64)
|
||||
}
|
||||
|
||||
func progress_percentage(cue string) (float64, bool) {
|
||||
if !strings.HasPrefix(cue, progressCuePrefix) {
|
||||
return 0, false
|
||||
}
|
||||
percentage, err := strconv.ParseFloat(strings.TrimPrefix(cue, progressCuePrefix), 64)
|
||||
return percentage, err == nil && !math.IsNaN(percentage) && !math.IsInf(percentage, 0) && percentage >= 0 && percentage <= 100
|
||||
}
|
||||
|
||||
// Match NVDA's default ProgressBar event_valueChange and generateBeep:
|
||||
// 110 * 2^(percentage/25) Hz, 40 ms padded to the next integer cycle,
|
||||
// 44100 Hz stereo PCM, and a sine clipped after multiplying by two.
|
||||
// NVDA passes frequency through a C float and defaults each channel to 50%.
|
||||
// Reference: nvaccess/nvda a4a89720ccbf3973a0e1e50a76dd8bff38db3d15,
|
||||
// source/NVDAObjects/behaviors.py and nvdaHelper/local/beeps.cpp.
|
||||
func progress_tone_wav(percentage float64) []byte {
|
||||
const sampleRate = 44100
|
||||
frequency := float64(float32(110 * math.Pow(2, percentage/25)))
|
||||
// sampleRate/hz is evaluated as a float in NVDA's C++ generator.
|
||||
cycleLength := float64(float32(sampleRate) / float32(frequency))
|
||||
samplesPerCycle := int(cycleLength)
|
||||
sampleCount := int(0.040 / (1.0 / sampleRate))
|
||||
sampleCount += samplesPerCycle - sampleCount%samplesPerCycle
|
||||
dataSize := sampleCount * 4
|
||||
buffer := bytes.NewBuffer(make([]byte, 0, 44+dataSize))
|
||||
buffer.WriteString("RIFF")
|
||||
_ = binary.Write(buffer, binary.LittleEndian, uint32(36+dataSize))
|
||||
buffer.WriteString("WAVEfmt ")
|
||||
_ = binary.Write(buffer, binary.LittleEndian, uint32(16))
|
||||
_ = binary.Write(buffer, binary.LittleEndian, uint16(1))
|
||||
_ = binary.Write(buffer, binary.LittleEndian, uint16(2))
|
||||
_ = binary.Write(buffer, binary.LittleEndian, uint32(sampleRate))
|
||||
_ = binary.Write(buffer, binary.LittleEndian, uint32(sampleRate*4))
|
||||
_ = binary.Write(buffer, binary.LittleEndian, uint16(4))
|
||||
_ = binary.Write(buffer, binary.LittleEndian, uint16(16))
|
||||
buffer.WriteString("data")
|
||||
_ = binary.Write(buffer, binary.LittleEndian, uint32(dataSize))
|
||||
sinFrequency := 2 * math.Pi / cycleLength
|
||||
for sample := 0; sample < sampleCount; sample++ {
|
||||
value := int16(min(1.0, max(-1.0, math.Sin(float64(sample%sampleRate)*sinFrequency)*2)) * 7000)
|
||||
_ = binary.Write(buffer, binary.LittleEndian, value)
|
||||
_ = binary.Write(buffer, binary.LittleEndian, value)
|
||||
}
|
||||
return buffer.Bytes()
|
||||
}
|
||||
Reference in new issue
Block a user