Skip to content

libquiet ctypes binding #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include quiet/*
104 changes: 101 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,102 @@
# quiet.py
Python bindings for libquiet
quiet.py
========

[![](https://img.shields.io/pypi/v/quiet.py.svg)](https://pypi.org/project/quiet.py/)


Python ctypes bindings for libquiet to transmit data with sound.

## Requirements
+ numpy

## Install

+ For ARM platform, binary package is available on pypi, just use `pip` to install it:

```
sudo apt install python-numpy
pip install --no-deps quiet.py
```

We install `numpy` separately, as installing `numpy` via pip requires compiling numpy from source.

+ For x86/amd64

```
sudo apt install cmake
git clone https://github.com/xiongyihui/quiet.py && cd quiet.py
./scripts/libs.sh
pip install .
```


## Usage
1. Encode a message, and then decode it
```
from quiet import Encode, Decoder

def test():
encoder = Encoder()
decoder = Decoder()

for chunk in encoder.encode('hello, world'):
message = decoder.decode(chunk)
if message is not None:
print(message)


test()
```

2. decode messages from recording in realtime

```
import sys
import numpy
import pyaudio
from quiet import Encode, Decoder

def decode():
if sys.version_info[0] < 3:
import Queue as queue
else:
import queue

FORMAT = pyaudio.paFloat32
CHANNELS = 1
RATE = 44100
CHUNK = 16384 # int(RATE / 100)

p = pyaudio.PyAudio()
q = queue.Queue()

def callback(in_data, frame_count, time_info, status):
q.put(in_data)
return (None, pyaudio.paContinue)

stream = p.open(format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
frames_per_buffer=CHUNK,
stream_callback=callback)

count = 0
with Decoder(profile_name='ultrasonic-experimental') as decoder:
while True:
try:
audio = q.get()
audio = numpy.fromstring(audio, dtype='float32')
# audio = audio[::CHANNELS]
code = decoder.decode(audio)
if code is not None:
count += 1
print(code.tostring().decode('utf-8', 'ignore'))
except KeyboardInterrupt:
break


decode()
```


This repo is not yet ready. It is a work in progress.
4 changes: 4 additions & 0 deletions quiet/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# -*- coding:utf-8 -*-

from .quiet import Decoder, Encoder

270 changes: 270 additions & 0 deletions quiet/quiet-profiles.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
{
"audible": {
"mod_scheme": "gmsk",
"checksum_scheme": "crc32",
"inner_fec_scheme": "v27",
"outer_fec_scheme": "none",
"frame_length": 100,
"modulation": {
"center_frequency": 4200,
"gain": 0.1
},
"interpolation": {
"shape": "kaiser",
"samples_per_symbol": 10,
"symbol_delay": 4,
"excess_bandwidth": 0.35
},
"encoder_filters": {
"dc_filter_alpha": 0.01
},
"resampler": {
"delay": 13,
"bandwidth": 0.45,
"attenuation": 60,
"filter_bank_size": 64
}
},
"audible-7k-channel-0": {
"mod_scheme": "arb16opt",
"checksum_scheme": "crc32",
"inner_fec_scheme": "rs8",
"outer_fec_scheme": "v29",
"frame_length": 600,
"modulation": {
"center_frequency": 9200,
"gain": 0.1
},
"interpolation": {
"shape": "kaiser",
"samples_per_symbol": 6,
"symbol_delay": 4,
"excess_bandwidth": 0.31
},
"encoder_filters": {
"dc_filter_alpha": 0.01
},
"resampler": {
"delay": 13,
"bandwidth": 0.45,
"attenuation": 60,
"filter_bank_size": 64
},
"ofdm": {
"num_subcarriers": 48,
"cyclic_prefix_length": 8,
"taper_length": 4,
"left_band": 0,
"right_band": 0
}
},
"audible-7k-channel-1": {
"mod_scheme": "arb16opt",
"checksum_scheme": "crc32",
"inner_fec_scheme": "rs8",
"outer_fec_scheme": "v29",
"frame_length": 600,
"modulation": {
"center_frequency": 15500,
"gain": 0.1
},
"interpolation": {
"shape": "kaiser",
"samples_per_symbol": 6,
"symbol_delay": 4,
"excess_bandwidth": 0.31
},
"encoder_filters": {
"dc_filter_alpha": 0.01
},
"resampler": {
"delay": 13,
"bandwidth": 0.45,
"attenuation": 60,
"filter_bank_size": 64
},
"ofdm": {
"num_subcarriers": 48,
"cyclic_prefix_length": 8,
"taper_length": 4,
"left_band": 0,
"right_band": 0
}
},
"cable-64k": {
"mod_scheme": "qam1024",
"checksum_scheme": "crc32",
"inner_fec_scheme": "v27p23",
"outer_fec_scheme": "rs8",
"frame_length": 7500,
"modulation": {
"center_frequency": 10200,
"gain": 0.09
},
"interpolation": {
"shape": "kaiser",
"samples_per_symbol": 2,
"symbol_delay": 4,
"excess_bandwidth": 0.35
},
"encoder_filters": {
"dc_filter_alpha": 0.03
},
"resampler": {
"delay": 13,
"bandwidth": 0.45,
"attenuation": 60,
"filter_bank_size": 64
},
"ofdm": {
"num_subcarriers": 128,
"cyclic_prefix_length": 16,
"taper_length": 8,
"left_band": 6,
"right_band": 12
}
},
"hello-world": {
"mod_scheme": "gmsk",
"checksum_scheme": "crc32",
"inner_fec_scheme": "v27",
"outer_fec_scheme": "none",
"frame_length": 25,
"modulation": {
"center_frequency": 4400,
"gain": 0.08
},
"interpolation": {
"shape": "kaiser",
"samples_per_symbol": 20,
"symbol_delay": 4,
"excess_bandwidth": 0.38
},
"encoder_filters": {
"dc_filter_alpha": 0.01
},
"resampler": {
"delay": 13,
"bandwidth": 0.45,
"attenuation": 60,
"filter_bank_size": 64
}
},
"ultrasonic": {
"mod_scheme": "gmsk",
"checksum_scheme": "crc32",
"inner_fec_scheme": "v27",
"outer_fec_scheme": "none",
"frame_length": 75,
"modulation": {
"center_frequency": 19000,
"gain": 0.1
},
"interpolation": {
"shape": "rrcos",
"samples_per_symbol": 14,
"symbol_delay": 4,
"excess_bandwidth": 0.35
},
"encoder_filters": {
"dc_filter_alpha": 0.01
},
"resampler": {
"delay": 13,
"bandwidth": 0.45,
"attenuation": 60,
"filter_bank_size": 64
}
},
"ultrasonic-3600": {
"ofdm": {
"num_subcarriers": 64,
"cyclic_prefix_length": 20,
"taper_length": 8,
"left_band": 4,
"right_band": 13
},
"mod_scheme": "V29",
"checksum_scheme": "crc8",
"inner_fec_scheme": "v27",
"outer_fec_scheme": "none",
"frame_length": 550,
"modulation": {
"center_frequency": 18500,
"gain": 0.1
},
"interpolation": {
"shape": "kaiser",
"samples_per_symbol": 7,
"symbol_delay": 4,
"excess_bandwidth": 0.33
},
"encoder_filters": {
"dc_filter_alpha": 0.01
},
"resampler": {
"delay": 13,
"bandwidth": 0.45,
"attenuation": 60,
"filter_bank_size": 64
}
},
"ultrasonic-whisper": {
"mod_scheme": "gmsk",
"checksum_scheme": "crc32",
"inner_fec_scheme": "v27",
"outer_fec_scheme": "none",
"frame_length": 16,
"modulation": {
"center_frequency": 19500,
"gain": 0.1
},
"interpolation": {
"shape": "rrcos",
"samples_per_symbol": 30,
"symbol_delay": 4,
"excess_bandwidth": 0.35
},
"encoder_filters": {
"dc_filter_alpha": 0.01
},
"resampler": {
"delay": 13,
"bandwidth": 0.45,
"attenuation": 60,
"filter_bank_size": 64
}
},
"ultrasonic-experimental": {
"mod_scheme": "bpsk",
"checksum_scheme": "crc32",
"inner_fec_scheme": "rs8",
"outer_fec_scheme": "v29",
"frame_length": 100,
"modulation": {
"center_frequency": 19000,
"gain": 0.2
},
"interpolation": {
"shape": "kaiser",
"samples_per_symbol": 10,
"symbol_delay": 4,
"excess_bandwidth": 0.31
},
"encoder_filters": {
"dc_filter_alpha": 0.01
},
"resampler": {
"delay": 13,
"bandwidth": 0.45,
"attenuation": 60,
"filter_bank_size": 64
},
"header": {
"checksum_scheme": "crc32",
"inner_fec_scheme": "secded7264",
"outer_fec_scheme": "v29",
"mod_scheme": "bpsk"
}
}
}
246 changes: 246 additions & 0 deletions quiet/quiet.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@


import time
import os
import json
from ctypes import c_char_p, c_void_p, c_float, c_size_t, c_ssize_t, c_uint, c_uint8, POINTER, cdll
from numpy.ctypeslib import ndpointer
import numpy


PROFILES = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'quiet-profiles.json')

c_float_p = POINTER(c_float)


class Quiet(object):
# for lazy loading
lib = None

@staticmethod
def load_lib():
lib_name = 'libquiet.so'
lib_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), lib_name)

