Skip to content

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
mush42 committed Jul 13, 2016
0 parents commit 57132e0
Show file tree
Hide file tree
Showing 72 changed files with 12,028 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Set default behaviour, in case users don't have core.autocrlf set.
* text=auto

# Try to ensure that po files in the repo does not include
# source code line numbers.
# Every person expected to commit po files should change their personal config file as described here:
# https://mail.gnome.org/archives/kupfer-list/2010-June/msg00002.html
*.po filter=cleanpo
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
addon/doc/*.css
addon/doc/en/
*_docHandler.py
*.html
*.ini
*.mo
*.pot
*.pyc
*.nvda-addon
.sconsign.dblite
340 changes: 340 additions & 0 deletions COPYING.txt

Large diffs are not rendered by default.

27 changes: 27 additions & 0 deletions README
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Audio Themes Add-on For NVDA
This add-on creates a virtual audio display that plays sounds when focusing or navigating objects, the audio will be played in a location that corresponds to the object's location in the visual display.

The add-on also enables the user to activate, install, remove, edit, create, and distribute audio theme packages.


## A Note on Third-party Code:
Although this add-on was started as an independent project, it evolved to be an enhanced version of the 'Unspoken' add-on by Austin Hicks ([email protected]) and Bryan Smart ([email protected]). The majority of this add-on's development went into creating the tools to manage, edit and create audio theme packages. So a big thank you to them for creating such a wonderful add-on, and making it available for us to build on top of their work.


## A Note on Third-party audio files:
The **Default** audio theme package in this add-on uses sounds from several sources, here is a breakdown for them:
- Unspoken 3D Audio: An add-on for NVDA
- TWBlue: A free and open source twitter client
- Mushy TalkBack: An alternative talkback with a better sound scheme.


## Contribute:
In addition to code contribution, take a look at the **contribute.txt** file to see if you can help in other aspects.


## Copyright:
Copyright (c) 2014-2016 Musharraf Omer<[email protected]> and Others


## Licence
Licensed under the GNU General Public License. See the file **copying** for more details.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
1 change: 1 addition & 0 deletions addon/globalPlugins/audioThemes/Themes/Default/info.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"summary": "The default audio theme package.", "author": "Musharraf Omer"}
200 changes: 200 additions & 0 deletions addon/globalPlugins/audioThemes/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
r"""
* This add-on creates a virtual audio display that plays sounds when focusing or navigating objects, the audio will be played in a location that corresponds to the object's location in the visual display.
* it also enables the user to activate, install, remove, edit, create, and distribute audio theme packages.
* Started as an indipendant project, this addon evolved to be an enhanced version of the 'Unspoken' addon by Bryan Smart ([email protected]) and Austin Hicks ([email protected]).
* The development of this addon is happening on GitHub <https://github.com/mush42/audio-themes-nvda-addon/>
* Crafted by Musharraf Omer <[email protected]> using code published by others from the NVDA community.
* Licensed under the GNU General Public License.
"""

import time
import wx

import globalPluginHandler
import appModuleHandler
import NVDAObjects
import gui
import speech
import controlTypes
import globalVars

from .dialogs.manage_dg import ManagerDialog
from .dialogs.edit_dg import EditorDialog
from .dialogs.create_dg import CreaterDialog

from .backend import helpers
from .backend import audioThemeHandler
from .backend.audioThemeHandler import SIMULATION, MIXER, SpecialProps, libaudioverse

import addonHandler
addonHandler.initTranslation()

class GlobalPlugin(globalPluginHandler.GlobalPlugin):
def __init__(self):
super(globalPluginHandler.GlobalPlugin, self).__init__()
self.allowedApps = [u'firefox', u'iexplore', u'chrome', u'opera']
self.hrtf_panner = libaudioverse.HrtfNode(SIMULATION, "default")
self.hrtf_panner.should_crossfade = False
self.hrtf_panner.connect_simulation(0)
self._previous_mouse_object = None
self._last_played_object = None
self._last_played_time = 0
#these are in degrees.
self._display_width = 180.0
self._display_height_min = -40.0
self._display_height_magnitude = 50.0
#the mixer feeds us through NVDA.
self.mixer = MIXER
#Create GUI
self.themesMenu = wx.Menu()
self.manage_themes_item = self.themesMenu.Append(wx.ID_ANY,
# Translators: the label of the menu item to open the audio themes manager dialog.
_("&Manage audio themes..."),
# Translators: the tooltip text of the menu item that opens audio themes manager dialog
_("Manage themes")
)
self.edit_theme_item = self.themesMenu.Append(wx.ID_ANY,
# Translators: The label of the menu item that opens the audio themes editor dialog
_("&Edit the active audio theme..."),
# Translators: The tooltip of the menu item that opens the audio themes editor dialog
_("Edit the current theme"))
self.create_theme_item = self.themesMenu.Append(wx.ID_ANY,
# Translators: the label of the menu item to open the audio themes Creater dialog.
_("&Create a new audio theme..."),
# Translators: the tooltip text of the menu item that opens audio themes creater dialog
_("Create a new audio theme")
)
self.submenu_item = gui.mainFrame.sysTrayIcon.menu.InsertMenu(2, wx.ID_ANY,
# Translators: The label for this add-on's menu
_("&Audio Themes"),
self.themesMenu)
gui.mainFrame.sysTrayIcon.Bind(wx.EVT_MENU, lambda evt: helpers.activate(ManagerDialog), self.manage_themes_item)
gui.mainFrame.sysTrayIcon.Bind(wx.EVT_MENU, self.onEditorDialog, self.edit_theme_item)
gui.mainFrame.sysTrayIcon.Bind(wx.EVT_MENU, lambda e: helpers.activate(CreaterDialog), self.create_theme_item)
self.postInit()

def postInit(self):
helpers.setupConfig()
if helpers.getCfgVal("using"):
audioThemeHandler.initialize()
if not helpers.getCfgVal("speakRole"):
speech.getSpeechTextForProperties = audioThemeHandler.hook_getSpeechTextForProperties

def onEditorDialog(self, evt):
if not helpers.getCfgVal("using"):
gui.messageBox(
# Translators: The text in a message box telling the user to activate an audio theme before being able to start the themes editor.
_("There is no active audio theme. Please activate an audio theme first."),
# Translators: The title of the message Box indicating an error.
_("Error"))
return
helpers.activate(EditorDialog)

def terminate(self):
if not self.submenu_item: return
try:
gui.mainFrame.sysTrayIcon.menu.RemoveItem(self.submenu_item)
except wx.PyDeadObjectError:
pass

def event_gainFocus(self, obj, nextHandler):
self.playObject(obj)
nextHandler()

def event_becomeNavigatorObject(self, obj, nextHandler):
self.playObject(obj)
nextHandler()

def event_mouseMove(self, obj, nextHandler, x, y):
if obj != self._previous_mouse_object:
self._previous_mouse_object = obj
self.playObject(obj)
nextHandler()

def event_show(self, obj, nextHandler):
if obj.role == controlTypes.ROLE_HELPBALLOON or isinstance(obj, NVDAObjects.UIA.Toast):
obj.snd = SpecialProps.notify
self.playObject(obj)
nextHandler()

def event_documentLoadComplete(self, obj, nextHandler):
if appModuleHandler.getAppNameFromProcessID(obj.processID) in self.allowedApps:
location = obj.location
obj.location = None
self.playObject(obj)
obj.location = location
nextHandler()

def playObject(self, obj):
self._playObject(obj)

def _playObject(self, obj):
# aboart early!
if not helpers.getCfgVal("using"): return
activeTheme = audioThemeHandler.findThemeWithProp("isActive", True)
if not activeTheme: return
soundpack = activeTheme.soundobjects
curtime = time.time()
if curtime-self._last_played_time < 0.1 and obj is self._last_played_object:
return
self._last_played_object = obj
self._last_played_time = curtime
order = self.getOrder(obj)
# if the object has a snd property, then play directly!
if getattr(obj, "snd", None):
pass
elif 16384 in obj.states:
obj.snd = SpecialProps.protected
elif order and soundpack.get(order, None):
obj.snd = order
else:
obj.snd = obj.role
if not obj.snd in soundpack:
return
if helpers.getCfgVal("threeD"):
self.play(obj, soundpack, _3d=True)
else:
self.play(obj, soundpack, _3d=False)

def play(self, obj, soundPack, _3d):
snd = obj.snd
# Get coordinate bounds of desktop.
desktop = NVDAObjects.api.getDesktopObject()
desktop_max_x = desktop.location[2]
desktop_max_y = desktop.location[3]
# Get location of the object.
if _3d and obj.location != None:
# Object has a location. Get its center.
obj_x = obj.location[0] + (obj.location[2] / 2.0)
obj_y = obj.location[1] + (obj.location[3] / 2.0)
else:
# Objects without location are assumed in the center of the screen.
obj_x = desktop_max_x / 2.0
obj_y = desktop_max_y / 2.0
# Scale object position to audio display.
angle_x = ((obj_x-desktop_max_x/2.0)/desktop_max_x)*self._display_width
#angle_y is a bit more involved.
percent = (desktop_max_y-obj_y)/desktop_max_y
angle_y = self._display_height_magnitude*percent+self._display_height_min
#clamp these to Libaudioverse's internal ranges.
angle_x = helpers.clamp(angle_x, -90.0, 90.0)
angle_y = helpers.clamp(angle_y, -90.0, 90.0)
#In theory, this can be made faster if we remember which is last, but that shouldn't matter here
with SIMULATION:
for i, j in soundPack.iteritems():
j.disconnect(0)
soundPack[snd].connect(0, self.hrtf_panner, 0)
soundPack[snd].position = 0.0
self.hrtf_panner.azimuth = angle_x
self.hrtf_panner.elevation = angle_y
volume = helpers.compute_volume(helpers.getCfgVal("volume"))
self.hrtf_panner.mul = volume

def getOrder(self, obj, parrole = 14, chrole = 15):
if obj.parent and obj.parent.role != parrole:
return None
if (obj.previous is None) or (obj.previous.role != chrole):
return SpecialProps.first
elif (obj.next is None) or (obj.next.role != chrole):
return SpecialProps.last

14 changes: 14 additions & 0 deletions addon/globalPlugins/audioThemes/backend/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import sys, os
import ctypes


sys.path.append(os.path.join(os.path.dirname(__file__), 'unspoken', 'deps'))

file_directory = os.path.split(os.path.abspath(__file__))[0]
libaudioverse_directory = os.path.join(file_directory, 'unspoken', 'deps', 'libaudioverse')

dll_hack = [ctypes.cdll.LoadLibrary(os.path.join(libaudioverse_directory, 'libsndfile-1.dll'))]
dll_hack.append(ctypes.cdll.LoadLibrary(os.path.join(libaudioverse_directory, 'libaudioverse.dll')))

import libaudioverse
libaudioverse.initialize()
Loading

0 comments on commit 57132e0

Please sign in to comment.