diff --git a/README.md b/README.md index 86ab714..7ee0665 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,14 @@ SOFASonix was initially developed as a supplementary tool for usage alongside an - Powered by the lightweight and powerful SQLite3, providing support for different versions of each convention. - Simple syntax for quickly generating / reading SOFA files. +## Installation + +SOFASonix can be installed as a PyPI package using + +``` +pip install sofasonix +``` + ## Supported Conventions The following conventions are currently supported by SOFASonix for reading and writing, as per the [SOFAConventions website](https://www.sofaconventions.org/mediawiki/index.php/SOFA_conventions): diff --git a/SOFASonix/SOFASonix.py b/SOFASonix/SOFASonix.py index 6b07223..fcea7d6 100644 --- a/SOFASonix/SOFASonix.py +++ b/SOFASonix/SOFASonix.py @@ -45,14 +45,12 @@ import pandas as pd import datetime import os +from .SOFASonixAPI import SOFASonixAPI from .SOFASonixField import SOFASonixField from .SOFASonixError import SOFAError, SOFAFieldError -class SOFASonix: - APIName = "SOFASonix" - APIVersion = "1.0" - dbfile = "ss_db.db" +class SOFASonix(object): def __init__(self, conv, version=False, specVersion=False, load=False, **dims): @@ -62,7 +60,7 @@ def __init__(self, conv, version=False, specVersion=False, load=False, except NameError: cwdpath = os.path.dirname(os.path.realpath('__file__')) finally: - self.dbpath = "{}/{}".format(cwdpath, self.dbfile) + self.dbpath = "{}/{}".format(cwdpath, SOFASonixAPI.getDBFile()) # Return convention data if valid params supplied. self.convention = self._getConvention(conv, version, specVersion) @@ -78,8 +76,10 @@ def __init__(self, conv, version=False, specVersion=False, load=False, self.params = self._getParams(self.convention["id"]) # Assign application information - self.getParam("GLOBAL:APIName", True).value = self.APIName - self.getParam("GLOBAL:APIVersion", True).value = self.APIVersion + self.getParam("GLOBAL:APIName", + True).value = SOFASonixAPI.getName() + self.getParam("GLOBAL:APIVersion", + True).value = SOFASonixAPI.getVersion() # Check if creating new SOFA file or loading an existing one if(load is False): @@ -103,7 +103,7 @@ def __setattr__(self, name, value): [params.index(nameTrim)], value) # Otherwise assign normally else: - super().__setattr__(name, value) + super(SOFASonix, self).__setattr__(name, value) def __getattr__(self, name): error = ("'{}' object has no attribute '{}'" @@ -326,8 +326,11 @@ def _getParams(self, convention_id): # Create a SOFASonixField object name = pi.pop("name") + fieldParams = {} + fieldParams.update(pi) + fieldParams.update(properties) parsedParams[pc][name] = SOFASonixField(self, name, pc, units, - {**pi, **properties}) + fieldParams) return parsedParams @@ -354,6 +357,7 @@ def flatten(self): {flat.update(category) for category in self.params.values()} return flat + @staticmethod def load(file): raw = netCDF4.Dataset(file, "r", "NETCDF4") # Try to find a convention @@ -550,8 +554,14 @@ def setParam(self, key, value, force=False): # Force insertion of foreign parameter -- use for load ONLY elif force: print("Inserting foreign parameter: '{}'".format(key)) + # Python2 fix + try: + basetype = unicode + except NameError: + basetype = str + # Find out parameter type - if(type(value) == str or type(value) == bytes): + if(type(value) in [str, basetype, bytes]): inputType = "attribute" inputDims = 0 else: @@ -581,11 +591,13 @@ def setParam(self, key, value, force=False): "dimensions": inputDims, "ro": 0} properties = params.pop("properties") + fieldParams = {} + fieldParams.update(params) + fieldParams.update(properties) self.params["__unclassed"][key] = SOFASonixField(self, key, "__unclassed", self._getUnits(), - {**params, - **properties}) + fieldParams) else: raise SOFAError("Invalid parameter supplied for convention: '{}'" @@ -654,14 +666,13 @@ def export(self, filename): for key, element in attributes.items(): variable, attrname = key.split(":") if (":" in key)\ else ["global", key] - value = np.string_(element.value) # For global attributes, create in root if(key in self.params["global"] or variable.lower() == "global"): - setattr(file, attrname, value) + setattr(file, attrname, element.value) # Otherwise create the attribute within the variable else: - setattr(file[variable], attrname, value) + setattr(file[variable], attrname, element.value) file.close() except Exception: # Close file if errors encountered and re-raise exception. diff --git a/SOFASonix/SOFASonixAPI.py b/SOFASonix/SOFASonixAPI.py new file mode 100644 index 0000000..75c0d0e --- /dev/null +++ b/SOFASonix/SOFASonixAPI.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (c) 2018, I.Laghidze +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of SOFASonix nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# ============================================================================= +# +# File: SOFASonixAPI.py +# Project: SOFASonix +# Author: I.Laghidze +# License: BSD 3 +# +# ============================================================================= + + +class SOFASonixAPI: + APIName = "SOFASonix" + APIVersion = "1.0.1" + DBFile = "ss_db.db" + + @classmethod + def getName(cls): + return cls.APIName + + @classmethod + def getVersion(cls): + return cls.APIVersion + + @classmethod + def getDBFile(cls): + return cls.DBFile diff --git a/SOFASonix/__init__.py b/SOFASonix/__init__.py index 158c357..e82d998 100644 --- a/SOFASonix/__init__.py +++ b/SOFASonix/__init__.py @@ -1 +1,2 @@ +from .SOFASonixAPI import SOFASonixAPI from .SOFASonix import SOFASonix as SOFAFile diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..224a779 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[metadata] +description-file = README.md \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..7a47b8f --- /dev/null +++ b/setup.py @@ -0,0 +1,39 @@ +from distutils.core import setup + +setup( + name="SOFASonix", + packages=["SOFASonix"], + version="1.0.1", + license='BSD 3-Clause', + description="A Lightweight Python API for the AES69-2015 Spatially Oriented Format for Acoustics (SOFA) File Format", + author='Ioseb Laghidze', + author_email='developer@artisan-one.com', + url='https://github.com/OneBadNinja/SOFASonix/', + download_url='https://github.com/OneBadNinja/SOFASonix/archive/1.0.1.tar.gz', + keywords=['SOFA', 'Spatially Oriented Format For Acoustics', + 'Audio', '3D Audio', 'Binaural', 'Transaural', 'Acoustics'], + package_data={ + # Include database file + '': ['*.db'], + }, + install_requires=[ + 'netCDF4', + 'numpy', + 'pandas', + ], + classifiers=[ + 'Development Status :: 4 - Beta', + 'Intended Audience :: Developers', + 'Intended Audience :: Science/Research', + 'Topic :: Software Development :: Build Tools', + 'License :: OSI Approved :: BSD License', + 'Operating System :: OS Independent', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + ], +)