if os.path.isfile(lib_path):
lib = cdll.LoadLibrary(lib_path)
else:
lib = cdll.LoadLibrary(lib_name)

# quiet_encoder_options *quiet_encoder_profile_filename(const char *fname, const char *profilename)
lib.quiet_encoder_profile_filename.argtypes = [c_char_p, c_char_p]
lib.quiet_encoder_profile_filename.restype = c_void_p

# quiet_decoder_options *quiet_decoder_profile_filename(const char *fname, const char *profilename)
lib.quiet_decoder_profile_filename.argtypes = [c_char_p, c_char_p]
lib.quiet_decoder_profile_filename.restype = c_void_p

# quiet_encoder *quiet_encoder_create(const quiet_encoder_options *opt, float sample_rate)
lib.quiet_encoder_create.argtypes = [c_void_p, c_float]
lib.quiet_encoder_create.restype = c_void_p

# ssize_t quiet_encoder_send(quiet_encoder *e, const void *buf, size_t len)
lib.quiet_encoder_send.argtypes = [c_void_p, c_char_p, c_size_t]
lib.quiet_encoder_send.restype = c_ssize_t

# void quiet_encoder_set_blocking(quiet_encoder *e, time_t sec, long nano)
# lib.quiet_encoder_set_blocking.argtypes = [c_void_p, c_uint, c_long]
# lib.quiet_encoder_set_blocking.restype = None

