Project import
diff --git a/sonic/Android.mk b/sonic/Android.mk
new file mode 100644
index 0000000..8fb625b
--- /dev/null
+++ b/sonic/Android.mk
@@ -0,0 +1,13 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := sonic.c
+
+LOCAL_CFLAGS += -Wno-sequence-point -Wno-extra
+
+LOCAL_CPPFLAGS += -std=c++98
+
+LOCAL_MODULE:= libsonic
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/sonic/COPYING b/sonic/COPYING
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/sonic/COPYING
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/sonic/MODULE_LICENSE_APACHE2 b/sonic/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/sonic/MODULE_LICENSE_APACHE2
diff --git a/sonic/Main.java b/sonic/Main.java
new file mode 100644
index 0000000..31a68d4
--- /dev/null
+++ b/sonic/Main.java
@@ -0,0 +1,84 @@
+/* This file was written by Bill Cox in 2011, and is licensed under the Apache
+   2.0 license. */
+
+package sonic;
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.DataLine;
+import javax.sound.sampled.LineUnavailableException;
+import javax.sound.sampled.SourceDataLine;
+import javax.sound.sampled.UnsupportedAudioFileException;
+
+public class Main {
+
+    // Run sonic.
+    private static void runSonic(
+        AudioInputStream audioStream,
+        SourceDataLine line,
+        float speed,
+        float pitch,
+        float rate,
+        float volume,
+        boolean emulateChordPitch,
+        int quality,
+        int sampleRate,
+        int numChannels) throws IOException
+    {
+        Sonic sonic = new Sonic(sampleRate, numChannels);
+        int bufferSize = line.getBufferSize();
+        byte inBuffer[] = new byte[bufferSize];
+        byte outBuffer[] = new byte[bufferSize];
+        int numRead, numWritten;
+
+        sonic.setSpeed(speed);
+        sonic.setPitch(pitch);
+        sonic.setRate(rate);
+        sonic.setVolume(volume);
+        sonic.setChordPitch(emulateChordPitch);
+        sonic.setQuality(quality);
+        do {
+            numRead = audioStream.read(inBuffer, 0, bufferSize);
+            if(numRead <= 0) {
+                sonic.flushStream();
+            } else {
+                sonic.writeBytesToStream(inBuffer, numRead);
+            }
+            do {
+                numWritten = sonic.readBytesFromStream(outBuffer, bufferSize);
+                if(numWritten > 0) {
+                    line.write(outBuffer, 0, numWritten);
+                }
+            } while(numWritten > 0);
+        } while(numRead > 0);
+    }
+
+    public static void main(
+    	String[] argv) throws UnsupportedAudioFileException, IOException, LineUnavailableException
+    {
+        float speed = 2.0f;
+        float pitch = 1.0f;
+        float rate = 1.0f;
+        float volume = 1.0f;
+        boolean emulateChordPitch = false;
+        int quality = 0;
+        
+        AudioInputStream stream = AudioSystem.getAudioInputStream(new File("talking.wav"));
+        AudioFormat format = stream.getFormat();
+        int sampleRate = (int)format.getSampleRate();
+        int numChannels = format.getChannels(); 
+        SourceDataLine.Info info = new DataLine.Info(SourceDataLine.class, format,
+        	((int)stream.getFrameLength()*format.getFrameSize()));
+        SourceDataLine line = (SourceDataLine)AudioSystem.getLine(info);
+        line.open(stream.getFormat());
+        line.start();
+        runSonic(stream, line, speed, pitch, rate, volume, emulateChordPitch, quality,
+            sampleRate, numChannels);
+        line.drain();
+        line.stop();
+    }
+}
diff --git a/sonic/Makefile b/sonic/Makefile
new file mode 100644
index 0000000..696e211
--- /dev/null
+++ b/sonic/Makefile
@@ -0,0 +1,60 @@
+# This file was written by Bill Cox in 2010, and is licensed under the Apache
+# 2.0 license.
+#
+# Note that -pthread is only included so that older Linux builds will be thread
+# safe.  We call malloc, and older Linux versions only linked in the thread-safe
+# malloc if -pthread is specified.
+
+SONAME=soname
+UNAME := $(shell uname)
+ifeq ($(UNAME), Darwin)
+  SONAME=install_name
+endif
+#CFLAGS=-Wall -g -ansi -fPIC -pthread
+CFLAGS=-Wall -O3 -ansi -fPIC -pthread
+LIB_TAG=0.2.0
+CC=gcc
+PREFIX=/usr
+LIBDIR=$(PREFIX)/lib
+
+all: sonic libsonic.so.$(LIB_TAG) libsonic.a
+
+sonic: wave.o main.o libsonic.so.$(LIB_TAG)
+	$(CC) $(CFLAGS) -o sonic wave.o main.o libsonic.so.$(LIB_TAG)
+
+sonic.o: sonic.c sonic.h
+	$(CC) $(CFLAGS) -c sonic.c
+
+wave.o: wave.c wave.h
+	$(CC) $(CFLAGS) -c wave.c
+
+main.o: main.c sonic.h wave.h
+	$(CC) $(CFLAGS) -c main.c
+
+libsonic.so.$(LIB_TAG): sonic.o
+	$(CC) $(CFLAGS) -shared -Wl,-$(SONAME),libsonic.so.0 sonic.o -o libsonic.so.$(LIB_TAG)
+	ln -sf libsonic.so.$(LIB_TAG) libsonic.so
+	ln -sf libsonic.so.$(LIB_TAG) libsonic.so.0
+
+libsonic.a: sonic.o
+	$(AR) cqs libsonic.a sonic.o
+
+install: sonic libsonic.so.$(LIB_TAG) sonic.h
+	install -d $(DESTDIR)$(PREFIX)/bin $(DESTDIR)$(PREFIX)/include $(DESTDIR)$(PREFIX)/lib
+	install sonic $(DESTDIR)$(PREFIX)/bin
+	install sonic.h $(DESTDIR)$(PREFIX)/include
+	install libsonic.so.$(LIB_TAG) $(DESTDIR)$(PREFIX)/lib
+	install libsonic.a $(DESTDIR)$(LIBDIR)
+	ln -sf libsonic.so.$(LIB_TAG) $(DESTDIR)$(PREFIX)/lib/libsonic.so
+	ln -sf libsonic.so.$(LIB_TAG) $(DESTDIR)$(PREFIX)/lib/libsonic.so.0
+
+uninstall: 
+	rm -f $(DESTDIR)$(PREFIX)/bin/sonic 
+	rm -f $(DESTDIR)$(PREFIX)/include/sonic.h
+	rm -f $(DESTDIR)$(PREFIX)/lib/libsonic.so.$(LIB_TAG)
+	rm -f $(DESTDIR)$(PREFIX)/lib/libsonic.so
+	rm -f $(DESTDIR)$(PREFIX)/lib/libsonic.so.0
+	rm -f $(DESTDIR)$(LIBDIR)/libsonic.a
+
+clean:
+	rm -f *.o sonic libsonic.so* libsonic.a
diff --git a/sonic/NOTICE b/sonic/NOTICE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/sonic/NOTICE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/sonic/README b/sonic/README
new file mode 100644
index 0000000..9a0b9fe
--- /dev/null
+++ b/sonic/README
@@ -0,0 +1,36 @@
+Sonic is a simple algorithm for speeding up or slowing down speech.  However,
+it's optimized for speed ups of over 2X, unlike previous algorithms for changing
+speech rate.  The Sonic library is a very simple ANSI C library that is designed
+to easily be integrated into streaming voice applications, like TTS back ends.
+
+The primary motivation behind Sonic is to enable the blind and visually impaired
+to improve their productivity with open source speech engines, like espeak.
+Sonic can also be used by the sighted.  For example, Sonic can improve the
+experience of listening to an audio book on an Android phone.
+
+A native Java port of Sonic is in Sonic.java.  Main.java is a simple example of
+how to use Sonic.java.  To play with it, you'll need a "talking.wav" file in the
+current directory, and you'll want to change the speed, pitch or other
+parameters manually in Main.java, in the main method.
+
+Sonic is Copyright 2010, 2011, Bill Cox, all rights reserved.  It is released
+under the Apache 2.0 license, to promote usage as widely as possible.
+
+Performance test:
+
+I sped up a 751958176 byte wav file with sonic (a 9 hour, 28 minute mono audio
+file encoded at 16-bit 11.KHz), but with the output writing disabled.  The
+reported time, running Ubuntu 11.04 on my HP Pavilion dm4 laptop was:
+
+real    0m50.839s
+user    0m47.370s
+sys     0m0.620s
+
+The Java version is not much slower.  It reported:
+
+real    0m52.043s
+user    0m51.190s
+sys     0m0.310s
+
+Author: Bill Cox
+email: waywardgeek@gmail.com
diff --git a/sonic/Sonic.java b/sonic/Sonic.java
new file mode 100644
index 0000000..a3394d7
--- /dev/null
+++ b/sonic/Sonic.java
@@ -0,0 +1,996 @@
+/* Sonic library
+   Copyright 2010, 2011
+   Bill Cox
+   This file is part of the Sonic Library.
+
+   This file is licensed under the Apache 2.0 license.
+*/
+
+package sonic;
+
+public class Sonic {
+
+	private static final int SONIC_MIN_PITCH = 65;
+	private static final int SONIC_MAX_PITCH = 400;
+	/* This is used to down-sample some inputs to improve speed */
+	private static final int SONIC_AMDF_FREQ = 4000;
+
+    private short inputBuffer[];
+    private short outputBuffer[];
+    private short pitchBuffer[];
+    private short downSampleBuffer[];
+    private float speed;
+    private float volume;
+    private float pitch;
+    private float rate;
+    private int oldRatePosition;
+    private int newRatePosition;
+    private boolean useChordPitch;
+    private int quality;
+    private int numChannels;
+    private int inputBufferSize;
+    private int pitchBufferSize;
+    private int outputBufferSize;
+    private int numInputSamples;
+    private int numOutputSamples;
+    private int numPitchSamples;
+    private int minPeriod;
+    private int maxPeriod;
+    private int maxRequired;
+    private int remainingInputToCopy;
+    private int sampleRate;
+    private int prevPeriod;
+    private int prevMinDiff;
+
+    // Resize the array.
+    private short[] resize(
+    	short[] oldArray,
+    	int newLength)
+    {
+    	newLength *= numChannels;
+        short[]	newArray = new short[newLength];
+        int length = oldArray.length <= newLength? oldArray.length : newLength;
+        
+        
+        for(int x = 0; x < length; x++) {
+            newArray[x] = oldArray[x];
+        }
+        return newArray;
+    }
+
+    // Move samples from one array to another.  May move samples down within an array, but not up.
+    private void move(
+    	short dest[],
+    	int destPos,
+    	short source[],
+    	int sourcePos,
+    	int numSamples)
+    {
+    	for(int xSample = 0; xSample < numSamples*numChannels; xSample++) {
+    	    dest[destPos*numChannels + xSample] = source[sourcePos*numChannels + xSample];
+    	}
+    }
+
+    // Scale the samples by the factor.
+    private void scaleSamples(
+        short samples[],
+        int position,
+        int numSamples,
+        float volume)
+    {
+        int fixedPointVolume = (int)(volume*4096.0f);
+        int start = position*numChannels;
+        int stop = start + numSamples*numChannels;
+
+        for(int xSample = start; xSample < stop; xSample++) {
+            int value = (samples[xSample]*fixedPointVolume) >> 12;
+            if(value > 32767) {
+                value = 32767;
+            } else if(value < -32767) {
+                value = -32767;
+            }
+            samples[xSample] = (short)value;
+        }
+    }
+
+    // Get the speed of the stream.
+    public float getSpeed()
+    {
+        return speed;
+    }
+
+    // Set the speed of the stream.
+    public void setSpeed(
+        float speed)
+    {
+        this.speed = speed;
+    }
+
+    // Get the pitch of the stream.
+    public float getPitch()
+    {
+        return pitch;
+    }
+
+    // Set the pitch of the stream.
+    public void setPitch(
+        float pitch)
+    {
+        this.pitch = pitch;
+    }
+
+    // Get the rate of the stream.
+    public float getRate()
+    {
+        return rate;
+    }
+
+    // Set the playback rate of the stream. This scales pitch and speed at the same time.
+    public void setRate(
+        float rate)
+    {
+        this.rate = rate;
+        this.oldRatePosition = 0;
+        this.newRatePosition = 0;
+    }
+
+    // Get the vocal chord pitch setting.
+    public boolean getChordPitch()
+    {
+        return useChordPitch;
+    }
+
+    // Set the vocal chord mode for pitch computation.  Default is off.
+    public void setChordPitch(
+        boolean useChordPitch)
+    {
+        this.useChordPitch = useChordPitch;
+    }
+
+    // Get the quality setting.
+    public int getQuality()
+    {
+        return quality;
+    }
+
+    // Set the "quality".  Default 0 is virtually as good as 1, but very much faster.
+    public void setQuality(
+        int quality)
+    {
+        this.quality = quality;
+    }
+
+    // Get the scaling factor of the stream.
+    public float getVolume()
+    {
+        return volume;
+    }
+
+    // Set the scaling factor of the stream.
+    public void setVolume(
+        float volume)
+    {
+        this.volume = volume;
+    }
+
+    // Allocate stream buffers.
+    private void allocateStreamBuffers(
+        int sampleRate,
+        int numChannels)
+    {
+        minPeriod = sampleRate/SONIC_MAX_PITCH;
+        maxPeriod = sampleRate/SONIC_MIN_PITCH;
+        maxRequired = 2*maxPeriod;
+        inputBufferSize = maxRequired;
+        inputBuffer = new short[maxRequired*numChannels];
+        outputBufferSize = maxRequired;
+        outputBuffer = new short[maxRequired*numChannels];
+        pitchBufferSize = maxRequired;
+        pitchBuffer = new short[maxRequired*numChannels];
+        downSampleBuffer = new short[maxRequired];
+        this.sampleRate = sampleRate;
+        this.numChannels = numChannels;
+        oldRatePosition = 0;
+        newRatePosition = 0;
+        prevPeriod = 0;
+    }
+
+    // Create a sonic stream.
+    public Sonic(
+        int sampleRate,
+        int numChannels)
+    {
+        allocateStreamBuffers(sampleRate, numChannels);
+        speed = 1.0f;
+        pitch = 1.0f;
+        volume = 1.0f;
+        rate = 1.0f;
+        oldRatePosition = 0;
+        newRatePosition = 0;
+        useChordPitch = false;
+        quality = 0;
+    }
+
+    // Get the sample rate of the stream.
+    public int getSampleRate()
+    {
+        return sampleRate;
+    }
+
+    // Set the sample rate of the stream.  This will cause samples buffered in the stream to be lost.
+    public void setSampleRate(
+        int sampleRate)
+    {
+        allocateStreamBuffers(sampleRate, numChannels);
+    }
+
+    // Get the number of channels.
+    public int getNumChannels()
+    {
+        return numChannels;
+    }
+
+    // Set the num channels of the stream.  This will cause samples buffered in the stream to be lost.
+    public void setNumChannels(
+        int numChannels)
+    {
+        allocateStreamBuffers(sampleRate, numChannels);
+    }
+
+    // Enlarge the output buffer if needed.
+    private void enlargeOutputBufferIfNeeded(
+        int numSamples)
+    {
+        if(numOutputSamples + numSamples > outputBufferSize) {
+            outputBufferSize += (outputBufferSize >> 1) + numSamples;
+            outputBuffer = resize(outputBuffer, outputBufferSize);
+        }
+    }
+
+    // Enlarge the input buffer if needed.
+    private void enlargeInputBufferIfNeeded(
+        int numSamples)
+    {
+        if(numInputSamples + numSamples > inputBufferSize) {
+            inputBufferSize += (inputBufferSize >> 1) + numSamples;
+            inputBuffer = resize(inputBuffer, inputBufferSize);
+        }
+    }
+
+    // Add the input samples to the input buffer.
+    private void addFloatSamplesToInputBuffer(
+        float samples[],
+        int numSamples)
+    {
+        if(numSamples == 0) {
+            return;
+        }
+        enlargeInputBufferIfNeeded(numSamples);
+        int xBuffer = numInputSamples*numChannels;
+        for(int xSample = 0; xSample < numSamples*numChannels; xSample++) {
+            inputBuffer[xBuffer++] = (short)(samples[xSample]*32767.0f);
+        }
+        numInputSamples += numSamples;
+    }
+
+    // Add the input samples to the input buffer.
+    private void addShortSamplesToInputBuffer(
+        short samples[],
+        int numSamples)
+    {
+        if(numSamples == 0) {
+            return;
+        }
+        enlargeInputBufferIfNeeded(numSamples);
+        move(inputBuffer, numInputSamples, samples, 0, numSamples);
+        numInputSamples += numSamples;
+    }
+
+    // Add the input samples to the input buffer.
+    private void addUnsignedByteSamplesToInputBuffer(
+        byte samples[],
+        int numSamples)
+    {
+        short sample;
+
+        enlargeInputBufferIfNeeded(numSamples);
+        int xBuffer = numInputSamples*numChannels;
+        for(int xSample = 0; xSample < numSamples*numChannels; xSample++) {
+        	sample = (short)((samples[xSample] & 0xff) - 128); // Convert from unsigned to signed
+            inputBuffer[xBuffer++] = (short) (sample << 8);
+        }
+        numInputSamples += numSamples;
+    }
+
+    // Add the input samples to the input buffer.  They must be 16-bit little-endian encoded in a byte array.
+    private void addBytesToInputBuffer(
+        byte inBuffer[],
+        int numBytes)
+    {
+    	int numSamples = numBytes/(2*numChannels);
+        short sample;
+
+        enlargeInputBufferIfNeeded(numSamples);
+        int xBuffer = numInputSamples*numChannels;
+        for(int xByte = 0; xByte + 1 < numBytes; xByte += 2) {
+        	sample = (short)((inBuffer[xByte] & 0xff) | (inBuffer[xByte + 1] << 8));
+            inputBuffer[xBuffer++] = sample;
+        }
+        numInputSamples += numSamples;
+    }
+
+    // Remove input samples that we have already processed.
+    private void removeInputSamples(
+        int position)
+    {
+        int remainingSamples = numInputSamples - position;
+
+        move(inputBuffer, 0, inputBuffer, position, remainingSamples);
+        numInputSamples = remainingSamples;
+    }
+
+    // Just copy from the array to the output buffer
+    private void copyToOutput(
+        short samples[],
+        int position,
+        int numSamples)
+    {
+        enlargeOutputBufferIfNeeded(numSamples);
+        move(outputBuffer, numOutputSamples, samples, position, numSamples);
+        numOutputSamples += numSamples;
+    }
+
+    // Just copy from the input buffer to the output buffer.  Return num samples copied.
+    private int copyInputToOutput(
+        int position)
+    {
+        int numSamples = remainingInputToCopy;
+
+        if(numSamples > maxRequired) {
+            numSamples = maxRequired;
+        }
+        copyToOutput(inputBuffer, position, numSamples);
+        remainingInputToCopy -= numSamples;
+        return numSamples;
+    }
+
+    // Read data out of the stream.  Sometimes no data will be available, and zero
+    // is returned, which is not an error condition.
+    public int readFloatFromStream(
+        float samples[],
+        int maxSamples)
+    {
+        int numSamples = numOutputSamples;
+        int remainingSamples = 0;
+
+        if(numSamples == 0) {
+            return 0;
+        }
+        if(numSamples > maxSamples) {
+            remainingSamples = numSamples - maxSamples;
+            numSamples = maxSamples;
+        }
+        for(int xSample = 0; xSample < numSamples*numChannels; xSample++) {
+            samples[xSample++] = (outputBuffer[xSample])/32767.0f;
+        }
+        move(outputBuffer, 0, outputBuffer, numSamples, remainingSamples);
+        numOutputSamples = remainingSamples;
+        return numSamples;
+    }
+
+    // Read short data out of the stream.  Sometimes no data will be available, and zero
+    // is returned, which is not an error condition.
+    public int readShortFromStream(
+        short samples[],
+        int maxSamples)
+    {
+        int numSamples = numOutputSamples;
+        int remainingSamples = 0;
+
+        if(numSamples == 0) {
+            return 0;
+        }
+        if(numSamples > maxSamples) {
+            remainingSamples = numSamples - maxSamples;
+            numSamples = maxSamples;
+        }
+        move(samples, 0, outputBuffer, 0, numSamples);
+        move(outputBuffer, 0, outputBuffer, numSamples, remainingSamples);
+        numOutputSamples = remainingSamples;
+        return numSamples;
+    }
+
+    // Read unsigned byte data out of the stream.  Sometimes no data will be available, and zero
+    // is returned, which is not an error condition.
+    public int readUnsignedByteFromStream(
+        byte samples[],
+        int maxSamples)
+    {
+        int numSamples = numOutputSamples;
+        int remainingSamples = 0;
+
+        if(numSamples == 0) {
+            return 0;
+        }
+        if(numSamples > maxSamples) {
+            remainingSamples = numSamples - maxSamples;
+            numSamples = maxSamples;
+        }
+        for(int xSample = 0; xSample < numSamples*numChannels; xSample++) {
+        	samples[xSample] = (byte)((outputBuffer[xSample] >> 8) + 128);
+        }
+        move(outputBuffer, 0, outputBuffer, numSamples, remainingSamples);
+        numOutputSamples = remainingSamples;
+        return numSamples;
+    }
+
+    // Read unsigned byte data out of the stream.  Sometimes no data will be available, and zero
+    // is returned, which is not an error condition.
+    public int readBytesFromStream(
+        byte outBuffer[],
+        int maxBytes)
+    {
+    	int maxSamples = maxBytes/(2*numChannels);
+        int numSamples = numOutputSamples;
+        int remainingSamples = 0;
+
+        if(numSamples == 0 || maxSamples == 0) {
+            return 0;
+        }
+        if(numSamples > maxSamples) {
+            remainingSamples = numSamples - maxSamples;
+            numSamples = maxSamples;
+        }
+        for(int xSample = 0; xSample < numSamples*numChannels; xSample++) {
+        	short sample = outputBuffer[xSample];
+        	outBuffer[xSample << 1] = (byte)(sample & 0xff);
+        	outBuffer[(xSample << 1) + 1] = (byte)(sample >> 8);
+        }
+        move(outputBuffer, 0, outputBuffer, numSamples, remainingSamples);
+        numOutputSamples = remainingSamples;
+        return 2*numSamples*numChannels;
+    }
+
+    // Force the sonic stream to generate output using whatever data it currently
+    // has.  No extra delay will be added to the output, but flushing in the middle of
+    // words could introduce distortion.
+    public void flushStream()
+    {
+        int remainingSamples = numInputSamples;
+        float s = speed/pitch;
+        float r = rate*pitch;
+        int expectedOutputSamples = numOutputSamples + (int)((remainingSamples/s + numPitchSamples)/r + 0.5f);
+
+        // Add enough silence to flush both input and pitch buffers.
+        enlargeInputBufferIfNeeded(remainingSamples + 2*maxRequired);
+        for(int xSample = 0; xSample < 2*maxRequired*numChannels; xSample++) {
+            inputBuffer[remainingSamples*numChannels + xSample] = 0;
+        }
+        numInputSamples += 2*maxRequired;
+        writeShortToStream(null, 0);
+        // Throw away any extra samples we generated due to the silence we added.
+        if(numOutputSamples > expectedOutputSamples) {
+            numOutputSamples = expectedOutputSamples;
+        }
+        // Empty input and pitch buffers.
+        numInputSamples = 0;
+        remainingInputToCopy = 0;
+        numPitchSamples = 0;
+    }
+
+    // Return the number of samples in the output buffer
+    public int samplesAvailable()
+    {
+        return numOutputSamples;
+    }
+
+    // If skip is greater than one, average skip samples together and write them to
+    // the down-sample buffer.  If numChannels is greater than one, mix the channels
+    // together as we down sample.
+    private void downSampleInput(
+        short samples[],
+        int position,
+        int skip)
+    {
+        int numSamples = maxRequired/skip;
+        int samplesPerValue = numChannels*skip;
+        int value;
+
+        position *= numChannels;
+        for(int i = 0; i < numSamples; i++) {
+            value = 0;
+            for(int j = 0; j < samplesPerValue; j++) {
+                value += samples[position + i*samplesPerValue + j];
+            }
+            value /= samplesPerValue;
+            downSampleBuffer[i] = (short)value;
+        }
+    }
+
+    // Find the best frequency match in the range, and given a sample skip multiple.
+    // For now, just find the pitch of the first channel.  Note that retMinDiff and
+    // retMaxDiff are Int objects, which the caller will need to create with new.
+    private int findPitchPeriodInRange(
+        short samples[],
+        int position,
+        int minPeriod,
+        int maxPeriod,
+        Integer retMinDiff,
+        Integer retMaxDiff)
+    {
+        int bestPeriod = 0, worstPeriod = 255;
+        int minDiff = 1, maxDiff = 0;
+
+        position *= numChannels;
+        for(int period = minPeriod; period <= maxPeriod; period++) {
+            int diff = 0;
+            for(int i = 0; i < period; i++) {
+                short sVal = samples[position + i];
+                short pVal = samples[position + period + i];
+                diff += sVal >= pVal? sVal - pVal : pVal - sVal;
+            }
+            /* Note that the highest number of samples we add into diff will be less
+               than 256, since we skip samples.  Thus, diff is a 24 bit number, and
+               we can safely multiply by numSamples without overflow */
+            if(diff*bestPeriod < minDiff*period) {
+                minDiff = diff;
+                bestPeriod = period;
+            }
+            if(diff*worstPeriod > maxDiff*period) {
+                maxDiff = diff;
+                worstPeriod = period;
+            }
+        }
+        retMinDiff = minDiff/bestPeriod;
+        retMaxDiff = maxDiff/worstPeriod;
+        return bestPeriod;
+    }
+
+    // At abrupt ends of voiced words, we can have pitch periods that are better
+    // approximated by the previous pitch period estimate.  Try to detect this case.
+    private boolean prevPeriodBetter(
+        int period,
+        int minDiff,
+        int maxDiff,
+        boolean preferNewPeriod)
+    {
+        if(minDiff == 0 || prevPeriod == 0) {
+            return false;
+        }
+        if(preferNewPeriod) {
+            if(maxDiff > minDiff*3) {
+                // Got a reasonable match this period
+                return false;
+            }
+            if(minDiff*2 <= prevMinDiff*3) {
+                // Mismatch is not that much greater this period
+                return false;
+            }
+        } else {
+            if(minDiff <= prevMinDiff) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    // Find the pitch period.  This is a critical step, and we may have to try
+    // multiple ways to get a good answer.  This version uses AMDF.  To improve
+    // speed, we down sample by an integer factor get in the 11KHz range, and then
+    // do it again with a narrower frequency range without down sampling
+    private int findPitchPeriod(
+        short samples[],
+        int position,
+        boolean preferNewPeriod)
+    {
+        Integer minDiff = new Integer(0);
+        Integer maxDiff = new Integer(0);
+        int period, retPeriod;
+        int skip = 1;
+
+        if(sampleRate > SONIC_AMDF_FREQ && quality == 0) {
+            skip = sampleRate/SONIC_AMDF_FREQ;
+        }
+        if(numChannels == 1 && skip == 1) {
+            period = findPitchPeriodInRange(samples, position, minPeriod, maxPeriod, minDiff, maxDiff);
+        } else {
+            downSampleInput(samples, position, skip);
+            period = findPitchPeriodInRange(downSampleBuffer, 0, minPeriod/skip,
+                maxPeriod/skip, minDiff, maxDiff);
+            if(skip != 1) {
+                period *= skip;
+                int minP = period - (skip << 2);
+                int maxP = period + (skip << 2);
+                if(minP < minPeriod) {
+                    minP = minPeriod;
+                }
+                if(maxP > maxPeriod) {
+                    maxP = maxPeriod;
+                }
+                if(numChannels == 1) {
+                    period = findPitchPeriodInRange(samples, position, minP, maxP, minDiff, maxDiff);
+                } else {
+                    downSampleInput(samples, position, 1);
+                    period = findPitchPeriodInRange(downSampleBuffer, 0, minP, maxP, minDiff, maxDiff);
+                }
+            }
+        }
+        if(prevPeriodBetter(period, minDiff, maxDiff, preferNewPeriod)) {
+            retPeriod = prevPeriod;
+        } else {
+            retPeriod = period;
+        }
+        prevMinDiff = minDiff;
+        prevPeriod = period;
+        return retPeriod;
+    }
+
+    // Overlap two sound segments, ramp the volume of one down, while ramping the
+    // other one from zero up, and add them, storing the result at the output.
+    private void overlapAdd(
+        int numSamples,
+        int numChannels,
+        short out[],
+        int outPos,
+        short rampDown[],
+        int rampDownPos,
+        short rampUp[],
+        int rampUpPos)
+    {
+         for(int i = 0; i < numChannels; i++) {
+            int o = outPos*numChannels + i;
+            int u = rampUpPos*numChannels + i;
+            int d = rampDownPos*numChannels + i;
+            for(int t = 0; t < numSamples; t++) {
+                out[o] = (short)((rampDown[d]*(numSamples - t) + rampUp[u]*t)/numSamples);
+                o += numChannels;
+                d += numChannels;
+                u += numChannels;
+            }
+        }
+    }
+
+    // Overlap two sound segments, ramp the volume of one down, while ramping the
+    // other one from zero up, and add them, storing the result at the output.
+    private void overlapAddWithSeparation(
+        int numSamples,
+        int numChannels,
+        int separation,
+        short out[],
+        int outPos,
+        short rampDown[],
+        int rampDownPos,
+        short rampUp[],
+        int rampUpPos)
+    {
+        for(int i = 0; i < numChannels; i++) {
+            int o = outPos*numChannels + i;
+            int u = rampUpPos*numChannels + i;
+            int d = rampDownPos*numChannels + i;
+            for(int t = 0; t < numSamples + separation; t++) {
+                if(t < separation) {
+                    out[o] = (short)(rampDown[d]*(numSamples - t)/numSamples);
+                    d += numChannels;
+                } else if(t < numSamples) {
+                    out[o] = (short)((rampDown[d]*(numSamples - t) + rampUp[u]*(t - separation))/numSamples);
+                    d += numChannels;
+                    u += numChannels;
+                } else {
+                    out[o] = (short)(rampUp[u]*(t - separation)/numSamples);
+                    u += numChannels;
+                }
+                o += numChannels;
+            }
+        }
+    }
+
+    // Just move the new samples in the output buffer to the pitch buffer
+    private void moveNewSamplesToPitchBuffer(
+        int originalNumOutputSamples)
+    {
+        int numSamples = numOutputSamples - originalNumOutputSamples;
+
+        if(numPitchSamples + numSamples > pitchBufferSize) {
+            pitchBufferSize += (pitchBufferSize >> 1) + numSamples;
+            pitchBuffer = resize(pitchBuffer, pitchBufferSize);
+        }
+        move(pitchBuffer, numPitchSamples, outputBuffer, originalNumOutputSamples, numSamples);
+        numOutputSamples = originalNumOutputSamples;
+        numPitchSamples += numSamples;
+    }
+
+    // Remove processed samples from the pitch buffer.
+    private void removePitchSamples(
+        int numSamples)
+    {
+        if(numSamples == 0) {
+            return;
+        }
+        move(pitchBuffer, 0, pitchBuffer, numSamples, numPitchSamples - numSamples);
+        numPitchSamples -= numSamples;
+    }
+
+    // Change the pitch.  The latency this introduces could be reduced by looking at
+    // past samples to determine pitch, rather than future.
+    private void adjustPitch(
+        int originalNumOutputSamples)
+    {
+        int period, newPeriod, separation;
+        int position = 0;
+
+        if(numOutputSamples == originalNumOutputSamples) {
+            return;
+        }
+        moveNewSamplesToPitchBuffer(originalNumOutputSamples);
+        while(numPitchSamples - position >= maxRequired) {
+            period = findPitchPeriod(pitchBuffer, position, false);
+            newPeriod = (int)(period/pitch);
+            enlargeOutputBufferIfNeeded(newPeriod);
+            if(pitch >= 1.0f) {
+                overlapAdd(newPeriod, numChannels, outputBuffer, numOutputSamples, pitchBuffer,
+                	position, pitchBuffer, position + period - newPeriod);
+            } else {
+                separation = newPeriod - period;
+                overlapAddWithSeparation(period, numChannels, separation, outputBuffer, numOutputSamples,
+                	pitchBuffer, position, pitchBuffer, position);
+            }
+            numOutputSamples += newPeriod;
+            position += period;
+        }
+        removePitchSamples(position);
+    }
+
+    // Interpolate the new output sample.
+    private short interpolate(
+        short in[],
+        int inPos,
+        int oldSampleRate,
+        int newSampleRate)
+    {
+        short left = in[inPos*numChannels];
+        short right = in[inPos*numChannels + numChannels];
+        int position = newRatePosition*oldSampleRate;
+        int leftPosition = oldRatePosition*newSampleRate;
+        int rightPosition = (oldRatePosition + 1)*newSampleRate;
+        int ratio = rightPosition - position;
+        int width = rightPosition - leftPosition;
+
+        return (short)((ratio*left + (width - ratio)*right)/width);
+    }
+
+    // Change the rate.
+    private void adjustRate(
+        float rate,
+        int originalNumOutputSamples)
+    {
+        int newSampleRate = (int)(sampleRate/rate);
+        int oldSampleRate = sampleRate;
+        int position;
+
+        // Set these values to help with the integer math
+        while(newSampleRate > (1 << 14) || oldSampleRate > (1 << 14)) {
+            newSampleRate >>= 1;
+            oldSampleRate >>= 1;
+        }
+        if(numOutputSamples == originalNumOutputSamples) {
+            return;
+        }
+        moveNewSamplesToPitchBuffer(originalNumOutputSamples);
+        // Leave at least one pitch sample in the buffer
+        for(position = 0; position < numPitchSamples - 1; position++) {
+            while((oldRatePosition + 1)*newSampleRate > newRatePosition*oldSampleRate) {
+                enlargeOutputBufferIfNeeded(1);
+                for(int i = 0; i < numChannels; i++) {
+                    outputBuffer[numOutputSamples*numChannels + i] = interpolate(pitchBuffer, position + i,
+                    	oldSampleRate, newSampleRate);
+                }
+                newRatePosition++;
+                numOutputSamples++;
+            }
+            oldRatePosition++;
+            if(oldRatePosition == oldSampleRate) {
+                oldRatePosition = 0;
+                if(newRatePosition != newSampleRate) {
+                    System.out.printf("Assertion failed: newRatePosition != newSampleRate\n");
+                    assert false;
+                }
+                newRatePosition = 0;
+            }
+        }
+        removePitchSamples(position);
+    }
+
+
+    // Skip over a pitch period, and copy period/speed samples to the output
+    private int skipPitchPeriod(
+        short samples[],
+        int position,
+        float speed,
+        int period)
+    {
+        int newSamples;
+
+        if(speed >= 2.0f) {
+            newSamples = (int)(period/(speed - 1.0f));
+        } else {
+            newSamples = period;
+            remainingInputToCopy = (int)(period*(2.0f - speed)/(speed - 1.0f));
+        }
+        enlargeOutputBufferIfNeeded(newSamples);
+        overlapAdd(newSamples, numChannels, outputBuffer, numOutputSamples, samples, position,
+        	samples, position + period);
+        numOutputSamples += newSamples;
+        return newSamples;
+    }
+
+    // Insert a pitch period, and determine how much input to copy directly.
+    private int insertPitchPeriod(
+        short samples[],
+        int position,
+        float speed,
+        int period)
+    {
+        int newSamples;
+
+        if(speed < 0.5f) {
+            newSamples = (int)(period*speed/(1.0f - speed));
+        } else {
+            newSamples = period;
+            remainingInputToCopy = (int)(period*(2.0f*speed - 1.0f)/(1.0f - speed));
+        }
+        enlargeOutputBufferIfNeeded(period + newSamples);
+        move(outputBuffer, numOutputSamples, samples, position, period);
+        overlapAdd(newSamples, numChannels, outputBuffer, numOutputSamples + period, samples,
+        	position + period, samples, position);
+        numOutputSamples += period + newSamples;
+        return newSamples;
+    }
+
+    // Resample as many pitch periods as we have buffered on the input.  Return 0 if
+    // we fail to resize an input or output buffer.  Also scale the output by the volume.
+    private void changeSpeed(
+        float speed)
+    {
+        int numSamples = numInputSamples;
+        int position = 0, period, newSamples;
+
+        if(numInputSamples < maxRequired) {
+            return;
+        }
+        do {
+            if(remainingInputToCopy > 0) {
+                newSamples = copyInputToOutput(position);
+                position += newSamples;
+            } else {
+                period = findPitchPeriod(inputBuffer, position, true);
+                if(speed > 1.0) {
+                    newSamples = skipPitchPeriod(inputBuffer, position, speed, period);
+                    position += period + newSamples;
+                } else {
+                    newSamples = insertPitchPeriod(inputBuffer, position, speed, period);
+                    position += newSamples;
+                }
+            }
+        } while(position + maxRequired <= numSamples);
+        removeInputSamples(position);
+    }
+
+    // Resample as many pitch periods as we have buffered on the input.  Scale the output by the volume.
+    private void processStreamInput()
+    {
+        int originalNumOutputSamples = numOutputSamples;
+        float s = speed/pitch;
+        float r = rate;
+
+        if(!useChordPitch) {
+            r *= pitch;
+        }
+        if(s > 1.00001 || s < 0.99999) {
+            changeSpeed(s);
+        } else {
+            copyToOutput(inputBuffer, 0, numInputSamples);
+            numInputSamples = 0;
+        }
+        if(useChordPitch) {
+            if(pitch != 1.0f) {
+                adjustPitch(originalNumOutputSamples);
+            }
+        } else if(r != 1.0f) {
+            adjustRate(r, originalNumOutputSamples);
+        }
+        if(volume != 1.0f) {
+            // Adjust output volume.
+            scaleSamples(outputBuffer, originalNumOutputSamples, numOutputSamples - originalNumOutputSamples,
+                volume);
+        }
+    }
+
+    // Write floating point data to the input buffer and process it.
+    public void writeFloatToStream(
+        float samples[],
+        int numSamples)
+    {
+        addFloatSamplesToInputBuffer(samples, numSamples);
+        processStreamInput();
+    }
+
+    // Write the data to the input stream, and process it.
+    public void writeShortToStream(
+        short samples[],
+        int numSamples)
+    {
+        addShortSamplesToInputBuffer(samples, numSamples);
+        processStreamInput();
+    }
+
+    // Simple wrapper around sonicWriteFloatToStream that does the unsigned byte to short
+    // conversion for you.
+    public void writeUnsignedByteToStream(
+        byte samples[],
+        int numSamples)
+    {
+        addUnsignedByteSamplesToInputBuffer(samples, numSamples);
+        processStreamInput();
+    }
+
+    // Simple wrapper around sonicWriteBytesToStream that does the byte to 16-bit LE conversion.
+    public void writeBytesToStream(
+        byte inBuffer[],
+        int numBytes)
+    {
+        addBytesToInputBuffer(inBuffer, numBytes);
+        processStreamInput();
+    }
+
+    // This is a non-stream oriented interface to just change the speed of a sound sample
+    public static int changeFloatSpeed(
+        float samples[],
+        int numSamples,
+        float speed,
+        float pitch,
+        float rate,
+        float volume,
+        boolean useChordPitch,
+        int sampleRate,
+        int numChannels)
+    {
+        Sonic stream = new Sonic(sampleRate, numChannels);
+
+        stream.setSpeed(speed);
+        stream.setPitch(pitch);
+        stream.setRate(rate);
+        stream.setVolume(volume);
+        stream.setChordPitch(useChordPitch);
+        stream.writeFloatToStream(samples, numSamples);
+        stream.flushStream();
+        numSamples = stream.samplesAvailable();
+        stream.readFloatFromStream(samples, numSamples);
+        return numSamples;
+    }
+
+    /* This is a non-stream oriented interface to just change the speed of a sound sample */
+    public int sonicChangeShortSpeed(
+        short samples[],
+        int numSamples,
+        float speed,
+        float pitch,
+        float rate,
+        float volume,
+        boolean useChordPitch,
+        int sampleRate,
+        int numChannels)
+    {
+        Sonic stream = new Sonic(sampleRate, numChannels);
+
+        stream.setSpeed(speed);
+        stream.setPitch(pitch);
+        stream.setRate(rate);
+        stream.setVolume(volume);
+        stream.setChordPitch(useChordPitch);
+        stream.writeShortToStream(samples, numSamples);
+        stream.flushStream();
+        numSamples = stream.samplesAvailable();
+        stream.readShortFromStream(samples, numSamples);
+        return numSamples;
+    }
+}
diff --git a/sonic/debian/changelog b/sonic/debian/changelog
new file mode 100644
index 0000000..5a503fa
--- /dev/null
+++ b/sonic/debian/changelog
@@ -0,0 +1,61 @@
+sonic (0.2.0-1) unstable; urgency=low
+
+  * Relicensed under Apache 2.0.
+
+ -- Bill Cox <waywardgeek@gmail.com>  Fri, 27 Feb 2015 10:12:00 -0400
+
+sonic (0.1.18-1) unstable; urgency=low
+
+  * Added native Java port.
+
+ -- Bill Cox <waywardgeek@gmail.com>  Sat, 16 Jul 2011 12:12:32 -0400
+
+sonic (0.1.17-1) unstable; urgency=low
+
+  * Removed Sonic-NDK, which is a binary compiled for Android from the source
+    tar-ball.
+
+ -- Bill Cox <waywardgeek@gmail.com>  Tue, 19 Apr 2011 07:46:51 -0400
+
+sonic (0.1.16-1) unstable; urgency=low
+
+  * Added a bit more explanation about copyrights of various files.
+  * Dump git log to ChangeLog
+
+ -- Bill Cox <waywardgeek@gmail.com>  Sat, 16 Apr 2011 16:49:33 -0400
+
+sonic (0.1.15-1) unstable; urgency=low
+
+  * Updates to improve Debian packaging.
+
+ -- Bill Cox <waywardgeek@gmail.com>  Fri, 15 Apr 2011 12:32:30 -0400
+
+sonic (0.1.14-1) unstable; urgency=low
+
+  * Changed default install to /usr, rather than /usr/local
+
+ -- Bill Cox <waywardgeek@gmail.com>  Thu, 14 Apr 2011 14:37:47 -0400
+
+sonic (0.1.13-1) unstable; urgency=low
+
+  * Added rate control, and a new pitch control mode.
+
+ -- Bill Cox <waywardgeek@gmail.com>  Tue, 11 Jan 2011 07:44:38 -0500
+
+sonic (0.1.12-1) unstable; urgency=low
+
+  * Added web page, compile with -pthread.
+
+ -- Bill Cox <waywardgeek@gmail.com>  Sun, 09 Jan 2011 11:45:54 -0500
+
+sonic (0.1.11-1) unstable; urgency=low
+
+  * Removing .git and debian directory from orig.tar.gz file
+
+ -- Bill Cox <waywardgeek@gmail.com>  Mon, 20 Dec 2010 15:31:40 -0500
+
+sonic (0.1.10-1) unstable; urgency=low
+
+  * Initial packaging. Closes: #607477
+
+ -- Bill Cox <waywardgeek@gmail.com>  Fri, 17 Dec 2010 15:39:53 -0500
diff --git a/sonic/debian/changelog.vinux b/sonic/debian/changelog.vinux
new file mode 100644
index 0000000..3cade94
--- /dev/null
+++ b/sonic/debian/changelog.vinux
@@ -0,0 +1,53 @@
+sonic (0.1.8) lucid; urgency=low
+
+  * Converted to mostly integer arithmetic, and added stereo support.
+
+ -- Bill Cox <waywardgeek@gmail.com>  Fri, 19 Nov 2010 17:58:29 -0500
+
+sonic (0.1.7) lucid; urgency=low
+
+  * Changed copyright to LGPL.
+
+ -- Bill Cox <waywardgeek@gmail.com>  Wed, 10 Nov 2010 19:44:55 -0500
+
+sonic (0.1.6) maverick; urgency=low
+
+  * Added interface for using short data.
+
+ -- Bill Cox <waywardgeek@gmail.com>  Mon, 08 Nov 2010 11:43:41 -0500
+
+sonic (0.1.5) lucid; urgency=low
+
+  * Added .install files.
+
+ -- Bill Cox <waywardgeek@gmail.com>  Mon, 08 Nov 2010 10:10:06 -0500
+
+sonic (0.1.4) lucid; urgency=low
+
+  * Minor fixes, added minor functions.
+
+ -- Bill Cox <waywardgeek@gmail.com>  Mon, 08 Nov 2010 09:29:58 -0500
+
+sonic (0.1.3) lucid; urgency=low
+
+  * Added a README and some samples.
+
+ -- Bill Cox <waywardgeek@gmail.com>  Thu, 04 Nov 2010 09:21:28 -0400
+
+sonic (0.1.2) lucid; urgency=low
+
+  * One more try on that build dependency.
+
+ -- Bill Cox <waywardgeek@gmail.com>  Wed, 03 Nov 2010 15:08:19 -0400
+
+sonic (0.1.1) lucid; urgency=low
+
+  * Added build dependency on libsndfile for sonic executable.
+
+ -- Bill Cox <waywardgeek@gmail.com>  Wed, 03 Nov 2010 14:34:19 -0400
+
+sonic (0.1) lucid; urgency=low
+
+  * Initial release.
+
+ -- Bill Cox <waywardgeek@gmail.com>  Wed, 03 Nov 2010 11:15:19 -0400
diff --git a/sonic/debian/compat b/sonic/debian/compat
new file mode 100644
index 0000000..7f8f011
--- /dev/null
+++ b/sonic/debian/compat
@@ -0,0 +1 @@
+7
diff --git a/sonic/debian/control b/sonic/debian/control
new file mode 100644
index 0000000..caeba95
--- /dev/null
+++ b/sonic/debian/control
@@ -0,0 +1,41 @@
+Source: sonic
+Section: sound
+Priority: extra
+Maintainer: Bill Cox <waywardgeek@gmail.com>
+Build-Depends: debhelper (>= 7.0.50~)
+Standards-Version: 3.9.1
+Homepage: http://vinux-project.org/sonic
+Vcs-Browser: http://vinux-project.org/gitweb/?p=sonic.git;a=summary
+Vcs-Git: git://vinux-project.org/sonic
+
+Package: sonic
+Architecture: any
+Depends: libsonic0 (= ${binary:Version}), ${shlibs:Depends}, ${misc:Depends}
+Description: Simple utility to speed up or slow down speech
+ Sonic is a very simple utility that reads and writes wav files,
+ and speeds them up or slows them down, with low distortion.
+ The key new feature in Sonic versus other libraries is very
+ high quality at speed up factors well over 2X.
+
+Package: libsonic0
+Architecture: any
+Section: libs
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Description: Simple library to speed up or slow down speech
+ This package contains just the actual library.
+ libsonic is a very simple library for speeding up or slowing
+ down speech.  It has only basic dependencies, and is meant to
+ work on both Linux destop machines and embedded systems.
+ The key new feature in Sonic versus other libraries is very
+ high quality at speed up factors well over 2X.
+
+Package: libsonic-dev
+Architecture: any
+Section: libdevel
+Depends: libsonic0 (= ${binary:Version}), ${misc:Depends}
+Suggests: sonic
+Description: Header file for linking to libsonic
+ This package contains just the sonic header file.
+ It is needed only by developers wishing to link to libsonic.
+ The key new feature in Sonic versus other libraries is very
+ high quality at speed up factors well over 2X.
diff --git a/sonic/debian/copyright b/sonic/debian/copyright
new file mode 100644
index 0000000..34c45a3
--- /dev/null
+++ b/sonic/debian/copyright
@@ -0,0 +1,24 @@
+This work was packaged for Debian by:
+
+    Bill Cox <waywardgeek@gmail.com> on Wed, 03 Nov 2010 11:15:19 -0400
+
+It was downloaded from:
+
+    http://vinux-project.org
+
+Upstream Author(s):
+
+    Bill Cox <waywardgeek@gmail.com>
+
+Copyright:
+
+  Copyright (C) 2010 Bill Cox
+
+   The Sonic Library is licensed under the Apache 2.0 license.
+
+The Debian packaging is:
+
+    Copyright (C) 2010 Bill Cox <waywardgeek@gmail.com>
+
+and is licensed under the Apache 2.0 license,
+see "/usr/share/common-licenses/Apache-2.0".
diff --git a/sonic/debian/libsonic-dev.install b/sonic/debian/libsonic-dev.install
new file mode 100644
index 0000000..db77dac
--- /dev/null
+++ b/sonic/debian/libsonic-dev.install
@@ -0,0 +1,3 @@
+sonic.h /usr/include/
+libsonic.so /usr/lib/
+main.c /usr/share/doc/libsonic-dev
diff --git a/sonic/debian/libsonic0.install b/sonic/debian/libsonic0.install
new file mode 100644
index 0000000..f4b109d
--- /dev/null
+++ b/sonic/debian/libsonic0.install
@@ -0,0 +1 @@
+libsonic.so.* /usr/lib/
diff --git a/sonic/debian/libsonic0.symbols b/sonic/debian/libsonic0.symbols
new file mode 100644
index 0000000..9cc25f5
--- /dev/null
+++ b/sonic/debian/libsonic0.symbols
@@ -0,0 +1,29 @@
+libsonic.so.0 libsonic0 #MINVER#
+ sonicChangeFloatSpeed@Base 0.1.10
+ sonicChangeShortSpeed@Base 0.1.10
+ sonicCreateStream@Base 0.1.10
+ sonicDestroyStream@Base 0.1.10
+ sonicFlushStream@Base 0.1.10
+ sonicGetNumChannels@Base 0.1.10
+ sonicGetPitch@Base 0.1.10
+ sonicGetQuality@Base 0.1.10
+ sonicGetSampleRate@Base 0.1.10
+ sonicGetSpeed@Base 0.1.10
+ sonicGetVolume@Base 0.1.10
+ sonicReadFloatFromStream@Base 0.1.10
+ sonicReadShortFromStream@Base 0.1.10
+ sonicReadUnsignedCharFromStream@Base 0.1.10
+ sonicSamplesAvailable@Base 0.1.10
+ sonicSetPitch@Base 0.1.10
+ sonicSetQuality@Base 0.1.10
+ sonicSetSpeed@Base 0.1.10
+ sonicSetVolume@Base 0.1.10
+ sonicWriteFloatToStream@Base 0.1.10
+ sonicWriteShortToStream@Base 0.1.10
+ sonicWriteUnsignedCharToStream@Base 0.1.10
+ sonicGetRate@Base 0.1.13
+ sonicSetRate@Base 0.1.13
+ sonicGetChordPitch@Base 0.1.13
+ sonicSetChordPitch@Base 0.1.13
+ sonicSetNumChannels@Base 0.1.15
+ sonicSetSampleRate@Base 0.1.15
diff --git a/sonic/debian/rules b/sonic/debian/rules
new file mode 100755
index 0000000..121a8eb
--- /dev/null
+++ b/sonic/debian/rules
@@ -0,0 +1,15 @@
+#!/usr/bin/make -f
+# -*- makefile -*-
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+# This has to be exported to make some magic below work.
+export DH_OPTIONS
+
+
+%:
+	dh $@ 
+
+override_dh_installchangelogs:
+	dh_installchangelogs ChangeLog
diff --git a/sonic/debian/sonic.install b/sonic/debian/sonic.install
new file mode 100644
index 0000000..b2af45d
--- /dev/null
+++ b/sonic/debian/sonic.install
@@ -0,0 +1 @@
+sonic /usr/bin/
diff --git a/sonic/debian/sonic.manpages b/sonic/debian/sonic.manpages
new file mode 100644
index 0000000..fa28de4
--- /dev/null
+++ b/sonic/debian/sonic.manpages
@@ -0,0 +1,2 @@
+sonic.1
+
diff --git a/sonic/debian/source/format b/sonic/debian/source/format
new file mode 100644
index 0000000..163aaf8
--- /dev/null
+++ b/sonic/debian/source/format
@@ -0,0 +1 @@
+3.0 (quilt)
diff --git a/sonic/debian/watch b/sonic/debian/watch
new file mode 100644
index 0000000..8de16b2
--- /dev/null
+++ b/sonic/debian/watch
@@ -0,0 +1,3 @@
+# format version number, currently 3; this line is compulsory!
+version=3
+http://vinux-project.org/sonic/download/sonic_(.+).tar.gz
diff --git a/sonic/doc/index.md b/sonic/doc/index.md
new file mode 100644
index 0000000..1e02141
--- /dev/null
+++ b/sonic/doc/index.md
@@ -0,0 +1,173 @@
+# libsonic Home Page
+
+[Download the latest tar-ball from here](download).
+
+The source code repository can be cloned using git:
+
+    $ git clone git://github.com/waywardgeek/sonic.git
+
+The source code for the Android version, sonic-ndk, can be cloned with:
+
+    $ git clone git://github.com/waywardgeek/sonic-ndk.git
+
+There is a simple test app for android that demos capabilities.  You can
+[install the Android application from here](Sonic-NDK.apk)
+
+There is a new native Java port, which is very fast!  Checkout Sonic.java and
+Main.java in the latest tar-ball, or get the code from git.
+
+## Overview
+
+Sonic is free software for speeding up or slowing down speech.  While similar to
+other algorithms that came before, Sonic is optimized for speed ups of over 2X.
+There is a simple sonic library in ANSI C, and one in pure Java.  Both are
+designed to easily be integrated into streaming voice applications, like TTS
+back ends.  While a very new project, it is already integrated into:
+
+- espeak
+- Debian Sid as package libsonic
+- Android Astro Player Nova
+- Android Osplayer
+- Multiple closed source TTS engines
+
+The primary motivation behind sonic is to enable the blind and visually impaired
+to improve their productivity with free software speech engines, like espeak.
+Sonic can also be used by the sighted.  For example, sonic can improve the
+experience of listening to an audio book on an Android phone.
+
+Sonic is Copyright 2010, 2011, Bill Cox, all rights reserved.  It is released
+as under the Apache 2.0 license.  Feel free to contact me at
+<waywardgeek@gmail.com>.  One user was concerned about patents.  I believe the
+sonic algorithms do not violate any patents, as most of it is very old, based
+on [PICOLA](http://keizai.yokkaichi-u.ac.jp/~ikeda/research/picola.html), and
+the new part, for greater than 2X speed up, is clearly a capability most
+developers ignore, and would not bother to patent.
+
+## Comparison to Other Solutions
+
+In short, Sonic is better for speech, while WSOLA is better for music.
+
+A popular alternative is SoundTouch.  SoundTouch uses WSOLA, an algorithm
+optimized for changing the tempo of music.  No WSOLA based program performs well
+for speech (contrary to the inventor's estimate of WSOLA).  Listen to [this
+soundstretch sample](soundstretch.wav), which uses SoundTouch, and compare
+it to [this sonic sample](sonic.wav).  Both are sped up by 2X.  WSOLA
+introduces unacceptable levels of distortion, making speech impossible to
+understand at high speed (over 2.5X) by blind speed listeners.
+
+However, there are decent free software algorithms for speeding up speech.  They
+are all in the TD-PSOLA family.  For speech rates below 2X, sonic uses PICOLA,
+which I find to be the best algorithm available.  A slightly buggy
+implementation of PICOLA is available in the spandsp library.  I find the one in
+RockBox quite good, though it's limited to 2X speed up.  So far as I know, only
+sonic is optimized for speed factors needed by the blind, up to 6X.
+
+Sonic does all of it's CPU intensive work with integer math, and works well on
+ARM CPUs without FPUs.  It supports multiple channels (stereo), and is also able
+to change the pitch of a voice.  It works well in streaming audio applications,
+and can deal with sound streams in 16-bit signed integer, 32-bit floating point,
+or 8-bit unsigned formats.  The source code is in plain ANSI C.  In short, it's
+production ready.
+
+## Using libsonic in your program
+
+Sonic is still a new library, but is in Debian Sid.  It will take a while
+for it to filter out into all the other distros.  For now, feel free to simply
+add sonic.c and sonic.h to your application (or Sonic.java), but consider
+switching to -lsonic once the library is available on your distro.
+
+The file [main.c](main.c) is the source code for the sonic command-line application.  It
+is meant to be useful as example code.  Feel free to copy directly from main.c
+into your application, as main.c is in the public domain.  Dependencies listed
+in debian/control like libsndfile are there to compile the sonic command-line
+application.  Libsonic has no external dependencies.
+
+There are basically two ways to use sonic: batch or stream mode.  The simplest
+is batch mode where you pass an entire sound sample to sonic.  All you do is
+call one function, like this:
+
+    sonicChangeShortSpeed(samples, numSamples, speed, pitch, rate, volume, useChordPitch, sampleRate, numChannels);
+
+This will change the speed and pitch of the sound samples pointed to by samples,
+which should be 16-bit signed integers.  Stereo mode is supported, as
+is any arbitrary number of channels.  Samples for each channel should be
+adjacent in the input array.  Because the samples are modified in-place, be sure
+that there is room in the samples array for the speed-changed samples.  In
+general, if you are speeding up, rather than slowing down, it will be safe to
+have no extra padding.  If your sound samples are mono, and you don't want to
+scale volume or playback rate, and if you want normal pitch scaling, then call
+it like this:
+
+    sonicChangeShortSpeed(samples, numSamples, speed, pitch, 1.0f, 1.0f, 0, sampleRate, 1);
+
+The other way to use libsonic is in stream mode.  This is more complex, but
+allows sonic to be inserted into a sound stream with fairly low latency.  The
+current maximum latency in sonic is 31 milliseconds, which is enough to process
+two pitch periods of voice as low as 65 Hz.  In general, the latency is equal to
+two pitch periods, which is typically closer to 20 milliseconds.
+
+To process a sound stream, you must create a sonicStream object, which contains
+all of the state used by sonic.  Sonic should be thread safe, and multiple
+sonicStream objects can be used at the same time.  You create a sonicStream
+object like this:
+
+    sonicStream stream = sonicCreateStream(sampleRate, numChannels);
+
+When you're done with a sonic stream, you can free it's memory with:
+
+    sonicDestroyStream(stream);
+
+By default, a sonic stream sets the speed, pitch, rate, and volume to 1.0, which means
+no change at all to the sound stream.  Sonic detects this case, and simply
+copies the input to the output to reduce CPU load.  To change the speed, pitch,
+rate, or volume, set the parameters using:
+
+    sonicSetSpeed(stream, speed);
+    sonicSetPitch(stream, pitch);
+    sonicSetRate(stream, rate);
+    sonicSetVolume(stream, volume);
+
+These four parameters are floating point numbers.  A speed of 2.0 means to
+double speed of speech.  A pitch of 0.95 means to lower the pitch by about 5%,
+and a volume of 1.4 means to multiply the sound samples by 1.4, clipping if we
+exceed the maximum range of a 16-bit integer.  Speech rate scales how fast
+speech is played.  A 2.0 value will make you sound like a chipmunk talking very
+fast.  A 0.7 value will make you sound like a giant talking slowly.
+
+By default, pitch is modified by changing the rate, and then using speed
+modification to bring the speed back to normal.  This allows for a wide range of
+pitch changes, but changing the pitch makes the speaker sound larger or smaller,
+too.  If you want to make the person sound like the same person, but talking at
+a higher or lower pitch, then enable the vocal chord emulation mode for pitch
+scaling, using:
+
+    sonicSetChordPitch(stream, 1);
+
+However, only small changes to pitch should be used in this mode, as it
+introduces significant distortion otherwise.
+
+After setting the sound parameters, you write to the stream like this:
+
+    sonicWriteShortToStream(stream, samples, numSamples);
+
+You read the sped up speech samples from sonic like this:
+
+    samplesRead = sonicReadShortFromStream(stream, outBuffer, maxBufferSize);
+    if(samplesRead > 0) {
+	/* Do something with the output samples in outBuffer, like send them to
+	 * the sound device. */
+    }
+
+You may change the speed, pitch, rate, and volume parameters at any time, without
+having to flush or create a new sonic stream.
+
+When your sound stream ends, there may be several milliseconds of sound data in
+the sonic stream's buffers.  To force sonic to process those samples use:
+
+    sonicFlushStream(stream);
+
+Then, read those samples as above.  That's about all there is to using libsonic.
+There are some more functions as a convenience for the user, like
+sonicGetSpeed.  Other sound data formats are supported: signed char and float.
+If float, the sound data should be between -1.0 and 1.0.  Internally, all sound
+data is converted to 16-bit integers for processing.
diff --git a/sonic/doc/post b/sonic/doc/post
new file mode 100755
index 0000000..e8fed85
--- /dev/null
+++ b/sonic/doc/post
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+files="index.html
+../main.c
+../samples/sonic.wav
+../samples/soundstretch.wav"
+
+markdown_py index.md > index.html
+if [ -f ../version ]; then
+    version=`awk '{print $1}' ../version`
+    sourceTarball=../../$version.orig.tar.gz
+    if [ -f $sourceTarball ]; then
+	destTarball=$version.tar.gz
+	scp $sourceTarball root@dev.vinux-project.org:/var/www/dev.vinux-project.org/sonic/download/$destTarball
+    fi
+fi
+scp $files root@dev.vinux-project.org:/var/www/dev.vinux-project.org/sonic
+rm ../samples/*.wav index.html
diff --git a/sonic/doc/sonic.odt b/sonic/doc/sonic.odt
new file mode 100644
index 0000000..5411f4d
--- /dev/null
+++ b/sonic/doc/sonic.odt
Binary files differ
diff --git a/sonic/main.c b/sonic/main.c
new file mode 100644
index 0000000..77829c2
--- /dev/null
+++ b/sonic/main.c
@@ -0,0 +1,139 @@
+/* This file was written by Bill Cox in 2010, and is licensed under the Apache
+   2.0 license.
+
+   This file is meant as a simple example for how to use libsonic.  It is also a
+   useful utility on it's own, which can speed up or slow down wav files, change
+   pitch, and scale volume. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "sonic.h"
+#include "wave.h"
+
+#define BUFFER_SIZE 2048
+
+/* Run sonic. */
+static void runSonic(
+    waveFile inFile,
+    waveFile outFile,
+    float speed,
+    float pitch,
+    float rate,
+    float volume,
+    int emulateChordPitch,
+    int quality,
+    int sampleRate,
+    int numChannels)
+{
+    sonicStream stream = sonicCreateStream(sampleRate, numChannels);
+    short inBuffer[BUFFER_SIZE], outBuffer[BUFFER_SIZE];
+    int samplesRead, samplesWritten;
+
+    sonicSetSpeed(stream, speed);
+    sonicSetPitch(stream, pitch);
+    sonicSetRate(stream, rate);
+    sonicSetVolume(stream, volume);
+    sonicSetChordPitch(stream, emulateChordPitch);
+    sonicSetQuality(stream, quality);
+    do {
+        samplesRead = readFromWaveFile(inFile, inBuffer, BUFFER_SIZE/numChannels);
+	if(samplesRead == 0) {
+	    sonicFlushStream(stream);
+	} else {
+	    sonicWriteShortToStream(stream, inBuffer, samplesRead);
+	}
+	do {
+	    samplesWritten = sonicReadShortFromStream(stream, outBuffer,
+	        BUFFER_SIZE/numChannels);
+	    if(samplesWritten > 0) {
+		writeToWaveFile(outFile, outBuffer, samplesWritten);
+	    }
+	} while(samplesWritten > 0);
+    } while(samplesRead > 0);
+    sonicDestroyStream(stream);
+}
+
+/* Print the usage. */
+static void usage(void)
+{
+    fprintf(stderr, "Usage: sonic [OPTION]... infile outfile\n"
+        "    -c         -- Modify pitch by emulating vocal chords vibrating\n"
+	"                  faster or slower.\n"
+        "    -p pitch   -- Set pitch scaling factor.  1.3 means 30%% higher.\n"
+        "    -q         -- Disable speed-up heuristics.  May increase quality.\n"
+        "    -r rate    -- Set playback rate.  2.0 means 2X faster, and 2X pitch.\n"
+        "    -s speed   -- Set speed up factor.  2.0 means 2X faster.\n"
+	"    -v volume  -- Scale volume by a constant factor.\n");
+    exit(1);
+}
+
+int main(
+    int argc,
+    char **argv)
+{
+    waveFile inFile, outFile;
+    char *inFileName, *outFileName;
+    float speed = 1.0f;
+    float pitch = 1.0f;
+    float rate = 1.0f;
+    float volume = 1.0f;
+    int emulateChordPitch = 0;
+    int quality = 0;
+    int sampleRate, numChannels;
+    int xArg = 1;
+
+    while(xArg < argc && *(argv[xArg]) == '-') {
+	if(!strcmp(argv[xArg], "-c")) {
+	    emulateChordPitch = 1;
+	    printf("Scaling pitch linearly.\n");
+	} else if(!strcmp(argv[xArg], "-p")) {
+	    xArg++;
+	    if(xArg < argc) {
+	        pitch = atof(argv[xArg]);
+                printf("Setting pitch to %0.2fX\n", pitch);
+	    }
+	} else if(!strcmp(argv[xArg], "-q")) {
+	    quality = 1;
+	    printf("Disabling speed-up heuristics\n");
+	} else if(!strcmp(argv[xArg], "-r")) {
+	    xArg++;
+	    if(xArg < argc) {
+	        rate = atof(argv[xArg]);
+                printf("Setting rate to %0.2fX\n", rate);
+	    }
+	} else if(!strcmp(argv[xArg], "-s")) {
+	    xArg++;
+	    if(xArg < argc) {
+	        speed = atof(argv[xArg]);
+                printf("Setting speed to %0.2fX\n", speed);
+	    }
+	} else if(!strcmp(argv[xArg], "-v")) {
+	    xArg++;
+	    if(xArg < argc) {
+	        volume = atof(argv[xArg]);
+                printf("Setting volume to %0.2f\n", volume);
+	    }
+	}
+	xArg++;
+    }
+    if(argc - xArg != 2) {
+	usage();
+    }
+    inFileName = argv[xArg];
+    outFileName = argv[xArg + 1];
+    inFile = openInputWaveFile(inFileName, &sampleRate, &numChannels);
+    if(inFile == NULL) {
+	return 1;
+    }
+    outFile = openOutputWaveFile(outFileName, sampleRate, numChannels);
+    if(outFile == NULL) {
+	closeWaveFile(inFile);
+	return 1;
+    }
+    runSonic(inFile, outFile, speed, pitch, rate, volume, emulateChordPitch, quality,
+        sampleRate, numChannels);
+    closeWaveFile(inFile);
+    closeWaveFile(outFile);
+    return 0;
+}
diff --git a/sonic/mkorig b/sonic/mkorig
new file mode 100755
index 0000000..6535635
--- /dev/null
+++ b/sonic/mkorig
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+# Build an original tar-ball for a debian package free of a debian directory and
+# .git.
+
+version=`head -1 debian/changelog | sed 's/.*(//
+     s/-.*//'`
+sed -i "s/LIB_TAG=.*/LIB_TAG=$version/" Makefile
+if [ "`basename $PWD`" != "sonic-$version" ]; then
+    echo "Directory name should be sonic-$version"
+    exit 1
+fi
+make clean
+make -f debian/rules clean
+echo "sonic_${version} (git: `git log -1 --oneline | sed 's/ .*//'`)" > version
+git2cl > ChangeLog
+cd ..
+if ! tar -cvzf sonic_$version.orig.tar.gz --exclude-vcs --exclude=debian sonic-$version; then 
+    echo "Could not create sonic_$version.orig.tar.gz"
+    exit 1
+fi
+echo "Successfully created sonic_$version.orig.tar.gz"
diff --git a/sonic/samples/README b/sonic/samples/README
new file mode 100644
index 0000000..a9c630a
--- /dev/null
+++ b/sonic/samples/README
@@ -0,0 +1,34 @@
+These wav files show how Sonic performs at increasing speech rates.  All sound
+sampels are in the public domain.
+
+sonic.wav
+This is a sonic 2X sped-up version of a public domain librivox.org recording, from
+the audiobook "Princess of Mars".
+
+soundtouch.wav
+This is the same recording as sonic.wav, but sped up using soundtouch, which
+uses WSOLA rather than the sonic algorithm.  Even at 2X speed up, you should be
+able to hear the characteristic WSOLA distortion relative to the sonic version.
+
+talking.wav
+This is my father talking, using a decent microphone and 44KHz sample rate.
+
+talking_2x.wav
+This is his voice sped up by 2X using Sonic.
+
+espeak_s450.wav
+Sonic also performs well at increasing the speed of synthesized speech.
+espeak_s450.wav was generated using 'espeak -s450 -f test1.txt -w
+espeak_s450.wav'.  This is the highest speed currently supported by espeak,
+though Sonic can speed up espeak to much faster rates.
+
+espeak_sonic.wav
+This was generated with 'espeak -f test1.txt -w out.wav;
+sonic 2.6 out.wav espeak_sonic.wav'.  Sonic sped it up 2.6X, which is about the
+same speed as espeak at -s450.  I personally feel that the sonic sped up sample
+sounds better than espeak at -s450.
+
+twosineperiods.wav
+This is just two sine periods, which is too short to hear.  However, it's
+useful for making sure the flush function works correctly.  A 2-X speedup should
+result in one sine period with no distortion.
diff --git a/sonic/samples/espeak_s450.wav b/sonic/samples/espeak_s450.wav
new file mode 100644
index 0000000..4239477
--- /dev/null
+++ b/sonic/samples/espeak_s450.wav
Binary files differ
diff --git a/sonic/samples/espeak_sonic.wav b/sonic/samples/espeak_sonic.wav
new file mode 100644
index 0000000..cffdb96
--- /dev/null
+++ b/sonic/samples/espeak_sonic.wav
Binary files differ
diff --git a/sonic/samples/sonic.wav b/sonic/samples/sonic.wav
new file mode 100644
index 0000000..bc55109
--- /dev/null
+++ b/sonic/samples/sonic.wav
Binary files differ
diff --git a/sonic/samples/soundstretch.wav b/sonic/samples/soundstretch.wav
new file mode 100644
index 0000000..e0226d5
--- /dev/null
+++ b/sonic/samples/soundstretch.wav
Binary files differ
diff --git a/sonic/samples/stereo_test.wav b/sonic/samples/stereo_test.wav
new file mode 100644
index 0000000..6dfc809
--- /dev/null
+++ b/sonic/samples/stereo_test.wav
Binary files differ
diff --git a/sonic/samples/talking.wav b/sonic/samples/talking.wav
new file mode 100644
index 0000000..1df692e
--- /dev/null
+++ b/sonic/samples/talking.wav
Binary files differ
diff --git a/sonic/samples/talking_2x.wav b/sonic/samples/talking_2x.wav
new file mode 100644
index 0000000..e20f71f
--- /dev/null
+++ b/sonic/samples/talking_2x.wav
Binary files differ
diff --git a/sonic/samples/test1.txt b/sonic/samples/test1.txt
new file mode 100644
index 0000000..4287ec7
--- /dev/null
+++ b/sonic/samples/test1.txt
@@ -0,0 +1 @@
+You don't know about me without you have read a book by the name of The Adventures of Tom Sawyer; but that ain't no matter.  That book was made by Mr. Mark Twain, and he told the truth, mainly.  There was things which he stretched, but mainly he told the truth.
diff --git a/sonic/samples/twosineperiods.wav b/sonic/samples/twosineperiods.wav
new file mode 100644
index 0000000..c71dfff
--- /dev/null
+++ b/sonic/samples/twosineperiods.wav
Binary files differ
diff --git a/sonic/sonic.1 b/sonic/sonic.1
new file mode 100644
index 0000000..c407873
--- /dev/null
+++ b/sonic/sonic.1
@@ -0,0 +1,59 @@
+.TH SONIC 1 
+
+.SH NAME 
+sonic \- Speech speed manipulator
+
+.SH SYNOPSIS 
+.B sonic [OPTION]... inFile outFile 
+
+.SH DESCRIPTION 
+Sonic is used to make wav files of speech faster or slower.  The primary advance
+in sonic is the ability to speed speech up by much more than 2X, with minimal
+distortion.  However, sonic can be used for both speeding up and slowing down
+speech files.  Additionally, sonic can change the pitch and volume.
+
+.SH OPTIONS
+.TP
+.B \-c
+Modify pitch by emulating vocal chords vibrating faster or slower.  This causes
+more distortion than the default pitch scaling, but sounds more like the same
+person trying to talk higher or lower.  The default pitch changes makes the
+voice sound like a larger or smaller person, but introduces little distortion.
+.TP
+.B \-p pitch
+Set pitch scaling factor.  1.3 means 30%% higher.
+.TP
+.B \-q
+Disable all speed-up heuristics, possibly improving the quality slightly.  This
+is mainly used for debugging the speed-up heuristics.
+.TP
+.B \-r rate
+Adjust the speed of playback.  This scales both the pitch and speed equally.
+.TP
+.B \-s speed
+Set speed up factor.  1.0 means no change, 2.0 means 2X faster.
+.TP
+.B \-v scaleFactor
+Scale volume by scaleFactor.  1.5 increases by 50%.  Clips if the maximum range is
+exceeded.
+
+.SH EXAMPLES
+
+.B sonic -s 3.2 book.wav book_fast.wav
+
+The above command would increase the speed of an audio book called book.wav by a
+factor of 3.2, and write the result in book_fast.wav.
+
+.B sonic -s 0.5 -v 1.5 spanish.wav spanish_slow.wav
+
+This would slow down the file spanish.wav by a factor of 2, make the volume 50%
+louder, and write the result to spanish_slow.wav.
+
+.B sonic -p 2.0 low.wav high.wav
+
+This would make a low voice sound very high pitched.
+
+.SH AUTHOR 
+Bill Cox waywardgeek@gmail.com
+.BR
+Sonic Version 0.2, Copyright 2010, Bill Cox, Apache 2.0 license
diff --git a/sonic/sonic.c b/sonic/sonic.c
new file mode 100644
index 0000000..39cee28
--- /dev/null
+++ b/sonic/sonic.c
@@ -0,0 +1,1175 @@
+/* Sonic library
+   Copyright 2010
+   Bill Cox
+   This file is part of the Sonic Library.
+
+   This file is licensed under the Apache 2.0 license.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#ifdef SONIC_USE_SIN
+#include <math.h>
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+#endif
+#include "sonic.h"
+
+struct sonicStreamStruct {
+    short *inputBuffer;
+    short *outputBuffer;
+    short *pitchBuffer;
+    short *downSampleBuffer;
+    float speed;
+    float volume;
+    float pitch;
+    float rate;
+    int oldRatePosition;
+    int newRatePosition;
+    int useChordPitch;
+    int quality;
+    int numChannels;
+    int inputBufferSize;
+    int pitchBufferSize;
+    int outputBufferSize;
+    int numInputSamples;
+    int numOutputSamples;
+    int numPitchSamples;
+    int minPeriod;
+    int maxPeriod;
+    int maxRequired;
+    int remainingInputToCopy;
+    int sampleRate;
+    int prevPeriod;
+    int prevMinDiff;
+};
+
+/* Just used for debugging */
+/*
+void sonicMSG(char *format, ...)
+{
+    char buffer[4096];
+    va_list ap;
+    FILE *file;
+
+    va_start(ap, format);
+    vsprintf((char *)buffer, (char *)format, ap);
+    va_end(ap);
+    file=fopen("/tmp/sonic.log", "a");
+    fprintf(file, "%s", buffer);
+    fclose(file);
+}
+*/
+
+/* Scale the samples by the factor. */
+static void scaleSamples(
+    short *samples,
+    int numSamples,
+    float volume)
+{
+    int fixedPointVolume = volume*4096.0f;
+    int value;
+
+    while(numSamples--) {
+	value = (*samples*fixedPointVolume) >> 12;
+	if(value > 32767) {
+	    value = 32767;
+	} else if(value < -32767) {
+	    value = -32767;
+	}
+	*samples++ = value;
+    }
+}
+
+/* Get the speed of the stream. */
+float sonicGetSpeed(
+    sonicStream stream)
+{
+    return stream->speed;
+}
+
+/* Set the speed of the stream. */
+void sonicSetSpeed(
+    sonicStream stream,
+    float speed)
+{
+    stream->speed = speed;
+}
+
+/* Get the pitch of the stream. */
+float sonicGetPitch(
+    sonicStream stream)
+{
+    return stream->pitch;
+}
+
+/* Set the pitch of the stream. */
+void sonicSetPitch(
+    sonicStream stream,
+    float pitch)
+{
+    stream->pitch = pitch;
+}
+
+/* Get the rate of the stream. */
+float sonicGetRate(
+    sonicStream stream)
+{
+    return stream->rate;
+}
+
+/* Set the playback rate of the stream. This scales pitch and speed at the same time. */
+void sonicSetRate(
+    sonicStream stream,
+    float rate)
+{
+    stream->rate = rate;
+
+    stream->oldRatePosition = 0;
+    stream->newRatePosition = 0;
+}
+
+/* Get the vocal chord pitch setting. */
+int sonicGetChordPitch(
+    sonicStream stream)
+{
+    return stream->useChordPitch;
+}
+
+/* Set the vocal chord mode for pitch computation.  Default is off. */
+void sonicSetChordPitch(
+    sonicStream stream,
+    int useChordPitch)
+{
+    stream->useChordPitch = useChordPitch;
+}
+
+/* Get the quality setting. */
+int sonicGetQuality(
+    sonicStream stream)
+{
+    return stream->quality;
+}
+
+/* Set the "quality".  Default 0 is virtually as good as 1, but very much faster. */
+void sonicSetQuality(
+    sonicStream stream,
+    int quality)
+{
+    stream->quality = quality;
+}
+
+/* Get the scaling factor of the stream. */
+float sonicGetVolume(
+    sonicStream stream)
+{
+    return stream->volume;
+}
+
+/* Set the scaling factor of the stream. */
+void sonicSetVolume(
+    sonicStream stream,
+    float volume)
+{
+    stream->volume = volume;
+}
+
+/* Free stream buffers. */
+static void freeStreamBuffers(
+    sonicStream stream)
+{
+    if(stream->inputBuffer != NULL) {
+	free(stream->inputBuffer);
+    }
+    if(stream->outputBuffer != NULL) {
+	free(stream->outputBuffer);
+    }
+    if(stream->pitchBuffer != NULL) {
+	free(stream->pitchBuffer);
+    }
+    if(stream->downSampleBuffer != NULL) {
+	free(stream->downSampleBuffer);
+    }
+}
+
+/* Destroy the sonic stream. */
+void sonicDestroyStream(
+    sonicStream stream)
+{
+    freeStreamBuffers(stream);
+    free(stream);
+}
+
+/* Allocate stream buffers. */
+static int allocateStreamBuffers(
+    sonicStream stream,
+    int sampleRate,
+    int numChannels)
+{
+    int minPeriod = sampleRate/SONIC_MAX_PITCH;
+    int maxPeriod = sampleRate/SONIC_MIN_PITCH;
+    int maxRequired = 2*maxPeriod; 
+
+    stream->inputBufferSize = maxRequired;
+    stream->inputBuffer = (short *)calloc(maxRequired, sizeof(short)*numChannels);
+    if(stream->inputBuffer == NULL) {
+	sonicDestroyStream(stream);
+	return 0;
+    }
+    stream->outputBufferSize = maxRequired;
+    stream->outputBuffer = (short *)calloc(maxRequired, sizeof(short)*numChannels);
+    if(stream->outputBuffer == NULL) {
+	sonicDestroyStream(stream);
+	return 0;
+    }
+    stream->pitchBufferSize = maxRequired;
+    stream->pitchBuffer = (short *)calloc(maxRequired, sizeof(short)*numChannels);
+    if(stream->pitchBuffer == NULL) {
+	sonicDestroyStream(stream);
+	return 0;
+    }
+    stream->downSampleBuffer = (short *)calloc(maxRequired, sizeof(short));
+    if(stream->downSampleBuffer == NULL) {
+	sonicDestroyStream(stream);
+	return 0;
+    }
+    stream->sampleRate = sampleRate;
+    stream->numChannels = numChannels;
+    stream->oldRatePosition = 0;
+    stream->newRatePosition = 0;
+    stream->minPeriod = minPeriod;
+    stream->maxPeriod = maxPeriod;
+    stream->maxRequired = maxRequired;
+    stream->prevPeriod = 0;
+    return 1;
+}
+
+/* Create a sonic stream.  Return NULL only if we are out of memory and cannot
+   allocate the stream. */
+sonicStream sonicCreateStream(
+    int sampleRate,
+    int numChannels)
+{
+    sonicStream stream = (sonicStream)calloc(1, sizeof(struct sonicStreamStruct));
+
+    if(stream == NULL) {
+	return NULL;
+    }
+    if(!allocateStreamBuffers(stream, sampleRate, numChannels)) {
+        return NULL;
+    }
+    stream->speed = 1.0f;
+    stream->pitch = 1.0f;
+    stream->volume = 1.0f;
+    stream->rate = 1.0f;
+    stream->oldRatePosition = 0;
+    stream->newRatePosition = 0;
+    stream->useChordPitch = 0;
+    stream->quality = 0;
+    return stream;
+}
+
+/* Get the sample rate of the stream. */
+int sonicGetSampleRate(
+    sonicStream stream)
+{
+    return stream->sampleRate;
+}
+
+/* Set the sample rate of the stream.  This will cause samples buffered in the stream to
+   be lost. */
+void sonicSetSampleRate(
+    sonicStream stream,
+    int sampleRate)
+{
+    freeStreamBuffers(stream);
+    allocateStreamBuffers(stream, sampleRate, stream->numChannels);
+}
+
+/* Get the number of channels. */
+int sonicGetNumChannels(
+    sonicStream stream)
+{
+    return stream->numChannels;
+}
+
+/* Set the num channels of the stream.  This will cause samples buffered in the stream to
+   be lost. */
+void sonicSetNumChannels(
+    sonicStream stream,
+    int numChannels)
+{
+    freeStreamBuffers(stream);
+    allocateStreamBuffers(stream, stream->sampleRate, numChannels);
+}
+
+/* Enlarge the output buffer if needed. */
+static int enlargeOutputBufferIfNeeded(
+    sonicStream stream,
+    int numSamples)
+{
+    if(stream->numOutputSamples + numSamples > stream->outputBufferSize) {
+	stream->outputBufferSize += (stream->outputBufferSize >> 1) + numSamples;
+	stream->outputBuffer = (short *)realloc(stream->outputBuffer,
+	    stream->outputBufferSize*sizeof(short)*stream->numChannels);
+	if(stream->outputBuffer == NULL) {
+	    return 0;
+	}
+    }
+    return 1;
+}
+
+/* Enlarge the input buffer if needed. */
+static int enlargeInputBufferIfNeeded(
+    sonicStream stream,
+    int numSamples)
+{
+    if(stream->numInputSamples + numSamples > stream->inputBufferSize) {
+	stream->inputBufferSize += (stream->inputBufferSize >> 1) + numSamples;
+	stream->inputBuffer = (short *)realloc(stream->inputBuffer,
+	    stream->inputBufferSize*sizeof(short)*stream->numChannels);
+	if(stream->inputBuffer == NULL) {
+	    return 0;
+	}
+    }
+    return 1;
+}
+
+/* Add the input samples to the input buffer. */
+static int addFloatSamplesToInputBuffer(
+    sonicStream stream,
+    float *samples,
+    int numSamples)
+{
+    short *buffer;
+    int count = numSamples*stream->numChannels;
+
+    if(numSamples == 0) {
+	return 1;
+    }
+    if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
+	return 0;
+    }
+    buffer = stream->inputBuffer + stream->numInputSamples*stream->numChannels;
+    while(count--) {
+        *buffer++ = (*samples++)*32767.0f;
+    }
+    stream->numInputSamples += numSamples;
+    return 1;
+}
+
+/* Add the input samples to the input buffer. */
+static int addShortSamplesToInputBuffer(
+    sonicStream stream,
+    short *samples,
+    int numSamples)
+{
+    if(numSamples == 0) {
+	return 1;
+    }
+    if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
+	return 0;
+    }
+    memcpy(stream->inputBuffer + stream->numInputSamples*stream->numChannels, samples,
+        numSamples*sizeof(short)*stream->numChannels);
+    stream->numInputSamples += numSamples;
+    return 1;
+}
+
+/* Add the input samples to the input buffer. */
+static int addUnsignedCharSamplesToInputBuffer(
+    sonicStream stream,
+    unsigned char *samples,
+    int numSamples)
+{
+    short *buffer;
+    int count = numSamples*stream->numChannels;
+
+    if(numSamples == 0) {
+	return 1;
+    }
+    if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
+	return 0;
+    }
+    buffer = stream->inputBuffer + stream->numInputSamples*stream->numChannels;
+    while(count--) {
+        *buffer++ = (*samples++ - 128) << 8;
+    }
+    stream->numInputSamples += numSamples;
+    return 1;
+}
+
+/* Remove input samples that we have already processed. */
+static void removeInputSamples(
+    sonicStream stream,
+    int position)
+{
+    int remainingSamples = stream->numInputSamples - position;
+
+    if(remainingSamples > 0) {
+	memmove(stream->inputBuffer, stream->inputBuffer + position*stream->numChannels,
+	    remainingSamples*sizeof(short)*stream->numChannels);
+    }
+    stream->numInputSamples = remainingSamples;
+}
+
+/* Just copy from the array to the output buffer */
+static int copyToOutput(
+    sonicStream stream,
+    short *samples,
+    int numSamples)
+{
+    if(!enlargeOutputBufferIfNeeded(stream, numSamples)) {
+	return 0;
+    }
+    memcpy(stream->outputBuffer + stream->numOutputSamples*stream->numChannels,
+	samples, numSamples*sizeof(short)*stream->numChannels);
+    stream->numOutputSamples += numSamples;
+    return 1;
+}
+
+/* Just copy from the input buffer to the output buffer.  Return 0 if we fail to
+   resize the output buffer.  Otherwise, return numSamples */
+static int copyInputToOutput(
+    sonicStream stream,
+    int position)
+{
+    int numSamples = stream->remainingInputToCopy;
+
+    if(numSamples > stream->maxRequired) {
+	numSamples = stream->maxRequired;
+    }
+    if(!copyToOutput(stream, stream->inputBuffer + position*stream->numChannels,
+	    numSamples)) {
+	return 0;
+    }
+    stream->remainingInputToCopy -= numSamples;
+    return numSamples;
+}
+
+/* Read data out of the stream.  Sometimes no data will be available, and zero
+   is returned, which is not an error condition. */
+int sonicReadFloatFromStream(
+    sonicStream stream,
+    float *samples,
+    int maxSamples)
+{
+    int numSamples = stream->numOutputSamples;
+    int remainingSamples = 0;
+    short *buffer;
+    int count;
+
+    if(numSamples == 0) {
+	return 0;
+    }
+    if(numSamples > maxSamples) {
+	remainingSamples = numSamples - maxSamples;
+	numSamples = maxSamples;
+    }
+    buffer = stream->outputBuffer;
+    count = numSamples*stream->numChannels;
+    while(count--) {
+	*samples++ = (*buffer++)/32767.0f;
+    }
+    if(remainingSamples > 0) {
+	memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
+	    remainingSamples*sizeof(short)*stream->numChannels);
+    }
+    stream->numOutputSamples = remainingSamples;
+    return numSamples;
+}
+
+/* Read short data out of the stream.  Sometimes no data will be available, and zero
+   is returned, which is not an error condition. */
+int sonicReadShortFromStream(
+    sonicStream stream,
+    short *samples,
+    int maxSamples)
+{
+    int numSamples = stream->numOutputSamples;
+    int remainingSamples = 0;
+
+    if(numSamples == 0) {
+	return 0;
+    }
+    if(numSamples > maxSamples) {
+	remainingSamples = numSamples - maxSamples;
+	numSamples = maxSamples;
+    }
+    memcpy(samples, stream->outputBuffer, numSamples*sizeof(short)*stream->numChannels);
+    if(remainingSamples > 0) {
+	memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
+	    remainingSamples*sizeof(short)*stream->numChannels);
+    }
+    stream->numOutputSamples = remainingSamples;
+    return numSamples;
+}
+
+/* Read unsigned char data out of the stream.  Sometimes no data will be available, and zero
+   is returned, which is not an error condition. */
+int sonicReadUnsignedCharFromStream(
+    sonicStream stream,
+    unsigned char *samples,
+    int maxSamples)
+{
+    int numSamples = stream->numOutputSamples;
+    int remainingSamples = 0;
+    short *buffer;
+    int count;
+
+    if(numSamples == 0) {
+	return 0;
+    }
+    if(numSamples > maxSamples) {
+	remainingSamples = numSamples - maxSamples;
+	numSamples = maxSamples;
+    }
+    buffer = stream->outputBuffer;
+    count = numSamples*stream->numChannels;
+    while(count--) {
+	*samples++ = (char)((*buffer++) >> 8) + 128;
+    }
+    if(remainingSamples > 0) {
+	memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
+	    remainingSamples*sizeof(short)*stream->numChannels);
+    }
+    stream->numOutputSamples = remainingSamples;
+    return numSamples;
+}
+
+/* Force the sonic stream to generate output using whatever data it currently
+   has.  No extra delay will be added to the output, but flushing in the middle of
+   words could introduce distortion. */
+int sonicFlushStream(
+    sonicStream stream)
+{
+    int maxRequired = stream->maxRequired;
+    int remainingSamples = stream->numInputSamples;
+    float speed = stream->speed/stream->pitch;
+    float rate = stream->rate*stream->pitch;
+    int expectedOutputSamples = stream->numOutputSamples +
+	(int)((remainingSamples/speed + stream->numPitchSamples)/rate + 0.5f);
+
+    /* Add enough silence to flush both input and pitch buffers. */
+    if(!enlargeInputBufferIfNeeded(stream, remainingSamples + 2*maxRequired)) {
+        return 0;
+    }
+    memset(stream->inputBuffer + remainingSamples*stream->numChannels, 0,
+	2*maxRequired*sizeof(short)*stream->numChannels);
+    stream->numInputSamples += 2*maxRequired;
+    if(!sonicWriteShortToStream(stream, NULL, 0)) {
+	return 0;
+    }
+    /* Throw away any extra samples we generated due to the silence we added */
+    if(stream->numOutputSamples > expectedOutputSamples) {
+	stream->numOutputSamples = expectedOutputSamples;
+    }
+    /* Empty input and pitch buffers */
+    stream->numInputSamples = 0;
+    stream->remainingInputToCopy = 0;
+    stream->numPitchSamples = 0;
+    return 1;
+}
+
+/* Return the number of samples in the output buffer */
+int sonicSamplesAvailable(
+   sonicStream stream)
+{
+    return stream->numOutputSamples;
+}
+
+/* If skip is greater than one, average skip samples together and write them to
+   the down-sample buffer.  If numChannels is greater than one, mix the channels
+   together as we down sample. */
+static void downSampleInput(
+    sonicStream stream,
+    short *samples,
+    int skip)
+{
+    int numSamples = stream->maxRequired/skip;
+    int samplesPerValue = stream->numChannels*skip;
+    int i, j;
+    int value;
+    short *downSamples = stream->downSampleBuffer;
+
+    for(i = 0; i < numSamples; i++) {
+	value = 0;
+        for(j = 0; j < samplesPerValue; j++) {
+	    value += *samples++;
+	}
+	value /= samplesPerValue;
+        *downSamples++ = value;
+    }
+}
+
+/* Find the best frequency match in the range, and given a sample skip multiple.
+   For now, just find the pitch of the first channel. */
+static int findPitchPeriodInRange(
+    short *samples,
+    int minPeriod,
+    int maxPeriod,
+    int *retMinDiff,
+    int *retMaxDiff)
+{
+    int period, bestPeriod = 0, worstPeriod = 255;
+    short *s, *p, sVal, pVal;
+    unsigned long diff, minDiff = 1, maxDiff = 0;
+    int i;
+
+    for(period = minPeriod; period <= maxPeriod; period++) {
+	diff = 0;
+	s = samples;
+	p = samples + period;
+	for(i = 0; i < period; i++) {
+	    sVal = *s++;
+	    pVal = *p++;
+	    diff += sVal >= pVal? (unsigned short)(sVal - pVal) :
+	        (unsigned short)(pVal - sVal);
+	}
+	/* Note that the highest number of samples we add into diff will be less
+	   than 256, since we skip samples.  Thus, diff is a 24 bit number, and
+	   we can safely multiply by numSamples without overflow */
+	if(diff*bestPeriod < minDiff*period) {
+	    minDiff = diff;
+	    bestPeriod = period;
+	}
+	if(diff*worstPeriod > maxDiff*period) {
+	    maxDiff = diff;
+	    worstPeriod = period;
+	}
+    }
+    *retMinDiff = minDiff/bestPeriod;
+    *retMaxDiff = maxDiff/worstPeriod;
+    return bestPeriod;
+}
+
+/* At abrupt ends of voiced words, we can have pitch periods that are better
+   approximated by the previous pitch period estimate.  Try to detect this case. */
+static int prevPeriodBetter(
+    sonicStream stream,
+    int period,
+    int minDiff,
+    int maxDiff,
+    int preferNewPeriod)
+{
+    if(minDiff == 0 || stream->prevPeriod == 0) {
+	return 0;
+    }
+    if(preferNewPeriod) {
+	if(maxDiff > minDiff*3) {
+	    /* Got a reasonable match this period */
+	    return 0;
+	}
+	if(minDiff*2 <= stream->prevMinDiff*3) {
+	    /* Mismatch is not that much greater this period */
+	    return 0;
+	}
+    } else {
+	if(minDiff <= stream->prevMinDiff) {
+	    return 0;
+	}
+    }
+    return 1;
+}
+
+/* Find the pitch period.  This is a critical step, and we may have to try
+   multiple ways to get a good answer.  This version uses AMDF.  To improve
+   speed, we down sample by an integer factor get in the 11KHz range, and then
+   do it again with a narrower frequency range without down sampling */
+static int findPitchPeriod(
+    sonicStream stream,
+    short *samples,
+    int preferNewPeriod)
+{
+    int minPeriod = stream->minPeriod;
+    int maxPeriod = stream->maxPeriod;
+    int sampleRate = stream->sampleRate;
+    int minDiff, maxDiff, retPeriod;
+    int skip = 1;
+    int period;
+
+    if(sampleRate > SONIC_AMDF_FREQ && stream->quality == 0) {
+	skip = sampleRate/SONIC_AMDF_FREQ;
+    }
+    if(stream->numChannels == 1 && skip == 1) {
+	period = findPitchPeriodInRange(samples, minPeriod, maxPeriod, &minDiff, &maxDiff);
+    } else {
+	downSampleInput(stream, samples, skip);
+	period = findPitchPeriodInRange(stream->downSampleBuffer, minPeriod/skip,
+	    maxPeriod/skip, &minDiff, &maxDiff);
+	if(skip != 1) {
+	    period *= skip;
+	    minPeriod = period - (skip << 2);
+	    maxPeriod = period + (skip << 2);
+	    if(minPeriod < stream->minPeriod) {
+		minPeriod = stream->minPeriod;
+	    }
+	    if(maxPeriod > stream->maxPeriod) {
+		maxPeriod = stream->maxPeriod;
+	    }
+	    if(stream->numChannels == 1) {
+		period = findPitchPeriodInRange(samples, minPeriod, maxPeriod,
+		    &minDiff, &maxDiff);
+	    } else {
+		downSampleInput(stream, samples, 1);
+		period = findPitchPeriodInRange(stream->downSampleBuffer, minPeriod,
+		    maxPeriod, &minDiff, &maxDiff);
+	    }
+	}
+    }
+    if(prevPeriodBetter(stream, period, minDiff, maxDiff, preferNewPeriod)) {
+        retPeriod = stream->prevPeriod;
+    } else {
+	retPeriod = period;
+    }
+    stream->prevMinDiff = minDiff;
+    stream->prevPeriod = period;
+    return retPeriod;
+}
+
+/* Overlap two sound segments, ramp the volume of one down, while ramping the
+   other one from zero up, and add them, storing the result at the output. */
+static void overlapAdd(
+    int numSamples,
+    int numChannels,
+    short *out,
+    short *rampDown,
+    short *rampUp)
+{
+    short *o, *u, *d;
+    int i, t;
+
+    for(i = 0; i < numChannels; i++) {
+	o = out + i;
+	u = rampUp + i;
+	d = rampDown + i;
+	for(t = 0; t < numSamples; t++) {
+#ifdef SONIC_USE_SIN
+	    float ratio = sin(t*M_PI/(2*numSamples));
+	    *o = *d*(1.0f - ratio) + *u*ratio;
+#else
+	    *o = (*d*(numSamples - t) + *u*t)/numSamples;
+#endif
+	    o += numChannels;
+	    d += numChannels;
+	    u += numChannels;
+	}
+    }
+}
+
+/* Overlap two sound segments, ramp the volume of one down, while ramping the
+   other one from zero up, and add them, storing the result at the output. */
+static void overlapAddWithSeparation(
+    int numSamples,
+    int numChannels,
+    int separation,
+    short *out,
+    short *rampDown,
+    short *rampUp)
+{
+    short *o, *u, *d;
+    int i, t;
+
+    for(i = 0; i < numChannels; i++) {
+	o = out + i;
+	u = rampUp + i;
+	d = rampDown + i;
+	for(t = 0; t < numSamples + separation; t++) {
+	    if(t < separation) {
+		*o = *d*(numSamples - t)/numSamples;
+		d += numChannels;
+	    } else if(t < numSamples) {
+		*o = (*d*(numSamples - t) + *u*(t - separation))/numSamples;
+		d += numChannels;
+		u += numChannels;
+	    } else {
+		*o = *u*(t - separation)/numSamples;
+		u += numChannels;
+	    }
+	    o += numChannels;
+	}
+    }
+}
+
+/* Just move the new samples in the output buffer to the pitch buffer */
+static int moveNewSamplesToPitchBuffer(
+    sonicStream stream,
+    int originalNumOutputSamples)
+{
+    int numSamples = stream->numOutputSamples - originalNumOutputSamples;
+    int numChannels = stream->numChannels;
+
+    if(stream->numPitchSamples + numSamples > stream->pitchBufferSize) {
+	stream->pitchBufferSize += (stream->pitchBufferSize >> 1) + numSamples;
+	stream->pitchBuffer = (short *)realloc(stream->pitchBuffer,
+	    stream->pitchBufferSize*sizeof(short)*numChannels);
+	if(stream->pitchBuffer == NULL) {
+	    return 0;
+	}
+    }
+    memcpy(stream->pitchBuffer + stream->numPitchSamples*numChannels,
+        stream->outputBuffer + originalNumOutputSamples*numChannels,
+	numSamples*sizeof(short)*numChannels);
+    stream->numOutputSamples = originalNumOutputSamples;
+    stream->numPitchSamples += numSamples;
+    return 1;
+}
+
+/* Remove processed samples from the pitch buffer. */
+static void removePitchSamples(
+    sonicStream stream,
+    int numSamples)
+{
+    int numChannels = stream->numChannels;
+    short *source = stream->pitchBuffer + numSamples*numChannels;
+
+    if(numSamples == 0) {
+	return;
+    }
+    if(numSamples != stream->numPitchSamples) {
+	memmove(stream->pitchBuffer, source, (stream->numPitchSamples -
+	    numSamples)*sizeof(short)*numChannels);
+    }
+    stream->numPitchSamples -= numSamples;
+}
+
+/* Change the pitch.  The latency this introduces could be reduced by looking at
+   past samples to determine pitch, rather than future. */
+static int adjustPitch(
+    sonicStream stream,
+    int originalNumOutputSamples)
+{
+    float pitch = stream->pitch;
+    int numChannels = stream->numChannels;
+    int period, newPeriod, separation;
+    int position = 0;
+    short *out, *rampDown, *rampUp;
+
+    if(stream->numOutputSamples == originalNumOutputSamples) {
+	return 1;
+    }
+    if(!moveNewSamplesToPitchBuffer(stream, originalNumOutputSamples)) {
+	return 0;
+    }
+    while(stream->numPitchSamples - position >= stream->maxRequired) {
+	period = findPitchPeriod(stream, stream->pitchBuffer + position*numChannels, 0);
+	newPeriod = period/pitch;
+	if(!enlargeOutputBufferIfNeeded(stream, newPeriod)) {
+	    return 0;
+	}
+	out = stream->outputBuffer + stream->numOutputSamples*numChannels;
+	if(pitch >= 1.0f) {
+	    rampDown = stream->pitchBuffer + position*numChannels;
+	    rampUp = stream->pitchBuffer + (position + period - newPeriod)*numChannels;
+	    overlapAdd(newPeriod, numChannels, out, rampDown, rampUp);
+	} else {
+	    rampDown = stream->pitchBuffer + position*numChannels;
+	    rampUp = stream->pitchBuffer + position*numChannels;
+	    separation = newPeriod - period;
+	    overlapAddWithSeparation(period, numChannels, separation, out, rampDown, rampUp);
+	}
+	stream->numOutputSamples += newPeriod;
+	position += period;
+    }
+    removePitchSamples(stream, position);
+    return 1;
+}
+
+/* Interpolate the new output sample. */
+static short interpolate(
+    sonicStream stream,
+    short *in,
+    int oldSampleRate,
+    int newSampleRate)
+{
+    short left = *in;
+    short right = in[stream->numChannels];
+    int position = stream->newRatePosition*oldSampleRate;
+    int leftPosition = stream->oldRatePosition*newSampleRate;
+    int rightPosition = (stream->oldRatePosition + 1)*newSampleRate;
+    int ratio = rightPosition - position;
+    int width = rightPosition - leftPosition;
+
+    return (ratio*left + (width - ratio)*right)/width;
+}
+
+/* Change the rate. */
+static int adjustRate(
+    sonicStream stream,
+    float rate,
+    int originalNumOutputSamples)
+{
+    int newSampleRate = stream->sampleRate/rate;
+    int oldSampleRate = stream->sampleRate;
+    int numChannels = stream->numChannels;
+    int position = 0;
+    short *in, *out;
+    int i;
+
+    /* Set these values to help with the integer math */
+    while(newSampleRate > (1 << 14) || oldSampleRate > (1 << 14)) {
+	newSampleRate >>= 1;
+	oldSampleRate >>= 1;
+    }
+    if(stream->numOutputSamples == originalNumOutputSamples) {
+	return 1;
+    }
+    if(!moveNewSamplesToPitchBuffer(stream, originalNumOutputSamples)) {
+	return 0;
+    }
+    /* Leave at least one pitch sample in the buffer */
+    for(position = 0; position < stream->numPitchSamples - 1; position++) {
+	while((stream->oldRatePosition + 1)*newSampleRate >
+	        stream->newRatePosition*oldSampleRate) {
+	    if(!enlargeOutputBufferIfNeeded(stream, 1)) {
+		return 0;
+	    }
+	    out = stream->outputBuffer + stream->numOutputSamples*numChannels;
+	    in = stream->pitchBuffer + position;
+	    for(i = 0; i < numChannels; i++) {
+		*out++ = interpolate(stream, in, oldSampleRate, newSampleRate);
+		in++;
+	    }
+	    stream->newRatePosition++;
+	    stream->numOutputSamples++;
+	}
+	stream->oldRatePosition++;
+	if(stream->oldRatePosition == oldSampleRate) {
+	    stream->oldRatePosition = 0;
+	    if(stream->newRatePosition != newSampleRate) {
+		fprintf(stderr,
+		    "Assertion failed: stream->newRatePosition != newSampleRate\n");
+		exit(1);
+	    }
+	    stream->newRatePosition = 0;
+	}
+    }
+    removePitchSamples(stream, position);
+    return 1;
+}
+
+
+/* Skip over a pitch period, and copy period/speed samples to the output */
+static int skipPitchPeriod(
+    sonicStream stream,
+    short *samples,
+    float speed,
+    int period)
+{
+    long newSamples;
+    int numChannels = stream->numChannels;
+
+    if(speed >= 2.0f) {
+	newSamples = period/(speed - 1.0f);
+    } else {
+	newSamples = period;
+	stream->remainingInputToCopy = period*(2.0f - speed)/(speed - 1.0f);
+    }
+    if(!enlargeOutputBufferIfNeeded(stream, newSamples)) {
+	return 0;
+    }
+    overlapAdd(newSamples, numChannels, stream->outputBuffer +
+        stream->numOutputSamples*numChannels, samples, samples + period*numChannels);
+    stream->numOutputSamples += newSamples;
+    return newSamples;
+}
+
+/* Insert a pitch period, and determine how much input to copy directly. */
+static int insertPitchPeriod(
+    sonicStream stream,
+    short *samples,
+    float speed,
+    int period)
+{
+    long newSamples;
+    short *out;
+    int numChannels = stream->numChannels;
+
+    if(speed < 0.5f) {
+        newSamples = period*speed/(1.0f - speed);
+    } else {
+        newSamples = period;
+	stream->remainingInputToCopy = period*(2.0f*speed - 1.0f)/(1.0f - speed);
+    }
+    if(!enlargeOutputBufferIfNeeded(stream, period + newSamples)) {
+	return 0;
+    }
+    out = stream->outputBuffer + stream->numOutputSamples*numChannels;
+    memcpy(out, samples, period*sizeof(short)*numChannels);
+    out = stream->outputBuffer + (stream->numOutputSamples + period)*numChannels;
+    overlapAdd(newSamples, numChannels, out, samples + period*numChannels, samples);
+    stream->numOutputSamples += period + newSamples;
+    return newSamples;
+}
+
+/* Resample as many pitch periods as we have buffered on the input.  Return 0 if
+   we fail to resize an input or output buffer.  Also scale the output by the volume. */
+static int changeSpeed(
+    sonicStream stream,
+    float speed)
+{
+    short *samples;
+    int numSamples = stream->numInputSamples;
+    int position = 0, period, newSamples;
+    int maxRequired = stream->maxRequired;
+
+    if(stream->numInputSamples < maxRequired) {
+	return 1;
+    }
+    do {
+	if(stream->remainingInputToCopy > 0) {
+            newSamples = copyInputToOutput(stream, position);
+	    position += newSamples;
+	} else {
+	    samples = stream->inputBuffer + position*stream->numChannels;
+	    period = findPitchPeriod(stream, samples, 1);
+	    if(speed > 1.0) {
+		newSamples = skipPitchPeriod(stream, samples, speed, period);
+		position += period + newSamples;
+	    } else {
+		newSamples = insertPitchPeriod(stream, samples, speed, period);
+		position += newSamples;
+	    }
+	}
+	if(newSamples == 0) {
+	    return 0; /* Failed to resize output buffer */
+	}
+    } while(position + maxRequired <= numSamples);
+    removeInputSamples(stream, position);
+    return 1;
+}
+
+/* Resample as many pitch periods as we have buffered on the input.  Return 0 if
+   we fail to resize an input or output buffer.  Also scale the output by the volume. */
+static int processStreamInput(
+    sonicStream stream)
+{
+    int originalNumOutputSamples = stream->numOutputSamples;
+    float speed = stream->speed/stream->pitch;
+    float rate = stream->rate;
+
+    if(!stream->useChordPitch) {
+	rate *= stream->pitch;
+    }
+    if(speed > 1.00001 || speed < 0.99999) {
+	changeSpeed(stream, speed);
+    } else {
+        if(!copyToOutput(stream, stream->inputBuffer, stream->numInputSamples)) {
+	    return 0;
+	}
+	stream->numInputSamples = 0;
+    }
+    if(stream->useChordPitch) {
+	if(stream->pitch != 1.0f) {
+	    if(!adjustPitch(stream, originalNumOutputSamples)) {
+		return 0;
+	    }
+	}
+    } else if(rate != 1.0f) {
+	if(!adjustRate(stream, rate, originalNumOutputSamples)) {
+	    return 0;
+	}
+    }
+    if(stream->volume != 1.0f) {
+	/* Adjust output volume. */
+        scaleSamples(stream->outputBuffer + originalNumOutputSamples*stream->numChannels,
+	    (stream->numOutputSamples - originalNumOutputSamples)*stream->numChannels,
+	    stream->volume);
+    }
+    return 1;
+}
+
+/* Write floating point data to the input buffer and process it. */
+int sonicWriteFloatToStream(
+    sonicStream stream,
+    float *samples,
+    int numSamples)
+{
+    if(!addFloatSamplesToInputBuffer(stream, samples, numSamples)) {
+	return 0;
+    }
+    return processStreamInput(stream);
+}
+
+/* Simple wrapper around sonicWriteFloatToStream that does the short to float
+   conversion for you. */
+int sonicWriteShortToStream(
+    sonicStream stream,
+    short *samples,
+    int numSamples)
+{
+    if(!addShortSamplesToInputBuffer(stream, samples, numSamples)) {
+	return 0;
+    }
+    return processStreamInput(stream);
+}
+
+/* Simple wrapper around sonicWriteFloatToStream that does the unsigned char to float
+   conversion for you. */
+int sonicWriteUnsignedCharToStream(
+    sonicStream stream,
+    unsigned char *samples,
+    int numSamples)
+{
+    if(!addUnsignedCharSamplesToInputBuffer(stream, samples, numSamples)) {
+	return 0;
+    }
+    return processStreamInput(stream);
+}
+
+/* This is a non-stream oriented interface to just change the speed of a sound sample */
+int sonicChangeFloatSpeed(
+    float *samples,
+    int numSamples,
+    float speed,
+    float pitch,
+    float rate,
+    float volume,
+    int useChordPitch,
+    int sampleRate,
+    int numChannels)
+{
+    sonicStream stream = sonicCreateStream(sampleRate, numChannels);
+
+    sonicSetSpeed(stream, speed);
+    sonicSetPitch(stream, pitch);
+    sonicSetRate(stream, rate);
+    sonicSetVolume(stream, volume);
+    sonicSetChordPitch(stream, useChordPitch);
+    sonicWriteFloatToStream(stream, samples, numSamples);
+    sonicFlushStream(stream);
+    numSamples = sonicSamplesAvailable(stream);
+    sonicReadFloatFromStream(stream, samples, numSamples);
+    sonicDestroyStream(stream);
+    return numSamples;
+}
+
+/* This is a non-stream oriented interface to just change the speed of a sound sample */
+int sonicChangeShortSpeed(
+    short *samples,
+    int numSamples,
+    float speed,
+    float pitch,
+    float rate,
+    float volume,
+    int useChordPitch,
+    int sampleRate,
+    int numChannels)
+{
+    sonicStream stream = sonicCreateStream(sampleRate, numChannels);
+
+    sonicSetSpeed(stream, speed);
+    sonicSetPitch(stream, pitch);
+    sonicSetRate(stream, rate);
+    sonicSetVolume(stream, volume);
+    sonicSetChordPitch(stream, useChordPitch);
+    sonicWriteShortToStream(stream, samples, numSamples);
+    sonicFlushStream(stream);
+    numSamples = sonicSamplesAvailable(stream);
+    sonicReadShortFromStream(stream, samples, numSamples);
+    sonicDestroyStream(stream);
+    return numSamples;
+}
diff --git a/sonic/sonic.h b/sonic/sonic.h
new file mode 100644
index 0000000..9b44e68
--- /dev/null
+++ b/sonic/sonic.h
@@ -0,0 +1,147 @@
+/* Sonic library
+   Copyright 2010
+   Bill Cox
+   This file is part of the Sonic Library.
+
+   This file is licensed under the Apache 2.0 license.
+*/
+
+/*
+The Sonic Library implements a new algorithm invented by Bill Cox for the
+specific purpose of speeding up speech by high factors at high quality.  It
+generates smooth speech at speed up factors as high as 6X, possibly more.  It is
+also capable of slowing down speech, and generates high quality results
+regardless of the speed up or slow down factor.  For speeding up speech by 2X or
+more, the following equation is used:
+
+    newSamples = period/(speed - 1.0)
+    scale = 1.0/newSamples;
+
+where period is the current pitch period, determined using AMDF or any other
+pitch estimator, and speed is the speedup factor.  If the current position in
+the input stream is pointed to by "samples", and the current output stream
+position is pointed to by "out", then newSamples number of samples can be
+generated with:
+
+    out[t] = (samples[t]*(newSamples - t) + samples[t + period]*t)/newSamples;
+
+where t = 0 to newSamples - 1.
+
+For speed factors < 2X, the PICOLA algorithm is used.  The above
+algorithm is first used to double the speed of one pitch period.  Then, enough
+input is directly copied from the input to the output to achieve the desired
+speed up facter, where 1.0 < speed < 2.0.  The amount of data copied is derived:
+
+    speed = (2*period + length)/(period + length)
+    speed*length + speed*period = 2*period + length
+    length(speed - 1) = 2*period - speed*period
+    length = period*(2 - speed)/(speed - 1)
+
+For slowing down speech where 0.5 < speed < 1.0, a pitch period is inserted into
+the output twice, and length of input is copied from the input to the output
+until the output desired speed is reached.  The length of data copied is:
+
+    length = period*(speed - 0.5)/(1 - speed)
+
+For slow down factors below 0.5, no data is copied, and an algorithm
+similar to high speed factors is used.
+*/
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+/* Uncomment this to use sin-wav based overlap add which in theory can improve
+   sound quality slightly, at the expense of lots of floating point math. */
+/* #define SONIC_USE_SIN */
+
+/* This specifies the range of voice pitches we try to match.
+   Note that if we go lower than 65, we could overflow in findPitchInRange */
+#define SONIC_MIN_PITCH 65
+#define SONIC_MAX_PITCH 400
+
+/* These are used to down-sample some inputs to improve speed */
+#define SONIC_AMDF_FREQ 4000
+
+struct sonicStreamStruct;
+typedef struct sonicStreamStruct *sonicStream;
+
+/* For all of the following functions, numChannels is multiplied by numSamples
+   to determine the actual number of values read or returned. */
+
+/* Create a sonic stream.  Return NULL only if we are out of memory and cannot
+  allocate the stream. Set numChannels to 1 for mono, and 2 for stereo. */
+sonicStream sonicCreateStream(int sampleRate, int numChannels);
+/* Destroy the sonic stream. */
+void sonicDestroyStream(sonicStream stream);
+/* Use this to write floating point data to be speed up or down into the stream.
+   Values must be between -1 and 1.  Return 0 if memory realloc failed, otherwise 1 */
+int sonicWriteFloatToStream(sonicStream stream, float *samples, int numSamples);
+/* Use this to write 16-bit data to be speed up or down into the stream.
+   Return 0 if memory realloc failed, otherwise 1 */
+int sonicWriteShortToStream(sonicStream stream, short *samples, int numSamples);
+/* Use this to write 8-bit unsigned data to be speed up or down into the stream.
+   Return 0 if memory realloc failed, otherwise 1 */
+int sonicWriteUnsignedCharToStream(sonicStream stream, unsigned char *samples, int numSamples);
+/* Use this to read floating point data out of the stream.  Sometimes no data
+   will be available, and zero is returned, which is not an error condition. */
+int sonicReadFloatFromStream(sonicStream stream, float *samples, int maxSamples);
+/* Use this to read 16-bit data out of the stream.  Sometimes no data will
+   be available, and zero is returned, which is not an error condition. */
+int sonicReadShortFromStream(sonicStream stream, short *samples, int maxSamples);
+/* Use this to read 8-bit unsigned data out of the stream.  Sometimes no data will
+   be available, and zero is returned, which is not an error condition. */
+int sonicReadUnsignedCharFromStream(sonicStream stream, unsigned char *samples, int maxSamples);
+/* Force the sonic stream to generate output using whatever data it currently
+   has.  No extra delay will be added to the output, but flushing in the middle of
+   words could introduce distortion. */
+int sonicFlushStream(sonicStream stream);
+/* Return the number of samples in the output buffer */
+int sonicSamplesAvailable(sonicStream stream);
+/* Get the speed of the stream. */
+float sonicGetSpeed(sonicStream stream);
+/* Set the speed of the stream. */
+void sonicSetSpeed(sonicStream stream, float speed);
+/* Get the pitch of the stream. */
+float sonicGetPitch(sonicStream stream);
+/* Set the pitch of the stream. */
+void sonicSetPitch(sonicStream stream, float pitch);
+/* Get the rate of the stream. */
+float sonicGetRate(sonicStream stream);
+/* Set the rate of the stream. */
+void sonicSetRate(sonicStream stream, float rate);
+/* Get the scaling factor of the stream. */
+float sonicGetVolume(sonicStream stream);
+/* Set the scaling factor of the stream. */
+void sonicSetVolume(sonicStream stream, float volume);
+/* Get the chord pitch setting. */
+int sonicGetChordPitch(sonicStream stream);
+/* Set chord pitch mode on or off.  Default is off.  See the documentation
+   page for a description of this feature. */
+void sonicSetChordPitch(sonicStream stream, int useChordPitch);
+/* Get the quality setting. */
+int sonicGetQuality(sonicStream stream);
+/* Set the "quality".  Default 0 is virtually as good as 1, but very much faster. */
+void sonicSetQuality(sonicStream stream, int quality);
+/* Get the sample rate of the stream. */
+int sonicGetSampleRate(sonicStream stream);
+/* Set the sample rate of the stream.  This will drop any samples that have not been read. */
+void sonicSetSampleRate(sonicStream stream, int sampleRate);
+/* Get the number of channels. */
+int sonicGetNumChannels(sonicStream stream);
+/* Set the number of channels.  This will drop any samples that have not been read. */
+void sonicSetNumChannels(sonicStream stream, int numChannels);
+/* This is a non-stream oriented interface to just change the speed of a sound
+   sample.  It works in-place on the sample array, so there must be at least
+   speed*numSamples available space in the array. Returns the new number of samples. */
+int sonicChangeFloatSpeed(float *samples, int numSamples, float speed, float pitch,
+    float rate, float volume, int useChordPitch, int sampleRate, int numChannels);
+/* This is a non-stream oriented interface to just change the speed of a sound
+   sample.  It works in-place on the sample array, so there must be at least
+   speed*numSamples available space in the array. Returns the new number of samples. */
+int sonicChangeShortSpeed(short *samples, int numSamples, float speed, float pitch,
+    float rate, float volume, int useChordPitch, int sampleRate, int numChannels);
+
+#ifdef  __cplusplus
+}
+#endif
diff --git a/sonic/wave.c b/sonic/wave.c
new file mode 100644
index 0000000..05bbf1f
--- /dev/null
+++ b/sonic/wave.c
@@ -0,0 +1,373 @@
+/* Sonic library
+   Copyright 2010
+   Bill Cox
+   This file is part of the Sonic Library.
+
+   This file is licensed under the Apache 2.0 license.
+*/
+
+/*
+This file supports read/write wave files.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "wave.h"
+
+#define WAVE_BUF_LEN 4096
+
+struct waveFileStruct {
+    int numChannels;
+    int sampleRate;
+    FILE *soundFile;
+    int bytesWritten; /* The number of bytes written so far, including header */
+    int failed;
+    int isInput;
+};
+
+/* Write a string to a file. */
+static void writeBytes(
+    waveFile file,
+    void *bytes,
+    int length)
+{
+    size_t bytesWritten;
+
+    if(file->failed) {
+        return;
+    }
+    bytesWritten = fwrite(bytes, sizeof(char), length, file->soundFile);
+    if(bytesWritten != length) {
+        fprintf(stderr, "Unable to write to output file");
+        file->failed = 1;
+    }
+    file->bytesWritten += bytesWritten;
+}
+
+/* Write a string to a file. */
+static void writeString(
+    waveFile file,
+    char *string)
+{
+    writeBytes(file, string, strlen(string));
+}
+
+/* Write an integer to a file in little endian order. */
+static void writeInt(
+    waveFile file,
+    int value)
+{
+    char bytes[4];
+    int i;
+
+    for(i = 0; i < 4; i++) {
+        bytes[i] = value;
+        value >>= 8;
+    }
+    writeBytes(file, bytes, 4);
+}
+
+/* Write a short integer to a file in little endian order. */
+static void writeShort(
+    waveFile file,
+    short value)
+{
+    char bytes[2];
+    int i;
+
+    for(i = 0; i < 2; i++) {
+        bytes[i] = value;
+        value >>= 8;
+    }
+    writeBytes(file, bytes, 2);
+}
+
+/* Read bytes from the input file. Return the number of bytes actually read. */
+static int readBytes(
+    waveFile file,
+    void *bytes,
+    int length)
+{
+    if(file->failed) {
+        return 0;
+    }
+    return fread(bytes, sizeof(char), length, file->soundFile);
+}
+
+/* Read an exact number of bytes from the input file. */
+static void readExactBytes(
+    waveFile file,
+    void *bytes,
+    int length)
+{
+    int numRead;
+
+    if(file->failed) {
+        return;
+    }
+    numRead = fread(bytes, sizeof(char), length, file->soundFile);
+    if(numRead != length) {
+        fprintf(stderr, "Failed to read requested bytes from input file\n");
+        file->failed = 1;
+    }
+}
+
+/* Read an integer from the input file */
+static int readInt(
+    waveFile file)
+{
+    unsigned char bytes[4];
+    int value = 0, i;
+
+    readExactBytes(file, bytes, 4);
+    for(i = 3; i >= 0; i--) {
+        value <<= 8;
+        value |= bytes[i];
+    }
+    return value;
+}
+
+/* Read a short from the input file */
+static int readShort(
+    waveFile file)
+{
+    unsigned char bytes[2];
+    int value = 0, i;
+
+    readExactBytes(file, bytes, 2);
+    for(i = 1; i >= 0; i--) {
+        value <<= 8;
+        value |= bytes[i];
+    }
+    return value;
+}
+
+/* Read a string from the input and compare it to an expected string. */
+static void expectString(
+    waveFile file,
+    char *expectedString)
+{
+    char buf[11]; /* Be sure that we never call with a longer string */
+    int length = strlen(expectedString);
+
+    if(length > 10) {
+        fprintf(stderr, "Internal error: expected string too long\n");
+        file->failed = 1;
+    } else {
+        readExactBytes(file, buf, length);
+        buf[length] = '\0';
+        if(strcmp(expectedString, buf)) {
+            fprintf(stderr, "Unsupported wave file format\n");
+            file->failed = 1;
+        }
+    }
+}
+
+/* Write the header of the wave file. */
+static void writeHeader(
+    waveFile file,
+    int sampleRate)
+{
+    /* write the wav file per the wav file format */
+    writeString(file, "RIFF"); /* 00 - RIFF */
+    /* We have to fseek and overwrite this later when we close the file because */
+    /* we don't know how big it is until then. */
+    writeInt(file, 36 /* + dataLength */); /* 04 - how big is the rest of this file? */
+    writeString(file, "WAVE"); /* 08 - WAVE */
+    writeString(file, "fmt "); /* 12 - fmt */
+    writeInt(file, 16); /* 16 - size of this chunk */
+    writeShort(file, 1); /* 20 - what is the audio format? 1 for PCM = Pulse Code Modulation */
+    writeShort(file, 1); /* 22 - mono or stereo? 1 or 2?  (or 5 or ???) */
+    writeInt(file, sampleRate); /* 24 - samples per second (numbers per second) */
+    writeInt(file, sampleRate * 2); /* 28 - bytes per second */
+    writeShort(file, 2); /* 32 - # of bytes in one sample, for all channels */
+    writeShort(file, 16); /* 34 - how many bits in a sample(number)?  usually 16 or 24 */
+    writeString(file, "data"); /* 36 - data */
+    writeInt(file, 0); /* 40 - how big is this data chunk */
+}
+
+/* Read the header of the wave file. */
+static int readHeader(
+    waveFile file)
+{
+    int data;
+
+    expectString(file, "RIFF");
+    data = readInt(file); /* 04 - how big is the rest of this file? */
+    expectString(file, "WAVE"); /* 08 - WAVE */
+    expectString(file, "fmt "); /* 12 - fmt */
+    int chunkSize = readInt(file); /* 16 or 18 - size of this chunk */
+    if(chunkSize != 16 && chunkSize != 18) {
+        fprintf(stderr, "Only basic wave files are supported\n");
+        return 0;
+    }
+    data = readShort(file); /* 20 - what is the audio format? 1 for PCM = Pulse Code Modulation */
+    if(data != 1) {
+        fprintf(stderr, "Only PCM wave files are supported\n");
+        return 0;
+    }
+    file->numChannels = readShort(file); /* 22 - mono or stereo? 1 or 2?  (or 5 or ???) */
+    file->sampleRate = readInt(file); /* 24 - samples per second (numbers per second) */
+    readInt(file); /* 28 - bytes per second */
+    readShort(file); /* 32 - # of bytes in one sample, for all channels */
+    data = readShort(file); /* 34 - how many bits in a sample(number)?  usually 16 or 24 */
+    if(data != 16) {
+        fprintf(stderr, "Only 16 bit PCM wave files are supported\n");
+        return 0;
+    }
+    if (chunkSize == 18) { /* ffmpeg writes 18, and so has 2 extra bytes here */
+        data = readShort(file);
+    }
+    expectString(file, "data"); /* 36 - data */
+    readInt(file); /* 40 - how big is this data chunk */
+    return 1;
+}
+
+/* Close the input or output file and free the waveFile. */
+static void closeFile(
+    waveFile file)
+{
+    FILE *soundFile = file->soundFile;
+
+    if(soundFile != NULL) {
+        fclose(soundFile);
+        file->soundFile = NULL;
+    }
+    free(file);
+}
+
+/* Open a 16-bit little-endian wav file for reading.  It may be mono or stereo. */
+waveFile openInputWaveFile(
+    char *fileName,
+    int *sampleRate,
+    int *numChannels)
+{
+    waveFile file;
+    FILE *soundFile = fopen(fileName, "rb");
+
+    if(soundFile == NULL) {
+	fprintf(stderr, "Unable to open wave file %s for reading\n", fileName);
+	return NULL;
+    }
+    file = (waveFile)calloc(1, sizeof(struct waveFileStruct));
+    file->soundFile = soundFile;
+    file->isInput = 1;
+    if(!readHeader(file)) {
+        closeFile(file);
+        return NULL;
+    }
+    *sampleRate = file->sampleRate;
+    *numChannels = file->numChannels;
+    return file;
+}
+
+/* Open a 16-bit little-endian wav file for writing.  It may be mono or stereo. */
+waveFile openOutputWaveFile(
+    char *fileName,
+    int sampleRate,
+    int numChannels)
+{
+    waveFile file;
+    FILE *soundFile = fopen(fileName, "wb");
+
+    if(soundFile == NULL) {
+	fprintf(stderr, "Unable to open wave file %s for writing\n", fileName);
+	return NULL;
+    }
+    file = (waveFile)calloc(1, sizeof(struct waveFileStruct));
+    file->soundFile = soundFile;
+    file->sampleRate = sampleRate;
+    file->numChannels = numChannels;
+    writeHeader(file, sampleRate);
+    if(file->failed) {
+        closeFile(file);
+        return NULL;
+    }
+    return file;
+}
+
+/* Close the sound file. */
+int closeWaveFile(
+    waveFile file)
+{
+    FILE *soundFile = file->soundFile;
+    int passed = 1;
+
+    if(!file->isInput) {
+        if(fseek(soundFile, 4, SEEK_SET) != 0) {
+            fprintf(stderr, "Failed to seek on input file.\n");
+            passed = 0;
+        } else {
+            /* Now update the file to have the correct size. */
+            writeInt(file, file->bytesWritten - 8);
+            if(file->failed) {
+                fprintf(stderr, "Failed to write wave file size.\n");
+                passed = 0;
+            }
+            if(fseek(soundFile, 40, SEEK_SET) != 0) {
+                fprintf(stderr, "Failed to seek on input file.\n");
+                passed = 0;
+            } else {
+                /* Now update the file to have the correct size. */
+                writeInt(file, file->bytesWritten - 48);
+                if(file->failed) {
+                    fprintf(stderr, "Failed to write wave file size.\n");
+                    passed = 0;
+                }
+            }
+        }
+    }
+    closeFile(file);
+    return passed;
+}
+
+/* Read from the wave file.  Return the number of samples read. */
+int readFromWaveFile(
+    waveFile file,
+    short *buffer,
+    int maxSamples)
+{
+    int i, bytesRead, samplesRead;
+    int bytePos = 0;
+    unsigned char bytes[WAVE_BUF_LEN];
+    short sample;
+
+    if(maxSamples*file->numChannels*2 > WAVE_BUF_LEN) {
+        maxSamples = WAVE_BUF_LEN/(file->numChannels*2);
+    }
+    bytesRead = readBytes(file, bytes, maxSamples*file->numChannels*2);
+    samplesRead = bytesRead/(file->numChannels*2);
+    for(i = 0; i < samplesRead*file->numChannels; i++) {
+        sample = bytes[bytePos++];
+        sample |= (unsigned int)bytes[bytePos++] << 8;
+        *buffer++ = sample;
+    }
+    return samplesRead;
+}
+
+/* Write to the wave file. */
+int writeToWaveFile(
+    waveFile file,
+    short *buffer,
+    int numSamples)
+{
+    int i;
+    int bytePos = 0;
+    unsigned char bytes[WAVE_BUF_LEN];
+    short sample;
+    int total = numSamples*file->numChannels;
+
+    for(i = 0; i < total; i++) {
+        if(bytePos == WAVE_BUF_LEN) {
+            writeBytes(file, bytes, bytePos);
+            bytePos = 0;
+        }
+        sample = buffer[i];
+        bytes[bytePos++] = sample;
+        bytes[bytePos++] = sample >> 8;
+    }
+    if(bytePos != 0) {
+        writeBytes(file, bytes, bytePos);
+    }
+    return file->failed;
+}
diff --git a/sonic/wave.h b/sonic/wave.h
new file mode 100644
index 0000000..aad45c5
--- /dev/null
+++ b/sonic/wave.h
@@ -0,0 +1,16 @@
+/* Sonic library
+   Copyright 2010
+   Bill Cox
+   This file is part of the Sonic Library.
+
+   This file is licensed under the Apache 2.0 license. */
+
+/* Support for reading and writing wave files. */
+
+typedef struct waveFileStruct *waveFile;
+
+waveFile openInputWaveFile(char *fileName, int *sampleRate, int *numChannels);
+waveFile openOutputWaveFile(char *fileName, int sampleRate, int numChannels);
+int closeWaveFile(waveFile file);
+int readFromWaveFile(waveFile file, short *buffer, int maxSamples);
+int writeToWaveFile(waveFile file, short *buffer, int numSamples);