Giter Club home page Giter Club logo

opensauce-python's Introduction

opensauce-python

This is a Python port of VoiceSauce (written in MATLAB) / OpenSauce (written in GNU Octave). It provides a set of command-line tools for taking automatic voice measurements from audio recordings.

How to cite this software

Here is the BibTeX citation:

@misc{opensauce_2019_2638411,
  author = {Terri M.~Yu and
            R.~David Murray and
            Kate Silverstein and
            Kristine M.~Yu},
  title  = {OpenSauce: Open source software for voice analysis, v0.1},
  year   = 2019,
  doi    = {10.5281/zenodo.2638411},
  url    = {https://github.com/voicesauce/opensauce-python}
}

Depending on the bibliography style file you're using, the DOI and URL fields might not appear. If your bibliography style file doesn't support DOI and URL fields, you may need to find a workaround, like using the note field.

Alternatively, here's a sample plain text citation (your journal may differ in style):

Terri M. Yu, R. David Murray, Kate Silverstein and Kristine M. Yu. OpenSauce: Open source software for voice analysis, v0.1. (2019). doi:10.5281/zenodo.2638411

Here is the link to the DOI on Zenodo: DOI

Project status

Travis CI build status codecov status Appveyor build status

Requirements

  • Windows, Mac OS X, or Linux operating system
  • Python 2.7 or 3.6+
  • Python packages NumPy and SciPy

We do continuous integration testing on the following:

  • Python 2.7, 3.6, and 3.7 on Ubuntu 16.04
  • Anaconda Python 2.7 and 3.6 on Ubuntu 16.04
  • Homebrew Python 2.7 and 3.6 on Mac OS X 10.12 Sierra
  • Anaconda Python 2.7 and 3.6 on Mac OS X 10.12 Sierra
  • Homebrew Python 2.7 and 3.6 on Mac OS X 10.13 High Sierra

Previously, we have tested OpenSauce successfully tested on Ubuntu 14.04, Manjaro 17.0, Windows 7 64-bit, and Mac OS X 10.11 El Capitan. However, these are not part of the current continuous integration testing setup.

If you want to use Snack to estimate parameters, you need to install the following:

If you want to use Praat to estimate parameters, you need to download the Praat software.

  • Praat (version 6.0.20+)

OpenSauce has been tested with Praat v6.0.20 - v6.0.49 on Linux. It's possible that OpenSauce may work with Praat versions 6.0.03 - 6.0.19, but those have not been tested.

If you want to use REAPER to estimate F0, you need to either install the corresponding Python package pyreaper or build it using a C compiler.

  • Python packages Cython and pyreaper (Note: Cython and NumPy are requirements for pyreaper)

OR

Note: Currently, the only input files supported are WAV files in 16-bit integer PCM format. Praat can only read certain file types described in their documentation. Snack can only read the file types described in their documentation. REAPER only supports 16-bit integer PCM input files. If you would like to see other file types supported, please let us know in the issue tracker!

Installation

  1. Install Python, the Python packages NumPy / SciPy / Reaper, and Snack Sound Toolkit, if you don't already have them. Installing Snack can be non-trivial, so we recommend that you follow the recommendations in the instructions below for this step.

  2. Download Praat, if you don't already have it. On Linux, if you have trouble with the full featured Praat executable, you can download and use the "barren" version instead. The barren version lacks the GUI and other features, but those aren't needed for OpenSauce.

  3. Install Git, if you don't have it on your machine. See the official Git website for recommendations on how to install Git.

  4. Finally, navigate to the directory where you want to download opensauce and clone this repository using Git.

    $ git clone https://github.com/voicesauce/opensauce-python.git
    
  5. To get updates to the software, use the git pull command inside the opensauce-python directory.

    $ git pull
    

Quickstart

(Note that these are interim instructions. Eventually there will be an 'opensauce' command.)

To run OpenSauce, open a new terminal window, cd into the directory where you cloned opensauce-python:

$ cd /path/to/opensauce-python

You can view the command help by typing:

$ python -m opensauce --help

or alternatively:

$ python -m opensauce --h

Here's an example of how to use the command line interface. To process a sound file and get the SHR measurements written to a CSV file, do:

$ python -m opensauce --measurements SHR -o out.csv /path/to/file.wav

Or alternatively, you can put file path to the sound file first.

$ python -m opensauce /path/to/file.wav --measurements SHR -o out.csv

The default output format used is Excel tab delimited. You can also output files that are comma delimited using the option --output-delimiter.

You can list multiple measurements in a single command. To process a sound file and get the PraatF0 and SHR measurements written to a CSV file, do:

$ python -m opensauce --measurements praatF0 SHR -o out.csv /path/to/file.wav

(Note that if a Snack, Praat, or REAPER command doesn't appear to be working even though you installed the program, you may need to move the Snack / Praat / REAPER executable to the default location or set the correct path by using a settings file or passing the correct path through a parameter. See next section for details.)

You can process multiple wav files by using shell wildcards. Suppose your wav files are in the directory data/sample1. You can process all of them by typing:

$ python -m opensauce --measurements SHR -o out.csv data/sample1/*.wav

If you want to write the output to stdout (that is, displayed on the terminal) instead of writing to a file, leave off the -o optional argument. For example, this command writes the SHR measurements to stdout and does not use TextGrid information.

$ python -m opensauce --measurements SHR --no-textgrid /path/to/file.wav

To view other measurement options, run $ python -m opensauce --help to see which measurements are available.)

The command line interface has default values for all the different options. To see the default values, run the help command: python -m opensauce -h.

If you want to use TextGrid files in your analysis, make sure that each sound file you analyze name.wav, has a corresponding TextGrid file with the same base filename followed by the .TextGrid extension, e.g. name.TextGrid. Capitalization is important, so name.TextGrid will be found, but name.textgrid will not.

Note there is one subtlety with using the command line interface. You cannot specify the sound files right after the --measurements option.

A command, such as,

$ python -m opensauce --measurements SHR /path/to/file.wav

would fail to be parsed correctly because the path of the sound file is interpreted as a measurement. Either make sure that there is another option specified after measurements,

$ python -m opensauce --measurements SHR --no-labels /path/to/file.wav

or specify the sound file at the beginning.

$ python -m opensauce /path/to/file.wav --measurements SHR

Adjusting settings for Snack, Praat, and REAPER

Snack, Praat, and REAPER are all external programs, outside of OpenSauce. OpenSauce calls these programs when the corresponding measurements (Snack / Praat / REAPER) are requested by the user.

When Snack measurements are requested, OpenSauce tries to use default values for the Tcl shell command. When Praat measurements are requested, OpenSauce calls the Praat executable. When REAPER measurements are requested with the use-creaper parameter, OpenSauce calls the REAPER executable. If the default values don't match your installation, you will need to either move the executables to the default locations or explicitly specify the path to the executable location on your system.

Note, if you install the pyreaper Python package and request REAPER measurements with the use-pyreaper parameter, you don't need to worry about setting the path to the REAPER executable.

For Snack, the default Tcl shell command is tclsh8.4 on Mac OS X, and tclsh on Windows and Linux.

For Praat, the default path is /Applications/Praat.app/Contents/MacOS/Praat on Mac OS X, C:\Program Files\Praat.exe on Windows, and /usr/bin/praat on Linux.

For REAPER, the default path to the REAPER executable is /usr/bin/reaper.

If you are calling Snack via the Tcl shell, you can set the command that runs the Tcl shell interpreter using the --tcl-cmd command line option. For example, if we use run

$ python -m opensauce --measurements snackF0 --tcl-cmd tclsh8.5 /path/to/file.wav

this tells OpenSauce to use the command tclsh8.5 to run the Tcl shell interpreter (specifically Tcl version 8.5). This is especially useful if you have multiple versions of Tcl installed on your machine.

To specify your own path for the Praat executable, use the command line option --praat-path. For example,

$ python -m opensauce --measurements praatF0 --praat-path /home/username/praat /path/to/file.wav

will run the Praat executable located at /home/username/praat.

To specify your own path for the REAPER executable, use the command line option --reaper-path. For example,

$ python -m opensauce --measurements reaperF0 --reaper-path /home/username/reaper /path/to/file.wav

will run the REAPER executable located at /home/username/reaper.

Since you will probably be running Snack / Praat / REAPER the same way every time you use OpenSauce, it is best to set these preferences automatically through a settings file, as described in the next section.

Settings and measurement files

If it is inconvenient to enter options on the command line, you can also do this via a settings and/or measurements file.

Any options you can specify on the command line you can put into a settings file, one option and its arguments per line. On the command line, you can specify a settings file to use, with a command like

$ python -m opensauce -s /path/to/my_settings /path/to/file.wav

or alternatively,

$ python -m opensauce --settings /path/to/my_settings /path/to/file.wav

To run your favorite settings by default, you can copy your settings file to one of the locations ./opensauce.settings, ~/.config/opensauce/settings, or ~/.opensaucerc. Opensauce will automatically search each of these locations, in the above order, for a default settings file, if no settings file is explicitly specified on the command line. The first file found will be used.

An example settings file is included in this repository, called opensauce.settings.example. You can copy this file to opensauce.settings to see how the settings file works. Run OpenSauce on a sound file with no settings file specified, like this:

$ python -m opensauce /path/to/file.wav

You can also have a measurements file containing one measurement per line. On the command line, you can specify a measurements file to use, with a command like

$ python -m opensauce -m my_measurements /path/to/file.wav

To run your favorite settings by default, you can copy your measurements file to one of the locations ./opensauce.measurements, ~/.config/opensauce/measurements, or ~/.opensauce.measurements. Opensauce will automatically search each of these locations, in the above order, for a default settings file, if no settings file is explicitly specified on the command line. The first file found will be used.

An example settings file is included in this repository, called opensauce.measurements.example. You can copy this file to opensauce.measurements to see how the measurements file works. Run OpenSauce on a sound file with no measurements file specified, like this:

$ python -m opensauce /path/to/file.wav

Note that if you specify measurements in both the settings and measurements files, the measurements in the settings file will override those in the measurements file. Similarly, options and measurements specified on the command line override those specified in a settings or measurements file.

Contributing

See CONTRIBUTING.

Resources

Questions

Please feel free to post a question on the Issue tracker (use the label 'question').

Acknowledgments

Thanks to Ryuichi Yamamoto for his help getting pyreaper working.

opensauce-python's People

Contributors

bitdancer avatar helenegene avatar krismyu avatar terriyu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

opensauce-python's Issues

Continuous integration testing in Windows

Currently, we only do continuous integration testing on the Linux and Mac OS X operating systems. It would be nice to support Windows as well, though that would be more complicated compared to Linux and Mac. There's a free online continuous integration service called Appveyor which specifically does continuous integration for Windows. Appveyor has support for Miniconda and Cygwin, which is exactly what we need. An example of using Appveyor for testing scientific Python is the AstroPy project. Their appveyor.yml configuration file is a good reference.

Support for testing multiple Python versions on OS X in Travis continuous integration

Currently, there is no official Python support for OS X on Travis. For full details, see the GitHub issue #2312.

There are hacks to run multiple Python versions on OS X in Travis continuous integration. For example, one can use utilities here. But it makes the build more complicated.

For simplicity, we get around the lack of Python official support by using Homebrew to install the latest Python 3 in a virtualenv as described here. This only works for a single version of Python.

If there is time later, we might try supporting multiple Python versions.

Rounding issue on Python 3

Apparently, there is an issue with rounding in Python 3. This comes up in the implementation of shr_pitch():

# "time locations rounded to nearest ms"
#
# XXX numpy uses round-half-even, while matlab appears to use
# round-away-from-zero.  We can emulate this in python2 by using
# python2 round function instead of numpy's, but in python3 the
# rounding will have to take a detour through the Decimal module,
# which fortunately in python3 has a C accelerator.  Or perhaps
# we can ultimately just use round-half-even, if it turns out
# not to affect the results.  (This matters here because the time
# intervals produced by shrp are normally x.5 values.)
import sys
if sys.version_info.major > 2:
    raise NotImplementedError("python3 rounding will produce bad results")
t = np.vectorize(round)(f0_time)

Quoted from this commit: 7dcb050#diff-7953eb3de436b3d0e74a60971a8b027aR78

Currently, the code passes all tests except for anything involving shr_pitch() because of this rounding problem.

MATLAB display corruption problem in Linux

Note this is an issue for MATLAB, not for OpenSauce. There isn't a good place to document this error, so I decided to put it here.

I often need to run VoiceSauce in MATLAB, to get test data that I can use a benchmark for OpenSauce.

When running MATLAB in Linux, the display may become corrupted. The corruption is due to the version of Java being used to render the display. To fix the problem, add these lines to your .bashrc or .profile to tell MATLAB to use your system's version of Java:

export J2D_D3D=false
export MATLAB_JAVA=/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre

You may need to edit the path or version of Java to match the one installed on your system.

Matlab can't call external programs like Praat in Linux

Note this is an issue for VoiceSauce in MATLAB, not for OpenSauce. There isn't a good place to document this error, so I decided to put it here.

When I try to run VoiceSauce in Matlab to obtain test data for OpenSauce, I get an error for the Praat calculations:

/usr/bin/praat: /usr/local/MATLAB/R2016b/sys/os/glnxa64/libstdc++.so.6: version `CXXABI_1.3.9' not found (required by /usr/bin/praat)

Apparently, this error occurs when MATLAB tries to call an external program on newer versions of Linux. The version of libstdc++ that MATLAB uses is too old, so you need to tell MATLAB to use a newer version in your Linux operating system. See more details in this discussion.

I was able to get the error to go away by running the command

$ alias matlab='LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21 /usr/local/MATLAB/R2016b/bin/matlab -desktop'

before running MATLAB via $ matlab &.

You can make the solution permanent by adding that above alias command to your .bashrc or .profile.

Why download Praat instead of using parselmouth?

In order to use praat functions, this requires the user to download praat... Why not use the PyPi package parselmouth that uses praat's backend with a python front-end? Want a hand? Let me know.

Lots of obsolete code

Since the command line interface was re-written, there are lots of obsolete files / code which aren't used anymore. In the opensauce directory, these include: measure.py, process.py, snack_ks.py, test_f0.f0, run.sh and most of the code in helpers.py .

OpenSauce doesn't work on Windows

I tested the code on Windows 7, using Anaconda and Python 2.7. The big problem right now is that the new command line interface uses a new method for calling Snack in snack.py, which doesn't work on Windows. The old version, snack_ks.py, seems to work, so we should look into using the old code.

Python 3.6 ImportWarning from Cython

When running the REAPER tests (via the command python -m unittest test.test_reaper), there is an ImportWarning of the form:

.../lib/python3.6/importlib/_bootstrap.py:219: ImportWarning: can't resolve package from __spec__ or __package__, falling back on __name__ and __path__
  return f(*args, **kwds)

Apparently, this warning comes from Cython upstream, so we can't fix it in our code. We'll have to wait for it to be fixed in a future Cython release. For more details, see this AstroPy report.

Snack formant estimates differ drastically between Windows executable and Tcl shell

There are two main ways to run Snack commands. One way is to use the Windows standalone executable from VoiceSauce. The other way is to call the Snack command through the Tcl shell. Even when I use the same input sound file and same parameters to call the Snack formant command, I get very different results depending on whether I use the Windows executable or call Snack through the Tcl shell. There are discrepancies in the numbers which are greater than 10%.

Make software into Python package or standalone executable

Instead of running opensauce-python as a module on the command line, eventually, we'd like to make it into a proper Python package and/or a standalone executable ("freezing" software). Here are some resources on how to do this:

We will want to freeze and not package for Windows, since there is no standard Python packaging system on Windows.

Snack tests fail on Mac OS X High Sierra because Tcl 8.4 has been removed

In versions of OS X previous to High Sierra (OS X 10.13), we relied on Snack being pre-installed on OS X, as part of Tcl 8.4. Unfortunately, in High Sierra, it seems that Tcl 8.4 has been removed, so OpenSauce can no longer find Snack pre-installed. We need to figure out an alternative method to get Snack installed on OS X.

Resampling function gives different values in SciPy v1.0+

The SciPy function signal.resample() gives different values for SciPy versions < 1.0 and versions >= 1.0. The values don't seem drastically different for most of the resampled points, but it is numerically significant and causes the unit tests to fail if SciPy version 1.0 is used. This is because the resampled data in the test data was generated using SciPy version < 1.0.

run.sh is out of date

run.sh is out of date after the command line interface was changed. This should be fixed later.

Computing Snack formants via Tkinter produces non-deterministic results

When the snack formant command is run, it appears to produce non-deterministic results, despite having the same input sound file and parameter values. This is especially apparent if we call the formant command through Python's Tkinter library and calculate formants for multiple sound files in the same Tcl shell. Changing the order in which the sound files are processed, changes the results slightly. This suggests that the formant command uses a random seed (unfortunately, we can't confirm if it's actually a random seed issue, since the snack formant documentation doesn't mention anything about a random seed.) If we run the formant command for a single sound file via a Tcl script, then we get consistent results, presumably because the same random seed is used each time.

For now, it's best to avoid calling Snack through the Python Tkinter library since the Tkinter-based implementation evaluates Snack commands line by line through the Tcl interpreter, which can lead to non-deterministic results depending on the order in which sound files are processed.

Doubling of data point at edges of TextGrid intervals

VoiceSauce and OpenSauce have a strange behavior when processing files using TextGrids. At the edge of a TextGrid interval, where the label switches, the data point is calculated twice.

For example:

Filename Label seg_Start seg_End t_ms snackF0
beijing_f3_50_a.wav C1 0.766 0.866 865.000 211.009
beijing_f3_50_a.wav C1 0.766 0.866 866.000 210.769
beijing_f3_50_a.wav V1 0.866 1.074 866.000 210.769
beijing_f3_50_a.wav V1 0.866 1.074 867.000 210.632

Notice that the data point for t = 866 ms is repeated twice, each with a different label C1 and V1. The values calculated are the same for both entries with t = 866 ms, but it's strange that it's repeated twice with different labels.

Design of CLI: looking for "user stories"

So, I think I understand the basics of how the python code fits together. I think I might as well start by refining the command line interface, since that will improve the usabiliy of the project.

To confirm my understandingl: in general someone will start with a set of WAV files. These files may contain "markup", using a format whose name I don't remember and will need more information on (I believe this isn't actually implemented yet?). (Is this the textgrid?) The user then runs an analysis, producing some output files. The current process doesn't seem to write any output files, so I don't actually know what those are supposed to look like yet, and probably need some additional information on that as well. Currently the analysis process is driven by a combination of a 'parameters' file, which indicates which measurements to compute and a 'settings' file, which provides parameters and thresholds for the measurements.

I presume that in voicesauce these parameters and settings were adjusted in a GUI before each processing run, with some sort of persistent storage of whatever was set.

I presume the model behind the current CLI is that the user will have a set of WAV files to process, and will set up parameters and settings for the goal of that analysis, and run process....and then what? Use the output for further analysis? Tweak the parameters and settings and run it again? Run the same analysis on different input files? Most likely all of the above at one time or another?

What I'm looking for here is what the software development community calls "user stories": a short description of various scenarios in which the project might get used. For example:

"Kristine has a set of WAV files of people saying XXXX. She wants to compare YYYY between the files, and then correlate that with ZZZZ. To do this she will select measurements A, B, and C, constrained by settings M, N, and P, run the analysis, and inspect the output file(s)."

The above is very rough, I don't care about the details I've represented by letters, what I want to know about is what the various possible data, goals, and workflows are and what the project needs to allow the user to do in order to acheive those goals. I'm thinking there are probably multiple scenarios, and that there are pieces missing from the above simple example (like marking up the WAV files). In particular talk about what it is you need to do with the output of the processing run once you have it (perhaps we want multiple output formats?), or possible sources of input if a simple directory of WAV files is not the only way the user might want to organize the input data.

So, please supply me with some user stories, and I'll suggest improvements to the CLI to facilitate them, and then implement them as I go along.

Visualizations

It'd be nice to have a module for generating visualizations of the data being processed. These visualizations could include:

  • Spectral slice of wave
  • Spectrogram of wave
  • Waveform
  • Graph of [insert measurement here]
    etc.

In the original Voicesauce, you had the option to see an image of the waveform as you were taking measurements so that might be a good place to start. These visualizations could be generated something like matplotlib.

Praat F0

Implement the Praat F0 algorithm. See func_PraatPitch

The complication here is that the Octave code actually opens the Praat application and uses Praat to make the calculations. While it'd be fine to do it this way from Python to start with, it might be nice/faster to port the algorithm that Praat uses to Python instead. This would be an interesting project for someone who has a good grasp on C++ (because that's what Praat is written in).

The Praat source code is available here

Testing on Windows

We should make sure to test our code on Windows, probably using the Anaconda Python distribution.

Output has off-by-one-frameshift issue for time points

When output is generated, the output file contains measurement numbers corresponding to each time point. The question is for each measurement vector, does the first element correspond to t=0 or does it correspond to t=first frameshift. Since we usually assume a frameshift of 1 ms, then the first element can either correspond to t=0 ms or t=1 ms. If we make the wrong choice, we get an off-by-one error when we index into the vector to obtain the measurement value corresponding to the time point.

Python 3 support

Currently, Python 3.x seems to work and passes the tests. For now, the docs say to use Python 2.7. When the software is fully tested, we should update the docs to say that Python 3.x is also supported.

Would you like Energy?

I believe I have worked out the Energy Algorithm from Voice Sauce in Python... at least I get the same data if I calculate using pitch from Praat. Would you be interested in a PR with Energy? I would have to do significant work to incorporate it into this codebase. If you are interested, I can do it. Have a look. Here is my code: https://github.com/drfeinberg/sauce/blob/main/energy.py

SciPy FutureWarning about multidimensional indexing

We're seeing the following FutureWarning when we run the unit tests in any version of Python or operating system.

FutureWarning: Using a non-tuple sequence for multidimensional indexing is deprecated; use arr[tuple(seq)] instead of arr[seq]. In the future this will be interpreted as an array index, arr[np.array(seq)], which will result either in an error or a different result.

It's coming from the latest version of SciPy. It sounds like it will be fixed in the next release, according to this GitHub issue from the SciPy repository.

Reading in Praat TextGrids with non-ASCII characters

Something that a colleague mentioned to me that we should keep an eye out for in working on the function to read in Praat TextGrids, is that they were having trouble with non-ASCII characters in the TextGrids: apparently, if they had non-ASCII characters anywhere in the TextGrid (even if in a part of the TextGrid not being analyzed, i.e. in another tier), there were problems.

Implementation of measurements of harmonics

It seems that opensauce-python only implements various methods to extract f0 and formants, which voicesauce had more measures implemented, right?

Opensauce is stated to not be updated anymore, so I guess harmonic amplitudes and cpp will be implemented here? Or is VoiceSauce actually the most updated version still? Perhaps the state of packages needs to be clarified so the user may know which one to use.

It seems now that the praatsauce port may be the most up-to-date right now?
https://github.com/kirbyj/praatsauce

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.