# void quiet_encoder_set_nonblocking(quiet_encoder *e)

# size_t quiet_encoder_clamp_frame_len(quiet_encoder *e, size_t sample_len)
lib.quiet_encoder_clamp_frame_len.argtypes = [c_void_p, c_size_t]
lib.quiet_encoder_clamp_frame_len.restype = c_size_t

# size_t quiet_encoder_get_frame_len(const quiet_encoder *e)
lib.quiet_encoder_get_frame_len.argtypes = [c_void_p]
lib.quiet_encoder_get_frame_len.restype = c_size_t

# ssize_t quiet_encoder_emit(quiet_encoder *e, quiet_sample_t *samplebuf, size_t samplebuf_len)
lib.quiet_encoder_emit.argtypes = [
c_void_p, ndpointer(c_float, flags="C_CONTIGUOUS"), c_size_t]
lib.quiet_encoder_emit.restype = c_ssize_t

# void quiet_encoder_close(quiet_encoder *e)
lib.quiet_encoder_close.argtypes = [c_void_p]
lib.quiet_encoder_close.restype = None

# void quiet_encoder_destroy(quiet_encoder *e)
lib.quiet_encoder_destroy.argtypes = [c_void_p]
lib.quiet_encoder_destroy.restype = None

# quiet_decoder *quiet_decoder_create(const quiet_decoder_options *opt, float sample_rate)
lib.quiet_decoder_create.argtypes = [c_void_p, c_float]
lib.quiet_decoder_create.restype = c_void_p

