├── manifest-translated.ini.tpl ├── changelog.md ├── .gitignore ├── manifest.ini.tpl ├── .gitattributes ├── updateVersion.py ├── style.css ├── site_scons └── site_tools │ └── gettexttool │ └── __init__.py ├── EnhancedTones.code-workspace ├── .github └── workflows │ └── release-on-tag.yaml ├── addon ├── locale │ ├── pt │ │ └── LC_MESSAGES │ │ │ └── nvda.po │ ├── tr │ │ └── LC_MESSAGES │ │ │ └── nvda.po │ └── es │ │ └── LC_MESSAGES │ │ └── nvda.po ├── doc │ ├── pt │ │ └── readme.md │ ├── tr │ │ └── readme.md │ └── es │ │ └── readme.md └── globalPlugins │ └── enhancedTones │ ├── __init__.py │ └── _tones.py ├── enhancedTones.pot ├── readme.md ├── sconstruct ├── COPYING.txt └── LICENSE /manifest-translated.ini.tpl: -------------------------------------------------------------------------------- 1 | summary = "{addon_summary}" 2 | description = """{addon_description}""" 3 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # version 24.1.1. 2 | 3 | * Updated support for NVDA 2024.2. 4 | * Updated spanish locale strings. 5 | * added donation options to support the author. 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | addon/doc/*.css 2 | addon/doc/en/ 3 | *_docHandler.py 4 | *.html 5 | *.ini 6 | *.mo 7 | *.py[co] 8 | *.nvda-addon 9 | .sconsign.dblite 10 | *.code-workspace 11 | *.json -------------------------------------------------------------------------------- /manifest.ini.tpl: -------------------------------------------------------------------------------- 1 | name = {addon_name} 2 | summary = "{addon_summary}" 3 | description = """{addon_description}""" 4 | author = "{addon_author}" 5 | url = {addon_url} 6 | version = {addon_version} 7 | docFileName = {addon_docFileName} 8 | minimumNVDAVersion = {addon_minimumNVDAVersion} 9 | lastTestedNVDAVersion = {addon_lastTestedNVDAVersion} 10 | updateChannel = {addon_updateChannel} 11 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set default behaviour, in case users don't have core.autocrlf set. 2 | * text=auto 3 | 4 | # Try to ensure that po files in the repo does not include 5 | # source code line numbers. 6 | # Every person expected to commit po files should change their personal config file as described here: 7 | # https://mail.gnome.org/archives/kupfer-list/2010-June/msg00002.html 8 | *.po filter=cleanpo 9 | -------------------------------------------------------------------------------- /updateVersion.py: -------------------------------------------------------------------------------- 1 | import re, sys 2 | 3 | if len(sys.argv) < 2: 4 | print("the version was not detected") 5 | exit(1) 6 | version = sys.argv[1] 7 | print(f"the version recognized is: {version}") 8 | with open("buildVars.py", 'r+', encoding='utf-8') as f: 9 | text = f.read() 10 | text = re.sub('"addon_version" *:.*,', f'"addon_version" : "{version}",', text) 11 | f.seek(0) 12 | f.write(text) 13 | f.truncate() 14 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | body { 3 | font-family : Verdana, Arial, Helvetica, Sans-serif; 4 | line-height: 1.2em; 5 | } 6 | h1, h2 {text-align: center} 7 | dt { 8 | font-weight : bold; 9 | float : left; 10 | width: 10%; 11 | clear: left 12 | } 13 | dd { 14 | margin : 0 0 0.4em 0; 15 | float : left; 16 | width: 90%; 17 | display: block; 18 | } 19 | p { clear : both; 20 | } 21 | a { text-decoration : underline; 22 | } 23 | :active { 24 | text-decoration : none; 25 | } 26 | a:focus, a:hover {outline: solid} 27 | -------------------------------------------------------------------------------- /site_scons/site_tools/gettexttool/__init__.py: -------------------------------------------------------------------------------- 1 | """ This tool allows generation of gettext .mo compiled files, pot files from source code files 2 | and pot files for merging. 3 | 4 | Three new builders are added into the constructed environment: 5 | 6 | - gettextMoFile: generates .mo file from .pot file using msgfmt. 7 | - gettextPotFile: Generates .pot file from source code files. 8 | - gettextMergePotFile: Creates a .pot file appropriate for merging into existing .po files. 9 | 10 | To properly configure get text, define the following variables: 11 | 12 | - gettext_package_bugs_address 13 | - gettext_package_name 14 | - gettext_package_version 15 | 16 | 17 | """ 18 | from SCons.Action import Action 19 | 20 | def exists(env): 21 | return True 22 | 23 | XGETTEXT_COMMON_ARGS = ( 24 | "--msgid-bugs-address='$gettext_package_bugs_address' " 25 | "--package-name='$gettext_package_name' " 26 | "--package-version='$gettext_package_version' " 27 | "-c -o $TARGET $SOURCES" 28 | ) 29 | 30 | def generate(env): 31 | env.SetDefault(gettext_package_bugs_address="example@example.com") 32 | env.SetDefault(gettext_package_name="") 33 | env.SetDefault(gettext_package_version="") 34 | 35 | env['BUILDERS']['gettextMoFile']=env.Builder( 36 | action=Action("msgfmt -o $TARGET $SOURCE", "Compiling translation $SOURCE"), 37 | suffix=".mo", 38 | src_suffix=".po" 39 | ) 40 | 41 | env['BUILDERS']['gettextPotFile']=env.Builder( 42 | action=Action("xgettext " + XGETTEXT_COMMON_ARGS, "Generating pot file $TARGET"), 43 | suffix=".pot") 44 | 45 | env['BUILDERS']['gettextMergePotFile']=env.Builder( 46 | action=Action("xgettext " + "--omit-header --no-location " + XGETTEXT_COMMON_ARGS, 47 | "Generating pot file $TARGET"), 48 | suffix=".pot") 49 | 50 | -------------------------------------------------------------------------------- /EnhancedTones.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ], 7 | "settings": { 8 | "editor.accessibilitySupport": "on", 9 | "python.linting.enabled": true, 10 | "python.linting.maxNumberOfProblems": 10000, 11 | "python.linting.flake8Args": [ 12 | "--config=..\\..\\nvda\\tests\\lint\\flake8.ini", 13 | "--use-flake8-tabs=true" 14 | ], 15 | "python.linting.flake8Enabled": true, 16 | "python.linting.pylintEnabled": false, 17 | "python.autoComplete.extraPaths": [ 18 | "addon", 19 | "../../nvda/source", 20 | "../../nvda/include/comtypes", 21 | "../../nvda/include/configobj/src", 22 | "../../nvda/include/pyserial", 23 | "../../nvda/include/wxPython", 24 | "../../nvda/miscDeps/python" 25 | ], 26 | "files.insertFinalNewline": true, 27 | "files.trimFinalNewlines": true, 28 | "editor.insertSpaces": false, 29 | "python.testing.unittestArgs": [ 30 | "-v", 31 | "-s", 32 | "tests.unit", 33 | "-p", 34 | "test_*.py" 35 | ], 36 | "python.testing.pytestEnabled": false, 37 | "python.testing.nosetestsEnabled": false, 38 | "python.testing.unittestEnabled": false, 39 | "python.analysis.extraPaths": [ 40 | "addon", 41 | "../../nvda/source", 42 | "../../nvda/include/comtypes", 43 | "../../nvda/include/configobj/src", 44 | "../../nvda/include/pyserial", 45 | "../../nvda/include/wxPython", 46 | "../../nvda/miscDeps/python" 47 | ] 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /.github/workflows/release-on-tag.yaml: -------------------------------------------------------------------------------- 1 | permissions: 2 | contents: write 3 | name: Upload on new tags 4 | on: 5 | push: 6 | tags: 7 | ['*'] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout code 15 | uses: actions/checkout@v3 16 | - run: echo -e "scons\nmarkdown">requirements.txt 17 | - name: Set up Python 18 | uses: actions/setup-python@v4 19 | with: 20 | python-version: 3.11 21 | cache: 'pip' 22 | - name: Install dependencies 23 | run: | 24 | pip install scons markdown 25 | sudo apt update 26 | sudo apt install gettext 27 | 28 | - name: Set add-on version from tag 29 | run: | 30 | import re 31 | with open("buildVars.py", 'r+', encoding='utf-8') as f: 32 | text = f.read() 33 | version = "${{ github.ref }}".split("/")[-1] 34 | text = re.sub('"addon_version" *:.*,', '"addon_version" : "%s",' % version, text) 35 | f.seek(0) 36 | f.write(text) 37 | f.truncate() 38 | shell: python 39 | 40 | - name: Build add-on 41 | run: scons 42 | - name: load latest changes from changelog 43 | run: awk '/^# / && !flag {flag=1; next} /^# / && flag {exit} flag && NF' changelog.md > newChanges.md 44 | - name: Calculate sha256 45 | run: sha256sum *.nvda-addon >> newChanges.md 46 | - uses: actions/upload-artifact@v3 47 | with: 48 | name: packaged_addon 49 | path: | 50 | ./*.nvda-addon 51 | ./newChanges.md 52 | 53 | upload_release: 54 | runs-on: ubuntu-latest 55 | if: ${{ startsWith(github.ref, 'refs/tags/') }} 56 | needs: ["build"] 57 | steps: 58 | - name: download releases files 59 | uses: actions/download-artifact@v3 60 | - name: Display structure of downloaded files 61 | run: ls -R 62 | 63 | - name: Release 64 | uses: softprops/action-gh-release@v1 65 | with: 66 | body_path: packaged_addon/newChanges.md 67 | files: packaged_addon/*.nvda-addon 68 | fail_on_unmatched_files: true 69 | prerelease: ${{ contains(github.ref, '-') }} 70 | -------------------------------------------------------------------------------- /addon/locale/pt/LC_MESSAGES/nvda.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the 'enhancedTones' package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: 'enhancedTones' '1.1-dev'\n" 9 | "Report-Msgid-Bugs-To: 'nvda-translations@freelists.org'\n" 10 | "POT-Creation-Date: 2022-08-05 15:36-0600\n" 11 | "PO-Revision-Date: 2022-08-06 22:56+0100\n" 12 | "Last-Translator: Ângelo Abrantes \n" 13 | "Language-Team: Equipa Portuguesa do NVDA: Ângelo Abrantes e Rui Fontes \n" 15 | "Language: pt\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" 20 | "X-Generator: Poedit 3.1.1\n" 21 | "X-Poedit-SourceCharset: UTF-8\n" 22 | 23 | #. Translators: This is the label for the enhanced tones settings panel. 24 | #. Add-on summary, usually the user visible name of the addon. 25 | #. Translators: Summary for this add-on to be shown on installation and add-on information. 26 | #: addon\globalPlugins\enhancedTones\__init__.py:52 buildVars.py:17 27 | msgid "Enhanced tones" 28 | msgstr "Tons aprimorados" 29 | 30 | #: addon\globalPlugins\enhancedTones\__init__.py:60 31 | msgid "" 32 | "&Enable this add-on. If disabled, the original function of NVDA will be used" 33 | msgstr "" 34 | "&Activar este extra. Se desactivado, a função original do NVDA será utilizada" 35 | 36 | #. Translators: This is the label for a combobox in the 37 | #. Enhanced tones settings panel. 38 | #: addon\globalPlugins\enhancedTones\__init__.py:66 39 | msgid "&Library to generate tones:" 40 | msgstr "&Biblioteca para gerar tons:" 41 | 42 | #: addon\globalPlugins\enhancedTones\_tones.py:15 43 | msgid "Native tone generator" 44 | msgstr "Gerador de tom nativo" 45 | 46 | #: addon\globalPlugins\enhancedTones\_tones.py:27 47 | msgid "Custom tone generator" 48 | msgstr "Gerador de tons personalizados" 49 | 50 | #. Add-on description 51 | #. Translators: Long description to be shown for this add-on on add-on information from add-ons manager 52 | #: buildVars.py:20 53 | msgid "" 54 | "This add-on replaces the native tones library, this try to solve issues with " 55 | "some realtec and other drivers. Also, it adds another way of tone " 56 | "generation, so you can get a different sound when generating tones. You can " 57 | "configure it in the add-on settings" 58 | msgstr "" 59 | "Este extra substitui a biblioteca de tons nativos, isto tenta resolver " 60 | "problemas com alguns drivers da realtec e outros. Além disso, adiciona outra " 61 | "forma de geração de tons, para que possa obter um som diferente ao gerar " 62 | "tons. Pode configurá-lo nas configurações do extra" 63 | -------------------------------------------------------------------------------- /addon/doc/pt/readme.md: -------------------------------------------------------------------------------- 1 | # Extra de tons aprimorados para o NVDA. 2 | 3 | Este extra muda a maneira como os tons no NVDA são gerados. 4 | Para estar em contexto. Quando o NVDA emite um tom, ele faz o seguinte: 5 | 6 | 1. abre o player. 7 | 2. gera o tom. 8 | 3. envia o tom gerado para o player. 9 | 4. fecha o player. 10 | 11 | Isso pode ser problemático em algumas placas de som, apresentando alta latência ao reproduzir os tons ou não reproduzindo os primeiros tons. 12 | Eu tive esse problema no passado com um dos meus computadores. E essa foi a razão para criar este extra. 13 | 14 | Se usar este extra, mesmo que não tenha problemas com a forma original, poderá ver que os tons são mais fluidos, especialmente em tons que se repetem rapidamente. 15 | 16 | Este extra usa uma thread para enviar os tons para o player, e o player nunca fecha. 17 | Além disso, este extra implementa um gerador de tom personalizado, que é definido por padrão. Mas pode trocá-lo pelo gerador de tons do NVDA. 18 | O meu gerador de tons personalizado é escrito puramente em Python. Portanto, é menos eficiente que o gerador de tons do NVDA, mas a diferença não é perceptível. 19 | 20 | Decidi manter meu gerador de tons porque algumas pessoas gostaram, inclusive eu. Um utilizador com perda auditiva relatou que estava mais confortável com meu gerador de tons. 21 | 22 | Nota. A geração de tons não é o mesmo que a função para emitir os tons. Portanto, mesmo se usar o gerador de tons nativo do NVDA, poderá observar as melhorias. 23 | 24 | ## Faça o download. 25 | \tA versão mais recente está disponível em 26 | [este link] (https://davidacm.github.io/getlatest/gh/davidacm/EnhancedTones) 27 | 28 | ## Requisitos 29 | Precisa do NVDA 2018.3 ou posterior. 30 | 31 | ## Instalação 32 | Basta instalá-lo como um extra do NVDA. 33 | 34 | ## Utilização 35 | A funcionalidade do extra será activada assim que o instalar. 36 | Para activá-la ou desactivá-la, acesse as configurações do NVDA e seleccione "tons aprimorados". Nessa categoria, pode definir os seguintes parâmetros: 37 | 38 | * Activar o extra. Se desactivado, a função original será usada em seu lugar. 39 | * Biblioteca para geração de tons. 40 | 41 | ## contribuições, relatórios e doações 42 | 43 | Se gosta do meu projecto ou este softWare é útil na sua vida diária e quer contribuir de alguma forma, pode doar através dos seguintes métodos: 44 | 45 | * [PayPal.] (https://paypal.me/davicm) 46 | * [Ko-fi.] (https://ko-fi.com/davidacm) 47 | * [criptomoedas e outros métodos.] (https://davidacm.github.io/donations/) 48 | 49 | Se quiser corrigir bugs, relatar problemas ou novos recursos, entre em contato comigo em: . 50 | 51 | Ou no repositório github deste projeto: 52 | [Enhanced tones no GitHub](https://github.com/davidacm/enhancedtones) 53 | 54 | Pode obter a versão mais recente deste extra nesse repositório. 55 | 56 | ## Tradução: 57 | Equipa Portuguesa do NVDA: Ângelo Abrantes e Rui Fontes 58 | Cascais, 6 de Agosto de 2022 -------------------------------------------------------------------------------- /addon/locale/tr/LC_MESSAGES/nvda.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the 'enhancedTones' package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: 'enhancedTones' '2.0'\n" 9 | "Report-Msgid-Bugs-To: 'nvda-translations@freelists.org'\n" 10 | "POT-Creation-Date: 2023-04-14 13:23-0600\n" 11 | "PO-Revision-Date: 2023-04-14 23:55+0300\n" 12 | "Last-Translator: Umut KORKMAZ \n" 13 | "Language-Team: Umut KORKMAZ \n" 14 | "Language: tr_TR\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 19 | "X-Generator: Poedit 3.2.2\n" 20 | 21 | #. Translators: This is the label for the enhanced tones settings panel. 22 | #. Add-on summary, usually the user visible name of the addon. 23 | #. Translators: Summary for this add-on to be shown on installation and add-on information. 24 | #: addon\globalPlugins\enhancedTones\__init__.py:66 buildVars.py:17 25 | msgid "Enhanced tones" 26 | msgstr "Gelişmiş tonlar" 27 | 28 | #: addon\globalPlugins\enhancedTones\__init__.py:74 29 | msgid "" 30 | "&Enable this add-on. If disabled, the original function of NVDA will be used" 31 | msgstr "" 32 | "&Eklentiyi etkinleştir. Devre dışı bırakılırsa, NVDA'nın orijinal tonları " 33 | "kullanılacaktır" 34 | 35 | #. Translators: This is the label for a combobox in the 36 | #. Enhanced tones settings panel. 37 | #: addon\globalPlugins\enhancedTones\__init__.py:80 38 | msgid "&Generator to produce tones:" 39 | msgstr "Ton &geliştirmek için Üretici:" 40 | 41 | #: addon\globalPlugins\enhancedTones\_tones.py:20 42 | msgid "Native tone generator" 43 | msgstr "Yerel ton Üretici" 44 | 45 | #: addon\globalPlugins\enhancedTones\_tones.py:120 46 | msgid "Sine Generator" 47 | msgstr "Sinüs Üretici" 48 | 49 | #: addon\globalPlugins\enhancedTones\_tones.py:145 50 | msgid "Triangle Generator" 51 | msgstr "Üçgen dalga Üretici" 52 | 53 | #: addon\globalPlugins\enhancedTones\_tones.py:163 54 | msgid "Square Generator" 55 | msgstr "Kare dalga Üretici" 56 | 57 | #: addon\globalPlugins\enhancedTones\_tones.py:181 58 | msgid "Sawtooth Generator" 59 | msgstr "Testere dalgası Üretici" 60 | 61 | #. Add-on description 62 | #. Translators: Long description to be shown for this add-on on add-on information from add-ons manager 63 | #: buildVars.py:20 64 | msgid "" 65 | "This add-on replaces the native tones library, this try to solve issues with " 66 | "some realtec and other drivers. Also, it adds another way of tone " 67 | "generation, so you can get a different sound when generating tones. You can " 68 | "configure it in the add-on settings" 69 | msgstr "" 70 | "Bu eklenti, bazı realtek sürücülerinde bulunan kimi sorunları çözmek için " 71 | "orijinal ton oluşturma işlevinin yerini alır. Ayrıca, farklı bir ses için " 72 | "ton üretmenin farklı bir yolunu da ekler. Eklenti ayarlarında ton üretme " 73 | "yolunu yapılandırabilirsiniz" 74 | 75 | #~ msgid "Custom tone generator" 76 | #~ msgstr "Generador de tonos personalizado" 77 | -------------------------------------------------------------------------------- /enhancedTones.pot: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the 'enhancedTones' package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: 'enhancedTones' '1.2-dev'\n" 10 | "Report-Msgid-Bugs-To: 'nvda-translations@groups.io'\n" 11 | "POT-Creation-Date: 2024-01-04 23:28-0600\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=CHARSET\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | 20 | #: addon\globalPlugins\enhancedTones\__init__.py:73 21 | msgid "&Not now" 22 | msgstr "" 23 | 24 | #: addon\globalPlugins\enhancedTones\__init__.py:84 25 | #, python-format 26 | msgid "Request for contributions to %s" 27 | msgstr "" 28 | 29 | #: addon\globalPlugins\enhancedTones\__init__.py:85 30 | msgid "" 31 | "Creating add-ons demands substantial time and effort. With limited job " 32 | "prospects in my country, your donations could significantly aid in " 33 | "dedicating more time to developing free plugins for the community.\n" 34 | "Your contribution would support the development of this and other free " 35 | "projects.\n" 36 | "Would you like to contribute to this cause? Select from our available " 37 | "payment methods below. You will be redirected to the corresponding website " 38 | "to complete your donation.\n" 39 | "Thank you for your support and generosity." 40 | msgstr "" 41 | 42 | #: addon\globalPlugins\enhancedTones\__init__.py:94 43 | msgid "Using Paypal" 44 | msgstr "" 45 | 46 | #: addon\globalPlugins\enhancedTones\__init__.py:98 47 | msgid "using Co-fi" 48 | msgstr "" 49 | 50 | #: addon\globalPlugins\enhancedTones\__init__.py:102 51 | msgid "See more methods on my github Page" 52 | msgstr "" 53 | 54 | #. Translators: This is the label for the enhanced tones settings panel. 55 | #. Add-on summary, usually the user visible name of the addon. 56 | #. Translators: Summary for this add-on to be shown on installation and add-on information. 57 | #: addon\globalPlugins\enhancedTones\__init__.py:110 buildVars.py:17 58 | msgid "Enhanced tones" 59 | msgstr "" 60 | 61 | #: addon\globalPlugins\enhancedTones\__init__.py:118 62 | msgid "" 63 | "&Enable this add-on. If disabled, the original function of NVDA will be used" 64 | msgstr "" 65 | 66 | #. Translators: This is the label for a combobox in the 67 | #. Enhanced tones settings panel. 68 | #: addon\globalPlugins\enhancedTones\__init__.py:124 69 | msgid "&Generator to produce tones:" 70 | msgstr "" 71 | 72 | #: addon\globalPlugins\enhancedTones\__init__.py:133 73 | msgid "&Support Enhanced tones add-on" 74 | msgstr "" 75 | 76 | #: addon\globalPlugins\enhancedTones\_tones.py:20 77 | msgid "Native tone generator" 78 | msgstr "" 79 | 80 | #: addon\globalPlugins\enhancedTones\_tones.py:45 81 | #: addon\globalPlugins\enhancedTones\_tones.py:122 82 | msgid "Sine Generator" 83 | msgstr "" 84 | 85 | #: addon\globalPlugins\enhancedTones\_tones.py:147 86 | msgid "Square Generator" 87 | msgstr "" 88 | 89 | #: addon\globalPlugins\enhancedTones\_tones.py:173 90 | msgid "Sawtooth Generator" 91 | msgstr "" 92 | 93 | #: addon\globalPlugins\enhancedTones\_tones.py:193 94 | msgid "Triangle Generator" 95 | msgstr "" 96 | 97 | #. Add-on description 98 | #. Translators: Long description to be shown for this add-on on add-on information from add-ons manager 99 | #: buildVars.py:20 100 | msgid "" 101 | "This add-on replaces the native tones library, this try to solve issues with " 102 | "some realtec and other drivers. Also, it adds another way of tone " 103 | "generation, so you can get a different sound when generating tones. You can " 104 | "configure it in the add-on settings" 105 | msgstr "" 106 | -------------------------------------------------------------------------------- /addon/locale/es/LC_MESSAGES/nvda.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the 'enhancedTones' package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: 'enhancedTones' '1.1-dev'\n" 9 | "Report-Msgid-Bugs-To: 'nvda-translations@groups.io'\n" 10 | "POT-Creation-Date: 2024-01-04 23:28-0600\n" 11 | "PO-Revision-Date: 2024-01-04 23:32-0600\n" 12 | "Last-Translator: \n" 13 | "Language-Team: \n" 14 | "Language: es\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 19 | "X-Generator: Poedit 3.4.2\n" 20 | 21 | #: addon\globalPlugins\enhancedTones\__init__.py:73 22 | msgid "&Not now" 23 | msgstr "&Ahora no" 24 | 25 | #: addon\globalPlugins\enhancedTones\__init__.py:84 26 | #, python-format 27 | msgid "Request for contributions to %s" 28 | msgstr "Solicitud de contribuciones para %s" 29 | 30 | #: addon\globalPlugins\enhancedTones\__init__.py:85 31 | msgid "" 32 | "Creating add-ons demands substantial time and effort. With limited job " 33 | "prospects in my country, your donations could significantly aid in " 34 | "dedicating more time to developing free plugins for the community.\n" 35 | "Your contribution would support the development of this and other free " 36 | "projects.\n" 37 | "Would you like to contribute to this cause? Select from our available " 38 | "payment methods below. You will be redirected to the corresponding website " 39 | "to complete your donation.\n" 40 | "Thank you for your support and generosity." 41 | msgstr "" 42 | "La creación de complementos exige mucho tiempo y esfuerzo. Con bajas " 43 | "oportunidades laborales en mi país, sus donaciones podrían ayudar " 44 | "significativamente a dedicar más tiempo al desarrollo de complementos " 45 | "gratuitos para la comunidad.\n" 46 | "Tu contribución apoyaría el desarrollo de este y otros proyectos gratuitos.\n" 47 | "¿Te gustaría contribuir a esta causa? Selecciona entre nuestros métodos de " 48 | "pago disponibles a continuación. Serás redirigido al sitio web " 49 | "correspondiente para completar tu donación.\n" 50 | "Gracias por tu apoyo y generosidad." 51 | 52 | #: addon\globalPlugins\enhancedTones\__init__.py:94 53 | msgid "Using Paypal" 54 | msgstr "Usando Paypal" 55 | 56 | #: addon\globalPlugins\enhancedTones\__init__.py:98 57 | msgid "using Co-fi" 58 | msgstr "usando co-fi" 59 | 60 | #: addon\globalPlugins\enhancedTones\__init__.py:102 61 | msgid "See more methods on my github Page" 62 | msgstr "Ver más métodos en mi página de github" 63 | 64 | #. Translators: This is the label for the enhanced tones settings panel. 65 | #. Add-on summary, usually the user visible name of the addon. 66 | #. Translators: Summary for this add-on to be shown on installation and add-on information. 67 | #: addon\globalPlugins\enhancedTones\__init__.py:110 buildVars.py:17 68 | msgid "Enhanced tones" 69 | msgstr "" 70 | 71 | #: addon\globalPlugins\enhancedTones\__init__.py:118 72 | msgid "" 73 | "&Enable this add-on. If disabled, the original function of NVDA will be used" 74 | msgstr "" 75 | "&Habilitar el complemento. Si se deshabilita, la función original será usada " 76 | "en su lugar." 77 | 78 | #. Translators: This is the label for a combobox in the 79 | #. Enhanced tones settings panel. 80 | #: addon\globalPlugins\enhancedTones\__init__.py:124 81 | msgid "&Generator to produce tones:" 82 | msgstr "Generador para producir los tonos:" 83 | 84 | #: addon\globalPlugins\enhancedTones\__init__.py:133 85 | msgid "&Support Enhanced tones add-on" 86 | msgstr "&Apoyar al complemento Enhanced tones" 87 | 88 | #: addon\globalPlugins\enhancedTones\_tones.py:20 89 | msgid "Native tone generator" 90 | msgstr "Generador de tonos nativo" 91 | 92 | #: addon\globalPlugins\enhancedTones\_tones.py:45 93 | #: addon\globalPlugins\enhancedTones\_tones.py:122 94 | msgid "Sine Generator" 95 | msgstr "Generador de onda sinusoide " 96 | 97 | #: addon\globalPlugins\enhancedTones\_tones.py:147 98 | msgid "Square Generator" 99 | msgstr "Generador de onda cuadrada" 100 | 101 | #: addon\globalPlugins\enhancedTones\_tones.py:173 102 | msgid "Sawtooth Generator" 103 | msgstr "Generador de diente de sierra" 104 | 105 | #: addon\globalPlugins\enhancedTones\_tones.py:193 106 | msgid "Triangle Generator" 107 | msgstr "Generador de ondas triangulares" 108 | 109 | #. Add-on description 110 | #. Translators: Long description to be shown for this add-on on add-on information from add-ons manager 111 | #: buildVars.py:20 112 | msgid "" 113 | "This add-on replaces the native tones library, this try to solve issues with " 114 | "some realtec and other drivers. Also, it adds another way of tone " 115 | "generation, so you can get a different sound when generating tones. You can " 116 | "configure it in the add-on settings" 117 | msgstr "" 118 | "Este complemento remplaza la función original de generación de tonos, para " 119 | "intentar resolver algunos problemas en algunos drivers de realtec. También " 120 | "añade una forma distinta de generar tonos, para obtener un sonido diferente. " 121 | "Puedes configurar la forma de generar tonos en las configuraciones del " 122 | "complemento." 123 | 124 | #~ msgid "Custom tone generator" 125 | #~ msgstr "Generador de tonos personalizado" 126 | -------------------------------------------------------------------------------- /addon/globalPlugins/enhancedTones/__init__.py: -------------------------------------------------------------------------------- 1 | # ● enhanced tones 2 | # Copyright (C) 2022 - 2023 David CM 3 | import addonHandler, config, globalPluginHandler, os, wx 4 | from gui import guiHelper, settingsDialogs, nvdaControls 5 | from ._tones import * 6 | 7 | addonHandler.initTranslation() 8 | 9 | 10 | confspec = { 11 | "enableAddon": 'boolean(default=True)', 12 | "toneGenerator": f'string(default="{SineGenerator.id}")' 13 | } 14 | CONFIG_PATH = 'enhancedTones' 15 | config.conf.spec[CONFIG_PATH] = confspec 16 | 17 | origBeep = tones.beep 18 | def replaceBeepFunction(): 19 | try: 20 | from tones import decide_beep as d 21 | d.register(beep) 22 | except: 23 | tones.beep = beep 24 | 25 | def resetBeepFunction(): 26 | try: 27 | from tones import decide_beep as d 28 | d.unregister(beep) 29 | except: 30 | tones.beep = origBeep 31 | 32 | 33 | class GlobalPlugin(globalPluginHandler.GlobalPlugin): 34 | def __init__(self): 35 | super(globalPluginHandler.GlobalPlugin, self).__init__() 36 | self.handleConfigProfileSwitch() 37 | settingsDialogs.NVDASettingsDialog.categoryClasses.append(EnhancedTonesSettings) 38 | config.post_configProfileSwitch.register(self.handleConfigProfileSwitch) 39 | 40 | def setGenerator(self, toneGen): 41 | terminate() 42 | initialize(toneGen, 44100) 43 | 44 | def terminate(self): 45 | super(GlobalPlugin, self).terminate() 46 | self.disableAddon() 47 | 48 | def disableAddon(self): 49 | resetBeepFunction() 50 | terminate() 51 | 52 | def handleConfigProfileSwitch(self): 53 | if not config.conf[CONFIG_PATH]["enableAddon"]: 54 | self.disableAddon() 55 | return 56 | replaceBeepFunction() 57 | name = config.conf[CONFIG_PATH]["toneGenerator"] 58 | if name in availableToneGenerators: 59 | self.setGenerator(availableToneGenerators[name]) 60 | else: 61 | self.setGenerator(OrigTone) 62 | 63 | 64 | class DonationDialog(nvdaControls.MessageDialog): 65 | def __init__(self, parent, title, message, donateOptions): 66 | self.donateOptions = donateOptions 67 | super().__init__(parent, title, message, dialogType=nvdaControls.MessageDialog.DIALOG_TYPE_WARNING) 68 | 69 | def _addButtons(self, buttonHelper): 70 | for k in self.donateOptions: 71 | btn = buttonHelper.addButton(self, label=k['label'], name=k['url']) 72 | btn.Bind(wx.EVT_BUTTON, self.onDonate) 73 | cancelBtn = buttonHelper.addButton(self, id=wx.ID_CANCEL, label=_("&Not now")) 74 | cancelBtn.Bind(wx.EVT_BUTTON, lambda evt: self.EndModal(wx.CANCEL)) 75 | 76 | def onDonate(self, evt): 77 | donateBtn = evt.GetEventObject() 78 | donateUrl = donateBtn.Name 79 | os.startfile(donateUrl) 80 | self.EndModal(wx.OK) 81 | 82 | 83 | def showDonationsDialog(parentWindow, addonName, donateOptions): 84 | title = _("Request for contributions to %s") % addonName 85 | message = _("""Creating add-ons demands substantial time and effort. With limited job prospects in my country, your donations could significantly aid in dedicating more time to developing free plugins for the community. 86 | Your contribution would support the development of this and other free projects. 87 | Would you like to contribute to this cause? Select from our available payment methods below. You will be redirected to the corresponding website to complete your donation. 88 | Thank you for your support and generosity.""") 89 | return DonationDialog(parentWindow, title, message, donateOptions).ShowModal() 90 | 91 | 92 | DONATE_METHODS = ( 93 | { 94 | 'label': _('Using Paypal'), 95 | 'url': 'https://paypal.me/davicm' 96 | }, 97 | { 98 | 'label': _('using Co-fi'), 99 | 'url': 'https://ko-fi.com/davidacm' 100 | }, 101 | { 102 | 'label': _('See more methods on my github Page'), 103 | 'url': 'https://davidacm.github.io/donations/' 104 | } 105 | ) 106 | 107 | 108 | class EnhancedTonesSettings(settingsDialogs.SettingsPanel): 109 | # Translators: This is the label for the enhanced tones settings panel. 110 | title = _("Enhanced tones") 111 | helpId = "EnhancedTonesSettings" 112 | 113 | def makeSettings(self, settingsSizer): 114 | sHelper = guiHelper.BoxSizerHelper(self, sizer=settingsSizer) 115 | 116 | # Translators: label for a checkbox option in the enhanced tones settings panel. 117 | self.enableAddon = sHelper.addItem( 118 | wx.CheckBox(self, label=_("&Enable this add-on. If disabled, the original function of NVDA will be used")) 119 | ) 120 | self.enableAddon.SetValue(config.conf[CONFIG_PATH]["enableAddon"]) 121 | 122 | # Translators: This is the label for a combobox in the 123 | # Enhanced tones settings panel. 124 | generatorLabel = _("&Generator to produce tones:") 125 | self.lbs = sorted(availableToneGenerators.values(), key = lambda k: k.name) 126 | choises = [k.name for k in self.lbs] 127 | self.lbsList = sHelper.addLabeledControl(generatorLabel, wx.Choice, choices=choises) 128 | index = 0 129 | for i, k in enumerate(self.lbs): 130 | if config.conf[CONFIG_PATH]["toneGenerator"] == k.id: 131 | index = i 132 | self.lbsList.SetSelection(index) 133 | donateButton = sHelper.addItem(wx.Button(self, label=_("&Support Enhanced tones add-on"))) 134 | donateButton.Bind(wx.EVT_BUTTON, lambda e: showDonationsDialog(self, "Enhanced tones", DONATE_METHODS)) 135 | 136 | def onSave(self): 137 | l = self.lbs[self.lbsList.GetSelection()] 138 | config.conf[CONFIG_PATH]["toneGenerator"] = l.id 139 | config.conf[CONFIG_PATH]["enableAddon"] = self.enableAddon.GetValue() 140 | config.post_configProfileSwitch.notify() 141 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Enhanced tones NVDA Add-on # 2 | This add-on redefines the way of manage the beep tones in NVDA to do the process more eficient. 3 | 4 | Also, this add-on implements several wave generators, to let the user to customize the beep sound. It allows you to implement more generators very easily. 5 | 6 | If you are interested in creating your own wave generator and integrating it into this add-on, see the developer section. 7 | 8 | ## Features: 9 | 10 | * Improves the beeping process by playing the tone in chunks until it completes, or is interrupted by a new tone. 11 | * It implements several types of wave generators, which sound very different from each other. Have fun trying the different generators, maybe one you like! 12 | * If a tone is interrupted by another with the same frequency, the wave will not be interrupted, it will only extend its duration. Thus avoiding annoying breaks when tones are played quickly. it's not always possible to avoid this situation, due to different factors. 13 | * If a tone is interrupted by another of a different frequency, a frequency sweep will be made to jump to the new frequency. This also allows you to avoid breaks between tones. 14 | 15 | The last two features allow for more pleasant sound when using features like sound mouse tracking. 16 | 17 | ## Initial idea of this add-on. 18 | 19 | This add-on was created to solve some issues with specific soundcards, those issues are less common now that we have better soundcard drivers. But some people reported that those issues are still present, like high delays when playing the tones, or not playing the first tones at all. Now, this add-on has more features, so this can be useful even if you don't have issues with the native tone generation. Try this yourself and see if it works for you. 20 | 21 | ### Description of the original beep process. 22 | 23 | To be in context. When NVDA beep a tone, it does the following: 24 | 25 | 1. import the generateBeep. 26 | 2. stops the player. 27 | 3. generates the waveform tone. 28 | 4. sends the generated tone to the player. 29 | 30 | This can be problematic in some sound cards, like high delays when playing the tones, or not playing the first tones at all. Seems that the issue happen by stopping the player, especially when this is repeated quickly. 31 | I had this issue in the past with one of my computers. So, that was the reason to create this add-on. My add-on doesn't stop the player, and that fixed the issue. 32 | 33 | ### Description of the add-on beep process. 34 | 35 | 1. First, a background thread is created, this thread whill handle the beeps and communication with the player output. 36 | 2. The thread is kept waiting for data to emit a beep, using an event lock. 37 | 3. When the beep function is called, the information is sent to the thread and the thread lock is released. 38 | 4. The thread calls the function that initiates the generation of the waveform for the tone, and locks the event signal again. 39 | 5. It asks the generator for the waveform in form of chunks and sends each chunk to the output player. The generator can generate the waveform in parallel while sending, or generate the whole waveform at the beginning. 40 | 6. If while sending the waveform to the player the lock is released, it means that a request for a new beep was received, then it stops sending the data and jumps to step number 3 to start handling the required new beep. 41 | 7. If the entire waveform was sent to the player without interruption, it jumps to step number 2 to wait for another beep signal. Remember that the lock was blocked in step 4 so step 2 will be on hold again. 42 | 43 | By this way, the output player is never stopped and the process is more efficient. 44 | 45 | ## Notes about this add-on. 46 | 47 | If you try this add-on, even if you don't have issues with the original way of tone generation, you can see that the tones are more fluid, especially in fastly repeated tones. 48 | 49 | Also, this add-on implements several tone generators, sine generator is enabled by default. But you can change it to the NVDA's tones generator. 50 | My custom tone generators are written purely in Python. So, they are less eficient than the NVDA tone generator, but the difference is not noticeable. 51 | 52 | I decided to create other tone generators to let users to customize the beep sound and some people liked it, including myself. An user with hearing loss reported that he felt more comfortable with sine tone generator. 53 | 54 | Note: Tone generation is not the same as the function to output the tones to your sound card. So even if you use NVDA's native tone generator, you will still see improvements. 55 | 56 | ## Download. 57 | The latest release is available to 58 | [download in this link](https://davidacm.github.io/getlatest/gh/davidacm/EnhancedTones/?index=1) 59 | 60 | ## Requirements 61 | You need NVDA 2018.3 or later. 62 | 63 | ## Installation 64 | Just install it as a NVDA add-on. 65 | 66 | ## Usage 67 | The add-on functionality will be enabled once you install it. 68 | To enable or disable it, go to NVDA settings and select "Enhanced tones". In that category you can set the following parameters: 69 | 70 | * Enable this add-on. If disabled, the original function of NVDA will be used. 71 | Generator to produce tones: you can change the tone generator here. Select one and press enter to save settings, then try the selected generator. 72 | 73 | ## for developers. 74 | 75 | If you want to implement new tone generation waveforms, just make a class similar to the tone generators available in the code, and register it using the registerGenerator function. 76 | 77 | For each generator class you must provide an id, name, startGenerate and nextChunk methods. 78 | 79 | you can implement the AbstractGenerator class that implement the most important methods. The minimum steps to extend this class correctly is to implement the sampleGenerator function, and you need to provide an ID and name to create a valid generator. It's easier than create a generator from scratch. 80 | 81 | ## contributions, reports and donations 82 | 83 | If you like my project or this software is useful for you in your daily life and you would like to contribute in some way, you can donate via the following methods: 84 | 85 | * [PayPal.](https://paypal.me/davicm) 86 | * [Ko-fi.](https://ko-fi.com/davidacm) 87 | * [cryptocurrencies and other methods.](https://davidacm.github.io/donations/) 88 | 89 | If you want to fix bugs, report problems or new features, you can contact me at: . 90 | 91 | Or in the github repository of this project: 92 | [Enhanced tones on GitHub](https://github.com/davidacm/enhancedtones) 93 | 94 | You can get the latest release of this add-on in that repository. 95 | -------------------------------------------------------------------------------- /addon/doc/tr/readme.md: -------------------------------------------------------------------------------- 1 | # Gelişmiş tonlar NVDA Eklentisi # 2 | Bu eklenti, işlemi daha verimli hale getirmek için NVDA'daki bip seslerini yönetme yöntemini yeniden tanımlar. 3 | 4 | Ayrıca, bu eklenti kullanıcının bip sesini özelleştirmesine izin vermek için çeşitli dalga üreticileri uygular. Daha fazla üreticiyi çok kolay bir şekilde uygulamanıza olanak tanır. 5 | 6 | Kendi dalga üretecinizi oluşturmak ve bu eklentiye entegre etmekle ilgileniyorsanız, geliştirici bölümüne bakın. 7 | 8 | ## Özellikler: 9 | 10 | * Tamamlanana veya yeni bir tonla kesilene kadar tonu parçalar halinde çalarak bipleme sürecini iyileştirir. 11 | * Birbirinden çok farklı sesler çıkaran çeşitli dalga üreticileri uygular. Farklı üreticileri denerken eğlenin, belki bir tanesini seversiniz! 12 | * Eğer bir ton aynı frekanstaki başka bir ton tarafından kesilirse, dalga kesilmez, sadece süresi uzar. Böylece tonlar hızlı bir şekilde çalındığında rahatsız edici kesintiler önlenir. farklı faktörler nedeniyle bu durumdan kaçınmak her zaman mümkün değildir. 13 | * Bir ton farklı frekanstaki başka bir ton tarafından kesilirse, yeni frekansa atlamak için bir frekans taraması yapılacaktır. Bu aynı zamanda tonlar arasındaki kesintileri önlemenizi sağlar. 14 | 15 | Son iki özellik, sesli fare izleme gibi özellikleri kullanırken daha hoş bir ses elde edilmesini sağlar. 16 | 17 | ## İndirme: 18 | [En son sürüm bu bağlantıdan indirilebilir](https://davidacm.github.io/getlatest/gh/davidacm/enhancedTones) 19 | 20 | ## Bu eklentinin ana fikri: 21 | 22 | Bu eklenti, belirli ses kartlarıyla ilgili bazı sorunları çözmek için oluşturuldu, daha iyi ses kartı sürücülerine sahip olduğumuz için bu sorunlar artık daha az yaygın. Ancak bazı kişiler, tonları çalarken yüksek gecikmeler veya ilk tonların hiç çalmaması gibi bu sorunların hala mevcut olduğunu bildirdi. Şimdi, bu eklentinin daha fazla özelliği var, bu nedenle yerel ton oluşturma ile ilgili sorunlarınız olmasa bile bu yararlı olabilir. Bunu kendiniz deneyin ve sizin için işe yarayıp yaramadığını görün. 23 | 24 | ### Orijinal bip işleminin açıklaması: 25 | 26 | Bağlam içinde olmak. NVDA bir bip sesi verdiğinde aşağıdakileri yapar: 27 | 28 | 1. Üretilen Bip sesini içe aktarma. 29 | 2. oynatıcıyı durdurur. 30 | 3. dalga formu tonunu üretir. 31 | 4. oluşturulan tonu oynatıcıya gönderir. 32 | 33 | Bu, bazı ses kartlarında tonları çalarken yüksek gecikmeler veya ilk tonların hiç çalınmaması gibi sorunlu olabilir. Sorun oynatıcının durdurulmasıyla ortaya çıkıyor gibi görünüyor, özellikle de bu hızlı bir şekilde tekrarlandığında. 34 | Geçmişte bilgisayarlarımdan birinde bu sorunu yaşadım. Bu yüzden, bu eklentiyi oluşturmamın nedeni buydu. Eklentim oynatıcıyı durdurmuyor ve bu sorunu çözdü. 35 | 36 | ### Ek bip sesi işleminin açıklaması: 37 | 38 | 1. İlk olarak, bir arka plan iş parçacığı oluşturulur, bu iş parçacığı bip seslerini ve oynatıcı çıkışı ile iletişimi idare eder. 39 | 2. İş parçacığı, bir olay kilidi kullanılarak verilerin bir bip sesi çıkarması için bekletilir. 40 | 3. Bip işlevi çağrıldığında, bilgi iş parçacığına gönderilir ve iş parçacığı kilidi serbest bırakılır. 41 | 4. İş parçacığı, ton için dalga biçiminin oluşturulmasını başlatan işlevi çağırır ve olay sinyalini yeniden kilitler. 42 | 5. Üreticiden dalga formunu parçalar halinde ister ve her bir parçayı çıkış oynatıcısına gönderir. Üretic, gönderme sırasında dalga biçimini paralel olarak oluşturabilir veya tüm dalga biçimini başlangıçta oluşturabilir. 43 | 6. Dalga formunu oynatıcıya gönderirken kilit serbest bırakılırsa, bu yeni bir bip sesi için bir istek alındığı anlamına gelir, o zaman veri göndermeyi durdurur ve gerekli yeni bip sesini işlemeye başlamak için 3 numaralı adıma atlar. 44 | 7. Dalga formunun tamamı kesintisiz olarak oynatıcıya gönderildiyse, başka bir bip sinyali beklemek için 2 numaralı adıma atlar. Kilidin 4. adımda bloke edildiğini unutmayın, bu nedenle 2. adım tekrar beklemeye alınacaktır. 45 | 46 | Bu sayede çıkış oynatıcısı hiçbir zaman durdurulmaz ve süreç daha verimli olur. 47 | 48 | ## Bu eklenti hakkında notlar: 49 | 50 | Bu eklentiyi denerseniz orijinal ton oluşturma yönteminde sorun yaşamasanız bile özellikle hızlı tekrarlanan tonlarda tonların daha akıcı olduğunu görebilirsiniz. 51 | 52 | Ayrıca, bu eklenti birkaç ton üreteci uygular, sinüs üreteci varsayılan olarak etkindir. Ancak bunu NVDA'nın ton üreteci ile değiştirebilirsiniz. 53 | Benim özel ton üreteçlerim tamamen Python ile yazılmıştır. Bu yüzden, NVDA ton üretecinden daha az etkilidirler, ancak fark fark edilebilir değildir. 54 | 55 | Kullanıcıların bip sesini özelleştirmesine izin vermek için başka ton üreteçleri oluşturmaya karar verdim ve ben de dahil olmak üzere bazı insanlar bunu beğendi. İşitme kaybı olan bir kullanıcı, sinüs ton üreteci ile daha rahat hissettiğini bildirdi. 56 | 57 | Not: Ton oluşturma, sesleri ses kartınıza verme işleviyle aynı değildir. Bu nedenle, NVDA'nın yerel ton üretecini kullansanız bile, iyileştirmeleri görmeye devam edeceksiniz. 58 | 59 | ## İndirme: 60 | [En son sürüm bu bağlantıdan indirilebilir.](https://davidacm.github.io/getlatest/gh/davidacm/EnhancedTones) 61 | 62 | ## Gereksinimler: 63 | 64 | NVDA 2018.3 veya sonrasına ihtiyacınız var. 65 | 66 | ## Kurulum: 67 | 68 | Sadece bir NVDA eklentisi olarak kurun. 69 | 70 | ## kullanım: 71 | 72 | Eklenti işlevi, yüklediğinizde etkinleştirilecektir. 73 | Etkinleştirmek veya devre dışı bırakmak için NVDA ayarlarına gidin ve "Geliştirilmiş tonlar "ı seçin. Bu kategoride aşağıdaki parametreleri ayarlayabilirsiniz: 74 | 75 | * Eklentiyi etkinleştir. Devre dışı bırakılırsa, NVDA'nın orijinal işlevi kullanılacaktır. 76 | Ton üretici: Ton üretecini buradan değiştirebilirsiniz. Birini seçin ve ayarları kaydetmek için enter tuşuna basın, ardından seçilen oluşturucuyu deneyin. 77 | 78 | ## geliştiriciler için: 79 | 80 | Yeni ton oluşturma dalga biçimleri uygulamak istiyorsanız, kodda bulunan ton oluşturuculara benzer bir sınıf oluşturun ve bunu registerGenerator işlevini kullanarak kaydedin. 81 | 82 | Her oluşturucu sınıfı için bir kimlik, ad, startGenerate ve nextChunk yöntemleri sağlamalısınız. 83 | 84 | en önemli yöntemleri uygulayan AbstractGenerator sınıfını uygulayabilirsiniz. Bu sınıfı doğru bir şekilde genişletmenin minimum adımları, sampleGenerator işlevini uygulamaktır ve geçerli bir oluşturucu oluşturmak için bir kimlik ve ad sağlamanız gerekir. Sıfırdan bir jeneratör oluşturmaktan daha kolaydır. 85 | 86 | ## katkılar, raporlar ve bağışlar: 87 | 88 | Projemi beğendiyseniz veya bu yazılım günlük hayatınızda işinize yararsa ve bir şekilde katkıda bulunmak isterseniz aşağıdaki yöntemlerle bağışta bulunabilirsiniz: 89 | 90 | * [PayPal.](https://paypal.me/davicm) 91 | * [Ko-fi.](https://ko-fi.com/davidacm) 92 | * [kripto para birimleri ve diğer yöntemler.](https://davidacm.github.io/donations/) 93 | 94 | Hataları düzeltmek, sorunları bildirmek veya yeni özellikler istiyorsanız benimle adresinden iletişime geçebilirsiniz. 95 | 96 | Veya bu projenin github deposunda: 97 | [GitHub'da geliştirilmiş tonlar](https://github.com/davidacm/enhancedtones) 98 | 99 | Bu eklentinin en son sürümünü bu depodan edinebilirsiniz. 100 | -------------------------------------------------------------------------------- /addon/doc/es/readme.md: -------------------------------------------------------------------------------- 1 | # Complemento Enhanced tones para NVDA. 2 | 3 | Este complemento cambia la forma de gestionar los tonos en NVDA para hacer el proceso más eficiente. 4 | 5 | Además, este complemento implementa varios generadores de ondas para que el usuario pueda personalizar el sonido del pitido. Te permite implementar más generadores muy fácilmente. 6 | 7 | Si está interesado en crear su propio generador de ondas e integrarlo en este complemento, consulte la sección para desarrolladores. 8 | 9 | ## Características: 10 | 11 | * Mejora el proceso de tonos al reproducir el tono en fragmentos hasta que se completa o se interrumpe con un nuevo tono. 12 | * Implementa varios tipos de generadores de ondas, que suenan muy diferentes entre sí. Diviértete probando los diferentes generadores, ¡tal vez alguno te guste! 13 | * Si un tono es interrumpido por otro de la misma frecuencia, la onda no se interrumpirá, solo prolongará su duración. Evitando así los molestos saltos cuando 2 o más tonos se reproducen rápidamente. no siempre es posible evitar esta situación, debido a diferentes factores. 14 | * Si un tono es interrumpido por otro de diferente frecuencia, se realizará un barrido de frecuencia para saltar a la nueva frecuencia. Esto también le permite evitar saltos entre tonos, cambiando de una frecuencia a otra suavemente para el oído. 15 | 16 | Las últimas dos funciones permiten un sonido más agradable cuando se usan funciones como el seguimiento del mouse con sonido. 17 | 18 | ## Descarga. 19 | El último release se puede [Descargar en este enlace.](https://davidacm.github.io/getlatest/gh/davidacm/enhancedTones) 20 | 21 | ## Idea inicial de este complemento. 22 | 23 | Este complemento se creó para resolver algunos problemas con tarjetas de sonido específicas, esos problemas son menos comunes ahora que tenemos mejores controladores de tarjetas de sonido. Pero algunas personas informaron que esos problemas aún están presentes, como alta latencia al reproducir los tonos, o no reproducir los primeros tonos en absoluto. Actualmente, este complemento posee más funcionalidades, por lo que puede ser útil incluso si no tiene problemas con la generación de tonos nativos. Intente esto usted mismo y vea si funciona para usted. 24 | 25 | ## Descripción de la función original "beep" de NVDA. 26 | 27 | Para estar en contexto. Cuando NVDA emite un tono, hace lo siguiente 28 | 29 | 1. importa generateBeep. 30 | 2. detiene el reproductor. 31 | 3. genera la forma de onda para el tono. 32 | 4. envía el tono generado al reproductor. 33 | 34 | 35 | Esto puede ser problemático en algunas tarjetas de sonido, como altos retrasos al reproducir los tonos, o no reproducir los primeros tonos en absoluto. Parece que el problema ocurre al detener el reproductor, especialmente cuando esto se repite rápidamente. 36 | 37 | Yo tuve este problema en el pasado con uno de mis ordenadores. Así que esa fue la razón para crear este complemento. Mi complemento no detiene el reproductor, y eso arregló el problema. 38 | 39 | ## Descripción de la función "beep" del complemento. 40 | 41 | 1. Primero, se crea un hilo de fondo, este hilo se encargará de los pitidos y la comunicación con la salida del reproductor. 42 | 2. El hilo se mantiene a la espera de datos para emitir un beep, utilizando un bloqueo de eventos. 43 | 3. Cuando se llama a la función beep, la información se envía al hilo y se libera el bloqueo del hilo. 44 | 4. El hilo llama a la función que inicia la generación de la onda para el tono, y bloquea la señal de evento de nuevo. 45 | 5. Pide al generador la forma de onda en fragmentos y envía cada trozo al reproductor de salida. El generador puede generar la forma de onda en paralelo mientras se envía, o generar toda la forma de onda al principio. 46 | 6. Si mientras se envía la onda al reproductor se libera el bloqueo, significa que se recibió una petición de un nuevo beep, entonces este deja de enviar los datos y salta al paso número 3 para emitir el nuevo beep requerido. 47 | 7. Si toda la forma de onda fue enviada al reproductor sin interrupción, salta al paso número 2 para esperar otra señal de beep. Recuerde que el bloqueo se bloqueó en el paso 4 así que el paso 2 quedará en espera nuevamente. 48 | 49 | De esta manera, el reproductor de salida nunca es detenido y el proceso es más eficiente. 50 | 51 | ## Notas sobre este complemento. 52 | 53 | Si pruebas este complemento, incluso si no tienes problemas con la forma original de generación de tonos, puedes ver que los tonos son más fluidos, especialmente en los tonos que se repiten rápidamente. 54 | 55 | Además, este complemento implementa varios generadores de tonos, el generador sinusoidal está habilitado de forma predeterminada. Pero puedes cambiarlo al generador de tonos de NVDA. 56 | Mis generadores de tonos personalizados están escritos puramente en Python. Por lo tanto, son menos eficientes que el generador de tonos NVDA, pero la diferencia no se nota debido a la mejor forma de gestionar el proceso de reproducción y a que la onda no es generada por completo desde el principio, si no que es generada al vuelo durante la reproducción. 57 | 58 | Decidí crear otros generadores de tonos para permitir a los usuarios personalizar el sonido del pitido y a algunas personas les gustó, incluyéndome a mí. Un usuario con pérdida auditiva informó que se sentía más cómodo con el generador de tonos sinusoidales. 59 | 60 | Nota: La generación de tonos no es lo mismo que la función de salida de los tonos a su tarjeta de sonido. Así que incluso si usas el generador de tonos nativo de NVDA, seguirás viendo mejoras. 61 | 62 | ## Descarga. 63 | La última versión está disponible en 64 | [este enlace](https://davidacm.github.io/getlatest/gh/davidacm/EnhancedTones) 65 | 66 | ## Requisitos 67 | Necesitas NVDA 2018.3 o posterior. 68 | 69 | ## Instalación 70 | Sólo tienes que instalarlo como un complemento de NVDA. 71 | 72 | ## Uso 73 | La funcionalidad del complemento se habilitará una vez que lo instales. 74 | Para habilitarla o deshabilitarla, ve a la configuración de NVDA y selecciona "Tonos mejorados". En esa categoría puedes establecer los siguientes parámetros: 75 | 76 | * Habilitar el complemento. Si se deshabilita, la función original será usada en su lugar. 77 | * Generador productor de tonos: aquí puedes cambiar el generador de tonos. Seleccione uno y presione enter para guardar la configuración, luego pruebe el generador seleccionado. 78 | 79 | ## Para desarrolladores. 80 | 81 | Si desea implementar nuevas formas de onda de generación de tonos, simplemente haga una clase similar a los generadores de tonos disponibles en el código y regístrelo usando la función registerGenerator. 82 | 83 | Para cada clase de generador, debe proporcionar los métodos id, name, startGenerate y nextChunk. 84 | 85 | puede implementar la clase AbstractGenerator que implementa los métodos más importantes. Los pasos mínimos para extender esta clase correctamente son implementar la función sampleGenerator, y debe proporcionar una identificación y un nombre para crear un generador válido. Es más fácil que crear un generador desde cero. 86 | 87 | ## contribuciones, reportes y donaciones 88 | 89 | Si te gusta mi proyecto o este software te es útil en tu vida diaria y quieres contribuir de alguna manera, puedes donar a través de los siguientes métodos: 90 | 91 | * [PayPal.](https://paypal.me/davicm) 92 | * [Ko-fi.](https://ko-fi.com/davidacm) 93 | * [criptomonedas y otros métodos.](https://davidacm.github.io/donations/) 94 | 95 | Si quieres corregir errores, informar de problemas o nuevas características, puedes contactar conmigo en: . 96 | 97 | O en el repositorio de github de este proyecto: 98 | [enhanced tones en GitHub](https://github.com/davidacm/enhancedtones) 99 | 100 | Puedes obtener la última versión de este complemento en ese repositorio. 101 | -------------------------------------------------------------------------------- /addon/globalPlugins/enhancedTones/_tones.py: -------------------------------------------------------------------------------- 1 | # ● _tones 2 | # an utility to generate tones. 3 | # Copyright (C) 2022 David CM 4 | 5 | import addonHandler, config, math, nvwave, threading, tones 6 | from ctypes import c_short, create_string_buffer 7 | from io import BytesIO 8 | from NVDAHelper import generateBeep 9 | 10 | addonHandler.initTranslation() 11 | 12 | 13 | availableToneGenerators = {} 14 | def registerGenerator(*generators): 15 | for k in generators: 16 | availableToneGenerators[k.id] = k 17 | 18 | 19 | class OrigTone: 20 | name = _("Native tone generator") 21 | id = 'OrigTone' 22 | def __init__(self, hz=None): 23 | self.samples = b'' 24 | 25 | def startGenerate(self, hz,length,left,right): 26 | buf=create_string_buffer(generateBeep(None,hz,length,left,right)) 27 | generateBeep(buf,hz,length,left,right) 28 | self.samples = BytesIO(buf) 29 | self.samples.size = len(buf) 30 | 31 | def nextChunk(self, size=8820): 32 | while True: 33 | rest = self.samples.size -self.samples.tell() 34 | # to avoid very small chunks at the end. Append the last part if is less than size*2 35 | if rest < size*2: 36 | size = rest 37 | d = self.samples.read(size) 38 | if d: 39 | yield d 40 | else: 41 | return 42 | 43 | 44 | class AbstractGenerator: 45 | name = _("Sine Generator") 46 | id = 'SineGenerator' 47 | def __init__(self, rate=44100): 48 | # parameters 49 | self.bytes = 2 50 | self.rate = rate 51 | self.freq = 1000 52 | # constants 53 | self._MAX_AMPLITUDE = int(2 ** (self.bytes * 8) / 2) - 1 54 | self._MAX_SWEEP = 1000 55 | # generator state 56 | self.isGenerating = False 57 | self._curFreq = 1000 58 | self._gen = self.sampleGenerator() 59 | self._stepFreq = 0 60 | self._numSamples = 0 61 | self._curChunk = 0 62 | self._ampL = 0 63 | self._ampR = 0 64 | self._sweepCount = 0 65 | 66 | def sampleGenerator(self): 67 | """ this method is used to generate each sample according to the wave features. 68 | it's a generator tat yields infinite wave samples following the waveform specs. 69 | you must update the generation data if the _curFreq is updated. 70 | the sample will be multiplied by the amplitude for each channel (left and right) 71 | """ 72 | raise NotImplementedError() 73 | 74 | def normalizeSample(self, sample): 75 | ret = int(sample * self._MAX_AMPLITUDE) 76 | if ret < -self._MAX_AMPLITUDE: 77 | ret = -self._MAX_AMPLITUDE 78 | elif ret > self._MAX_AMPLITUDE: 79 | ret = self._MAX_AMPLITUDE 80 | return c_short(ret) 81 | 82 | def setToneVals(self, freq, length, ampL=0.5, ampR=0.5): 83 | self._numSamples = int(length *self.rate) 84 | self._curChunk = 0 85 | self._ampL = ampL 86 | self._ampR = ampR 87 | if not self.isGenerating or freq == 0: 88 | self.freq = freq 89 | self._curFreq = freq 90 | self._gen = self.sampleGenerator() 91 | elif freq != self.freq: 92 | self._sweepCount = min(self._MAX_SWEEP, self._numSamples) 93 | self._stepFreq = ((freq -self._curFreq) / self._sweepCount) 94 | self.freq = freq 95 | 96 | def nextChunk(self, size=4000): 97 | while self._curChunk < self._numSamples: 98 | self.isGenerating = True 99 | rest = self._numSamples -self._curChunk 100 | # to avoid very small chunks at the end. Append the last part if is less than size*2 101 | if rest < size*2: 102 | size = rest 103 | self._curChunk += size 104 | samples = BytesIO() 105 | for i in range(size): 106 | w = next(self._gen) 107 | if self._sweepCount > 0: 108 | self._curFreq += self._stepFreq 109 | self._sweepCount -= 1 110 | else: 111 | self._curFreq = self.freq 112 | samples.write(self.normalizeSample(self._ampL * w)) 113 | samples.write(self.normalizeSample(self._ampR * w)) 114 | yield samples.getvalue() 115 | self.isGenerating = False 116 | 117 | def startGenerate(self, hz, length, left, right): 118 | self.setToneVals(hz, length/1000.0, left/100.0, right/100.0) 119 | 120 | 121 | class SineGenerator(AbstractGenerator): 122 | name = _("Sine Generator") 123 | id = 'SineGenerator' 124 | def __init__(self, rate=44100): 125 | super().__init__(rate) 126 | self._PHASE_BASE = float(2 *math.pi /self.rate) 127 | 128 | def sampleGenerator(self): 129 | freq = self._curFreq 130 | phaseAcc = 0.0 131 | delta = self._PHASE_BASE * freq 132 | i=0 133 | while True: 134 | if i == self.rate: 135 | i=0 136 | phaseAcc = 0.0 137 | if self._curFreq != freq: 138 | freq = self._curFreq 139 | delta = self._PHASE_BASE * freq 140 | yield math.sin(phaseAcc) 141 | phaseAcc += delta 142 | i+=1 143 | 144 | # the following generators need improvements. 145 | 146 | class SquareGenerator(AbstractGenerator): 147 | name = _("Square Generator") 148 | id = 'SquareGenerator' 149 | 150 | def sampleGenerator(self): 151 | freq = points = split = 0 152 | def updateValues(): 153 | nonlocal freq, points, split 154 | freq = self._curFreq 155 | points = self.rate // freq 156 | split = points / 2 157 | updateValues() 158 | while True: 159 | i = 0 160 | while i < split: 161 | if self._curFreq != freq: 162 | updateValues() 163 | yield 1 164 | i+=1 165 | while i < points: 166 | if self._curFreq != freq: 167 | updateValues() 168 | yield -1 169 | i+=1 170 | 171 | 172 | class SawtoothGenerator(AbstractGenerator): 173 | name = _("Sawtooth Generator") 174 | id = 'SawtoothGenerator' 175 | 176 | def sampleGenerator(self): 177 | freq = points = delta = 0 178 | def updateValues(): 179 | nonlocal freq, points, delta 180 | freq = self._curFreq 181 | points = int(self.rate // freq) 182 | delta = 2/points 183 | updateValues() 184 | while True: 185 | i = 0 186 | while i < points: 187 | if self._curFreq != freq: 188 | updateValues() 189 | yield (delta*i) -1 190 | i+=1 191 | 192 | class TriangleGenerator(AbstractGenerator): 193 | name = _("Triangle Generator") 194 | id = 'TriangleGenerator' 195 | 196 | def sampleGenerator(self): 197 | freq = points = delta = 0 198 | def updateValues(): 199 | nonlocal freq, points, delta 200 | freq = self._curFreq 201 | points = int(self.rate // freq) 202 | delta = 2/points 203 | updateValues() 204 | while True: 205 | i = 0 206 | while i < points: 207 | if self._curFreq != freq: 208 | updateValues() 209 | yield abs((delta*i) -1) 210 | i+=1 211 | 212 | 213 | class PlayerTone(threading.Thread): 214 | def __init__(self, toneGen, hz): 215 | super().__init__(target=self.run) 216 | self.tonePlayer = None 217 | self.hz = hz 218 | self.samples = b"" 219 | self.values = None 220 | self.waitBeep = threading.Event() 221 | self.stopFlag = False 222 | self.chunkSize = 2000 223 | self.setToneGen(toneGen, hz) 224 | self.setPlayer() 225 | 226 | def setPlayer(self, outputDevice=None): 227 | if not outputDevice: 228 | outputDevice = config.conf["speech"]["outputDevice"] 229 | self.tonePlayer = nvwave.WavePlayer(2, self.hz, 16, outputDevice=outputDevice, wantDucking=False) 230 | 231 | def setToneGen(self, toneGen, hz): 232 | if toneGen == OrigTone: 233 | hz = 44100 234 | self.hz = hz 235 | self.toneGen = toneGen(hz) 236 | if self.tonePlayer and self.hz != hz: 237 | self.player.close() 238 | self.setPlayer() 239 | 240 | def run(self): 241 | while True: 242 | self.waitBeep.wait() 243 | if self.stopFlag: break 244 | hz, length, left, right = self.values 245 | self.waitBeep.clear() 246 | self.toneGen.startGenerate(hz,length,left,right) 247 | for data in self.toneGen.nextChunk(): 248 | self.tonePlayer.feed(data) 249 | if self.waitBeep.is_set(): 250 | break 251 | self.tonePlayer.close() 252 | self.tonePlayer= None 253 | 254 | def beep(self, hz,length,left, right): 255 | self.values = (hz, length, left, right) 256 | self.waitBeep.set() 257 | 258 | def terminate(self): 259 | self.stopFlag = True 260 | self.waitBeep.set() 261 | 262 | 263 | toneThread= None 264 | def initialize(toneGen, hz): 265 | global toneThread 266 | toneThread = PlayerTone(toneGen, hz) 267 | toneThread.start() 268 | 269 | 270 | def beep( 271 | hz: float, 272 | length: int, 273 | left: int = 50, 274 | right: int = 50, 275 | isSpeechBeepCommand: bool = False 276 | ): 277 | toneThread.beep(hz, length, left, right) 278 | 279 | 280 | def terminate(): 281 | global toneThread 282 | if not toneThread: 283 | return 284 | toneThread.terminate() 285 | toneThread.join() 286 | toneThread = None 287 | 288 | registerGenerator(OrigTone, SineGenerator, SawtoothGenerator, SquareGenerator, TriangleGenerator) 289 | -------------------------------------------------------------------------------- /sconstruct: -------------------------------------------------------------------------------- 1 | # NVDA add-on template SCONSTRUCT file 2 | # Copyright (C) 2012-2023 Rui Batista, Noelia Martinez, Joseph Lee 3 | # This file is covered by the GNU General Public License. 4 | # See the file COPYING.txt for more details. 5 | 6 | import codecs 7 | import gettext 8 | import os 9 | import os.path 10 | import zipfile 11 | import sys 12 | 13 | # While names imported below are available by default in every SConscript 14 | # Linters aren't aware about them. 15 | # To avoid Flake8 F821 warnings about them they are imported explicitly. 16 | # When using other Scons functions please add them to the line below. 17 | from SCons.Script import BoolVariable, Builder, Copy, Environment, Variables 18 | 19 | sys.dont_write_bytecode = True 20 | 21 | # Bytecode should not be written for build vars module to keep the repository root folder clean. 22 | import buildVars # NOQA: E402 23 | 24 | 25 | def md2html(source, dest): 26 | import markdown 27 | # Use extensions if defined. 28 | mdExtensions = buildVars.markdownExtensions 29 | lang = os.path.basename(os.path.dirname(source)).replace('_', '-') 30 | localeLang = os.path.basename(os.path.dirname(source)) 31 | try: 32 | _ = gettext.translation("nvda", localedir=os.path.join("addon", "locale"), languages=[localeLang]).gettext 33 | summary = _(buildVars.addon_info["addon_summary"]) 34 | except Exception: 35 | summary = buildVars.addon_info["addon_summary"] 36 | title = "{addonSummary} {addonVersion}".format( 37 | addonSummary=summary, addonVersion=buildVars.addon_info["addon_version"] 38 | ) 39 | headerDic = { 40 | "[[!meta title=\"": "# ", 41 | "\"]]": " #", 42 | } 43 | with codecs.open(source, "r", "utf-8") as f: 44 | mdText = f.read() 45 | for k, v in headerDic.items(): 46 | mdText = mdText.replace(k, v, 1) 47 | htmlText = markdown.markdown(mdText, extensions=mdExtensions) 48 | # Optimization: build resulting HTML text in one go instead of writing parts separately. 49 | docText = "\n".join([ 50 | "", 51 | "" % lang, 52 | "", 53 | "" 54 | "", 55 | "", 56 | "%s" % title, 57 | "\n", 58 | htmlText, 59 | "\n" 60 | ]) 61 | with codecs.open(dest, "w", "utf-8") as f: 62 | f.write(docText) 63 | 64 | 65 | def mdTool(env): 66 | mdAction = env.Action( 67 | lambda target, source, env: md2html(source[0].path, target[0].path), 68 | lambda target, source, env: 'Generating % s' % target[0], 69 | ) 70 | mdBuilder = env.Builder( 71 | action=mdAction, 72 | suffix='.html', 73 | src_suffix='.md', 74 | ) 75 | env['BUILDERS']['markdown'] = mdBuilder 76 | 77 | 78 | def validateVersionNumber(key, val, env): 79 | # Used to make sure version major.minor.patch are integers to comply with NV Access add-on store. 80 | # Ignore all this if version number is not specified, in which case json generator will validate this info. 81 | if val == "0.0.0": 82 | return 83 | versionNumber = val.split(".") 84 | if len(versionNumber) < 3: 85 | raise ValueError("versionNumber must have three parts (major.minor.patch)") 86 | if not all([part.isnumeric() for part in versionNumber]): 87 | raise ValueError("versionNumber (major.minor.patch) must be integers") 88 | 89 | 90 | vars = Variables() 91 | vars.Add("version", "The version of this build", buildVars.addon_info["addon_version"]) 92 | vars.Add("versionNumber", "Version number of the form major.minor.patch", "0.0.0", validateVersionNumber) 93 | vars.Add(BoolVariable("dev", "Whether this is a daily development version", False)) 94 | vars.Add("channel", "Update channel for this build", buildVars.addon_info["addon_updateChannel"]) 95 | 96 | env = Environment(variables=vars, ENV=os.environ, tools=['gettexttool', mdTool]) 97 | env.Append(**buildVars.addon_info) 98 | 99 | if env["dev"]: 100 | import datetime 101 | buildDate = datetime.datetime.now() 102 | year, month, day = str(buildDate.year), str(buildDate.month), str(buildDate.day) 103 | versionTimestamp = "".join([year, month.zfill(2), day.zfill(2)]) 104 | env["addon_version"] = f"{versionTimestamp}.0.0" 105 | env["versionNumber"] = f"{versionTimestamp}.0.0" 106 | env["channel"] = "dev" 107 | elif env["version"] is not None: 108 | env["addon_version"] = env["version"] 109 | if "channel" in env and env["channel"] is not None: 110 | env["addon_updateChannel"] = env["channel"] 111 | 112 | buildVars.addon_info["addon_version"] = env["addon_version"] 113 | buildVars.addon_info["addon_updateChannel"] = env["addon_updateChannel"] 114 | 115 | addonFile = env.File("${addon_name}-${addon_version}.nvda-addon") 116 | 117 | 118 | def addonGenerator(target, source, env, for_signature): 119 | action = env.Action( 120 | lambda target, source, env: createAddonBundleFromPath(source[0].abspath, target[0].abspath) and None, 121 | lambda target, source, env: "Generating Addon %s" % target[0] 122 | ) 123 | return action 124 | 125 | 126 | def manifestGenerator(target, source, env, for_signature): 127 | action = env.Action( 128 | lambda target, source, env: generateManifest(source[0].abspath, target[0].abspath) and None, 129 | lambda target, source, env: "Generating manifest %s" % target[0] 130 | ) 131 | return action 132 | 133 | 134 | def translatedManifestGenerator(target, source, env, for_signature): 135 | dir = os.path.abspath(os.path.join(os.path.dirname(str(source[0])), "..")) 136 | lang = os.path.basename(dir) 137 | action = env.Action( 138 | lambda target, source, env: generateTranslatedManifest(source[1].abspath, lang, target[0].abspath) and None, 139 | lambda target, source, env: "Generating translated manifest %s" % target[0] 140 | ) 141 | return action 142 | 143 | 144 | env['BUILDERS']['NVDAAddon'] = Builder(generator=addonGenerator) 145 | env['BUILDERS']['NVDAManifest'] = Builder(generator=manifestGenerator) 146 | env['BUILDERS']['NVDATranslatedManifest'] = Builder(generator=translatedManifestGenerator) 147 | 148 | 149 | def createAddonHelp(dir): 150 | docsDir = os.path.join(dir, "doc") 151 | if os.path.isfile("style.css"): 152 | cssPath = os.path.join(docsDir, "style.css") 153 | cssTarget = env.Command(cssPath, "style.css", Copy("$TARGET", "$SOURCE")) 154 | env.Depends(addon, cssTarget) 155 | if os.path.isfile("readme.md"): 156 | readmePath = os.path.join(docsDir, buildVars.baseLanguage, "readme.md") 157 | readmeTarget = env.Command(readmePath, "readme.md", Copy("$TARGET", "$SOURCE")) 158 | env.Depends(addon, readmeTarget) 159 | 160 | 161 | def createAddonBundleFromPath(path, dest): 162 | """ Creates a bundle from a directory that contains an addon manifest file.""" 163 | basedir = os.path.abspath(path) 164 | with zipfile.ZipFile(dest, 'w', zipfile.ZIP_DEFLATED) as z: 165 | # FIXME: the include/exclude feature may or may not be useful. Also python files can be pre-compiled. 166 | for dir, dirnames, filenames in os.walk(basedir): 167 | relativePath = os.path.relpath(dir, basedir) 168 | for filename in filenames: 169 | pathInBundle = os.path.join(relativePath, filename) 170 | absPath = os.path.join(dir, filename) 171 | if pathInBundle not in buildVars.excludedFiles: 172 | z.write(absPath, pathInBundle) 173 | # Add-on store does not require submitting json files. 174 | # createAddonStoreJson(dest) 175 | return dest 176 | 177 | 178 | def createAddonStoreJson(bundle): 179 | """Creates add-on store JSON file from an add-on package and manifest data.""" 180 | import json 181 | import hashlib 182 | # Set different json file names and version number properties based on version number parsing results. 183 | if env["versionNumber"] == "0.0.0": 184 | env["versionNumber"] = buildVars.addon_info["addon_version"] 185 | versionNumberParsed = env["versionNumber"].split(".") 186 | if all([part.isnumeric() for part in versionNumberParsed]): 187 | if len(versionNumberParsed) == 1: 188 | versionNumberParsed += ["0", "0"] 189 | elif len(versionNumberParsed) == 2: 190 | versionNumberParsed.append("0") 191 | else: 192 | versionNumberParsed = [] 193 | if len(versionNumberParsed): 194 | major, minor, patch = [int(part) for part in versionNumberParsed] 195 | jsonFilename = f'{major}.{minor}.{patch}.json' 196 | else: 197 | jsonFilename = f'{buildVars.addon_info["addon_version"]}.json' 198 | major, minor, patch = 0, 0, 0 199 | print('Generating % s' % jsonFilename) 200 | sha256 = hashlib.sha256() 201 | with open(bundle, "rb") as f: 202 | for byte_block in iter(lambda: f.read(65536), b""): 203 | sha256.update(byte_block) 204 | hashValue = sha256.hexdigest() 205 | try: 206 | minimumNVDAVersion = buildVars.addon_info["addon_minimumNVDAVersion"].split(".") 207 | except AttributeError: 208 | minimumNVDAVersion = [0, 0, 0] 209 | minMajor, minMinor = minimumNVDAVersion[:2] 210 | minPatch = minimumNVDAVersion[-1] if len(minimumNVDAVersion) == 3 else "0" 211 | try: 212 | lastTestedNVDAVersion = buildVars.addon_info["addon_lastTestedNVDAVersion"].split(".") 213 | except AttributeError: 214 | lastTestedNVDAVersion = [0, 0, 0] 215 | lastTestedMajor, lastTestedMinor = lastTestedNVDAVersion[:2] 216 | lastTestedPatch = lastTestedNVDAVersion[-1] if len(lastTestedNVDAVersion) == 3 else "0" 217 | channel = buildVars.addon_info["addon_updateChannel"] 218 | if channel is None: 219 | channel = "stable" 220 | addonStoreEntry = { 221 | "addonId": buildVars.addon_info["addon_name"], 222 | "displayName": buildVars.addon_info["addon_summary"], 223 | "URL": "", 224 | "description": buildVars.addon_info["addon_description"], 225 | "sha256": hashValue, 226 | "homepage": buildVars.addon_info["addon_url"], 227 | "addonVersionName": buildVars.addon_info["addon_version"], 228 | "addonVersionNumber": { 229 | "major": major, 230 | "minor": minor, 231 | "patch": patch 232 | }, 233 | "minNVDAVersion": { 234 | "major": int(minMajor), 235 | "minor": int(minMinor), 236 | "patch": int(minPatch) 237 | }, 238 | "lastTestedVersion": { 239 | "major": int(lastTestedMajor), 240 | "minor": int(lastTestedMinor), 241 | "patch": int(lastTestedPatch) 242 | }, 243 | "channel": channel, 244 | "publisher": "", 245 | "sourceURL": buildVars.addon_info["addon_sourceURL"], 246 | "license": buildVars.addon_info["addon_license"], 247 | "licenseURL": buildVars.addon_info["addon_licenseURL"], 248 | } 249 | with open(jsonFilename, "w") as addonStoreJson: 250 | json.dump(addonStoreEntry, addonStoreJson, indent="\t") 251 | 252 | 253 | def generateManifest(source, dest): 254 | addon_info = buildVars.addon_info 255 | with codecs.open(source, "r", "utf-8") as f: 256 | manifest_template = f.read() 257 | manifest = manifest_template.format(**addon_info) 258 | with codecs.open(dest, "w", "utf-8") as f: 259 | f.write(manifest) 260 | 261 | 262 | def generateTranslatedManifest(source, language, out): 263 | _ = gettext.translation("nvda", localedir=os.path.join("addon", "locale"), languages=[language]).gettext 264 | vars = {} 265 | for var in ("addon_summary", "addon_description"): 266 | vars[var] = _(buildVars.addon_info[var]) 267 | with codecs.open(source, "r", "utf-8") as f: 268 | manifest_template = f.read() 269 | result = manifest_template.format(**vars) 270 | with codecs.open(out, "w", "utf-8") as f: 271 | f.write(result) 272 | 273 | 274 | def expandGlobs(files): 275 | return [f for pattern in files for f in env.Glob(pattern)] 276 | 277 | 278 | addon = env.NVDAAddon(addonFile, env.Dir('addon')) 279 | 280 | langDirs = [f for f in env.Glob(os.path.join("addon", "locale", "*"))] 281 | 282 | # Allow all NVDA's gettext po files to be compiled in source/locale, and manifest files to be generated 283 | for dir in langDirs: 284 | poFile = dir.File(os.path.join("LC_MESSAGES", "nvda.po")) 285 | moFile = env.gettextMoFile(poFile) 286 | env.Depends(moFile, poFile) 287 | translatedManifest = env.NVDATranslatedManifest( 288 | dir.File("manifest.ini"), 289 | [moFile, os.path.join("manifest-translated.ini.tpl")] 290 | ) 291 | env.Depends(translatedManifest, ["buildVars.py"]) 292 | env.Depends(addon, [translatedManifest, moFile]) 293 | 294 | pythonFiles = expandGlobs(buildVars.pythonSources) 295 | for file in pythonFiles: 296 | env.Depends(addon, file) 297 | 298 | # Convert markdown files to html 299 | # We need at least doc in English and should enable the Help button for the add-on in Add-ons Manager 300 | createAddonHelp("addon") 301 | for mdFile in env.Glob(os.path.join('addon', 'doc', '*', '*.md')): 302 | htmlFile = env.markdown(mdFile) 303 | try: # It is possible that no moFile was set, because an add-on has no translations. 304 | moFile 305 | except NameError: # Runs if there is no moFile 306 | env.Depends(htmlFile, mdFile) 307 | else: # Runs if there is a moFile 308 | env.Depends(htmlFile, [mdFile, moFile]) 309 | env.Depends(addon, htmlFile) 310 | 311 | # Pot target 312 | i18nFiles = expandGlobs(buildVars.i18nSources) 313 | gettextvars = { 314 | 'gettext_package_bugs_address': 'nvda-translations@groups.io', 315 | 'gettext_package_name': buildVars.addon_info['addon_name'], 316 | 'gettext_package_version': buildVars.addon_info['addon_version'] 317 | } 318 | 319 | pot = env.gettextPotFile("${addon_name}.pot", i18nFiles, **gettextvars) 320 | env.Alias('pot', pot) 321 | env.Depends(pot, i18nFiles) 322 | mergePot = env.gettextMergePotFile("${addon_name}-merge.pot", i18nFiles, **gettextvars) 323 | env.Alias('mergePot', mergePot) 324 | env.Depends(mergePot, i18nFiles) 325 | 326 | # Generate Manifest path 327 | manifest = env.NVDAManifest(os.path.join("addon", "manifest.ini"), os.path.join("manifest.ini.tpl")) 328 | # Ensure manifest is rebuilt if buildVars is updated. 329 | env.Depends(manifest, "buildVars.py") 330 | 331 | env.Depends(addon, manifest) 332 | env.Default(addon) 333 | env.Clean(addon, ['.sconsign.dblite', 'addon/doc/' + buildVars.baseLanguage + '/']) 334 | -------------------------------------------------------------------------------- /COPYING.txt: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Library General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License 307 | along with this program; if not, write to the Free Software 308 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 309 | 310 | 311 | Also add information on how to contact you by electronic and paper mail. 312 | 313 | If the program is interactive, make it output a short notice like this 314 | when it starts in an interactive mode: 315 | 316 | Gnomovision version 69, Copyright (C) year name of author 317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 318 | This is free software, and you are welcome to redistribute it 319 | under certain conditions; type `show c' for details. 320 | 321 | The hypothetical commands `show w' and `show c' should show the appropriate 322 | parts of the General Public License. Of course, the commands you use may 323 | be called something other than `show w' and `show c'; they could even be 324 | mouse-clicks or menu items--whatever suits your program. 325 | 326 | You should also get your employer (if you work as a programmer) or your 327 | school, if any, to sign a "copyright disclaimer" for the program, if 328 | necessary. Here is a sample; alter the names: 329 | 330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 331 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 332 | 333 | , 1 April 1989 334 | Ty Coon, President of Vice 335 | 336 | This General Public License does not permit incorporating your program into 337 | proprietary programs. If your program is a subroutine library, you may 338 | consider it more useful to permit linking proprietary applications with the 339 | library. If this is what you want to do, use the GNU Library General 340 | Public License instead of this License. 341 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | --------------------------------------------------------------------------------