commit f1b937ac7a5e3ced661f321d1283c2e6fd0ce22c Author: josch Date: Sat Jun 21 14:01:37 2014 +0200 initial commit diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ba67c42 --- /dev/null +++ b/Makefile @@ -0,0 +1,3 @@ +all: + gcc stride_fread.c -o stride + gcc concatenate_wav.c -o concatenate_wav diff --git a/concatenate_wav.c b/concatenate_wav.c new file mode 100644 index 0000000..96cc3be --- /dev/null +++ b/concatenate_wav.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include +#include + +void my_splice(int in, int out) { + int pipefd[2], nr, ret; + if (pipe(pipefd) < 0) + error(1, errno, "cannot create pipe"); + for (;;) { + nr = splice(in, 0, pipefd[1], 0, INT_MAX, 0); + if (nr == -1) + error(1, errno, "cannot read input"); + if (nr == 0) + break; + do { + ret = splice(pipefd[0], 0, out, 0, nr, 0); + if (ret == -1) + error(1, errno, "cannot write"); + nr -= ret; + } while (nr); + } + close(pipefd[0]); + close(pipefd[1]); +} + +int main(int argc, char **argv) { + int i; + char **argvp = argv+1; + int fd, ret; + ssize_t ret2; + for (i = 1; i < argc; i++, argvp++) { + fd = open(*argvp, O_RDONLY); + if (fd == -1) + error(1, errno, "cannot open file %s", *argvp); + ret = lseek(fd, 46, SEEK_SET); + if (ret == -1) + error(1, errno, "cannot seek in file %s", *argvp); + for (;;) { + ret2 = sendfile(STDOUT_FILENO, fd, 0, INT_MAX); + if (ret2 == -1) + error(1, errno, "cannot copy", *argvp); + if (ret2 < 4096) + break; + } + /* my_splice(fd, STDOUT_FILENO);*/ + ret = close(fd); + if (ret == -1) + error(1, errno, "cannot close file %s", *argvp); + } + return 0; +} diff --git a/concatenate_wav.sh b/concatenate_wav.sh new file mode 100755 index 0000000..1bf6058 --- /dev/null +++ b/concatenate_wav.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +#data_offset=$((0x$(xxd -plain -s 0x10 -l 4 $1 | sed 's/\(..\)\(..\)\(..\)\(..\)/\4\3\2\1/')+28)) +#channels=$((0x$(xxd -plain -s 0x16 -l 2 $1 | sed 's/\(..\)\(..\)/\2\1/'))) +#samples=$((0x$(xxd -plain -s 0x18 -l 4 $1 | sed 's/\(..\)\(..\)\(..\)\(..\)/\4\3\2\1/'))) +#bps=$((0x$(xxd -plain -s 0x22 -l 2 $1 | sed 's/\(..\)\(..\)/\2\1/'))) + +data=`xxd -plain -s 0x08 -l 28 $1` +offset=$((0x$(echo $data | sed 's/.\{16\}\(..\)\(..\)\(..\)\(..\).\{32\}/\4\3\2\1/')+28)) +dd if=$1 skip=1 bs=$offset 2>/dev/null +shift + +for f in $@; do + tmpdata=`xxd -plain -s 0x08 -l 28 $f` + if [ "$data" != "$tmpdata" ]; then + echo not matching wav properties for $f >&2 + exit + fi + dd if=$f skip=1 bs=$offset 2>/dev/null +done diff --git a/epub2audio.py b/epub2audio.py new file mode 100644 index 0000000..297f7ac --- /dev/null +++ b/epub2audio.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import zipfile +from lxml import etree +import sys +import os +from operator import itemgetter +from re import split +import multiprocessing, subprocess + +tempdir="/dev/shm" +cwd=os.getcwd() + +ns = { + 'n':'urn:oasis:names:tc:opendocument:xmlns:container', + 'pkg':'http://www.idpf.org/2007/opf', + 'dc':'http://purl.org/dc/elements/1.1/', + 'ncx':'http://www.daisy.org/z3986/2005/ncx/', + 'xhtml':'http://www.w3.org/1999/xhtml' +} + +if len(sys.argv) not in [2,3]: + print "usage: %d epub [number]" + exit(1) + +fzip = zipfile.ZipFile(sys.argv[1]) +txt = fzip.read('META-INF/container.xml') +tree = etree.fromstring(txt) +cfname = tree.xpath('n:rootfiles/n:rootfile/@full-path',namespaces=ns)[0] + +pdir = os.path.dirname(cfname) + +cf = fzip.read(cfname) +tree = etree.fromstring(cf) +ncxname = tree.xpath('/pkg:package/pkg:manifest/pkg:item[@id="ncx"]/@href',namespaces=ns)[0] + +ncx = fzip.read(os.path.join(pdir, ncxname)) +tree = etree.fromstring(ncx) +if len(sys.argv) == 2: + navpoints = tree.xpath('/ncx:ncx/ncx:navMap/ncx:navPoint',namespaces=ns) +else: + # the following xpath expression finds the subtree for the book we want to print + navpoints = tree.xpath("/ncx:ncx/ncx:navMap/ncx:navPoint[starts-with(ncx:navLabel/ncx:text, 'Nr. %s')]/ncx:navPoint"%sys.argv[2],namespaces=ns) + +title = tree.xpath("/ncx:ncx/ncx:navMap/ncx:navPoint/ncx:navLabel/ncx:text[starts-with(., 'Nr. %s')]/text()"%sys.argv[2], namespaces=ns)[0] + +dnavpoint = list() + +for navpoint in navpoints: + r = lambda expr: navpoint.xpath(expr, namespaces=ns)[0] + label = r('ncx:navLabel/ncx:text/text()') + if label not in ['Cover', 'PERRY RHODAN - die Serie', 'Impressum']: + order = int(r('@playOrder')) + content = r('ncx:content/@src') + dnavpoint.append((order, content)) + +# to be able to work offline, this needs the w3c-sgml-lib package +parser = etree.XMLParser(load_dtd=True) +i = 0 +tasks = [] +for _, pagename in sorted(dnavpoint, key=itemgetter(0)): + page = fzip.read(os.path.join(pdir, pagename)) + tree = etree.fromstring(page, parser) + paragraphs = tree.xpath('/xhtml:html/xhtml:body/xhtml:p', namespaces=ns) + for p in paragraphs: + p = split(r"(\xbb.+?\xab)", p.xpath('string()')) + for s in p: + s = s.strip(', .') + if s == '': + continue + if s == u'\xa0': + tasks.append(["ln", "-s", "%s/silence.wav"%cwd, "%s/%04d.wav"%(tempdir,i)]) + i+=1 + continue + + if s.startswith(u'\xbb') and s.endswith(u'\xab'): + tasks.append(["wine", "sapi2wav.exe", "%s/%04d.wav"%(tempdir, i), "3", "-t", s]) + else: + tasks.append(["wine", "sapi2wav.exe", "%s/%04d.wav"%(tempdir, i), "2", "-t", s]) + i+=1 + tasks.append(["ln", "-s", "%s/silence.wav"%cwd, "%s/%04d.wav"%(tempdir,i)]) + i+=1 + tasks.append(["ln", "-s", "%s/silence.wav"%cwd, "%s/%04d.wav"%(tempdir,i)]) + i+=1 + +wavs = ["%s/%04d.wav"%(tempdir,j) for j in range(i)] +for wav in wavs: + if os.path.exists(wav): + os.unlink(wav) + +def worker(cmd): + with open(os.devnull, "w") as fnull: + subprocess.call(cmd, shell=False, stdout = fnull, stderr = fnull) +cpucount = multiprocessing.cpu_count() +cpucount = 1 +pool = multiprocessing.Pool(processes=cpucount) +num_tasks = float(len(tasks)) +for i,_ in enumerate(pool.imap_unordered(worker, tasks)): + sys.stdout.write("%f\r"%(100*i/num_tasks)) + sys.stdout.flush() + +p1 = subprocess.Popen(["./concatenate_wav"]+wavs, stdout=subprocess.PIPE) +p2 = subprocess.Popen(["./stride"], stdin=p1.stdout, stdout=subprocess.PIPE) +p3 = subprocess.Popen(["sox", "--show-progress", "--type", "raw", "--rate", "22050", "--encoding", "signed-integer", "--bits", "16", "--channels", "1", "-", "--rate", "22050", "--comment", "", "--compression", "0", "%s.ogg"%title, "tempo", "-s", "2.0"], stdin=p2.stdout, stdout=None) +p1.stdout.close() +p2.stdout.close() +p3.wait() + +for wav in wavs: + os.unlink(wav) diff --git a/generate_silence.py b/generate_silence.py new file mode 100644 index 0000000..bc4b249 --- /dev/null +++ b/generate_silence.py @@ -0,0 +1,31 @@ +#!/usr/bin/python + +from struct import pack +from sys import stdout + +duration = 1 # seconds of silence +channels = 1 # number of channels +bps = 16 # bits per sample +sample = 44100 # sample rate +ExtraParamSize = 0 +Subchunk1Size = 16+2+ExtraParamSize +Subchunk2Size = duration*sample*channels*bps/8 +ChunkSize = 4 + (8 + Subchunk1Size) + (8 + Subchunk2Size) + +stdout.write("".join([ + 'RIFF', # ChunkID (magic) # 0x00 + pack(' out.ogg +#./concatenate_wav.sh [0-9]*.wav | python stride.py | sox --type raw --rate 22050 --encoding signed-integer --bits 16 --channels 1 - --rate 22050 --comment "" --compression 0 out.ogg tempo -s 2.0 + +# the stride of 2 is used because the bytes in the input wav +# files are of the form ABABCDCDEFEFGHGH +# since the input is 44100Hz, remove every 2nd byte tuple and output 22050Hz +# sox can also downsample but it easily converts +# 00000000 to 0100 or to FFFF +# using ./stride is also twice as fast as having sox do the downsampling + +#sudo mount -o remount,size=1200M /dev/shm +#rm -rf /dev/shm/[0-9]*.wav +#for f in ../Perry\ Rhodan\ Ebook\ Sammlung\ Komplett/txt/Perry\ Rhodan\ -\ Romanzyklus\ -\ 0300-0399\ -\ M\ 87/<365-399>*; do +# cat "$f" | tr '»' '"' | tr '«' '"' | tr "'" '"' > /tmp/out +# python novel2audio.py "/tmp/out" | /usr/bin/time xargs --delimiter='\n' --max-args=1 --max-procs=4 -I '{}' sh -c '{} > /dev/null' +# ./concatenate_wav /dev/shm/[0-9]*.wav | ./stride | sox --show-progress --type raw --rate 22050 --encoding signed-integer --bits 16 --channels 1 - --rate 22050 --comment "" --compression 0 "`basename \"$f\" .txt`.ogg" tempo -s 2.0 +# rm /dev/shm/[0-9]*.wav +#done diff --git a/parallel.sh b/parallel.sh new file mode 100755 index 0000000..c51e5f2 --- /dev/null +++ b/parallel.sh @@ -0,0 +1,36 @@ +#!/bin/sh + +NUM=0 +QUEUE="" +MAX_NPROC=12 + +while read CMD; do + sh -c "$CMD" & + + PID=$! + QUEUE="$QUEUE $PID" + NUM=$(($NUM+1)) + + # if enough processes were created + while [ $NUM -ge $MAX_NPROC ]; do + # check whether any process finished + for PID in $QUEUE; do + if [ ! -d /proc/$PID ]; then + TMPQUEUE=$QUEUE + QUEUE="" + NUM=0 + # rebuild new queue from processes + # that are still alive + for PID in $TMPQUEUE; do + if [ -d /proc/$PID ]; then + QUEUE="$QUEUE $PID" + NUM=$(($NUM+1)) + fi + done + break + fi + done + sleep 0.5 + done +done +wait diff --git a/sapi2wav.exe b/sapi2wav.exe new file mode 100644 index 0000000..9349656 Binary files /dev/null and b/sapi2wav.exe differ diff --git a/sapi2wav/License.txt b/sapi2wav/License.txt new file mode 100644 index 0000000..8c0a653 --- /dev/null +++ b/sapi2wav/License.txt @@ -0,0 +1,278 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. diff --git a/sapi2wav/sapi2wav.cfg b/sapi2wav/sapi2wav.cfg new file mode 100644 index 0000000..0d0774f --- /dev/null +++ b/sapi2wav/sapi2wav.cfg @@ -0,0 +1,38 @@ +-$A8 +-$B- +-$C+ +-$D+ +-$E- +-$F- +-$G+ +-$H+ +-$I+ +-$J- +-$K- +-$L+ +-$M- +-$N+ +-$O+ +-$P+ +-$Q- +-$R- +-$S- +-$T- +-$U- +-$V+ +-$W- +-$X+ +-$YD +-$Z1 +-cc +-AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; +-H+ +-W+ +-M +-$M16384,1048576 +-K$00400000 +-LE"c:\program files\borland\delphi7\Projects\Bpl" +-LN"c:\program files\borland\delphi7\Projects\Bpl" +-w-UNSAFE_TYPE +-w-UNSAFE_CODE +-w-UNSAFE_CAST diff --git a/sapi2wav/sapi2wav.dof b/sapi2wav/sapi2wav.dof new file mode 100644 index 0000000..a7cf4a6 --- /dev/null +++ b/sapi2wav/sapi2wav.dof @@ -0,0 +1,132 @@ +[FileVersion] +Version=7.0 +[Compiler] +A=8 +B=0 +C=1 +D=1 +E=0 +F=0 +G=1 +H=1 +I=1 +J=0 +K=0 +L=1 +M=0 +N=1 +O=1 +P=1 +Q=0 +R=0 +S=0 +T=0 +U=0 +V=1 +W=0 +X=1 +Y=1 +Z=1 +ShowHints=1 +ShowWarnings=1 +UnitAliases=WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; +NamespacePrefix= +SymbolDeprecated=1 +SymbolLibrary=1 +SymbolPlatform=1 +UnitLibrary=1 +UnitPlatform=1 +UnitDeprecated=1 +HResultCompat=1 +HidingMember=1 +HiddenVirtual=1 +Garbage=1 +BoundsError=1 +ZeroNilCompat=1 +StringConstTruncated=1 +ForLoopVarVarPar=1 +TypedConstVarPar=1 +AsgToTypedConst=1 +CaseLabelRange=1 +ForVariable=1 +ConstructingAbstract=1 +ComparisonFalse=1 +ComparisonTrue=1 +ComparingSignedUnsigned=1 +CombiningSignedUnsigned=1 +UnsupportedConstruct=1 +FileOpen=1 +FileOpenUnitSrc=1 +BadGlobalSymbol=1 +DuplicateConstructorDestructor=1 +InvalidDirective=1 +PackageNoLink=1 +PackageThreadVar=1 +ImplicitImport=1 +HPPEMITIgnored=1 +NoRetVal=1 +UseBeforeDef=1 +ForLoopVarUndef=1 +UnitNameMismatch=1 +NoCFGFileFound=1 +MessageDirective=1 +ImplicitVariants=1 +UnicodeToLocale=1 +LocaleToUnicode=1 +ImagebaseMultiple=1 +SuspiciousTypecast=1 +PrivatePropAccessor=1 +UnsafeType=0 +UnsafeCode=0 +UnsafeCast=0 +[Linker] +MapFile=0 +OutputObjs=0 +ConsoleApp=0 +DebugInfo=0 +RemoteSymbols=0 +MinStackSize=16384 +MaxStackSize=1048576 +ImageBase=4194304 +ExeDescription= +[Directories] +OutputDir= +UnitOutputDir= +PackageDLLOutputDir= +PackageDCPOutputDir= +SearchPath= +Packages=vcl;rtl;vclx;indy;inet;xmlrtl;vclie;inetdbbde;inetdbxpress;dbrtl;dsnap;dsnapcon;vcldb;soaprtl;VclSmp;dbexpress;dbxcds;inetdb;bdertl;vcldbx;webdsnap;websnap;adortl;ibxpress;teeui;teedb;tee;dss;visualclx;visualdbclx;vclactnband;vclshlctrls;dclOfficeXP;SAPI5 +Conditionals= +DebugSourceDirs= +UsePackages=0 +[Parameters] +RunParams=out.wav 1 -f c:\test.txt +HostApplication= +Launcher= +UseLauncher=0 +DebugCWD= +[Version Info] +IncludeVerInfo=0 +AutoIncBuild=0 +MajorVer=1 +MinorVer=0 +Release=0 +Build=0 +Debug=0 +PreRelease=0 +Special=0 +Private=0 +DLL=0 +Locale=1030 +CodePage=1252 +[Version Info Keys] +CompanyName= +FileDescription= +FileVersion=1.0.0.0 +InternalName= +LegalCopyright= +LegalTrademarks= +OriginalFilename= +ProductName= +ProductVersion=1.0.0.0 +Comments= diff --git a/sapi2wav/sapi2wav.dpr b/sapi2wav/sapi2wav.dpr new file mode 100644 index 0000000..c8601ae --- /dev/null +++ b/sapi2wav/sapi2wav.dpr @@ -0,0 +1,12 @@ +program sapi2wav; + +uses + Forms, + ttswav in 'ttswav.pas' {MainForm}; + +{$R *.res} +begin + Application.Initialize; + Application.CreateForm(TMainForm, MainForm); + Application.Run; +end. diff --git a/sapi2wav/ttswav.ddp b/sapi2wav/ttswav.ddp new file mode 100644 index 0000000..4370276 Binary files /dev/null and b/sapi2wav/ttswav.ddp differ diff --git a/sapi2wav/ttswav.dfm b/sapi2wav/ttswav.dfm new file mode 100644 index 0000000..8b3bcb3 --- /dev/null +++ b/sapi2wav/ttswav.dfm @@ -0,0 +1,31 @@ +object MainForm: TMainForm + Left = 283 + Top = 175 + BorderStyle = bsNone + Caption = 'Sapi2wav' + ClientHeight = 133 + ClientWidth = 228 + Color = clBtnFace + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'MS Sans Serif' + Font.Style = [] + OldCreateOrder = False + OnCreate = FormCreate + OnShow = FormShow + PixelsPerInch = 96 + TextHeight = 13 + object SpVoice1: TSpVoice + AutoConnect = False + ConnectKind = ckRunningOrNew + Left = 8 + Top = 8 + end + object SpFileStream: TSpFileStream + AutoConnect = False + ConnectKind = ckRunningOrNew + Left = 40 + Top = 8 + end +end diff --git a/sapi2wav/ttswav.pas b/sapi2wav/ttswav.pas new file mode 100644 index 0000000..f4df255 --- /dev/null +++ b/sapi2wav/ttswav.pas @@ -0,0 +1,224 @@ +{ + SAPI2Wav. Command line "Text to Speech" to Wav file using MS SAPI. + Copyright (C) 2008 Dan Faerch + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + Contact author at sapi2wav.dan [at] hacker.dk +} + +unit ttswav; + +interface + +uses +// Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, +// Dialogs, StdCtrls, OleServer, SpeechLib_TLB; + Windows, SysUtils, Forms, + OleServer, SpeechLib_TLB, Classes; + +type + TMainForm = class(TForm) + SpVoice1: TSpVoice; + SpFileStream: TSpFileStream; + procedure FormCreate(Sender: TObject); + procedure FormShow(Sender: TObject); + private + { Private declarations } + public + { Public declarations } + end; + +var + MainForm: TMainForm; + +implementation + +{$R *.dfm} + + +Function LoadFileToString(FileN: String):string; +var + ProgFile, FSize: Cardinal; + Buff: array[0..63999] of Char; +begin + Result := ''; + + if GetFileAttributesA(PChar(FileN)) = $FFFFFFFF then Exit; + + + try + ProgFile := CreateFile(PChar(FileN), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + if ProgFile <> INVALID_HANDLE_VALUE then + begin + FileSeek(ProgFile, 0, 0); + + repeat + FillChar(Buff, SizeOf(Buff), 0); + ReadFile(ProgFile, Buff, SizeOf(Buff), FSize, nil); + if FSize > 0 then + Result := Result + Buff; + until (FSize = 0); + + CloseHandle(ProgFile); + end; + Except + // do stuff.. + end; +end; + + + + +//------------------------------------------------------ + +procedure TMainForm.FormCreate(Sender: TObject); + var filename : string; + text : string; + bool : boolean; + mode : string; + i : integer; + + Voice : ISpeechObjectToken; + Voices: IspeechObjectTokens; +begin + MainForm.Height := 0; + MainForm.Width := 0; + + + Voices := SpVoice1.GetVoices('',''); + if (ParamStr(1) = '-list') then + begin + for i := 0 to Voices.Count - 1 do + begin + writeln(inttostr(i+1)+': '+Voices.Item(i).GetDescription(0)); + end; + Application.Terminate; + exit; + end; + + + if ParamCount < 4 then + begin + writeln('Usage:'); + writeln(' sapi2wav.exe -f '); + writeln(' sapi2wav.exe -t ""'); + writeln(''); + writeln('Arguments:'); + writeln(' -list List voices and the number'); + writeln(' -t Text to read out loud. Use quotes!'); + writeln(' -f filename of textfile to read out loud. Use either no quotes or double qoutes!'); + writeln(' '); + writeln('Examples:'); + writeln(' sapi2wav.exe c:\out.wav 1 -t "Hellow world"'); + writeln(' sapi2wav.exe readin.wav 3 -f my_data.txt'); + Application.Terminate; + exit; + end; + + //Check if filename is given + filename := ParamStr(1); + bool := true; + + if length(filename)<5 then + begin + bool := false; + end; + + if (copy(filename,length(filename)-3,4) <> '.wav') then + begin + bool:=false; + end; + + if (not bool) then + begin + writeln('Filename is the first argument given and MUST end on ".wav"'); + Application.Terminate; + exit; + end; + + //----------------- + //Voice number arg + if (strtoint(ParamStr(2))>Voices.Count) then + begin + writeln('Unknown voice.nr.'); + Application.Terminate; + exit; + end; + + Voice := ISpeechObjectToken(Voices.Item(strtoint(ParamStr(2))-1)); + writeln('Choosing voice '+ParamStr(2)+' - '+Voice.GetDescription(0)); + SpVoice1.Voice := Voice; + + //------------------- + //Get mode arg. + mode := ParamStr(3); + + if (copy(mode,1,1) <> '-') then bool:=false; + mode := copy(mode,2,1); + if (bool) then + if ( (copy(mode,1,1) <> 't') AND (copy(mode,1,1) <> 'f') ) then bool:=false; + + if (not bool) then + begin + writeln('3rd arg most be -t "" or -f '); + Application.Terminate; + exit; + end; + + //------------------ + //Get text/filename + text := ParamStr(4); + + if (mode = 'f') then + if FileExists(text) then + text := LoadFileToString(text) + else + begin + writeln('File not found: '+text); + Application.Terminate; + exit; + end; + + + //Set wave format +// SpFileStream.Format.type_ := SAFT22kHz16BitMono; + SpFileStream.Format.type_ := SAFT44kHz16BitMono; + + //Open wavefile + SpFileStream.Open(filename,SSFMCreateForWrite, False); + + //Hook wavefile to speak output. + SpVoice1.AudioOutputStream := SpFileStream.DefaultInterface; + + //Talk + //SVSFIsFilename +// if (mode = 'f') then +// SpVoice1.Speak(text, SVSFDefault or SVSFIsFilename) +// else + SpVoice1.Speak(text, SVSFDefault); + + SpVoice1.WaitUntilDone(-1); + + SpFileStream.Close; + Application.Terminate; +end; + +procedure TMainForm.FormShow(Sender: TObject); +begin +Application.Terminate; +end; + +end. diff --git a/sapi51.msi b/sapi51.msi new file mode 100644 index 0000000..3783cdf Binary files /dev/null and b/sapi51.msi differ diff --git a/silence.wav b/silence.wav new file mode 100644 index 0000000..a13aa92 Binary files /dev/null and b/silence.wav differ diff --git a/stride.py b/stride.py new file mode 100644 index 0000000..a679806 --- /dev/null +++ b/stride.py @@ -0,0 +1,12 @@ +from sys import stdin, stdout + +i = 0 +while True: + data = stdin.read(2) + if not data: + break + + if i%2 == 0: + stdout.write(data) + + i+=1 diff --git a/stride_fread.c b/stride_fread.c new file mode 100644 index 0000000..85af8dc --- /dev/null +++ b/stride_fread.c @@ -0,0 +1,30 @@ +#include + +#define BUF_SIZE 16384 + +int main(int argc, char **argv) +{ + size_t len, i; + char buf[BUF_SIZE]; + char *pos; + + /* + for (i=0; fread(buf, 1, 4, stdin) == 4; i++) + if (i%2 == 0 && fwrite(buf, 1, 2, stdout) != 2) + return 1; + */ + + while (1) { + len = fread(buf, 1, BUF_SIZE, stdin); + + pos = buf; + for (i = 0; i < len; pos+=4, i+=4) + if (fwrite(pos, 1, 2, stdout) != 2) + break; + + if (len != BUF_SIZE || i != len) + break; + } + + return 0; +} diff --git a/wavinfo.sh b/wavinfo.sh new file mode 100755 index 0000000..23cb2d7 --- /dev/null +++ b/wavinfo.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +[ $(dd if=$1 skip=0 bs=1 count=4 2>/dev/null) = "RIFF" ] && \ +[ $(dd if=$1 skip=8 bs=1 count=4 2>/dev/null) = "WAVE" ] && \ +[ "$((0x$(xxd -plain -s 0x14 -l 2 $1 | sed 's/\(..\)\(..\)/\2\1/')))" -eq 1 ] && \ +echo valid || echo invalid + +echo start of data: $((0x$(xxd -plain -s 0x10 -l 4 $1 | sed 's/\(..\)\(..\)\(..\)\(..\)/\4\3\2\1/')+28)) +echo number of channels: $((0x$(xxd -plain -s 0x16 -l 2 $1 | sed 's/\(..\)\(..\)/\2\1/'))) +echo sample rate: $((0x$(xxd -plain -s 0x18 -l 4 $1 | sed 's/\(..\)\(..\)\(..\)\(..\)/\4\3\2\1/'))) +echo bits per sample: $((0x$(xxd -plain -s 0x22 -l 2 $1 | sed 's/\(..\)\(..\)/\2\1/')))