# ssize_t quiet_decoder_recv(quiet_decoder *d, uint8_t *data, size_t len)
lib.quiet_decoder_recv.argtypes = [
c_void_p, ndpointer(c_uint8, flags="C_CONTIGUOUS"), c_size_t]
lib.quiet_decoder_recv.restype = c_ssize_t

# void quiet_decoder_set_nonblocking(quiet_decoder *d)
# lib.quiet_decoder_set_nonblocking.argtypes = [c_void_p]
# lib.quiet_decoder_set_nonblocking.restype = None

# void quiet_decoder_consume(quiet_decoder *d, const quiet_sample_t *samplebuf, size_t sample_len)
lib.quiet_decoder_consume.argtypes = [c_void_p, c_void_p, c_size_t]
lib.quiet_decoder_consume.restype = None

# bool quiet_decoder_frame_in_progress(quiet_decoder *d)

# void quiet_decoder_flush(quiet_decoder *d)
lib.quiet_decoder_flush.argtypes = [c_void_p]
lib.quiet_decoder_flush.restype = None

# void quiet_decoder_close(quiet_decoder *d)
lib.quiet_decoder_close.argtypes = [c_void_p]
lib.quiet_decoder_close.restype = None

# unsigned int quiet_decoder_checksum_fails(const quiet_decoder *d)
lib.quiet_decoder_checksum_fails.argtypes = [c_void_p]
lib.quiet_decoder_checksum_fails.restype = c_uint

# void quiet_decoder_enable_stats(quiet_decoder *d)

# void quiet_decoder_disable_stats(quiet_decoder *d)

# void quiet_decoder_set_stats_blocking(quiet_decoder *d, time_t sec, long nano)

# void quiet_decoder_set_stats_nonblocking(quiet_decoder *d)

# void quiet_decoder_destroy(quiet_decoder *d)
lib.quiet_decoder_destroy.argtypes = [c_void_p]
lib.quiet_decoder_destroy.restype = None

return lib


class Decoder(object):
def __init__(self, sample_rate=44100., profile_name='audible', profiles=PROFILES, max_frame=128):
if not Quiet.lib:
Quiet.lib = Quiet.load_lib()

self._decoder_options = Quiet.lib.quiet_decoder_profile_filename(
profiles.encode('utf-8'), profile_name.encode('utf-8'))
self._decoder = Quiet.lib.quiet_decoder_create(
self._decoder_options, sample_rate)

self.max_frame = max_frame

def __del__(self):
Quiet.lib.quiet_decoder_destroy(self._decoder)

def decode(self, data, flush=False):
Quiet.lib.quiet_decoder_consume(
self._decoder, data.ctypes.data_as(c_void_p), len(data))

if flush:
Quiet.lib.quiet_decoder_flush(self._decoder)

buf = numpy.empty(self.max_frame, dtype='uint8')
got = Quiet.lib.quiet_decoder_recv(self._decoder, buf, len(buf))

if got > 0:
return buf[:got]

def flush(self):
Quiet.lib.quiet_decoder_flush(self._decoder)

def __enter__(self):
return self

def __exit__(self, type, value, traceback):
pass


class Encoder(object):
def __init__(self, sample_rate=44100., profile_name='audible', profiles=PROFILES):
if not Quiet.lib:
Quiet.lib = Quiet.load_lib()

self._encoder_options = Quiet.lib.quiet_encoder_profile_filename(
profiles.encode('utf-8'), profile_name.encode('utf-8'))
self._encoder = Quiet.lib.quiet_encoder_create(
self._encoder_options, sample_rate)

def __del__(self):
Quiet.lib.quiet_encoder_destroy(self._encoder)

def encode(self, data, chunk_size=1024):
Quiet.lib.quiet_encoder_send(
self._encoder, data.encode('utf-8'), len(data))

buf = numpy.empty(chunk_size, dtype='float32')
while True:
got = Quiet.lib.quiet_encoder_emit(self._encoder, buf, len(buf))

if got < 0:
return
elif got < chunk_size:
yield buf
return
else:
yield buf

def __enter__(self):
return self

def __exit__(self, type, value, traceback):
pass


def decode():
import pyaudio
import sys
if sys.version_info[0] < 3:
import Queue as queue
else:
import queue

FORMAT = pyaudio.paFloat32
CHANNELS = 1
RATE = 44100
CHUNK = 16384 # int(RATE / 100)

p = pyaudio.PyAudio()
q = queue.Queue()

def callback(in_data, frame_count, time_info, status):
q.put(in_data)
return (None, pyaudio.paContinue)

stream = p.open(format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
frames_per_buffer=CHUNK,
stream_callback=callback)

count = 0
with Decoder(profile_name='ultrasonic-experimental') as decoder:
while True:
try:
audio = q.get()
audio = numpy.fromstring(audio, dtype='float32')
# audio = audio[::CHANNELS]
code = decoder.decode(audio)
if code is not None:
count += 1
print(code.tostring().decode('utf-8', 'ignore'))
except KeyboardInterrupt:
break


def test():
encoder = Encoder()
decoder = Decoder()

for chunk in encoder.encode('hello, world'):
message = decoder.decode(chunk)
if message is not None:
print(message)


if __name__ == '__main__':
decode()
25 changes: 18 additions & 7 deletions scripts/libs.sh
Original file line number Diff line number Diff line change
@@ -23,19 +23,19 @@ rm -f "$SYSROOT/usr/lib/libfec.dylib"

mkdir -p "$BUILDPATH/liquid-dsp"
cd "$BUILDPATH/liquid-dsp"
cmake -DCMAKE_BUILD_TYPE=Release "$SRCPATH/liquid-dsp" -DCMAKE_INSTALL_PREFIX="$SYSROOT/usr" -DCMAKE_PREFIX_PATH="$SYSROOT" -DCMAKE_SHARED_LINKER_FLAGS="-L$SYSROOT/usr/lib" -DLIQUID_BUILD_EXAMPLES="off" -DLIQUID_BUILD_SANDBOX="off" && make liquid-static && make install
cmake -DCMAKE_BUILD_TYPE=Release "$SRCPATH/liquid-dsp" -DCMAKE_C_FLAGS="-fPIC" -DLIQUID_FFTOVERRIDE=ON -DCMAKE_INSTALL_PREFIX="$SYSROOT/usr" -DCMAKE_PREFIX_PATH="$SYSROOT" -DCMAKE_SHARED_LINKER_FLAGS="-L$SYSROOT/usr/lib" -DLIQUID_BUILD_EXAMPLES="off" -DLIQUID_BUILD_SANDBOX="off" && make liquid-static && make install

mkdir -p "$BUILDPATH/jansson"
cd "$BUILDPATH/jansson"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="$SYSROOT/usr" -DJANSSON_BUILD_SHARED_LIBS=off -DJANSSON_WITHOUT_TESTS=on -DJANSSON_EXAMPLES=off -DJANSSON_BUILD_DOCS=off "$SRCPATH/jansson" && make && make install

mkdir -p "$BUILDPATH/portaudio"
cd "$BUILDPATH/portaudio"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="$SYSROOT/usr" -DCMAKE_PREFIX_PATH="$SYSROOT" "$SRCPATH/portaudio" && make && make install && cp libportaudio_static.a "$SYSROOT/usr/lib/libportaudio.a"
# mkdir -p "$BUILDPATH/portaudio"
# cd "$BUILDPATH/portaudio"
# cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS="-fPIC" -DCMAKE_INSTALL_PREFIX="$SYSROOT/usr" -DCMAKE_PREFIX_PATH="$SYSROOT" "$SRCPATH/portaudio" && make && make install && cp libportaudio_static.a "$SYSROOT/usr/lib/libportaudio.a"

mkdir -p "$BUILDPATH/libquiet"
cd "$BUILDPATH/libquiet"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="$SYSROOT/usr" -DCMAKE_PREFIX_PATH="$SYSROOT" "$SRCPATH/libquiet" && make
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS="-fPIC" -DCMAKE_INSTALL_PREFIX="$SYSROOT/usr" -DCMAKE_PREFIX_PATH="$SYSROOT" "$SRCPATH/libquiet" && make
CTEST_OUTPUT_ON_FAILURE=1 make check
make install

@@ -44,15 +44,26 @@ mkdir -p "$INCLUDEPATH"
cp "$SYSROOTPATH/usr/lib/libfec.a" "$LIBPATH"
cp "$SYSROOTPATH/usr/lib/libliquid.a" "$LIBPATH"
cp "$SYSROOTPATH/usr/lib/libjansson.a" "$LIBPATH"
cp "$SYSROOTPATH/usr/lib/libportaudio.a" "$LIBPATH"
# cp "$SYSROOTPATH/usr/lib/libportaudio.a" "$LIBPATH"
cp "$SYSROOTPATH/usr/lib/libquiet.a" "$LIBPATH"
cp "$SYSROOTPATH/usr/include/fec.h" "$INCLUDEPATH"
cp -R "$SYSROOTPATH/usr/include/liquid" "$INCLUDEPATH"
cp "$SYSROOTPATH/usr/include/jansson.h" "$INCLUDEPATH"
cp "$SYSROOTPATH/usr/include/jansson_config.h" "$INCLUDEPATH"
cp "$SYSROOTPATH/usr/include/portaudio.h" "$INCLUDEPATH"
# cp "$SYSROOTPATH/usr/include/portaudio.h" "$INCLUDEPATH"
cp "$SYSROOTPATH/usr/include/quiet.h" "$INCLUDEPATH"

if [ "$(uname)" == "Darwin" ]; then
gcc -shared -o $ABSPATH/quiet/libquiet.so \
-Wl,-all_load $LIBPATH/libquiet.a $LIBPATH/libliquid.a $LIBPATH/libfec.a \
$LIBPATH/libjansson.a -Wl,-noall_load
else
gcc -shared -o $ABSPATH/quiet/libquiet.so \
-Wl,--whole-archive $LIBPATH/libquiet.a -Wl,--no-whole-archive $LIBPATH/libliquid.a $LIBPATH/libfec.a \
$LIBPATH/libjansson.a
fi


echo
echo "Build complete. Built libraries are in $LIBPATH"
echo "and includes in $INCLUDEPATH."
50 changes: 36 additions & 14 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,43 @@
from distutils.core import setup, Extension

# if osx

from setuptools import setup
from setuptools.command.build_py import build_py
import ctypes.util
import subprocess
import os
os.environ['LDFLAGS'] = '-framework Carbon -framework AudioUnit -framework AudioToolbox -framework CoreAudio'

module = Extension('cquiet',
sources=['quietmodule.c'],
extra_compile_args=['-std=c99', '-Iinclude'],
extra_link_args=['-Llib', 'libquiet.a', 'libportaudio.a', 'libjansson.a', 'libliquid.a', 'libfec.a'],
)

setup(name='quiet',
with open('README.md') as f:
long_description = f.read()


class BuildPyCommand(build_py):
"""Custom build command."""

def run(self):
# check if libquiet.so is at system lib paths
if not ctypes.util.find_library('quiet'):
libquiet = os.path.join(os.path.dirname(
__file__), 'quiet', 'libquiet.so')
if not os.path.isfile(libquiet):
# build libquiet.so
subprocess.check_call(['bash', 'scripts/libs.sh'])

build_py.run(self)


setup(name='quiet.py',
version='0.1',
description='Quiet Modem',
author='Brian Armstrong',
description='Quiet Modem, to transmit data with sound',
long_description=long_description,
long_description_content_type='text/markdown',
author='Brian Armstrong, Yihui Xiong',
author_email='brian.armstrong.ece+pypi@gmail.com',
url='https://github.com/quiet',
ext_modules=[module],
url='https://github.com/quiet/quiet.py',
cmdclass={
'build_py': BuildPyCommand,
},
packages=['quiet'],
)
include_package_data=True,
install_requires=['numpy'],
zip_safe=False)