initial version
This commit is contained in:
parent
58e127024e
commit
f787f8dd5c
52 changed files with 9207 additions and 0 deletions
1
AUTHORS
Normal file
1
AUTHORS
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Volker Fischer <corrados[at+]users[dot*]sourceforge[dot_]net>
|
280
COPYING
Normal file
280
COPYING
Normal file
|
@ -0,0 +1,280 @@
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 2, June 1991
|
||||||
|
|
||||||
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||||
|
675 Mass Ave, Cambridge, MA 02139, 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 Library 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.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
0
ChangeLog
Normal file
0
ChangeLog
Normal file
54
INSTALL
Executable file
54
INSTALL
Executable file
|
@ -0,0 +1,54 @@
|
||||||
|
/******************************************************************************\
|
||||||
|
* Copyright (c) 2004-2006
|
||||||
|
*
|
||||||
|
* Author(s):
|
||||||
|
* Volker Fischer
|
||||||
|
*
|
||||||
|
******************************************************************************
|
||||||
|
*
|
||||||
|
* 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.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
\******************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
Windows:
|
||||||
|
--------
|
||||||
|
|
||||||
|
Rquired software: QT 2 non-commercial, Visual C++ 6.0
|
||||||
|
|
||||||
|
- run llcon/windows/MocQT.bat
|
||||||
|
- open llcon.dsw in your Visual C++ 6.0 environment and compile
|
||||||
|
- run llcon/windows/Release/llcon.exe
|
||||||
|
|
||||||
|
|
||||||
|
Linux:
|
||||||
|
------
|
||||||
|
|
||||||
|
Required packages: QT (devel packages, too!), ALSA (devel packages, too!)
|
||||||
|
|
||||||
|
- cd llcon
|
||||||
|
- sh bootstrap
|
||||||
|
- ./configure
|
||||||
|
- make
|
||||||
|
- run llcon/linux/llcon
|
||||||
|
|
||||||
|
NOTES:
|
||||||
|
- it may be required by your Linux distrubution that you set the QTDIR
|
||||||
|
environment variable prior to the "./configure" call. E.g., for SUSE:
|
||||||
|
export QTDIR=/usr/lib/qt3
|
||||||
|
or for Debian:
|
||||||
|
export QTDIR=/usr/share/qt3
|
||||||
|
- if the file "bootstrap" is not available, skip this step
|
||||||
|
|
3
Makefile.am
Normal file
3
Makefile.am
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
AUTOMAKE_OPTIONS = foreign
|
||||||
|
|
||||||
|
SUBDIRS = linux
|
0
NEWS
Normal file
0
NEWS
Normal file
75
README
Executable file
75
README
Executable file
|
@ -0,0 +1,75 @@
|
||||||
|
/******************************************************************************\
|
||||||
|
* Copyright (c) 2004-2006
|
||||||
|
*
|
||||||
|
* Author(s):
|
||||||
|
* Volker Fischer
|
||||||
|
*
|
||||||
|
******************************************************************************
|
||||||
|
*
|
||||||
|
* 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.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
\******************************************************************************/
|
||||||
|
|
||||||
|
llcon
|
||||||
|
-----
|
||||||
|
|
||||||
|
Low-Latency Connection / client and server
|
||||||
|
|
||||||
|
|
||||||
|
OBJECTIVES:
|
||||||
|
The task is to build a client/server software to enable musicians to play together
|
||||||
|
over the internet. Target internet connection is DSL with 256 kbps upstream and
|
||||||
|
1 Mbit downstream. The server software must be located at a server with a very
|
||||||
|
fast internet connection (at least 1 Mbps for up- and downstream).
|
||||||
|
|
||||||
|
To get sufficient results, a sample rate of 24 kHz (mono channel) was chosen. An
|
||||||
|
audio compression algorithm with very low delay is IMA-ADPCM (delay is just one
|
||||||
|
sample). This gives a raw compressed audio data rate of 96 kbps.
|
||||||
|
|
||||||
|
Target hardware setup at the client is stereo audio input signal with one channel
|
||||||
|
is the instrument and the other channel is a microphone signal. On the microphne
|
||||||
|
channel a reverberation effect can be applied (maybe at a later time other audio
|
||||||
|
effects are added).
|
||||||
|
|
||||||
|
|
||||||
|
MANUAL:
|
||||||
|
For starting server type ./llcon -s
|
||||||
|
|
||||||
|
Start the llcon server on a remote computer with fast internet access. Start the
|
||||||
|
llcon client on your local computer and connect your sound card with your
|
||||||
|
instrument/microphone and headphone and type in the IP address of the server.
|
||||||
|
There are levelers for adjusting the sound card (in/out) and network buffer sizes.
|
||||||
|
It seems that 2 blocks for network buffer is optimal choice. For the sound card
|
||||||
|
buffer, try to make them as short as possible by watching the LEDs below the
|
||||||
|
levelers (they should stay green) and the timing standard deviation (should be
|
||||||
|
as low as approx. 0.5 ms).
|
||||||
|
|
||||||
|
For test purpose it is possible to run server and client on the same computer. For
|
||||||
|
this setup firstly start server and then the client. Type in 127.0.0.1 for the
|
||||||
|
IP address in the client.
|
||||||
|
|
||||||
|
|
||||||
|
EXTERNAL CODE:
|
||||||
|
This code contains open source code from different sources. The developer(s) want
|
||||||
|
to thank the developer of this code for making their efforts available under open
|
||||||
|
source:
|
||||||
|
|
||||||
|
- audio reverberation code: by Perry R. Cook and Gary P. Scavone, 1995 - 2004
|
||||||
|
(taken from "The Synthesis ToolKit in C++ (STK)")
|
||||||
|
|
||||||
|
- IMA-ADPCM: by Erik de Castro Lopo
|
||||||
|
|
||||||
|
- some parts are taken from the project "Dream: a PC-based Digital Radio Mondiale
|
||||||
|
(DRM) receiver" written by one of the llcon authors, Volker Fischer
|
1
TODO
Normal file
1
TODO
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
8
bootstrap
Executable file
8
bootstrap
Executable file
|
@ -0,0 +1,8 @@
|
||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
aclocal -I . && \
|
||||||
|
autoheader && \
|
||||||
|
libtoolize --automake --copy && \
|
||||||
|
automake --add-missing --copy && \
|
||||||
|
autoconf
|
||||||
|
echo "Ready to run ./configure"
|
70
configure.in
Executable file
70
configure.in
Executable file
|
@ -0,0 +1,70 @@
|
||||||
|
dnl Process this file with autoconf to produce a configure script.
|
||||||
|
AC_PREREQ(2.50)
|
||||||
|
AC_INIT(src/main.cpp)
|
||||||
|
|
||||||
|
AM_INIT_AUTOMAKE(llcon,0.9.1)
|
||||||
|
|
||||||
|
|
||||||
|
AM_CONFIG_HEADER(config.h)
|
||||||
|
|
||||||
|
dnl Checks for programs.
|
||||||
|
AC_PROG_LIBTOOL
|
||||||
|
AC_PROG_CC
|
||||||
|
AC_PROG_CXX
|
||||||
|
AC_PROG_CPP
|
||||||
|
AC_PROG_INSTALL
|
||||||
|
AC_SUBST(LIBTOOL_DEPS)
|
||||||
|
AC_PROG_MAKE_SET
|
||||||
|
AC_CHECK_PROGS(RPMBUILD, rpmbuild, rpm)
|
||||||
|
|
||||||
|
dnl Configuration Arguments
|
||||||
|
|
||||||
|
AC_ARG_ENABLE( sound,[ --enable-sound generic sound support], enable_sound=$enableval, enable_sound=yes)
|
||||||
|
|
||||||
|
AC_ARG_WITH( qtdir,[ --with-qtdir=path to QT],QTDIR=$withval)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
AC_CHECK_HEADER(sys/asoundlib.h, , enable_sound=no)
|
||||||
|
AC_CHECK_LIB(asound, snd_pcm_open, , enable_sound=no)
|
||||||
|
|
||||||
|
if test "$enable_sound" = "yes"; then
|
||||||
|
AC_DEFINE(WITH_SOUND, 1, [Define if you want to use sound])
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "$enable_client" = "yes"; then
|
||||||
|
AC_DEFINE(APPL_TYPE_CLIENT, 1, [Define if you want to use client])
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
dnl Checks for header files.
|
||||||
|
AC_HEADER_STDC
|
||||||
|
AC_LANG_CPLUSPLUS
|
||||||
|
|
||||||
|
dnl Checks for some external libraries that need to be installed
|
||||||
|
AC_LANG(C++)
|
||||||
|
|
||||||
|
|
||||||
|
dnl QT --------------------------------------------------------------------------
|
||||||
|
if test "no$QTDIR" = "no"; then
|
||||||
|
AC_MSG_ERROR( "*** Please set QTDIR ***" )
|
||||||
|
fi
|
||||||
|
|
||||||
|
saved_ldflags="$LDFLAGS"
|
||||||
|
LDFLAGS="$LDFLAGS -L$QTDIR/lib"
|
||||||
|
|
||||||
|
AC_HAVE_LIBRARY(qt-mt,,exit 1)
|
||||||
|
|
||||||
|
AC_PATH_PROG(MOC, moc,, "$QTDIR/bin")
|
||||||
|
if test -z "$MOC"; then
|
||||||
|
AC_MSG_ERROR("No moc found in $QTDIR/bin");
|
||||||
|
fi
|
||||||
|
AC_PATH_PROG(UIC, uic,, "$QTDIR/bin")
|
||||||
|
if test -z "$UIC"; then
|
||||||
|
AC_MSG_ERROR("No uic found in $QTDIR/bin");
|
||||||
|
fi
|
||||||
|
AC_SUBST(QTDIR)
|
||||||
|
|
||||||
|
|
||||||
|
AC_CONFIG_FILES(Makefile linux/Makefile)
|
||||||
|
AC_OUTPUT
|
106
linux/Makefile.am
Executable file
106
linux/Makefile.am
Executable file
|
@ -0,0 +1,106 @@
|
||||||
|
bin_PROGRAMS = llcon
|
||||||
|
|
||||||
|
|
||||||
|
llcon_SOURCES = ../src/buffer.cpp \
|
||||||
|
../src/main.cpp \
|
||||||
|
../src/socket.cpp \
|
||||||
|
../src/audiocompr.cpp \
|
||||||
|
../src/resample.cpp \
|
||||||
|
../src/channel.cpp \
|
||||||
|
../src/util.cpp \
|
||||||
|
../src/llconclientdlg.cpp \
|
||||||
|
../src/client.cpp \
|
||||||
|
../src/llconserverdlg.cpp \
|
||||||
|
../src/server.cpp \
|
||||||
|
../src/settings.cpp \
|
||||||
|
../src/multicolorled.cpp \
|
||||||
|
sound.cpp \
|
||||||
|
../src/buffer.h \
|
||||||
|
../src/global.h \
|
||||||
|
../src/socket.h \
|
||||||
|
../src/audiocompr.h \
|
||||||
|
../src/resample.h \
|
||||||
|
../src/resamplefilter.h \
|
||||||
|
../src/channel.h \
|
||||||
|
../src/util.h \
|
||||||
|
../src/client.h \
|
||||||
|
../src/server.h \
|
||||||
|
../src/settings.h \
|
||||||
|
../src/multicolorled.h \
|
||||||
|
../src/llconserverdlg.h \
|
||||||
|
../src/llconclientdlg.h \
|
||||||
|
../src/llconclientdlgbase.ui \
|
||||||
|
../src/llconserverdlgbase.ui \
|
||||||
|
../src/aboutdlgbase.ui \
|
||||||
|
sound.h
|
||||||
|
|
||||||
|
# these need to be generated before the rest can be compiled
|
||||||
|
|
||||||
|
BUILT_SOURCES=moc/moc_server.cpp \
|
||||||
|
moc/moc_socket.cpp \
|
||||||
|
moc/moc_multicolorled.cpp \
|
||||||
|
moc/moc_util.cpp \
|
||||||
|
moc/moc_llconclientdlg.cpp moc/moc_llconclientdlgbase.cpp moc/llconclientdlgbase.h moc/llconclientdlgbase.cpp \
|
||||||
|
moc/moc_llconserverdlg.cpp moc/moc_llconserverdlgbase.cpp moc/llconserverdlgbase.h moc/llconserverdlgbase.cpp \
|
||||||
|
moc/moc_aboutdlgbase.cpp moc/aboutdlgbase.h moc/aboutdlgbase.cpp
|
||||||
|
|
||||||
|
# and should be cleaned by make clean
|
||||||
|
|
||||||
|
CLEANFILES=$(BUILT_SOURCES)
|
||||||
|
nodist_llcon_SOURCES=$(BUILT_SOURCES)
|
||||||
|
|
||||||
|
dist-hook:
|
||||||
|
mkdir $(distdir)/moc
|
||||||
|
|
||||||
|
moc/moc_server.cpp: ../src/server.h
|
||||||
|
$(MOC) ../src/server.h -o moc/moc_server.cpp
|
||||||
|
|
||||||
|
moc/moc_socket.cpp: ../src/socket.h
|
||||||
|
$(MOC) ../src/socket.h -o moc/moc_socket.cpp
|
||||||
|
|
||||||
|
moc/moc_multicolorled.cpp: ../src/multicolorled.h
|
||||||
|
$(MOC) ../src/multicolorled.h -o moc/moc_multicolorled.cpp
|
||||||
|
|
||||||
|
moc/moc_util.cpp: ../src/util.h
|
||||||
|
$(MOC) ../src/util.h -o moc/moc_util.cpp
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
moc/moc_aboutdlgbase.cpp: moc/aboutdlgbase.h
|
||||||
|
$(MOC) moc/aboutdlgbase.h -o moc/moc_aboutdlgbase.cpp
|
||||||
|
|
||||||
|
moc/aboutdlgbase.h: ../src/aboutdlgbase.ui
|
||||||
|
$(UIC) ../src/aboutdlgbase.ui -o moc/aboutdlgbase.h
|
||||||
|
|
||||||
|
moc/aboutdlgbase.cpp: ../src/aboutdlgbase.ui moc/aboutdlgbase.h
|
||||||
|
$(UIC) ../src/aboutdlgbase.ui -i moc/aboutdlgbase.h -o moc/aboutdlgbase.cpp
|
||||||
|
|
||||||
|
|
||||||
|
moc/moc_llconclientdlg.cpp: ../src/llconclientdlg.h
|
||||||
|
$(MOC) ../src/llconclientdlg.h -o moc/moc_llconclientdlg.cpp
|
||||||
|
|
||||||
|
moc/moc_llconclientdlgbase.cpp: moc/llconclientdlgbase.h
|
||||||
|
$(MOC) moc/llconclientdlgbase.h -o moc/moc_llconclientdlgbase.cpp
|
||||||
|
|
||||||
|
moc/llconclientdlgbase.h: ../src/llconclientdlgbase.ui
|
||||||
|
$(UIC) ../src/llconclientdlgbase.ui -o moc/llconclientdlgbase.h
|
||||||
|
|
||||||
|
moc/llconclientdlgbase.cpp: ../src/llconclientdlgbase.ui moc/llconclientdlgbase.h
|
||||||
|
$(UIC) ../src/llconclientdlgbase.ui -i moc/llconclientdlgbase.h -o moc/llconclientdlgbase.cpp
|
||||||
|
|
||||||
|
|
||||||
|
moc/moc_llconserverdlg.cpp: ../src/llconserverdlg.h
|
||||||
|
$(MOC) ../src/llconserverdlg.h -o moc/moc_llconserverdlg.cpp
|
||||||
|
|
||||||
|
moc/moc_llconserverdlgbase.cpp: moc/llconserverdlgbase.h
|
||||||
|
$(MOC) moc/llconserverdlgbase.h -o moc/moc_llconserverdlgbase.cpp
|
||||||
|
|
||||||
|
moc/llconserverdlgbase.h: ../src/llconserverdlgbase.ui
|
||||||
|
$(UIC) ../src/llconserverdlgbase.ui -o moc/llconserverdlgbase.h
|
||||||
|
|
||||||
|
moc/llconserverdlgbase.cpp: ../src/llconserverdlgbase.ui moc/llconserverdlgbase.h
|
||||||
|
$(UIC) ../src/llconserverdlgbase.ui -i moc/llconserverdlgbase.h -o moc/llconserverdlgbase.cpp
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
llcon_CXXFLAGS=$(QWTINCL) -I../src -I$(QTDIR)/include -DQT_THREAD_SUPPORT -D_REENTRANT -g
|
480
linux/sound.cpp
Executable file
480
linux/sound.cpp
Executable file
|
@ -0,0 +1,480 @@
|
||||||
|
/******************************************************************************\
|
||||||
|
* Copyright (c) 2004-2005
|
||||||
|
*
|
||||||
|
* Author(s):
|
||||||
|
* Volker Fischer, Alexander Kurpiers
|
||||||
|
*
|
||||||
|
* This code is based on the Open-Source sound interface implementation of
|
||||||
|
* the Dream DRM Receiver project.
|
||||||
|
*
|
||||||
|
\******************************************************************************/
|
||||||
|
|
||||||
|
#include "sound.h"
|
||||||
|
|
||||||
|
#ifdef WITH_SOUND
|
||||||
|
/* Wave in ********************************************************************/
|
||||||
|
void CSound::InitRecording(int iNewBufferSize, bool bNewBlocking)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/* set internal buffer size for read */
|
||||||
|
iBufferSizeIn = iNewBufferSize / NUM_IN_OUT_CHANNELS; /* mono size */
|
||||||
|
|
||||||
|
/* if recording device was already open, close it first */
|
||||||
|
if (rhandle != NULL)
|
||||||
|
snd_pcm_close(rhandle);
|
||||||
|
|
||||||
|
/* record device: The most important ALSA interfaces to the PCM devices are
|
||||||
|
the "plughw" and the "hw" interface. If you use the "plughw" interface,
|
||||||
|
you need not care much about the sound hardware. If your soundcard does
|
||||||
|
not support the sample rate or sample format you specify, your data will
|
||||||
|
be automatically converted. This also applies to the access type and the
|
||||||
|
number of channels. With the "hw" interface, you have to check whether
|
||||||
|
your hardware supports the configuration you would like to use */
|
||||||
|
/* either "hw:0,0" or "plughw:0,0" */
|
||||||
|
if (err = snd_pcm_open(&rhandle, "hw:0,0", SND_PCM_STREAM_CAPTURE, 0) != 0)
|
||||||
|
{
|
||||||
|
qDebug("open error: %s", snd_strerror(err));
|
||||||
|
// throw CGenErr("alsa CSound::Init_HW record");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* recording should be blocking */
|
||||||
|
if (err = snd_pcm_nonblock(rhandle, FALSE) != 0)
|
||||||
|
{
|
||||||
|
qDebug("cannot set blocking: %s", snd_strerror(err));
|
||||||
|
// throw CGenErr("alsa CSound::Init_HW record");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set hardware parameters */
|
||||||
|
SetHWParams(rhandle, true, iBufferSizeIn, iCurPeriodSizeIn);
|
||||||
|
|
||||||
|
|
||||||
|
/* sw parameters --------------------------------------------------------- */
|
||||||
|
snd_pcm_sw_params_t* swparams;
|
||||||
|
|
||||||
|
if (err = snd_pcm_sw_params_malloc (&swparams) != 0)
|
||||||
|
{
|
||||||
|
qDebug("snd_pcm_sw_params_malloc: %s", snd_strerror (err));
|
||||||
|
// return NULL ;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the current swparams */
|
||||||
|
if (err = snd_pcm_sw_params_current(rhandle, swparams) < 0)
|
||||||
|
{
|
||||||
|
qDebug("Unable to determine current swparams : %s", snd_strerror(err));
|
||||||
|
// throw CGenErr("alsa CSound::Init_HW ");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start the transfer when the buffer immediately */
|
||||||
|
err = snd_pcm_sw_params_set_start_threshold(rhandle, swparams, 0);
|
||||||
|
if (err < 0) {
|
||||||
|
qDebug("Unable to set start threshold mode : %s", snd_strerror(err));
|
||||||
|
// throw CGenErr("alsa CSound::Init_HW ");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Align all transfers to 1 sample */
|
||||||
|
err = snd_pcm_sw_params_set_xfer_align(rhandle, swparams, 1);
|
||||||
|
if (err < 0) {
|
||||||
|
qDebug("Unable to set transfer align : %s", snd_strerror(err));
|
||||||
|
// throw CGenErr("alsa CSound::Init_HW ");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TEST
|
||||||
|
/* Allow the transfer when at least period_size samples can be processed */
|
||||||
|
// /* round up to closest transfer boundary */
|
||||||
|
// start_threshold = (buffer_size / xfer_align) * xfer_align ;
|
||||||
|
// if (start_threshold < 1)
|
||||||
|
// start_threshold = 1 ;
|
||||||
|
|
||||||
|
|
||||||
|
// TEST
|
||||||
|
snd_pcm_uframes_t period_size = iBufferSizeIn;
|
||||||
|
|
||||||
|
err = snd_pcm_sw_params_set_avail_min(rhandle, swparams, period_size);
|
||||||
|
if (err < 0) {
|
||||||
|
qDebug("Unable to set avail min : %s", snd_strerror(err));
|
||||||
|
// throw CGenErr("alsa CSound::Init_HW ");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Write the parameters to the record/playback device */
|
||||||
|
err = snd_pcm_sw_params(rhandle, swparams);
|
||||||
|
if (err < 0) {
|
||||||
|
qDebug("Unable to set sw params : %s", snd_strerror(err));
|
||||||
|
// throw CGenErr("alsa CSound::Init_HW ");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* clean-up */
|
||||||
|
snd_pcm_sw_params_free(swparams);
|
||||||
|
|
||||||
|
snd_pcm_reset(rhandle);
|
||||||
|
snd_pcm_start(rhandle);
|
||||||
|
|
||||||
|
qDebug("alsa init record done");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSound::Read(CVector<short>& psData)
|
||||||
|
{
|
||||||
|
/* Check if device must be opened or reinitialized */
|
||||||
|
if (bChangParamIn == true)
|
||||||
|
{
|
||||||
|
InitRecording ( iBufferSizeIn * NUM_IN_OUT_CHANNELS );
|
||||||
|
|
||||||
|
/* Reset flag */
|
||||||
|
bChangParamIn = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = snd_pcm_readi(rhandle, &psData[0], iBufferSizeIn);
|
||||||
|
//qDebug("ret: %d, iBufferSizeIn: %d", ret, iBufferSizeIn);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
if (ret == -EPIPE)
|
||||||
|
{
|
||||||
|
/* Under-run */
|
||||||
|
qDebug ( "rprepare" );
|
||||||
|
|
||||||
|
ret = snd_pcm_prepare ( rhandle );
|
||||||
|
|
||||||
|
if ( ret < 0 )
|
||||||
|
{
|
||||||
|
qDebug ( "Can't recover from underrun, prepare failed: %s", snd_strerror ( ret ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = snd_pcm_start ( rhandle );
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
qDebug ( "Can't recover from underrun, start failed: %s", snd_strerror ( ret ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
else if ( ret == -ESTRPIPE )
|
||||||
|
{
|
||||||
|
qDebug ( "strpipe" );
|
||||||
|
|
||||||
|
/* Wait until the suspend flag is released */
|
||||||
|
while ( ( ret = snd_pcm_resume ( rhandle ) ) == -EAGAIN )
|
||||||
|
{
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ret < 0 )
|
||||||
|
{
|
||||||
|
ret = snd_pcm_prepare(rhandle);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
qDebug ( "Can't recover from suspend, prepare failed: %s", snd_strerror ( ret ) );
|
||||||
|
}
|
||||||
|
throw CGenErr ( "CSound:Read" );
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qDebug ( "CSound::Read: %s", snd_strerror ( ret ) );
|
||||||
|
throw CGenErr ( "CSound:Read" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSound::SetInNumBuf ( int iNewNum )
|
||||||
|
{
|
||||||
|
/* check new parameter */
|
||||||
|
if ( ( iNewNum >= MAX_SND_BUF_IN ) || ( iNewNum < 1 ) )
|
||||||
|
{
|
||||||
|
iNewNum = NUM_PERIOD_BLOCKS_IN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Change only if parameter is different */
|
||||||
|
if ( iNewNum != iCurPeriodSizeIn )
|
||||||
|
{
|
||||||
|
iCurPeriodSizeIn = iNewNum;
|
||||||
|
bChangParamIn = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Wave out *******************************************************************/
|
||||||
|
void CSound::InitPlayback ( int iNewBufferSize, bool bNewBlocking )
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/* Save buffer size */
|
||||||
|
iBufferSizeOut = iNewBufferSize / NUM_IN_OUT_CHANNELS; /* mono size */
|
||||||
|
|
||||||
|
/* if playback device was already open, close it first */
|
||||||
|
if ( phandle != NULL )
|
||||||
|
{
|
||||||
|
snd_pcm_close ( phandle );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* playback device */
|
||||||
|
/* either "hw:0,0" or "plughw:0,0" */
|
||||||
|
if ( err = snd_pcm_open ( &phandle, "hw:0,0", SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK ) != 0)
|
||||||
|
{
|
||||||
|
qDebug ( "open error: %s", snd_strerror ( err ) );
|
||||||
|
// throw CGenErr("alsa CSound::Init_HW playback");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* non-blocking playback */
|
||||||
|
if ( err = snd_pcm_nonblock ( phandle, TRUE ) != 0 )
|
||||||
|
{
|
||||||
|
qDebug ( "cannot set blocking: %s", snd_strerror ( err ) );
|
||||||
|
// throw CGenErr("alsa CSound::Init_HW record");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set hardware parameters */
|
||||||
|
SetHWParams ( phandle, false, iBufferSizeOut, iCurPeriodSizeOut );
|
||||||
|
|
||||||
|
snd_pcm_start ( phandle );
|
||||||
|
qDebug ( "alsa init playback done" );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSound::Write ( CVector<short>& psData )
|
||||||
|
{
|
||||||
|
int size = iBufferSizeIn;
|
||||||
|
int start = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Check if device must be opened or reinitialized */
|
||||||
|
if ( bChangParamOut == true )
|
||||||
|
{
|
||||||
|
InitPlayback ( iBufferSizeOut * NUM_IN_OUT_CHANNELS );
|
||||||
|
|
||||||
|
/* Reset flag */
|
||||||
|
bChangParamOut = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ( size )
|
||||||
|
{
|
||||||
|
ret = snd_pcm_writei ( phandle, &psData[start], size );
|
||||||
|
//qDebug("start: %d, iBufferSizeIn: %d", start, iBufferSizeIn);
|
||||||
|
|
||||||
|
if ( ret < 0 )
|
||||||
|
{
|
||||||
|
if ( ret == -EAGAIN )
|
||||||
|
{
|
||||||
|
if ( ( ret = snd_pcm_wait ( phandle, 1 ) ) < 0 )
|
||||||
|
{
|
||||||
|
qDebug ( "poll failed (%s)", snd_strerror ( ret ) );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if ( ret == -EPIPE )
|
||||||
|
{
|
||||||
|
/* under-run */
|
||||||
|
qDebug ( "wunderrun" );
|
||||||
|
|
||||||
|
ret = snd_pcm_prepare ( phandle );
|
||||||
|
|
||||||
|
if ( ret < 0 )
|
||||||
|
{
|
||||||
|
qDebug ( "Can't recover from underrun, prepare failed: %s", snd_strerror ( ret ) );
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if ( ret == -ESTRPIPE )
|
||||||
|
{
|
||||||
|
qDebug("wstrpipe");
|
||||||
|
|
||||||
|
/* wait until the suspend flag is released */
|
||||||
|
while ( (ret = snd_pcm_resume ( phandle ) ) == -EAGAIN )
|
||||||
|
{
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ret < 0 )
|
||||||
|
{
|
||||||
|
ret = snd_pcm_prepare ( phandle );
|
||||||
|
|
||||||
|
if ( ret < 0 )
|
||||||
|
{
|
||||||
|
qDebug ( "Can't recover from suspend, prepare failed: %s", snd_strerror ( ret ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qDebug ( "Write error: %s", snd_strerror ( ret ) );
|
||||||
|
// throw CGenErr ( "Write error" );
|
||||||
|
}
|
||||||
|
break; /* skip one period */
|
||||||
|
}
|
||||||
|
|
||||||
|
size -= ret;
|
||||||
|
start += ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSound::SetOutNumBuf(int iNewNum)
|
||||||
|
{
|
||||||
|
/* check new parameter */
|
||||||
|
if ( ( iNewNum >= MAX_SND_BUF_OUT ) || ( iNewNum < 1 ) )
|
||||||
|
{
|
||||||
|
iNewNum = NUM_PERIOD_BLOCKS_OUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Change only if parameter is different */
|
||||||
|
if ( iNewNum != iCurPeriodSizeOut )
|
||||||
|
{
|
||||||
|
iCurPeriodSizeOut = iNewNum;
|
||||||
|
bChangParamOut = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* common **********************************************************************/
|
||||||
|
bool CSound::SetHWParams(snd_pcm_t* handle, const bool bIsRecord,
|
||||||
|
const int iBufferSizeIn, const int iNumPeriodBlocks)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
snd_pcm_hw_params_t* hwparams;
|
||||||
|
|
||||||
|
if (err = snd_pcm_hw_params_malloc(&hwparams) < 0)
|
||||||
|
{
|
||||||
|
qDebug("cannot allocate hardware parameter structure (%s)\n", snd_strerror(err));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err = snd_pcm_hw_params_any(handle, hwparams) < 0)
|
||||||
|
{
|
||||||
|
qDebug("cannot initialize hardware parameter structure (%s)\n", snd_strerror(err));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get configuration */
|
||||||
|
if (err = snd_pcm_hw_params_any(handle, hwparams) < 0)
|
||||||
|
{
|
||||||
|
qDebug("Broken configuration : no configurations available: %s", snd_strerror(err));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the interleaved read/write format */
|
||||||
|
if (err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0)
|
||||||
|
{
|
||||||
|
qDebug("Access type not available : %s", snd_strerror(err));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the sample format */
|
||||||
|
if (err = snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16) < 0)
|
||||||
|
{
|
||||||
|
qDebug("Sample format not available : %s", snd_strerror(err));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the count of channels */
|
||||||
|
if (err = snd_pcm_hw_params_set_channels(handle, hwparams, NUM_IN_OUT_CHANNELS) < 0)
|
||||||
|
{
|
||||||
|
qDebug("Channels count (%i) not available s: %s", NUM_IN_OUT_CHANNELS, snd_strerror(err));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the sample-rate */
|
||||||
|
unsigned int rrate = SND_CRD_SAMPLE_RATE;
|
||||||
|
if ( err = snd_pcm_hw_params_set_rate_near ( handle, hwparams, &rrate, 0 ) < 0 )
|
||||||
|
{
|
||||||
|
qDebug("Rate %iHz not available : %s", rrate, snd_strerror(err));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ( rrate != SND_CRD_SAMPLE_RATE )
|
||||||
|
{
|
||||||
|
qDebug ( "Rate doesn't match (requested %iHz, get %iHz)", rrate, err );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* set the buffer and period size */
|
||||||
|
|
||||||
|
// TEST
|
||||||
|
snd_pcm_uframes_t BufferFrames = iBufferSizeIn * iNumPeriodBlocks;
|
||||||
|
|
||||||
|
if (err = snd_pcm_hw_params_set_buffer_size_near(handle, hwparams, &BufferFrames) < 0)
|
||||||
|
{
|
||||||
|
qDebug("cannot set buffer size (%s)\n", snd_strerror (err));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEST
|
||||||
|
snd_pcm_uframes_t PeriodSize = iBufferSizeIn;
|
||||||
|
|
||||||
|
if (err = snd_pcm_hw_params_set_period_size_near(handle, hwparams, &PeriodSize, 0) < 0)
|
||||||
|
//if (err = snd_pcm_hw_params_set_period_size_max(handle, hwparams, &PeriodSize, 0) < 0)
|
||||||
|
{
|
||||||
|
qDebug("cannot set period size (%s)\n", snd_strerror (err));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Write the parameters to device */
|
||||||
|
if (err = snd_pcm_hw_params(handle, hwparams) < 0)
|
||||||
|
{
|
||||||
|
qDebug("Unable to set hw params : %s", snd_strerror(err));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check period and buffer size */
|
||||||
|
qDebug("desired block size: %d / desired buffer size: %d", iBufferSizeIn, iBufferSizeIn * iNumPeriodBlocks);
|
||||||
|
|
||||||
|
// TEST
|
||||||
|
snd_pcm_uframes_t buffer_size;
|
||||||
|
if (err = snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size) < 0) {
|
||||||
|
qDebug("Unable to get buffer size for playback: %s\n", snd_strerror(err));
|
||||||
|
// throw CGenErr("alsa CSound::Init_HW ");
|
||||||
|
}
|
||||||
|
qDebug("buffer size %d", buffer_size);
|
||||||
|
|
||||||
|
snd_pcm_uframes_t period_size;
|
||||||
|
err = snd_pcm_hw_params_get_period_size(hwparams, &period_size, 0);
|
||||||
|
if (err < 0)
|
||||||
|
{
|
||||||
|
qDebug("Unable to get period size for playback: %s\n", snd_strerror(err));
|
||||||
|
// throw CGenErr("alsa CSound::Init_HW ");
|
||||||
|
}
|
||||||
|
qDebug("period size %d", period_size);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* clean-up */
|
||||||
|
snd_pcm_hw_params_free(hwparams);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSound::Close()
|
||||||
|
{
|
||||||
|
/* read */
|
||||||
|
if (rhandle != NULL)
|
||||||
|
snd_pcm_close(rhandle);
|
||||||
|
|
||||||
|
rhandle = NULL;
|
||||||
|
|
||||||
|
/* playback */
|
||||||
|
if (phandle != NULL)
|
||||||
|
snd_pcm_close(phandle);
|
||||||
|
|
||||||
|
phandle = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* WITH_SOUND */
|
105
linux/sound.h
Executable file
105
linux/sound.h
Executable file
|
@ -0,0 +1,105 @@
|
||||||
|
/******************************************************************************\
|
||||||
|
* Copyright (c) 2004-2005
|
||||||
|
*
|
||||||
|
* Author(s):
|
||||||
|
* Volker Fischer, Alexander Kurpiers
|
||||||
|
*
|
||||||
|
* This code is based on the Open-Source sound interface implementation of
|
||||||
|
* the Dream DRM Receiver project.
|
||||||
|
*
|
||||||
|
\******************************************************************************/
|
||||||
|
|
||||||
|
#if !defined(_SOUND_H__9518A621345F78_3634567_8C0D_EEBF182CF549__INCLUDED_)
|
||||||
|
#define _SOUND_H__9518A621345F78_3634567_8C0D_EEBF182CF549__INCLUDED_
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "util.h"
|
||||||
|
#include "global.h"
|
||||||
|
|
||||||
|
#if WITH_SOUND
|
||||||
|
# define ALSA_PCM_NEW_HW_PARAMS_API
|
||||||
|
# define ALSA_PCM_NEW_SW_PARAMS_API
|
||||||
|
# include <alsa/asoundlib.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Definitions ****************************************************************/
|
||||||
|
#define NUM_IN_OUT_CHANNELS 2 /* always stereo */
|
||||||
|
|
||||||
|
/* the number of periods is critical for latency */
|
||||||
|
#define NUM_PERIOD_BLOCKS_IN 2
|
||||||
|
#define NUM_PERIOD_BLOCKS_OUT 2
|
||||||
|
|
||||||
|
#define MAX_SND_BUF_IN 200
|
||||||
|
#define MAX_SND_BUF_OUT 200
|
||||||
|
|
||||||
|
/* Classes ********************************************************************/
|
||||||
|
class CSound
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CSound()
|
||||||
|
#if WITH_SOUND
|
||||||
|
: rhandle(NULL), phandle(NULL), iCurPeriodSizeIn(NUM_PERIOD_BLOCKS_IN),
|
||||||
|
iCurPeriodSizeOut(NUM_PERIOD_BLOCKS_OUT)
|
||||||
|
#endif
|
||||||
|
{}
|
||||||
|
virtual ~CSound() {Close();}
|
||||||
|
|
||||||
|
/* Not implemented yet, always return one device and default string */
|
||||||
|
int GetNumDev() {return 1;}
|
||||||
|
void SetOutDev(int iNewDev) {}
|
||||||
|
void SetInDev(int iNewDev) {}
|
||||||
|
|
||||||
|
/* Return invalid device ID which is the same as using "wave mapper" which
|
||||||
|
we assume here to be used */
|
||||||
|
int GetOutDev() {return 1;}
|
||||||
|
int GetInDev() {return 1;}
|
||||||
|
|
||||||
|
#if WITH_SOUND
|
||||||
|
void SetInNumBuf(int iNewNum);
|
||||||
|
int GetInNumBuf() {return iCurPeriodSizeIn;}
|
||||||
|
void SetOutNumBuf(int iNewNum);
|
||||||
|
int GetOutNumBuf() {return iCurPeriodSizeOut;}
|
||||||
|
void InitRecording(int iNewBufferSize, bool bNewBlocking = true);
|
||||||
|
void InitPlayback(int iNewBufferSize, bool bNewBlocking = false);
|
||||||
|
bool Read(CVector<short>& psData);
|
||||||
|
bool Write(CVector<short>& psData);
|
||||||
|
|
||||||
|
void Close();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
snd_pcm_t* rhandle;
|
||||||
|
snd_pcm_t* phandle;
|
||||||
|
|
||||||
|
bool SetHWParams(snd_pcm_t* handle, const bool bIsRecord,
|
||||||
|
const int iBufferSizeIn, const int iNumPeriodBlocks);
|
||||||
|
|
||||||
|
int iBufferSizeOut;
|
||||||
|
int iBufferSizeIn;
|
||||||
|
bool bChangParamIn;
|
||||||
|
int iCurPeriodSizeIn;
|
||||||
|
bool bChangParamOut;
|
||||||
|
int iCurPeriodSizeOut;
|
||||||
|
#else
|
||||||
|
/* Dummy definitions */
|
||||||
|
void SetInNumBuf(int iNewNum) {}
|
||||||
|
int GetInNumBuf() {return 1;}
|
||||||
|
void SetOutNumBuf(int iNewNum) {}
|
||||||
|
int GetOutNumBuf() {return 1;}
|
||||||
|
void InitRecording(int iNewBufferSize, bool bNewBlocking = true) {printf("no sound!");}
|
||||||
|
void InitPlayback(int iNewBufferSize, bool bNewBlocking = false) {printf("no sound!");}
|
||||||
|
bool Read(CVector<short>& psData) {printf("no sound!"); return false;}
|
||||||
|
bool Write(CVector<short>& psData) {printf("no sound!"); return false;}
|
||||||
|
void Close() {}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // !defined(_SOUND_H__9518A621345F78_3634567_8C0D_EEBF182CF549__INCLUDED_)
|
281
src/aboutdlgbase.ui
Executable file
281
src/aboutdlgbase.ui
Executable file
|
@ -0,0 +1,281 @@
|
||||||
|
<!DOCTYPE UI><UI>
|
||||||
|
<class>CAboutDlgBase</class>
|
||||||
|
<widget>
|
||||||
|
<class>QDialog</class>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>name</name>
|
||||||
|
<cstring>CAboutDlgBase</cstring>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>geometry</name>
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>546</width>
|
||||||
|
<height>423</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>sizePolicy</name>
|
||||||
|
<sizepolicy>
|
||||||
|
<hsizetype>1</hsizetype>
|
||||||
|
<vsizetype>1</vsizetype>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>caption</name>
|
||||||
|
<string>About llcon</string>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>icon</name>
|
||||||
|
<pixmap>image0</pixmap>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>sizeGripEnabled</name>
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property>
|
||||||
|
<name>layoutMargin</name>
|
||||||
|
</property>
|
||||||
|
<property>
|
||||||
|
<name>layoutSpacing</name>
|
||||||
|
</property>
|
||||||
|
<vbox>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>margin</name>
|
||||||
|
<number>11</number>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>spacing</name>
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<widget>
|
||||||
|
<class>QLayoutWidget</class>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>name</name>
|
||||||
|
<cstring>Layout21</cstring>
|
||||||
|
</property>
|
||||||
|
<hbox>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>margin</name>
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>spacing</name>
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<widget>
|
||||||
|
<class>QLabel</class>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>name</name>
|
||||||
|
<cstring>PixmapLabelDreamLogo</cstring>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>sizePolicy</name>
|
||||||
|
<sizepolicy>
|
||||||
|
<hsizetype>0</hsizetype>
|
||||||
|
<vsizetype>0</vsizetype>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>pixmap</name>
|
||||||
|
<pixmap>image1</pixmap>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>scaledContents</name>
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget>
|
||||||
|
<class>QLabel</class>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>name</name>
|
||||||
|
<cstring>TextLabelVersion</cstring>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>sizePolicy</name>
|
||||||
|
<sizepolicy>
|
||||||
|
<hsizetype>7</hsizetype>
|
||||||
|
<vsizetype>5</vsizetype>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>text</name>
|
||||||
|
<string>TextLabelVersion</string>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>alignment</name>
|
||||||
|
<set>AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
<property>
|
||||||
|
<name>hAlign</name>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</hbox>
|
||||||
|
</widget>
|
||||||
|
<widget>
|
||||||
|
<class>QLayoutWidget</class>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>name</name>
|
||||||
|
<cstring>Layout8</cstring>
|
||||||
|
</property>
|
||||||
|
<hbox>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>margin</name>
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>spacing</name>
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<widget>
|
||||||
|
<class>QLayoutWidget</class>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>name</name>
|
||||||
|
<cstring>Layout16</cstring>
|
||||||
|
</property>
|
||||||
|
<vbox>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>margin</name>
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>spacing</name>
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<widget>
|
||||||
|
<class>QLabel</class>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>name</name>
|
||||||
|
<cstring>TextLabelAuthorNames</cstring>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>text</name>
|
||||||
|
<string>Author: Volker Fischer</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget>
|
||||||
|
<class>QLabel</class>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>name</name>
|
||||||
|
<cstring>TextLabelCopyright</cstring>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>text</name>
|
||||||
|
<string>Copyright (C) 2005 - 2006</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</vbox>
|
||||||
|
</widget>
|
||||||
|
<spacer>
|
||||||
|
<property>
|
||||||
|
<name>name</name>
|
||||||
|
<cstring>Spacer10</cstring>
|
||||||
|
</property>
|
||||||
|
<property>
|
||||||
|
<name>orientation</name>
|
||||||
|
<enum>Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property>
|
||||||
|
<name>sizeType</name>
|
||||||
|
<enum>Expanding</enum>
|
||||||
|
</property>
|
||||||
|
<property>
|
||||||
|
<name>sizeHint</name>
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</hbox>
|
||||||
|
</widget>
|
||||||
|
<widget>
|
||||||
|
<class>QTextView</class>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>name</name>
|
||||||
|
<cstring>TextViewCredits</cstring>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>text</name>
|
||||||
|
<string>TextViewCredits</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget>
|
||||||
|
<class>QLayoutWidget</class>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>name</name>
|
||||||
|
<cstring>Layout6</cstring>
|
||||||
|
</property>
|
||||||
|
<hbox>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>margin</name>
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>spacing</name>
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<spacer>
|
||||||
|
<property>
|
||||||
|
<name>name</name>
|
||||||
|
<cstring>Spacer12</cstring>
|
||||||
|
</property>
|
||||||
|
<property>
|
||||||
|
<name>orientation</name>
|
||||||
|
<enum>Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property>
|
||||||
|
<name>sizeType</name>
|
||||||
|
<enum>Expanding</enum>
|
||||||
|
</property>
|
||||||
|
<property>
|
||||||
|
<name>sizeHint</name>
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
<widget>
|
||||||
|
<class>QPushButton</class>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>name</name>
|
||||||
|
<cstring>buttonOk</cstring>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>text</name>
|
||||||
|
<string>&OK</string>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>autoDefault</name>
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>default</name>
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</hbox>
|
||||||
|
</widget>
|
||||||
|
</vbox>
|
||||||
|
</widget>
|
||||||
|
<images>
|
||||||
|
<image>
|
||||||
|
<name>image0</name>
|
||||||
|
<data format="XPM.GZ" length="427">789cd3d7528808f055d0d2e72a2e492cc94c5648ce482c52d04a29cdcdad8c8eb5ade6523234530022130543251d2ea5248564056503300071f5205c0b2004719541dcb434986c22840b0260c56800454c9918b1c444e54454b1c4c4a424e5a4c4442431a0085008081231c4949511621021656565b042843a908032bade24a832547b21c6a1ba0f08d0fda18ccd6fd8c2009f58ad351700407358e1</data>
|
||||||
|
</image>
|
||||||
|
<image>
|
||||||
|
<name>image1</name>
|
||||||
|
<data format="XPM.GZ" length="427">789cd3d7528808f055d0d2e72a2e492cc94c5648ce482c52d04a29cdcdad8c8eb5ade6523234530022130543251d2ea5248564056503300071f5205c0b2004719541dcb434986c22840b0260c56800454c9918b1c444e54454b1c4c4a424e5a4c4442431a0085008081231c4949511621021656565b042843a908032bade24a832547b21c6a1ba0f08d0fda18ccd6fd8c2009f58ad351700407358e1</data>
|
||||||
|
</image>
|
||||||
|
</images>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonOk</sender>
|
||||||
|
<signal>clicked()</signal>
|
||||||
|
<receiver>CAboutDlgBase</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</UI>
|
255
src/audiocompr.cpp
Executable file
255
src/audiocompr.cpp
Executable file
|
@ -0,0 +1,255 @@
|
||||||
|
/******************************************************************************\
|
||||||
|
* Copyright (c) 2004-2006
|
||||||
|
*
|
||||||
|
* Author(s):
|
||||||
|
* Volker Fischer, Erik de Castro Lopo
|
||||||
|
*
|
||||||
|
* This code is based on the Open-Source implementation of IMA-ADPCM written
|
||||||
|
* by Erik de Castro Lopo <erikd[at-#]mega-nerd[dot*]com> in 1999-2004
|
||||||
|
*
|
||||||
|
* Changes:
|
||||||
|
* - only support for one channel
|
||||||
|
* - put 2 audio samples in header to get even number of audio samples encoded
|
||||||
|
*
|
||||||
|
******************************************************************************
|
||||||
|
*
|
||||||
|
* 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.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
\******************************************************************************/
|
||||||
|
|
||||||
|
#include "audiocompr.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Implementation *************************************************************/
|
||||||
|
int CAudioCompression::Init(const int iNewAudioLen,
|
||||||
|
const EAudComprType eNewAuCoTy)
|
||||||
|
{
|
||||||
|
eAudComprType = eNewAuCoTy;
|
||||||
|
|
||||||
|
switch (eNewAuCoTy)
|
||||||
|
{
|
||||||
|
case CT_NONE: return iCodeSize = 2 * iNewAudioLen; /* short = 2 * byte */
|
||||||
|
case CT_IMAADPCM: return ImaAdpcm.Init(iNewAudioLen);
|
||||||
|
default: return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CVector<unsigned char> CAudioCompression::Encode(const CVector<short>& vecsAudio)
|
||||||
|
{
|
||||||
|
if (eAudComprType == CT_NONE)
|
||||||
|
{
|
||||||
|
/* no compression, simply ship pure samples */
|
||||||
|
CVector<unsigned char> vecbyOut(iCodeSize);
|
||||||
|
const int iAudSize = iCodeSize / 2;
|
||||||
|
|
||||||
|
for (int i = 0; i < iAudSize; i++)
|
||||||
|
{
|
||||||
|
vecbyOut[2 * i] = vecsAudio[i] & 0xFF;
|
||||||
|
vecbyOut[2 * i + 1] = (vecsAudio[i] >> 8) & 0xFF;
|
||||||
|
}
|
||||||
|
return vecbyOut;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (eAudComprType)
|
||||||
|
{
|
||||||
|
case CT_IMAADPCM: return ImaAdpcm.Encode(vecsAudio); /* IMA-ADPCM */
|
||||||
|
default: return CVector<unsigned char>(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CVector<short> CAudioCompression::Decode(const CVector<unsigned char>& vecbyAdpcm)
|
||||||
|
{
|
||||||
|
if (eAudComprType == CT_NONE)
|
||||||
|
{
|
||||||
|
/* no compression, reassemble pure samples */
|
||||||
|
const int iAudSize = iCodeSize / 2;
|
||||||
|
CVector<short> vecsOut(iAudSize);
|
||||||
|
|
||||||
|
for (int i = 0; i < iAudSize; i++)
|
||||||
|
{
|
||||||
|
int current = vecbyAdpcm[2 * i] | (vecbyAdpcm[2 * i + 1] << 8);
|
||||||
|
if (current & 0x8000)
|
||||||
|
current -= 0x10000;
|
||||||
|
|
||||||
|
vecsOut[i] = (short) current;
|
||||||
|
}
|
||||||
|
return vecsOut;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (eAudComprType)
|
||||||
|
{
|
||||||
|
case CT_IMAADPCM: return ImaAdpcm.Decode(vecbyAdpcm); /* IMA-ADPCM */
|
||||||
|
default: return CVector<short>(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* IMA-ADPCM implementation ------------------------------------------------- */
|
||||||
|
int CImaAdpcm::Init(const int iNewAudioLen)
|
||||||
|
{
|
||||||
|
/* set lengths for audio and compressed data */
|
||||||
|
iAudSize = iNewAudioLen;
|
||||||
|
iAdpcmSize = 4 /* bytes header */ + (int) ceil(
|
||||||
|
(double) (iAudSize - 2 /* first two samples are in header */) / 2);
|
||||||
|
|
||||||
|
iStepindEnc = 0;
|
||||||
|
|
||||||
|
return iAdpcmSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
CVector<unsigned char> CImaAdpcm::Encode(const CVector<short>& vecsAudio)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
CVector<unsigned char> vecbyAdpcm;
|
||||||
|
CVector<unsigned char> vecbyAdpcmTemp;
|
||||||
|
|
||||||
|
/* init size */
|
||||||
|
vecbyAdpcm.Init(iAdpcmSize);
|
||||||
|
vecbyAdpcmTemp.Init(iAudSize);
|
||||||
|
|
||||||
|
/* encode the block header ----------------------------------------------- */
|
||||||
|
vecbyAdpcm[0] = vecsAudio[0] & 0xFF;
|
||||||
|
vecbyAdpcm[1] = (vecsAudio[0] >> 8) & 0xFF;
|
||||||
|
vecbyAdpcm[2] = iStepindEnc;
|
||||||
|
|
||||||
|
int iPrevAudio = vecsAudio[0];
|
||||||
|
|
||||||
|
|
||||||
|
/* encode the samples as 4 bit ------------------------------------------- */
|
||||||
|
for (i = 1; i < iAudSize; i++)
|
||||||
|
{
|
||||||
|
/* init diff and step */
|
||||||
|
int diff = vecsAudio[i] - iPrevAudio;
|
||||||
|
int step = ima_step_size[iStepindEnc];
|
||||||
|
|
||||||
|
short bytecode = 0;
|
||||||
|
|
||||||
|
int vpdiff = step >> 3;
|
||||||
|
if (diff < 0)
|
||||||
|
{
|
||||||
|
bytecode = 8;
|
||||||
|
diff = -diff;
|
||||||
|
}
|
||||||
|
short mask = 4;
|
||||||
|
while (mask)
|
||||||
|
{
|
||||||
|
if (diff >= step)
|
||||||
|
{
|
||||||
|
bytecode |= mask;
|
||||||
|
diff -= step;
|
||||||
|
vpdiff += step;
|
||||||
|
}
|
||||||
|
step >>= 1;
|
||||||
|
mask >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytecode & 8)
|
||||||
|
iPrevAudio -= vpdiff;
|
||||||
|
else
|
||||||
|
iPrevAudio += vpdiff;
|
||||||
|
|
||||||
|
/* adjust step size */
|
||||||
|
iStepindEnc += ima_indx_adjust[bytecode];
|
||||||
|
|
||||||
|
/* check that values do not exceed the bounds */
|
||||||
|
iPrevAudio = CheckBounds(iPrevAudio, _MINSHORT, _MAXSHORT);
|
||||||
|
iStepindEnc = CheckBounds(iStepindEnc, 0, IMA_STEP_SIZE_TAB_LEN - 1);
|
||||||
|
|
||||||
|
/* use the input buffer as an intermediate result buffer */
|
||||||
|
vecbyAdpcmTemp[i] = bytecode;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* pack the 4 bit encoded samples ---------------------------------------- */
|
||||||
|
/* The first encoded audio sample is in header */
|
||||||
|
vecbyAdpcm[3] = vecbyAdpcmTemp[1] & 0x0F;
|
||||||
|
|
||||||
|
for (i = 4; i < iAdpcmSize; i++)
|
||||||
|
{
|
||||||
|
vecbyAdpcm[i] = vecbyAdpcmTemp[2 * i - 6] & 0x0F;
|
||||||
|
vecbyAdpcm[i] |= (vecbyAdpcmTemp[2 * i - 5] << 4) & 0xF0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vecbyAdpcm;
|
||||||
|
}
|
||||||
|
|
||||||
|
CVector<short> CImaAdpcm::Decode(const CVector<unsigned char>& vecbyAdpcm)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
CVector<short> vecsAudio;
|
||||||
|
|
||||||
|
vecsAudio.Init(iAudSize);
|
||||||
|
|
||||||
|
|
||||||
|
/* read and check the block header --------------------------------------- */
|
||||||
|
int current = vecbyAdpcm[0] | (vecbyAdpcm[1] << 8);
|
||||||
|
if (current & 0x8000)
|
||||||
|
current -= 0x10000;
|
||||||
|
|
||||||
|
/* get and bound step index */
|
||||||
|
int iStepindDec = CheckBounds(vecbyAdpcm[2], 0, IMA_STEP_SIZE_TAB_LEN - 1);
|
||||||
|
|
||||||
|
/* set first sample which was delivered in the header */
|
||||||
|
vecsAudio[0] = current;
|
||||||
|
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------
|
||||||
|
pull apart the packed 4 bit samples and store them in their correct sample
|
||||||
|
positions */
|
||||||
|
/* The first encoded audio sample is in header */
|
||||||
|
vecsAudio[1] = vecbyAdpcm[3] & 0x0F;
|
||||||
|
|
||||||
|
for (i = 4; i < iAdpcmSize; i++)
|
||||||
|
{
|
||||||
|
const short bytecode = vecbyAdpcm[i];
|
||||||
|
vecsAudio[2 * i - 6] = bytecode & 0x0F;
|
||||||
|
vecsAudio[2 * i - 5] = (bytecode >> 4) & 0x0F;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* decode the encoded 4 bit samples -------------------------------------- */
|
||||||
|
for (i = 1; i < iAudSize; i++)
|
||||||
|
{
|
||||||
|
const short bytecode = vecsAudio[i] & 0xF ;
|
||||||
|
|
||||||
|
short step = ima_step_size[iStepindDec];
|
||||||
|
int current = vecsAudio[i - 1];
|
||||||
|
|
||||||
|
int diff = step >> 3;
|
||||||
|
if (bytecode & 1)
|
||||||
|
diff += step >> 2;
|
||||||
|
if (bytecode & 2)
|
||||||
|
diff += step >> 1;
|
||||||
|
if (bytecode & 4)
|
||||||
|
diff += step;
|
||||||
|
if (bytecode & 8)
|
||||||
|
diff = -diff;
|
||||||
|
|
||||||
|
current += diff;
|
||||||
|
iStepindDec += ima_indx_adjust[bytecode];
|
||||||
|
|
||||||
|
/* check that values do not exceed the bounds */
|
||||||
|
current = CheckBounds(current, _MINSHORT, _MAXSHORT);
|
||||||
|
iStepindDec = CheckBounds(iStepindDec, 0, IMA_STEP_SIZE_TAB_LEN - 1);
|
||||||
|
|
||||||
|
vecsAudio[i] = current;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vecsAudio;
|
||||||
|
}
|
104
src/audiocompr.h
Executable file
104
src/audiocompr.h
Executable file
|
@ -0,0 +1,104 @@
|
||||||
|
/******************************************************************************\
|
||||||
|
* Copyright (c) 2004-2006
|
||||||
|
*
|
||||||
|
* Author(s):
|
||||||
|
* Volker Fischer, Erik de Castro Lopo
|
||||||
|
*
|
||||||
|
******************************************************************************
|
||||||
|
*
|
||||||
|
* 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.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
\******************************************************************************/
|
||||||
|
|
||||||
|
#if !defined(AUDIOCOMPR_H_OIHGE76GEKJH3249_GEG98EG3_43441912__INCLUDED_)
|
||||||
|
#define AUDIOCOMPR_H_OIHGE76GEKJH3249_GEG98EG3_43441912__INCLUDED_
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
#include "global.h"
|
||||||
|
#include "buffer.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Definitions ****************************************************************/
|
||||||
|
/* tables */
|
||||||
|
static int ima_indx_adjust[16] =
|
||||||
|
{ -1, -1, -1, -1, /* +0 - +3, decrease the step size */
|
||||||
|
2, 4, 6, 8, /* +4 - +7, increase the step size */
|
||||||
|
-1, -1, -1, -1, /* -0 - -3, decrease the step size */
|
||||||
|
2, 4, 6, 8, /* -4 - -7, increase the step size */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define IMA_STEP_SIZE_TAB_LEN 89
|
||||||
|
static int ima_step_size[IMA_STEP_SIZE_TAB_LEN] =
|
||||||
|
{ 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
|
||||||
|
50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230,
|
||||||
|
253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963,
|
||||||
|
1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327,
|
||||||
|
3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442,
|
||||||
|
11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
|
||||||
|
32767
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* Classes ********************************************************************/
|
||||||
|
/* IMA-ADPCM ---------------------------------------------------------------- */
|
||||||
|
class CImaAdpcm
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CImaAdpcm() : iStepindEnc(0) {}
|
||||||
|
virtual ~CImaAdpcm() {}
|
||||||
|
|
||||||
|
int Init(const int iNewAudioLen);
|
||||||
|
CVector<unsigned char> Encode(const CVector<short>& vecsAudio);
|
||||||
|
CVector<short> Decode(const CVector<unsigned char>& vecbyAdpcm);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int iAudSize;
|
||||||
|
int iAdpcmSize;
|
||||||
|
int iStepindEnc;
|
||||||
|
|
||||||
|
/* inline functions must be declared in the header */
|
||||||
|
inline int CheckBounds(const int iData, const int iMin, const int iMax)
|
||||||
|
{
|
||||||
|
if (iData > iMax)
|
||||||
|
return iMax;
|
||||||
|
if (iData < iMin)
|
||||||
|
return iMin;
|
||||||
|
|
||||||
|
return iData;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* Audio compression class -------------------------------------------------- */
|
||||||
|
class CAudioCompression
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum EAudComprType {CT_NONE, CT_IMAADPCM};
|
||||||
|
|
||||||
|
CAudioCompression() {}
|
||||||
|
virtual ~CAudioCompression() {}
|
||||||
|
|
||||||
|
int Init(const int iNewAudioLen, const EAudComprType eNewAuCoTy);
|
||||||
|
CVector<unsigned char> Encode(const CVector<short>& vecsAudio);
|
||||||
|
CVector<short> Decode(const CVector<unsigned char>& vecbyAdpcm);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
EAudComprType eAudComprType;
|
||||||
|
CImaAdpcm ImaAdpcm;
|
||||||
|
int iCodeSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* !defined(AUDIOCOMPR_H_OIHGE76GEKJH3249_GEG98EG3_43441912__INCLUDED_) */
|
381
src/buffer.cpp
Executable file
381
src/buffer.cpp
Executable file
|
@ -0,0 +1,381 @@
|
||||||
|
/******************************************************************************\
|
||||||
|
* Copyright (c) 2004-2006
|
||||||
|
*
|
||||||
|
* Author(s):
|
||||||
|
* Volker Fischer
|
||||||
|
*
|
||||||
|
* Note: we assuming here that put and get operations are secured by a mutex
|
||||||
|
* and do not take place at the same time
|
||||||
|
*
|
||||||
|
******************************************************************************
|
||||||
|
*
|
||||||
|
* 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.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
\******************************************************************************/
|
||||||
|
|
||||||
|
#include "buffer.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Implementation *************************************************************/
|
||||||
|
void CNetBuf::Init ( const int iNewBlockSize, const int iNewNumBlocks )
|
||||||
|
{
|
||||||
|
/* total size -> size of one block times number of blocks */
|
||||||
|
iBlockSize = iNewBlockSize;
|
||||||
|
iMemSize = iNewBlockSize * iNewNumBlocks;
|
||||||
|
|
||||||
|
/* fade in first block added to the buffer */
|
||||||
|
bFadeInNewPutData = true;
|
||||||
|
|
||||||
|
/* allocate and clear memory for actual data buffer */
|
||||||
|
vecdMemory.Init(iMemSize);
|
||||||
|
|
||||||
|
/* use the "get" flag to make sure the buffer is cleared */
|
||||||
|
Clear(CT_GET);
|
||||||
|
|
||||||
|
/* initialize number of samples for fading effect */
|
||||||
|
if (FADE_IN_OUT_NUM_SAM < iBlockSize)
|
||||||
|
iNumSamFading = iBlockSize;
|
||||||
|
else
|
||||||
|
iNumSamFading = FADE_IN_OUT_NUM_SAM;
|
||||||
|
|
||||||
|
if (FADE_IN_OUT_NUM_SAM_EXTRA > iBlockSize)
|
||||||
|
iNumSamFadingExtra = iBlockSize;
|
||||||
|
else
|
||||||
|
iNumSamFadingExtra = FADE_IN_OUT_NUM_SAM;
|
||||||
|
|
||||||
|
/* init variables for extrapolation (in case a fade out is needed) */
|
||||||
|
dExPDiff = 0.0;
|
||||||
|
dExPLastV = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CNetBuf::Put(CVector<double>& vecdData)
|
||||||
|
{
|
||||||
|
#ifdef _DEBUG_
|
||||||
|
static FILE* pFileBI = fopen("bufferin.dat", "w");
|
||||||
|
fprintf(pFileBI, "%d %d\n", GetAvailSpace() / iBlockSize, iMemSize / iBlockSize);
|
||||||
|
fflush(pFileBI);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool bPutOK = true;
|
||||||
|
|
||||||
|
/* get size of data to be added to the buffer */
|
||||||
|
const int iInSize = vecdData.Size();
|
||||||
|
|
||||||
|
/* Check if there is not enough space available -> correct */
|
||||||
|
if (GetAvailSpace() < iInSize)
|
||||||
|
{
|
||||||
|
/* not enough space in buffer for put operation, correct buffer to
|
||||||
|
prepare for new data */
|
||||||
|
Clear(CT_PUT);
|
||||||
|
|
||||||
|
/* set flag to fade in new block to avoid clicks */
|
||||||
|
bFadeInNewPutData = true;
|
||||||
|
|
||||||
|
bPutOK = false; /* return error flag */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fade in new block if required */
|
||||||
|
if (bFadeInNewPutData)
|
||||||
|
FadeInAudioDataBlock(vecdData);
|
||||||
|
|
||||||
|
/* copy new data in internal buffer */
|
||||||
|
int iCurPos = 0;
|
||||||
|
if (iPutPos + iInSize > iMemSize)
|
||||||
|
{
|
||||||
|
/* remaining space size for second block */
|
||||||
|
const int iRemSpace = iPutPos + iInSize - iMemSize;
|
||||||
|
|
||||||
|
/* data must be written in two steps because of wrap around */
|
||||||
|
while (iPutPos < iMemSize)
|
||||||
|
vecdMemory[iPutPos++] = vecdData[iCurPos++];
|
||||||
|
|
||||||
|
for (iPutPos = 0; iPutPos < iRemSpace; iPutPos++)
|
||||||
|
vecdMemory[iPutPos] = vecdData[iCurPos++];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* data can be written in one step */
|
||||||
|
const int iEnd = iPutPos + iInSize;
|
||||||
|
while (iPutPos < iEnd)
|
||||||
|
vecdMemory[iPutPos++] = vecdData[iCurPos++];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set buffer state flag */
|
||||||
|
if (iPutPos == iGetPos)
|
||||||
|
eBufState = CNetBuf::BS_FULL;
|
||||||
|
else
|
||||||
|
eBufState = CNetBuf::BS_OK;
|
||||||
|
|
||||||
|
return bPutOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CNetBuf::Get(CVector<double>& vecdData)
|
||||||
|
{
|
||||||
|
bool bGetOK = true; /* init return value */
|
||||||
|
bool bFadeOutExtrap = false;
|
||||||
|
|
||||||
|
/* get size of data to be get from the buffer */
|
||||||
|
const int iInSize = vecdData.Size();
|
||||||
|
|
||||||
|
/* Check if there is not enough data available -> correct */
|
||||||
|
if (GetAvailData() < iInSize)
|
||||||
|
{
|
||||||
|
/* not enough data in buffer for get operation, correct buffer to
|
||||||
|
prepare for getting data */
|
||||||
|
Clear(CT_GET);
|
||||||
|
|
||||||
|
/* set flag to fade in next new block in buffer and fade out last
|
||||||
|
block by extrapolation to avoid clicks */
|
||||||
|
bFadeInNewPutData = true;
|
||||||
|
bFadeOutExtrap = true;
|
||||||
|
|
||||||
|
bGetOK = false; /* return error flag */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* copy data from internal buffer in output buffer */
|
||||||
|
int iCurPos = 0;
|
||||||
|
if (iGetPos + iInSize > iMemSize)
|
||||||
|
{
|
||||||
|
/* remaining data size for second block */
|
||||||
|
const int iRemData = iGetPos + iInSize - iMemSize;
|
||||||
|
|
||||||
|
/* data must be read in two steps because of wrap around */
|
||||||
|
while (iGetPos < iMemSize)
|
||||||
|
vecdData[iCurPos++] = vecdMemory[iGetPos++];
|
||||||
|
|
||||||
|
for (iGetPos = 0; iGetPos < iRemData; iGetPos++)
|
||||||
|
vecdData[iCurPos++] = vecdMemory[iGetPos];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* data can be read in one step */
|
||||||
|
const int iEnd = iGetPos + iInSize;
|
||||||
|
while (iGetPos < iEnd)
|
||||||
|
vecdData[iCurPos++] = vecdMemory[iGetPos++];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set buffer state flag */
|
||||||
|
if (iPutPos == iGetPos)
|
||||||
|
eBufState = CNetBuf::BS_EMPTY;
|
||||||
|
else
|
||||||
|
eBufState = CNetBuf::BS_OK;
|
||||||
|
|
||||||
|
|
||||||
|
/* extrapolate data from old block to avoid "clicks"
|
||||||
|
we have to do this method since we cannot fade out the old block
|
||||||
|
anymore since it is already gone (processed or send through the
|
||||||
|
network) */
|
||||||
|
if (bFadeOutExtrap)
|
||||||
|
FadeOutExtrapolateAudioDataBlock(vecdData, dExPDiff, dExPLastV);
|
||||||
|
|
||||||
|
/* save some paramters from last block which is needed in case we do not
|
||||||
|
have enough data for next "get" operation and need to extrapolate the
|
||||||
|
signal to avoid "clicks"
|
||||||
|
we assume here that "iBlockSize" is larger than 1! */
|
||||||
|
dExPDiff = vecdData[iInSize - 1] - vecdData[iInSize - 2];
|
||||||
|
dExPLastV = vecdData[iInSize - 1];
|
||||||
|
|
||||||
|
return bGetOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CNetBuf::GetAvailSpace() const
|
||||||
|
{
|
||||||
|
/* calculate available space in buffer */
|
||||||
|
int iAvSpace = iGetPos - iPutPos;
|
||||||
|
|
||||||
|
/* check for special case and wrap around */
|
||||||
|
if (iAvSpace < 0)
|
||||||
|
iAvSpace += iMemSize; /* wrap around */
|
||||||
|
else if ((iAvSpace == 0) && (eBufState == BS_EMPTY))
|
||||||
|
iAvSpace = iMemSize;
|
||||||
|
|
||||||
|
return iAvSpace;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CNetBuf::GetAvailData() const
|
||||||
|
{
|
||||||
|
/* calculate available data in buffer */
|
||||||
|
int iAvData = iPutPos - iGetPos;
|
||||||
|
|
||||||
|
/* check for special case and wrap around */
|
||||||
|
if (iAvData < 0)
|
||||||
|
iAvData += iMemSize; /* wrap around */
|
||||||
|
else if ((iAvData == 0) && (eBufState == BS_FULL))
|
||||||
|
iAvData = iMemSize;
|
||||||
|
|
||||||
|
return iAvData;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CNetBuf::Clear(const EClearType eClearType)
|
||||||
|
{
|
||||||
|
|
||||||
|
int iMiddleOfBuffer;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
/* with the following operation we set the new get pos to a block
|
||||||
|
boundary (one block below the middle of the buffer in case of odd
|
||||||
|
number of blocks, e.g.:
|
||||||
|
[buffer size]: [get pos]
|
||||||
|
1: 0 / 2: 0 / 3: 1 / 4: 1 / ... */
|
||||||
|
iMiddleOfBuffer = (((iMemSize - iBlockSize) / 2) / iBlockSize) * iBlockSize;
|
||||||
|
#else
|
||||||
|
// old code
|
||||||
|
|
||||||
|
// somehow the old code seems to work better than the sophisticated new one....?
|
||||||
|
/* 1: 0 / 2: 1 / 3: 1 / 4: 2 / ... */
|
||||||
|
iMiddleOfBuffer = ((iMemSize / 2) / iBlockSize) * iBlockSize;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* different behaviour for get and put corrections */
|
||||||
|
if (eClearType == CT_GET)
|
||||||
|
{
|
||||||
|
/* clear buffer */
|
||||||
|
vecdMemory.Reset(0.0);
|
||||||
|
|
||||||
|
/* correct buffer so that after the current get operation the pointer
|
||||||
|
are at maximum distance */
|
||||||
|
iPutPos = 0;
|
||||||
|
iGetPos = iMiddleOfBuffer;
|
||||||
|
|
||||||
|
/* check for special case */
|
||||||
|
if (iPutPos == iGetPos)
|
||||||
|
eBufState = CNetBuf::BS_FULL;
|
||||||
|
else
|
||||||
|
eBufState = CNetBuf::BS_OK;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* in case of "put" correction, do not delete old data but only shift
|
||||||
|
the pointers */
|
||||||
|
iPutPos = iMiddleOfBuffer;
|
||||||
|
|
||||||
|
/* adjust put pointer relative to current get pointer, take care of
|
||||||
|
wrap around */
|
||||||
|
iPutPos += iGetPos;
|
||||||
|
if (iPutPos > iMemSize)
|
||||||
|
iPutPos -= iMemSize;
|
||||||
|
|
||||||
|
/* fade out old data right before new put pointer */
|
||||||
|
int iCurPos = iPutPos - iNumSamFading;
|
||||||
|
int i = iNumSamFading;
|
||||||
|
|
||||||
|
if (iCurPos < 0)
|
||||||
|
{
|
||||||
|
/* wrap around */
|
||||||
|
iCurPos += iMemSize;
|
||||||
|
|
||||||
|
/* data must be processed in two steps because of wrap around */
|
||||||
|
while (iCurPos < iMemSize)
|
||||||
|
{
|
||||||
|
vecdMemory[iCurPos++] *= ((double) i / iNumSamFading);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (iCurPos = 0; iCurPos < iPutPos; iCurPos++)
|
||||||
|
{
|
||||||
|
vecdMemory[iCurPos] *= ((double) i / iNumSamFading);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* data can be processed in one step */
|
||||||
|
while (iCurPos < iPutPos)
|
||||||
|
{
|
||||||
|
vecdMemory[iCurPos++] *= ((double) i / iNumSamFading);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for special case */
|
||||||
|
if (iPutPos == iGetPos)
|
||||||
|
{
|
||||||
|
eBufState = CNetBuf::BS_EMPTY;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eBufState = CNetBuf::BS_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CNetBuf::FadeInAudioDataBlock(CVector<double>& vecdData)
|
||||||
|
{
|
||||||
|
/* apply linear fading */
|
||||||
|
for (int i = 0; i < iNumSamFading; i++)
|
||||||
|
{
|
||||||
|
vecdData[i] *= ((double) i / iNumSamFading);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* reset flag */
|
||||||
|
bFadeInNewPutData = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CNetBuf::FadeOutExtrapolateAudioDataBlock(CVector<double>& vecdData,
|
||||||
|
const double dExPDiff,
|
||||||
|
const double dExPLastV)
|
||||||
|
{
|
||||||
|
/* apply linear extrapolation and linear fading */
|
||||||
|
for (int i = 0; i < iNumSamFadingExtra; i++)
|
||||||
|
{
|
||||||
|
/* calculate extrapolated value */
|
||||||
|
vecdData[i] = ((i + 1) * dExPDiff + dExPLastV);
|
||||||
|
|
||||||
|
/* linear fading */
|
||||||
|
vecdData[i] *= ((double) (iNumSamFadingExtra - i) / iNumSamFadingExtra);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* conversion buffer implementation *******************************************/
|
||||||
|
void CConvBuf::Init ( const int iNewMemSize )
|
||||||
|
{
|
||||||
|
/* set memory size */
|
||||||
|
iMemSize = iNewMemSize;
|
||||||
|
|
||||||
|
/* allocate and clear memory for actual data buffer */
|
||||||
|
vecsMemory.Init(iMemSize);
|
||||||
|
|
||||||
|
iPutPos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CConvBuf::Put ( const CVector<short>& vecsData)
|
||||||
|
{
|
||||||
|
const int iVecSize = vecsData.Size();
|
||||||
|
|
||||||
|
/* copy new data in internal buffer */
|
||||||
|
int iCurPos = 0;
|
||||||
|
const int iEnd = iPutPos + iVecSize;
|
||||||
|
while ( iPutPos < iEnd )
|
||||||
|
{
|
||||||
|
vecsMemory[iPutPos++] = vecsData[iCurPos++];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bBufOk = false;
|
||||||
|
if ( iEnd == iMemSize )
|
||||||
|
{
|
||||||
|
bBufOk = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bBufOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
CVector<short> CConvBuf::Get()
|
||||||
|
{
|
||||||
|
iPutPos = 0;
|
||||||
|
return vecsMemory;
|
||||||
|
}
|
100
src/buffer.h
Executable file
100
src/buffer.h
Executable file
|
@ -0,0 +1,100 @@
|
||||||
|
/******************************************************************************\
|
||||||
|
* Copyright (c) 2004-2006
|
||||||
|
*
|
||||||
|
* Author(s):
|
||||||
|
* Volker Fischer
|
||||||
|
*
|
||||||
|
******************************************************************************
|
||||||
|
*
|
||||||
|
* 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.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
\******************************************************************************/
|
||||||
|
|
||||||
|
#if !defined(BUFFER_H__3B123453_4344_BB23945IUHF1912__INCLUDED_)
|
||||||
|
#define BUFFER_H__3B123453_4344_BB23945IUHF1912__INCLUDED_
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
#include "global.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Definitions ****************************************************************/
|
||||||
|
/* time for fading effect for masking drop outs */
|
||||||
|
#define FADE_IN_OUT_TIME ((double) 0.3) /* ms */
|
||||||
|
#define FADE_IN_OUT_NUM_SAM ((int) (SAMPLE_RATE * FADE_IN_OUT_TIME) / 1000)
|
||||||
|
|
||||||
|
/* for extrapolation a shorter time for fading */
|
||||||
|
#define FADE_IN_OUT_NUM_SAM_EXTRA 5 /* samples */
|
||||||
|
|
||||||
|
|
||||||
|
/* Classes ********************************************************************/
|
||||||
|
class CNetBuf
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CNetBuf() {}
|
||||||
|
virtual ~CNetBuf() {}
|
||||||
|
|
||||||
|
void Init(const int iNewBlockSize, const int iNewNumBlocks);
|
||||||
|
int GetSize() {return iMemSize / iBlockSize;}
|
||||||
|
|
||||||
|
bool Put(CVector<double>& vecdData);
|
||||||
|
bool Get(CVector<double>& vecdData);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
enum EBufState {BS_OK, BS_FULL, BS_EMPTY};
|
||||||
|
enum EClearType {CT_PUT, CT_GET};
|
||||||
|
void Clear(const EClearType eClearType);
|
||||||
|
int GetAvailSpace() const;
|
||||||
|
int GetAvailData() const;
|
||||||
|
void FadeInAudioDataBlock(CVector<double>& vecdData);
|
||||||
|
void FadeOutExtrapolateAudioDataBlock(CVector<double>& vecdData,
|
||||||
|
const double dExPDiff, const double dExPLastV);
|
||||||
|
|
||||||
|
CVector<double> vecdMemory;
|
||||||
|
int iMemSize;
|
||||||
|
int iBlockSize;
|
||||||
|
int iGetPos, iPutPos;
|
||||||
|
EBufState eBufState;
|
||||||
|
bool bFadeInNewPutData;
|
||||||
|
int iNumSamFading;
|
||||||
|
int iNumSamFadingExtra;
|
||||||
|
|
||||||
|
/* extrapolation parameters */
|
||||||
|
double dExPDiff;
|
||||||
|
double dExPLastV;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* conversion buffer (very simple buffer) */
|
||||||
|
class CConvBuf
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CConvBuf () {}
|
||||||
|
virtual ~CConvBuf () {}
|
||||||
|
|
||||||
|
void Init ( const int iNewMemSize );
|
||||||
|
int GetSize() { return iMemSize; }
|
||||||
|
|
||||||
|
bool Put ( const CVector<short>& vecsData );
|
||||||
|
CVector<short> Get ();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
CVector<short> vecsMemory;
|
||||||
|
int iMemSize;
|
||||||
|
int iBlockSize;
|
||||||
|
int iPutPos;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* !defined(BUFFER_H__3B123453_4344_BB23945IUHF1912__INCLUDED_) */
|
448
src/channel.cpp
Executable file
448
src/channel.cpp
Executable file
|
@ -0,0 +1,448 @@
|
||||||
|
/******************************************************************************\
|
||||||
|
* Copyright (c) 2004-2006
|
||||||
|
*
|
||||||
|
* Author(s):
|
||||||
|
* Volker Fischer
|
||||||
|
*
|
||||||
|
******************************************************************************
|
||||||
|
*
|
||||||
|
* 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.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
\******************************************************************************/
|
||||||
|
|
||||||
|
#include "channel.h"
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************\
|
||||||
|
* CChannelSet *
|
||||||
|
\******************************************************************************/
|
||||||
|
int CChannelSet::GetFreeChan()
|
||||||
|
{
|
||||||
|
/* look for a free channel */
|
||||||
|
for (int i = 0; i < MAX_NUM_CHANNELS; i++)
|
||||||
|
{
|
||||||
|
if (!vecChannels[i].IsConnected())
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* no free channel found, return invalid ID */
|
||||||
|
return INVALID_CHANNEL_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CChannelSet::CheckAddr(const CHostAddress& Addr)
|
||||||
|
{
|
||||||
|
CHostAddress InetAddr;
|
||||||
|
|
||||||
|
/* Check for all possible channels if IP is already in use */
|
||||||
|
for (int i = 0; i < MAX_NUM_CHANNELS; i++)
|
||||||
|
{
|
||||||
|
if (vecChannels[i].GetAddress(InetAddr))
|
||||||
|
{
|
||||||
|
/* IP found, return channel number */
|
||||||
|
if (InetAddr == Addr)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* IP not found, return invalid ID */
|
||||||
|
return INVALID_CHANNEL_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CChannelSet::PutData(const CVector<unsigned char>& vecbyRecBuf,
|
||||||
|
const int iNumBytesRead, const CHostAddress& HostAdr)
|
||||||
|
{
|
||||||
|
Mutex.lock();
|
||||||
|
|
||||||
|
/* get channel ID ------------------------------------------------------- */
|
||||||
|
bool bChanOK = true;
|
||||||
|
|
||||||
|
/* check address */
|
||||||
|
int iCurChanID = CheckAddr(HostAdr);
|
||||||
|
|
||||||
|
if (iCurChanID == INVALID_CHANNEL_ID)
|
||||||
|
{
|
||||||
|
/* a new client is calling, look for free channel */
|
||||||
|
iCurChanID = GetFreeChan();
|
||||||
|
|
||||||
|
if (iCurChanID != INVALID_CHANNEL_ID)
|
||||||
|
vecChannels[iCurChanID].SetAddress(HostAdr);
|
||||||
|
else
|
||||||
|
bChanOK = false; /* no free channel available */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* put received data in jitter buffer ----------------------------------- */
|
||||||
|
if (bChanOK)
|
||||||
|
{
|
||||||
|
/* put packet in socket buffer */
|
||||||
|
if (vecChannels[iCurChanID].PutData(vecbyRecBuf, iNumBytesRead))
|
||||||
|
PostWinMessage(MS_JIT_BUF_PUT, MUL_COL_LED_GREEN, iCurChanID);
|
||||||
|
else
|
||||||
|
PostWinMessage(MS_JIT_BUF_PUT, MUL_COL_LED_RED, iCurChanID);
|
||||||
|
}
|
||||||
|
|
||||||
|
Mutex.unlock();
|
||||||
|
|
||||||
|
return !bChanOK; /* return 1 if error */
|
||||||
|
}
|
||||||
|
|
||||||
|
void CChannelSet::GetBlockAllConC(CVector<int>& vecChanID,
|
||||||
|
CVector<CVector<double> >& vecvecdData)
|
||||||
|
{
|
||||||
|
/* init temporal data vector and clear input buffers */
|
||||||
|
CVector<double> vecdData(BLOCK_SIZE_SAMPLES);
|
||||||
|
|
||||||
|
vecChanID.Init(0);
|
||||||
|
vecvecdData.Init(0);
|
||||||
|
|
||||||
|
/* make put and get calls thread safe. Do not forget to unlock mutex
|
||||||
|
afterwards! */
|
||||||
|
Mutex.lock();
|
||||||
|
|
||||||
|
/* Check all possible channels */
|
||||||
|
for (int i = 0; i < MAX_NUM_CHANNELS; i++)
|
||||||
|
{
|
||||||
|
/* read out all input buffers to decrease timeout counter on
|
||||||
|
disconnected channels */
|
||||||
|
bool bGetOK = vecChannels[i].GetData(vecdData);
|
||||||
|
|
||||||
|
if (vecChannels[i].IsConnected())
|
||||||
|
{
|
||||||
|
/* add ID and data */
|
||||||
|
vecChanID.Add(i);
|
||||||
|
|
||||||
|
const int iOldSize = vecvecdData.Size();
|
||||||
|
vecvecdData.Enlarge(1);
|
||||||
|
vecvecdData[iOldSize].Init(vecdData.Size());
|
||||||
|
vecvecdData[iOldSize] = vecdData;
|
||||||
|
|
||||||
|
/* send message for get status (for GUI) */
|
||||||
|
if (bGetOK)
|
||||||
|
PostWinMessage(MS_JIT_BUF_GET, MUL_COL_LED_GREEN, i);
|
||||||
|
else
|
||||||
|
PostWinMessage(MS_JIT_BUF_GET, MUL_COL_LED_RED, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Mutex.unlock(); /* release mutex */
|
||||||
|
}
|
||||||
|
|
||||||
|
void CChannelSet::GetConCliParam(CVector<CHostAddress>& vecHostAddresses,
|
||||||
|
CVector<double>& vecdSamOffs)
|
||||||
|
{
|
||||||
|
CHostAddress InetAddr;
|
||||||
|
|
||||||
|
/* init return values */
|
||||||
|
vecHostAddresses.Init(MAX_NUM_CHANNELS);
|
||||||
|
vecdSamOffs.Init(MAX_NUM_CHANNELS);
|
||||||
|
|
||||||
|
/* Check all possible channels */
|
||||||
|
for (int i = 0; i < MAX_NUM_CHANNELS; i++)
|
||||||
|
{
|
||||||
|
if (vecChannels[i].GetAddress(InetAddr))
|
||||||
|
{
|
||||||
|
/* add new address and sample rate offset to vectors */
|
||||||
|
vecHostAddresses[i] = InetAddr;
|
||||||
|
vecdSamOffs[i] = vecChannels[i].GetResampleOffset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CChannelSet::SetSockBufSize ( const int iNewBlockSize, const int iNumBlocks )
|
||||||
|
{
|
||||||
|
/* this opperation must be done with mutex */
|
||||||
|
Mutex.lock ();
|
||||||
|
|
||||||
|
/* as a test we adjust the buffers of all channels to the new value. Maybe later
|
||||||
|
do change only for some channels -> take care to set value back to default if
|
||||||
|
channel is disconnected, afterwards! */
|
||||||
|
for ( int i = 0; i < MAX_NUM_CHANNELS; i++ )
|
||||||
|
vecChannels[i].SetSockBufSize ( iNewBlockSize, iNumBlocks );
|
||||||
|
|
||||||
|
Mutex.unlock ();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************\
|
||||||
|
* CChannel *
|
||||||
|
\******************************************************************************/
|
||||||
|
CChannel::CChannel()
|
||||||
|
{
|
||||||
|
/* init time stamp activation counter */
|
||||||
|
iTimeStampActCnt = NUM_BL_TIME_STAMPS;
|
||||||
|
|
||||||
|
/* init time stamp index counter */
|
||||||
|
byTimeStampIdxCnt = 0;
|
||||||
|
|
||||||
|
/* init the socket buffer */
|
||||||
|
SetSockBufSize ( BLOCK_SIZE_SAMPLES, DEF_NET_BUF_SIZE_NUM_BL );
|
||||||
|
|
||||||
|
/* init conversion buffer */
|
||||||
|
ConvBuf.Init ( BLOCK_SIZE_SAMPLES );
|
||||||
|
|
||||||
|
/* init audio compression unit */
|
||||||
|
iAudComprSize = AudioCompression.Init ( BLOCK_SIZE_SAMPLES,
|
||||||
|
CAudioCompression::CT_IMAADPCM );
|
||||||
|
|
||||||
|
/* init time-out for the buffer with zero -> no connection */
|
||||||
|
iConTimeOut = 0;
|
||||||
|
|
||||||
|
/* init sample rate offset estimation object */
|
||||||
|
SampleOffsetEst.Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CChannel::SetSockBufSize ( const int iNewBlockSize, const int iNumBlocks )
|
||||||
|
{
|
||||||
|
/* this opperation must be done with mutex */
|
||||||
|
Mutex.lock ();
|
||||||
|
|
||||||
|
SockBuf.Init ( iNewBlockSize, iNumBlocks );
|
||||||
|
|
||||||
|
Mutex.unlock ();
|
||||||
|
}
|
||||||
|
|
||||||
|
int CChannel::GetTimeStampIdx ()
|
||||||
|
{
|
||||||
|
/* only send time stamp index after a pre-defined number of packets */
|
||||||
|
if ( iTimeStampActCnt > 0 )
|
||||||
|
{
|
||||||
|
iTimeStampActCnt--;
|
||||||
|
return INVALID_TIME_STAMP_IDX;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* reset time stamp activation counter */
|
||||||
|
iTimeStampActCnt = NUM_BL_TIME_STAMPS - 1;
|
||||||
|
|
||||||
|
/* wraps around automatically */
|
||||||
|
byTimeStampIdxCnt++;
|
||||||
|
return byTimeStampIdxCnt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CChannel::GetAddress(CHostAddress& RetAddr)
|
||||||
|
{
|
||||||
|
if (IsConnected())
|
||||||
|
{
|
||||||
|
RetAddr = InetAddr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RetAddr = CHostAddress();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CChannel::PutData(const CVector<unsigned char>& vecbyData,
|
||||||
|
int iNumBytes)
|
||||||
|
{
|
||||||
|
bool bRet = true;
|
||||||
|
|
||||||
|
Mutex.lock(); /* put mutex lock */
|
||||||
|
|
||||||
|
/* only process if packet has correct size */
|
||||||
|
if (iNumBytes == iAudComprSize)
|
||||||
|
{
|
||||||
|
/* decompress audio */
|
||||||
|
CVector<short> vecsDecomprAudio(BLOCK_SIZE_SAMPLES);
|
||||||
|
vecsDecomprAudio = AudioCompression.Decode(vecbyData);
|
||||||
|
|
||||||
|
/* do resampling to compensate for sample rate offsets in the
|
||||||
|
different sound cards of the clients */
|
||||||
|
// we should not do resampling here since we already have resampling
|
||||||
|
// in the client audio path, we could use this resampling for this
|
||||||
|
// sample rate correction, too
|
||||||
|
/*
|
||||||
|
for (int i = 0; i < BLOCK_SIZE_SAMPLES; i++)
|
||||||
|
vecdResInData[i] = (double) vecsData[i];
|
||||||
|
|
||||||
|
const int iInSize = ResampleObj.Resample(vecdResInData, vecdResOutData,
|
||||||
|
(double) SAMPLE_RATE / (SAMPLE_RATE - dSamRateOffset));
|
||||||
|
*/
|
||||||
|
|
||||||
|
vecdResOutData.Init(BLOCK_SIZE_SAMPLES);
|
||||||
|
for (int i = 0; i < BLOCK_SIZE_SAMPLES; i++)
|
||||||
|
vecdResOutData[i] = (double) vecsDecomprAudio[i];
|
||||||
|
|
||||||
|
|
||||||
|
bRet = SockBuf.Put(vecdResOutData);
|
||||||
|
|
||||||
|
/* reset time-out counter */
|
||||||
|
iConTimeOut = CON_TIME_OUT_CNT_MAX;
|
||||||
|
}
|
||||||
|
else if (iNumBytes == 1)
|
||||||
|
{
|
||||||
|
/* time stamp packet */
|
||||||
|
SampleOffsetEst.AddTimeStampIdx(vecbyData[0]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
bRet = false; /* wrong packet size */
|
||||||
|
|
||||||
|
Mutex.unlock(); /* put mutex unlock */
|
||||||
|
|
||||||
|
return bRet;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CChannel::GetData(CVector<double>& vecdData)
|
||||||
|
{
|
||||||
|
Mutex.lock(); /* get mutex lock */
|
||||||
|
|
||||||
|
const bool bGetOK = SockBuf.Get(vecdData);
|
||||||
|
|
||||||
|
if (!bGetOK)
|
||||||
|
{
|
||||||
|
/* decrease time-out counter */
|
||||||
|
if (iConTimeOut > 0)
|
||||||
|
{
|
||||||
|
iConTimeOut--;
|
||||||
|
|
||||||
|
/* if time out is reached, re-init resample offset estimation
|
||||||
|
module */
|
||||||
|
if (iConTimeOut == 0)
|
||||||
|
SampleOffsetEst.Init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Mutex.unlock(); /* get mutex unlock */
|
||||||
|
|
||||||
|
return bGetOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
CVector<unsigned char> CChannel::PrepSendPacket(const CVector<short>& vecsNPacket)
|
||||||
|
{
|
||||||
|
/* if the block is not ready we have to initialize with zero length to
|
||||||
|
tell the following network send routine that nothing should be sent */
|
||||||
|
CVector<unsigned char> vecbySendBuf ( 0 );
|
||||||
|
|
||||||
|
/* use conversion buffer to convert sound card block size in network
|
||||||
|
block size */
|
||||||
|
if ( ConvBuf.Put ( vecsNPacket ) )
|
||||||
|
{
|
||||||
|
/* a packet is ready, compress audio */
|
||||||
|
vecbySendBuf.Init ( iAudComprSize );
|
||||||
|
vecbySendBuf = AudioCompression.Encode ( ConvBuf.Get () );
|
||||||
|
}
|
||||||
|
|
||||||
|
return vecbySendBuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************\
|
||||||
|
* CSampleOffsetEst *
|
||||||
|
\******************************************************************************/
|
||||||
|
void CSampleOffsetEst::Init()
|
||||||
|
{
|
||||||
|
/* init sample rate estimation */
|
||||||
|
dSamRateEst = SAMPLE_RATE;
|
||||||
|
|
||||||
|
/* init vectors storing the data */
|
||||||
|
veciTimeElapsed.Init(VEC_LEN_SAM_OFFS_EST);
|
||||||
|
veciTiStIdx.Init(VEC_LEN_SAM_OFFS_EST);
|
||||||
|
|
||||||
|
/* start reference time (the counter wraps to zero 24 hours after the last
|
||||||
|
call to start() or restart, but this should not concern us since this
|
||||||
|
software will most probably not be used that long) */
|
||||||
|
RefTime.start();
|
||||||
|
|
||||||
|
/* init accumulated time stamp variable */
|
||||||
|
iAccTiStVal = 0;
|
||||||
|
|
||||||
|
/* init count (do not ship any result in init phase) */
|
||||||
|
iInitCnt = VEC_LEN_SAM_OFFS_EST + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSampleOffsetEst::AddTimeStampIdx(const int iTimeStampIdx)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
const int iLastIdx = VEC_LEN_SAM_OFFS_EST - 1;
|
||||||
|
|
||||||
|
/* take care of wrap of the time stamp index (byte wrap) */
|
||||||
|
if (iTimeStampIdx < veciTiStIdx[iLastIdx] - iAccTiStVal)
|
||||||
|
iAccTiStVal += _MAXBYTE + 1;
|
||||||
|
|
||||||
|
/* add new data pair to the FIFO */
|
||||||
|
for (i = 1; i < VEC_LEN_SAM_OFFS_EST; i++)
|
||||||
|
{
|
||||||
|
/* move old data */
|
||||||
|
veciTimeElapsed[i - 1] = veciTimeElapsed[i];
|
||||||
|
veciTiStIdx[i - 1] = veciTiStIdx[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* add new data */
|
||||||
|
veciTimeElapsed[iLastIdx] = RefTime.elapsed();
|
||||||
|
veciTiStIdx[iLastIdx] = iAccTiStVal + iTimeStampIdx;
|
||||||
|
|
||||||
|
/*
|
||||||
|
static FILE* pFile = fopen("v.dat", "w");
|
||||||
|
for (i = 0; i < VEC_LEN_SAM_OFFS_EST; i++)
|
||||||
|
fprintf(pFile, "%d\n", veciTimeElapsed[i]);
|
||||||
|
fflush(pFile);
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* calculate linear regression for sample rate estimation */
|
||||||
|
/* first, calculate averages */
|
||||||
|
double dTimeAv = 0;
|
||||||
|
double dTiStAv = 0;
|
||||||
|
for (i = 0; i < VEC_LEN_SAM_OFFS_EST; i++)
|
||||||
|
{
|
||||||
|
dTimeAv += veciTimeElapsed[i];
|
||||||
|
dTiStAv += veciTiStIdx[i];
|
||||||
|
}
|
||||||
|
dTimeAv /= VEC_LEN_SAM_OFFS_EST;
|
||||||
|
dTiStAv /= VEC_LEN_SAM_OFFS_EST;
|
||||||
|
|
||||||
|
/* calculate gradient */
|
||||||
|
double dNom = 0;
|
||||||
|
double dDenom = 0;
|
||||||
|
for (i = 0; i < VEC_LEN_SAM_OFFS_EST; i++)
|
||||||
|
{
|
||||||
|
const double dCurTimeNoAv = veciTimeElapsed[i] - dTimeAv;
|
||||||
|
dNom += dCurTimeNoAv * (veciTiStIdx[i] - dTiStAv);
|
||||||
|
dDenom += dCurTimeNoAv * dCurTimeNoAv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* final sample rate offset estimation calculation */
|
||||||
|
if (iInitCnt > 0)
|
||||||
|
iInitCnt--;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dSamRateEst = dNom / dDenom * NUM_BL_TIME_STAMPS * BLOCK_SIZE_SAMPLES * 1000;
|
||||||
|
|
||||||
|
/*
|
||||||
|
static FILE* pFile = fopen("v.dat", "w");
|
||||||
|
for (i = 0; i < VEC_LEN_SAM_OFFS_EST; i++)
|
||||||
|
fprintf(pFile, "%d %d\n", veciTimeElapsed[i], veciTiStIdx[i]);
|
||||||
|
fflush(pFile);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
static FILE* pFile = fopen("v.dat", "w");
|
||||||
|
fprintf(pFile, "%e\n", dSamRateEst);
|
||||||
|
fflush(pFile);
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
169
src/channel.h
Executable file
169
src/channel.h
Executable file
|
@ -0,0 +1,169 @@
|
||||||
|
/******************************************************************************\
|
||||||
|
* Copyright (c) 2004-2006
|
||||||
|
*
|
||||||
|
* Author(s):
|
||||||
|
* Volker Fischer
|
||||||
|
*
|
||||||
|
******************************************************************************
|
||||||
|
*
|
||||||
|
* 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.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
\******************************************************************************/
|
||||||
|
|
||||||
|
#if !defined(CHANNEL_HOIH9345KJH98_3_4344_BB23945IUHF1912__INCLUDED_)
|
||||||
|
#define CHANNEL_HOIH9345KJH98_3_4344_BB23945IUHF1912__INCLUDED_
|
||||||
|
|
||||||
|
#include <qthread.h>
|
||||||
|
#include "global.h"
|
||||||
|
#include "buffer.h"
|
||||||
|
#include "audiocompr.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "resample.h"
|
||||||
|
#include "qdatetime.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Definitions ****************************************************************/
|
||||||
|
/* Set the time-out for the input buffer until the state changes from
|
||||||
|
connected to not-connected (the actual time depends on the way the error
|
||||||
|
correction is implemented) */
|
||||||
|
#define CON_TIME_OUT_CNT_MAX 50
|
||||||
|
|
||||||
|
/* maximum number of internet connections (channels) */
|
||||||
|
#define MAX_NUM_CHANNELS 10 /* max number channels for server */
|
||||||
|
|
||||||
|
/* no valid channel number */
|
||||||
|
#define INVALID_CHANNEL_ID (MAX_NUM_CHANNELS + 1)
|
||||||
|
|
||||||
|
/* no valid time stamp index */
|
||||||
|
#define INVALID_TIME_STAMP_IDX -1
|
||||||
|
|
||||||
|
|
||||||
|
/* Classes ********************************************************************/
|
||||||
|
class CSampleOffsetEst
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CSampleOffsetEst() {Init();}
|
||||||
|
virtual ~CSampleOffsetEst() {}
|
||||||
|
|
||||||
|
void Init();
|
||||||
|
void AddTimeStampIdx(const int iTimeStampIdx);
|
||||||
|
double GetSamRate() {return dSamRateEst;}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QTime RefTime;
|
||||||
|
int iAccTiStVal;
|
||||||
|
double dSamRateEst;
|
||||||
|
CVector<long int> veciTimeElapsed;
|
||||||
|
CVector<long int> veciTiStIdx;
|
||||||
|
int iInitCnt;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* CChannel ----------------------------------------------------------------- */
|
||||||
|
class CChannel
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CChannel();
|
||||||
|
virtual ~CChannel() {}
|
||||||
|
|
||||||
|
bool PutData(const CVector<unsigned char>& vecbyData,
|
||||||
|
int iNumBytes);
|
||||||
|
bool GetData(CVector<double>& vecdData);
|
||||||
|
|
||||||
|
CVector<unsigned char> PrepSendPacket(const CVector<short>& vecsNPacket);
|
||||||
|
|
||||||
|
bool GetAddress(CHostAddress& RetAddr);
|
||||||
|
CHostAddress GetAddress() {return InetAddr;}
|
||||||
|
int GetTimeStampIdx();
|
||||||
|
void SetAddress(const CHostAddress NAddr) {InetAddr = NAddr;}
|
||||||
|
bool IsConnected() const {return iConTimeOut > 0;}
|
||||||
|
int GetComprAudSize() {return iAudComprSize;}
|
||||||
|
double GetResampleOffset() {return SampleOffsetEst.GetSamRate();}
|
||||||
|
void SetSockBufSize ( const int iNewBlockSize, const int iNumBlocks );
|
||||||
|
int GetSockBufSize() {return SockBuf.GetSize();}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/* audio compression */
|
||||||
|
CAudioCompression AudioCompression;
|
||||||
|
int iAudComprSize;
|
||||||
|
|
||||||
|
/* resampling */
|
||||||
|
CResample ResampleObj;
|
||||||
|
double dSamRateOffset;
|
||||||
|
CVector<double> vecdResInData;
|
||||||
|
CVector<double> vecdResOutData;
|
||||||
|
|
||||||
|
CSampleOffsetEst SampleOffsetEst;
|
||||||
|
|
||||||
|
/* connection parameters */
|
||||||
|
CHostAddress InetAddr;
|
||||||
|
|
||||||
|
/* network jitter-buffer */
|
||||||
|
CNetBuf SockBuf;
|
||||||
|
|
||||||
|
/* network output conversion buffer */
|
||||||
|
CConvBuf ConvBuf;
|
||||||
|
|
||||||
|
/* time stamp index counter */
|
||||||
|
Q_UINT8 byTimeStampIdxCnt;
|
||||||
|
int iTimeStampActCnt;
|
||||||
|
|
||||||
|
int iConTimeOut;
|
||||||
|
|
||||||
|
QMutex Mutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* CChannelSet (for server) ------------------------------------------------- */
|
||||||
|
class CChannelSet
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CChannelSet() {}
|
||||||
|
virtual ~CChannelSet() {}
|
||||||
|
|
||||||
|
bool PutData(const CVector<unsigned char>& vecbyRecBuf,
|
||||||
|
const int iNumBytesRead, const CHostAddress& HostAdr);
|
||||||
|
|
||||||
|
int GetFreeChan();
|
||||||
|
int CheckAddr(const CHostAddress& Addr);
|
||||||
|
|
||||||
|
void GetBlockAllConC(CVector<int>& vecChanID,
|
||||||
|
CVector<CVector<double> >& vecvecdData);
|
||||||
|
void GetConCliParam(CVector<CHostAddress>& vecHostAddresses,
|
||||||
|
CVector<double>& vecdSamOffs);
|
||||||
|
|
||||||
|
/* access functions for actual channels */
|
||||||
|
bool IsConnected(const int iChanNum)
|
||||||
|
{return vecChannels[iChanNum].IsConnected();}
|
||||||
|
CVector<unsigned char> PrepSendPacket(const int iChanNum,
|
||||||
|
const CVector<short>& vecsNPacket)
|
||||||
|
{return vecChannels[iChanNum].PrepSendPacket(vecsNPacket);}
|
||||||
|
CHostAddress GetAddress(const int iChanNum)
|
||||||
|
{return vecChannels[iChanNum].GetAddress();}
|
||||||
|
int GetTimeStampIdx(const int iChanNum)
|
||||||
|
{return vecChannels[iChanNum].GetTimeStampIdx();}
|
||||||
|
|
||||||
|
void SetSockBufSize ( const int iNewBlockSize, const int iNumBlocks);
|
||||||
|
int GetSockBufSize() {return vecChannels[0].GetSockBufSize();}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/* do not use the vector class since CChannel does not have appropriate
|
||||||
|
copy constructor/operator */
|
||||||
|
CChannel vecChannels[MAX_NUM_CHANNELS];
|
||||||
|
QMutex Mutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* !defined(CHANNEL_HOIH9345KJH98_3_4344_BB23945IUHF1912__INCLUDED_) */
|
317
src/client.cpp
Executable file
317
src/client.cpp
Executable file
|
@ -0,0 +1,317 @@
|
||||||
|
/******************************************************************************\
|
||||||
|
* Copyright (c) 2004-2006
|
||||||
|
*
|
||||||
|
* Author(s):
|
||||||
|
* Volker Fischer
|
||||||
|
*
|
||||||
|
******************************************************************************
|
||||||
|
*
|
||||||
|
* 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.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
\******************************************************************************/
|
||||||
|
|
||||||
|
#include "client.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Implementation *************************************************************/
|
||||||
|
bool CClient::SetServerAddr(QString strNAddr)
|
||||||
|
{
|
||||||
|
QHostAddress InetAddr;
|
||||||
|
|
||||||
|
if (InetAddr.setAddress(strNAddr))
|
||||||
|
{
|
||||||
|
/* The server port is fixed and always the same */
|
||||||
|
Channel.SetAddress(CHostAddress(InetAddr, LLCON_PORT_NUMBER));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false; /* invalid address */
|
||||||
|
}
|
||||||
|
|
||||||
|
void CClient::Init()
|
||||||
|
{
|
||||||
|
/* set block sizes (in samples) */
|
||||||
|
iBlockSizeSam = MIN_BLOCK_SIZE_SAMPLES;
|
||||||
|
iSndCrdBlockSizeSam = MIN_SND_CRD_BLOCK_SIZE_SAMPLES;
|
||||||
|
|
||||||
|
vecsAudioSndCrd.Init(iSndCrdBlockSizeSam * 2); /* stereo */
|
||||||
|
vecdAudioSndCrdL.Init(iSndCrdBlockSizeSam);
|
||||||
|
vecdAudioSndCrdR.Init(iSndCrdBlockSizeSam);
|
||||||
|
|
||||||
|
vecdAudioL.Init(iBlockSizeSam);
|
||||||
|
vecdAudioR.Init(iBlockSizeSam);
|
||||||
|
|
||||||
|
Sound.InitRecording(iSndCrdBlockSizeSam * 2 /* stereo */);
|
||||||
|
Sound.InitPlayback(iSndCrdBlockSizeSam * 2 /* stereo */);
|
||||||
|
|
||||||
|
/* resample objects are always initialized with the input block size */
|
||||||
|
/* record */
|
||||||
|
ResampleObjDownL.Init(iSndCrdBlockSizeSam,
|
||||||
|
(double) SAMPLE_RATE / SND_CRD_SAMPLE_RATE);
|
||||||
|
ResampleObjDownR.Init(iSndCrdBlockSizeSam,
|
||||||
|
(double) SAMPLE_RATE / SND_CRD_SAMPLE_RATE);
|
||||||
|
|
||||||
|
/* playback */
|
||||||
|
ResampleObjUpL.Init(iBlockSizeSam,
|
||||||
|
(double) SND_CRD_SAMPLE_RATE / SAMPLE_RATE);
|
||||||
|
ResampleObjUpR.Init(iBlockSizeSam,
|
||||||
|
(double) SND_CRD_SAMPLE_RATE / SAMPLE_RATE);
|
||||||
|
|
||||||
|
/* init network buffers */
|
||||||
|
vecsNetwork.Init(iBlockSizeSam);
|
||||||
|
vecdNetwData.Init(iBlockSizeSam);
|
||||||
|
|
||||||
|
/* init moving average buffer for response time evaluation */
|
||||||
|
RespTimeMoAvBuf.Init(LEN_MOV_AV_RESPONSE);
|
||||||
|
|
||||||
|
/* init time for response time evaluation */
|
||||||
|
TimeLastBlock = QTime::currentTime();
|
||||||
|
|
||||||
|
AudioReverb.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CClient::run()
|
||||||
|
{
|
||||||
|
int i, iInCnt;
|
||||||
|
|
||||||
|
/* Set thread priority (The working thread should have a higher
|
||||||
|
priority than the GUI) */
|
||||||
|
#ifdef _WIN32
|
||||||
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
|
||||||
|
#else
|
||||||
|
/* set the process to realtime privs */
|
||||||
|
struct sched_param schp;
|
||||||
|
memset(&schp, 0, sizeof(schp));
|
||||||
|
schp.sched_priority = sched_get_priority_max(SCHED_FIFO);
|
||||||
|
sched_setscheduler(0, SCHED_FIFO, &schp);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* init object */
|
||||||
|
Init();
|
||||||
|
|
||||||
|
|
||||||
|
/* runtime phase --------------------------------------------------------- */
|
||||||
|
bRun = true;
|
||||||
|
|
||||||
|
/* main loop of working thread */
|
||||||
|
while (bRun)
|
||||||
|
{
|
||||||
|
/* get audio from sound card (blocking function) */
|
||||||
|
if (Sound.Read(vecsAudioSndCrd))
|
||||||
|
PostWinMessage(MS_SOUND_IN, MUL_COL_LED_RED);
|
||||||
|
else
|
||||||
|
PostWinMessage(MS_SOUND_IN, MUL_COL_LED_GREEN);
|
||||||
|
|
||||||
|
/* copy data from one stereo buffer in two separate buffers */
|
||||||
|
iInCnt = 0;
|
||||||
|
for (i = 0; i < iSndCrdBlockSizeSam; i++)
|
||||||
|
{
|
||||||
|
vecdAudioSndCrdL[i] = (double) vecsAudioSndCrd[iInCnt++];
|
||||||
|
vecdAudioSndCrdR[i] = (double) vecsAudioSndCrd[iInCnt++];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* resample data for each channel seaparately */
|
||||||
|
ResampleObjDownL.Resample(vecdAudioSndCrdL, vecdAudioL);
|
||||||
|
ResampleObjDownR.Resample(vecdAudioSndCrdR, vecdAudioR);
|
||||||
|
|
||||||
|
/* update signal level meters */
|
||||||
|
SignalLevelMeterL.Update(vecdAudioL);
|
||||||
|
SignalLevelMeterR.Update(vecdAudioR);
|
||||||
|
|
||||||
|
/* add reverberation effect if activated */
|
||||||
|
if (iReverbLevel != 0)
|
||||||
|
{
|
||||||
|
/* first attenuation amplification factor */
|
||||||
|
const double dRevLev = (double) iReverbLevel / AUD_REVERB_MAX / 2;
|
||||||
|
|
||||||
|
if (bReverbOnLeftChan)
|
||||||
|
{
|
||||||
|
for (i = 0; i < iBlockSizeSam; i++)
|
||||||
|
{
|
||||||
|
/* left channel */
|
||||||
|
vecdAudioL[i] +=
|
||||||
|
dRevLev * AudioReverb.ProcessSample(vecdAudioL[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (i = 0; i < iBlockSizeSam; i++)
|
||||||
|
{
|
||||||
|
/* right channel */
|
||||||
|
vecdAudioR[i] +=
|
||||||
|
dRevLev * AudioReverb.ProcessSample(vecdAudioR[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* mix both signals depending on the fading setting */
|
||||||
|
const int iMiddleOfFader = AUD_FADER_IN_MAX / 2;
|
||||||
|
const double dAttFact =
|
||||||
|
(double) (iMiddleOfFader - abs(iMiddleOfFader - iAudioInFader)) /
|
||||||
|
iMiddleOfFader;
|
||||||
|
for (i = 0; i < iBlockSizeSam; i++)
|
||||||
|
{
|
||||||
|
double dMixedSignal;
|
||||||
|
|
||||||
|
if (iAudioInFader > iMiddleOfFader)
|
||||||
|
dMixedSignal = vecdAudioL[i] + dAttFact * vecdAudioR[i];
|
||||||
|
else
|
||||||
|
dMixedSignal = vecdAudioR[i] + dAttFact * vecdAudioL[i];
|
||||||
|
|
||||||
|
vecsNetwork[i] = Double2Short(dMixedSignal);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* send it through the network */
|
||||||
|
Socket.SendPacket(Channel.PrepSendPacket(vecsNetwork),
|
||||||
|
Channel.GetAddress(), Channel.GetTimeStampIdx());
|
||||||
|
|
||||||
|
/* receive a new block */
|
||||||
|
if (Channel.GetData(vecdNetwData))
|
||||||
|
PostWinMessage(MS_JIT_BUF_GET, MUL_COL_LED_GREEN);
|
||||||
|
else
|
||||||
|
PostWinMessage(MS_JIT_BUF_GET, MUL_COL_LED_RED);
|
||||||
|
|
||||||
|
#ifdef _DEBUG_
|
||||||
|
#if 0
|
||||||
|
#if 0
|
||||||
|
/* Determine network delay. We can do this very simple if only this client is
|
||||||
|
connected to the server. In this case, exactly the same audio material is
|
||||||
|
coming back and we can simply compare the samples */
|
||||||
|
/* store send data instatic buffer (may delay is 100 ms) */
|
||||||
|
const int iMaxDelaySamples = (int) ((float) 0.3 /*0.1*/ * SAMPLE_RATE);
|
||||||
|
static CVector<short> vecsOutBuf(iMaxDelaySamples);
|
||||||
|
|
||||||
|
/* update buffer */
|
||||||
|
const int iBufDiff = iMaxDelaySamples - iBlockSizeSam;
|
||||||
|
for (i = 0; i < iBufDiff; i++)
|
||||||
|
vecsOutBuf[i + iBlockSizeSam] = vecsOutBuf[i];
|
||||||
|
for (i = 0; i < iBlockSizeSam; i++)
|
||||||
|
vecsOutBuf[i] = vecsNetwork[i];
|
||||||
|
|
||||||
|
/* now search for equal samples */
|
||||||
|
int iDelaySamples = 0;
|
||||||
|
for (i = 0; i < iMaxDelaySamples - 1; i++)
|
||||||
|
{
|
||||||
|
/* compare two successive samples */
|
||||||
|
if ((vecsOutBuf[i] == (short) vecdNetwData[0]) &&
|
||||||
|
(vecsOutBuf[i + 1] == (short) vecdNetwData[1]))
|
||||||
|
{
|
||||||
|
iDelaySamples = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static FILE* pFileDelay = fopen("delay.dat", "w");
|
||||||
|
fprintf(pFileDelay, "%d\n", iDelaySamples);
|
||||||
|
fflush(pFileDelay);
|
||||||
|
#else
|
||||||
|
/* just store both, input and output, streams */
|
||||||
|
// fid=fopen('v.dat','r');x=fread(fid,'int16');fclose(fid);
|
||||||
|
static FILE* pFileDelay = fopen("v.dat", "wb");
|
||||||
|
short sData[2];
|
||||||
|
for (i = 0; i < iBlockSizeSam; i++)
|
||||||
|
{
|
||||||
|
sData[0] = vecsNetwork[i];
|
||||||
|
sData[1] = (short) vecdNetwData[i];
|
||||||
|
fwrite(&sData, size_t(2), size_t(2), pFileDelay);
|
||||||
|
}
|
||||||
|
fflush(pFileDelay);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
// fid=fopen('v.dat','r');x=fread(fid,'int16');fclose(fid);
|
||||||
|
static FILE* pFileDelay = fopen("v.dat", "wb");
|
||||||
|
short sData[2];
|
||||||
|
for (i = 0; i < iBlockSizeSam; i++)
|
||||||
|
{
|
||||||
|
sData[0] = (short) vecdNetwData[i];
|
||||||
|
fwrite(&sData, size_t(2), size_t(1), pFileDelay);
|
||||||
|
}
|
||||||
|
fflush(pFileDelay);
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/* check if channel is connected */
|
||||||
|
if (Channel.IsConnected())
|
||||||
|
{
|
||||||
|
/* write mono input signal in both sound-card channels */
|
||||||
|
for (i = 0; i < iBlockSizeSam; i++)
|
||||||
|
vecdAudioL[i] = vecdAudioR[i] = vecdNetwData[i];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* if not connected, clear data */
|
||||||
|
for (i = 0; i < iBlockSizeSam; i++)
|
||||||
|
vecdAudioL[i] = vecdAudioR[i] = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* resample data for each channel separately */
|
||||||
|
ResampleObjUpL.Resample(vecdAudioL, vecdAudioSndCrdL);
|
||||||
|
ResampleObjUpR.Resample(vecdAudioR, vecdAudioSndCrdR);
|
||||||
|
|
||||||
|
/* copy data from one stereo buffer in two separate buffers */
|
||||||
|
iInCnt = 0;
|
||||||
|
for (i = 0; i < iSndCrdBlockSizeSam; i++)
|
||||||
|
{
|
||||||
|
vecsAudioSndCrd[iInCnt++] = Double2Short(vecdAudioSndCrdL[i]);
|
||||||
|
vecsAudioSndCrd[iInCnt++] = Double2Short(vecdAudioSndCrdR[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* play the new block */
|
||||||
|
if (Sound.Write(vecsAudioSndCrd))
|
||||||
|
PostWinMessage(MS_SOUND_OUT, MUL_COL_LED_RED);
|
||||||
|
else
|
||||||
|
PostWinMessage(MS_SOUND_OUT, MUL_COL_LED_GREEN);
|
||||||
|
|
||||||
|
|
||||||
|
/* update response time measurement --------------------------------- */
|
||||||
|
/* add time difference */
|
||||||
|
const QTime CurTime = QTime::currentTime();
|
||||||
|
|
||||||
|
/* we want to calculate the standard deviation (we assume that the mean
|
||||||
|
is correct at the block period time) */
|
||||||
|
const double dCurAddVal =
|
||||||
|
( (double) TimeLastBlock.msecsTo ( CurTime ) - MIN_BLOCK_DURATION_MS );
|
||||||
|
|
||||||
|
/*
|
||||||
|
// TEST
|
||||||
|
static FILE* pFileTest = fopen("sti.dat", "w");
|
||||||
|
fprintf(pFileTest, "%e\n", dCurAddVal);
|
||||||
|
fflush(pFileTest);
|
||||||
|
*/
|
||||||
|
|
||||||
|
RespTimeMoAvBuf.Add ( dCurAddVal * dCurAddVal ); /* add squared value */
|
||||||
|
|
||||||
|
/* store old time value */
|
||||||
|
TimeLastBlock = CurTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* reset current signal level and LEDs */
|
||||||
|
SignalLevelMeterL.Reset();
|
||||||
|
SignalLevelMeterR.Reset();
|
||||||
|
PostWinMessage(MS_RESET_ALL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CClient::Stop()
|
||||||
|
{
|
||||||
|
/* set flag so that thread can leave the main loop */
|
||||||
|
bRun = false;
|
||||||
|
|
||||||
|
/* give thread some time to terminate, return status */
|
||||||
|
return wait(5000);
|
||||||
|
}
|
135
src/client.h
Executable file
135
src/client.h
Executable file
|
@ -0,0 +1,135 @@
|
||||||
|
/******************************************************************************\
|
||||||
|
* Copyright (c) 2004-2006
|
||||||
|
*
|
||||||
|
* Author(s):
|
||||||
|
* Volker Fischer
|
||||||
|
*
|
||||||
|
******************************************************************************
|
||||||
|
*
|
||||||
|
* 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.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
\******************************************************************************/
|
||||||
|
|
||||||
|
#if !defined(CLIENT_HOIHGE76GEKJH98_3_43445KJIUHF1912__INCLUDED_)
|
||||||
|
#define CLIENT_HOIHGE76GEKJH98_3_43445KJIUHF1912__INCLUDED_
|
||||||
|
|
||||||
|
#include <qthread.h>
|
||||||
|
#include <qhostaddress.h>
|
||||||
|
#include <qstring.h>
|
||||||
|
#include <qdatetime.h>
|
||||||
|
#include "global.h"
|
||||||
|
#include "socket.h"
|
||||||
|
#include "resample.h"
|
||||||
|
#include "channel.h"
|
||||||
|
#include "util.h"
|
||||||
|
#ifdef _WIN32
|
||||||
|
# include "../windows/sound.h"
|
||||||
|
#else
|
||||||
|
# include "../linux/sound.h"
|
||||||
|
# include <sched.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Definitions ****************************************************************/
|
||||||
|
/* audio in fader range */
|
||||||
|
#define AUD_FADER_IN_MAX 100
|
||||||
|
|
||||||
|
/* audio reverberation range */
|
||||||
|
#define AUD_REVERB_MAX 100
|
||||||
|
|
||||||
|
|
||||||
|
/* Classes ********************************************************************/
|
||||||
|
class CClient : public QThread
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CClient() : bRun ( false ), Socket ( &Channel ),
|
||||||
|
iAudioInFader ( AUD_FADER_IN_MAX / 2 ),
|
||||||
|
iReverbLevel ( AUD_REVERB_MAX / 6 ),
|
||||||
|
bReverbOnLeftChan ( false ) {}
|
||||||
|
virtual ~CClient () {}
|
||||||
|
|
||||||
|
void Init();
|
||||||
|
bool Stop();
|
||||||
|
bool IsRunning() {return bRun;}
|
||||||
|
bool SetServerAddr(QString strNAddr);
|
||||||
|
double MicLevelL() {return SignalLevelMeterL.MicLevel();}
|
||||||
|
double MicLevelR() {return SignalLevelMeterR.MicLevel();}
|
||||||
|
bool IsConnected() {return Channel.IsConnected();}
|
||||||
|
|
||||||
|
/* we want to return the standard deviation. For that we need to calculate
|
||||||
|
the sqaure root */
|
||||||
|
double GetTimingStdDev() {return sqrt(RespTimeMoAvBuf.GetAverage());}
|
||||||
|
|
||||||
|
int GetAudioInFader() {return iAudioInFader;}
|
||||||
|
void SetAudioInFader(const int iNV) {iAudioInFader = iNV;}
|
||||||
|
|
||||||
|
int GetReverbLevel() {return iReverbLevel;}
|
||||||
|
void SetReverbLevel(const int iNL) {iReverbLevel = iNL;}
|
||||||
|
|
||||||
|
bool IsReverbOnLeftChan() {return bReverbOnLeftChan;}
|
||||||
|
void SetReverbOnLeftChan(const bool bIL)
|
||||||
|
{bReverbOnLeftChan = bIL; AudioReverb.Clear();}
|
||||||
|
|
||||||
|
CSound* GetSndInterface() {return &Sound;}
|
||||||
|
CChannel* GetChannel() {return &Channel;}
|
||||||
|
|
||||||
|
|
||||||
|
// settings
|
||||||
|
string strIPAddress;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void run();
|
||||||
|
|
||||||
|
/* only one channel is needed for client application */
|
||||||
|
CChannel Channel;
|
||||||
|
|
||||||
|
CSocket Socket;
|
||||||
|
CSound Sound;
|
||||||
|
CSignalLevelMeter SignalLevelMeterL;
|
||||||
|
CSignalLevelMeter SignalLevelMeterR;
|
||||||
|
|
||||||
|
bool bRun;
|
||||||
|
CVector<double> vecdNetwData;
|
||||||
|
|
||||||
|
int iAudioInFader;
|
||||||
|
bool bReverbOnLeftChan;
|
||||||
|
int iReverbLevel;
|
||||||
|
CAudioReverb AudioReverb;
|
||||||
|
|
||||||
|
int iSndCrdBlockSizeSam;
|
||||||
|
int iBlockSizeSam;
|
||||||
|
|
||||||
|
CVector<short> vecsAudioSndCrd;
|
||||||
|
CVector<double> vecdAudioSndCrdL;
|
||||||
|
CVector<double> vecdAudioSndCrdR;
|
||||||
|
|
||||||
|
CVector<double> vecdAudioL;
|
||||||
|
CVector<double> vecdAudioR;
|
||||||
|
|
||||||
|
CVector<short> vecsNetwork;
|
||||||
|
|
||||||
|
/* resample objects */
|
||||||
|
CAudioResample ResampleObjDownL; /* left channel */
|
||||||
|
CAudioResample ResampleObjDownR; /* right channel */
|
||||||
|
CAudioResample ResampleObjUpL; /* left channel */
|
||||||
|
CAudioResample ResampleObjUpR; /* right channel */
|
||||||
|
|
||||||
|
/* debugging, evaluating */
|
||||||
|
CMovingAv<double> RespTimeMoAvBuf;
|
||||||
|
QTime TimeLastBlock;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* !defined(CLIENT_HOIHGE76GEKJH98_3_43445KJIUHF1912__INCLUDED_) */
|
154
src/global.h
Executable file
154
src/global.h
Executable file
|
@ -0,0 +1,154 @@
|
||||||
|
/******************************************************************************\
|
||||||
|
* Copyright (c) 2004-2006
|
||||||
|
*
|
||||||
|
* Author(s):
|
||||||
|
* Volker Fischer
|
||||||
|
*
|
||||||
|
******************************************************************************
|
||||||
|
*
|
||||||
|
* 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.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
\******************************************************************************/
|
||||||
|
|
||||||
|
#if !defined(GLOBAL_H__3B123453_4344_BB2B_23E7A0D31912__INCLUDED_)
|
||||||
|
#define GLOBAL_H__3B123453_4344_BB2B_23E7A0D31912__INCLUDED_
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <string>
|
||||||
|
#include <qstring.h>
|
||||||
|
#include <qevent.h>
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
# include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Definitions ****************************************************************/
|
||||||
|
/* define this macro to get debug output */
|
||||||
|
#define _DEBUG_
|
||||||
|
#undef _DEBUG_
|
||||||
|
|
||||||
|
/* version and application name */
|
||||||
|
#ifndef VERSION
|
||||||
|
# define VERSION "0.9.1"
|
||||||
|
#endif
|
||||||
|
#define APP_NAME "llcon"
|
||||||
|
|
||||||
|
|
||||||
|
/* defined port number for client and server */
|
||||||
|
#define LLCON_PORT_NUMBER 22122
|
||||||
|
|
||||||
|
/* sample rate */
|
||||||
|
#define SAMPLE_RATE 24000
|
||||||
|
|
||||||
|
/* sound card sample rate. Should be always 48 kHz to avoid sound card driver
|
||||||
|
internal sample rate conversion which might be buggy */
|
||||||
|
#define SND_CRD_SAMPLE_RATE 48000
|
||||||
|
|
||||||
|
/* minimum block duration - all other buffer durations must be a multiple
|
||||||
|
of this duration */
|
||||||
|
#define MIN_BLOCK_DURATION_MS 2 /* ms */
|
||||||
|
|
||||||
|
#define MIN_BLOCK_SIZE_SAMPLES ( MIN_BLOCK_DURATION_MS * SAMPLE_RATE / 1000 )
|
||||||
|
#define MIN_SND_CRD_BLOCK_SIZE_SAMPLES ( MIN_BLOCK_DURATION_MS * SND_CRD_SAMPLE_RATE / 1000 )
|
||||||
|
|
||||||
|
/* block length in milliseconds (it seems that with the standard windows time
|
||||||
|
a minimum block duration of 10 ms can be used) */
|
||||||
|
#ifdef _WIN32
|
||||||
|
# define BLOCK_DURATION_MS 6 /* ms */
|
||||||
|
#else
|
||||||
|
/* first tests showed that with 24000 kHz a block time shorter than 5 ms leads to
|
||||||
|
much higher DSL network latencies. A length of 6 ms seems to be optimal */
|
||||||
|
# define BLOCK_DURATION_MS 6 /* ms */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#define BLOCK_SIZE_SAMPLES (BLOCK_DURATION_MS * SAMPLE_RATE / 1000)
|
||||||
|
#define SND_CRD_BLOCK_SIZE_SAMPLES (BLOCK_DURATION_MS * SND_CRD_SAMPLE_RATE / 1000)
|
||||||
|
|
||||||
|
/* maximum network buffer size (which can be chosen by slider) */
|
||||||
|
#define MAX_NET_BUF_SIZE_NUM_BL 10 /* number of blocks */
|
||||||
|
|
||||||
|
/* default network buffer size */
|
||||||
|
#define DEF_NET_BUF_SIZE_NUM_BL 2 /* number of blocks */
|
||||||
|
|
||||||
|
// number of ticks of audio in/out buffer sliders
|
||||||
|
#ifdef _WIN32
|
||||||
|
# define AUD_SLIDER_LENGTH 15
|
||||||
|
#else
|
||||||
|
# define AUD_SLIDER_LENGTH 6
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* sample rate offset estimation algorithm */
|
||||||
|
/* time interval for sample rate offset estimation */
|
||||||
|
#define TIME_INT_SAM_OFFS_EST 60 /* s */
|
||||||
|
|
||||||
|
/* time interval of taps for sample rate offset estimation (time stamps) */
|
||||||
|
#define INTVL_TAPS_SAM_OFF_SET 1 /* s */
|
||||||
|
|
||||||
|
#define NUM_BL_TIME_STAMPS ( ( INTVL_TAPS_SAM_OFF_SET * 1000 ) / MIN_BLOCK_DURATION_MS )
|
||||||
|
#define VEC_LEN_SAM_OFFS_EST ( TIME_INT_SAM_OFFS_EST / INTVL_TAPS_SAM_OFF_SET )
|
||||||
|
|
||||||
|
/* length of the moving average buffer for response time measurement */
|
||||||
|
#define TIME_MOV_AV_RESPONSE 30 /* seconds */
|
||||||
|
#define LEN_MOV_AV_RESPONSE (TIME_MOV_AV_RESPONSE * 1000 / BLOCK_DURATION_MS)
|
||||||
|
|
||||||
|
|
||||||
|
#define _MAXSHORT 32767
|
||||||
|
#define _MAXBYTE 255 /* binary: 11111111 */
|
||||||
|
#define _MINSHORT (-32768)
|
||||||
|
|
||||||
|
|
||||||
|
/* Definitions for window message system ------------------------------------ */
|
||||||
|
typedef unsigned int _MESSAGE_IDENT;
|
||||||
|
#define MS_RESET_ALL 0 /* MS: Message */
|
||||||
|
#define MS_SOUND_IN 1
|
||||||
|
#define MS_SOUND_OUT 2
|
||||||
|
#define MS_JIT_BUF_PUT 3
|
||||||
|
#define MS_JIT_BUF_GET 4
|
||||||
|
|
||||||
|
#define MUL_COL_LED_RED 0
|
||||||
|
#define MUL_COL_LED_YELLOW 1
|
||||||
|
#define MUL_COL_LED_GREEN 2
|
||||||
|
|
||||||
|
|
||||||
|
/* Classes ********************************************************************/
|
||||||
|
class CGenErr
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CGenErr(QString strNE) : strError(strNE) {}
|
||||||
|
QString strError;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CLlconEvent : public QCustomEvent
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CLlconEvent(int iNewMeTy, int iNewSt, int iNewChN = 0) :
|
||||||
|
QCustomEvent(QEvent::User + 11), iMessType(iNewMeTy), iStatus(iNewSt),
|
||||||
|
iChanNum(iNewChN) {}
|
||||||
|
|
||||||
|
int iMessType;
|
||||||
|
int iStatus;
|
||||||
|
int iChanNum;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* Prototypes for global functions ********************************************/
|
||||||
|
/* Posting a window message */
|
||||||
|
void PostWinMessage(const _MESSAGE_IDENT MessID, const int iMessageParam = 0,
|
||||||
|
const int iChanNum = 0);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* !defined(GLOBAL_H__3B123453_4344_BB2B_23E7A0D31912__INCLUDED_) */
|
328
src/llconclientdlg.cpp
Executable file
328
src/llconclientdlg.cpp
Executable file
|
@ -0,0 +1,328 @@
|
||||||
|
/******************************************************************************\
|
||||||
|
* Copyright (c) 2004-2006
|
||||||
|
*
|
||||||
|
* Author(s):
|
||||||
|
* Volker Fischer
|
||||||
|
*
|
||||||
|
******************************************************************************
|
||||||
|
*
|
||||||
|
* 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.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
\******************************************************************************/
|
||||||
|
|
||||||
|
#include "llconclientdlg.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Implementation *************************************************************/
|
||||||
|
CLlconClientDlg::CLlconClientDlg ( CClient* pNCliP, QWidget* parent,
|
||||||
|
const char* name, bool modal, WFlags f) : pClient ( pNCliP ),
|
||||||
|
CLlconClientDlgBase ( parent, name, modal, f )
|
||||||
|
{
|
||||||
|
/* add help text to controls */
|
||||||
|
QString strInpLevH = tr("<b>Input level meter:</b> Shows the level of the "
|
||||||
|
"input audio signal of the sound card. The level is in dB. Overload "
|
||||||
|
"should be avoided.");
|
||||||
|
QWhatsThis::add(TextLabelInputLevel, strInpLevH);
|
||||||
|
QWhatsThis::add(ProgressBarInputLevelL, strInpLevH);
|
||||||
|
QWhatsThis::add(ProgressBarInputLevelR, strInpLevH);
|
||||||
|
|
||||||
|
QWhatsThis::add(PushButtonConnect, tr("<b>Connect / Disconnect Button:"
|
||||||
|
"</b> Push this button to connect the server. A valid IP address has "
|
||||||
|
"to be specified before. If the client is connected, pressing this "
|
||||||
|
"button will disconnect the connection."));
|
||||||
|
|
||||||
|
QWhatsThis::add(TextLabelNameVersion, tr("<b>Version:</b> Shows the "
|
||||||
|
"current version of the software."));
|
||||||
|
|
||||||
|
QWhatsThis::add(TextLabelStatus, tr("<b>Status Bar:</b> In the status bar "
|
||||||
|
"different messages are displayed. E.g., if an error ocurred or the "
|
||||||
|
"status of the connection is shown."));
|
||||||
|
|
||||||
|
QString strServAddrH = tr("<b>Server Address:</b> In this edit control, "
|
||||||
|
"the IP address of the server can be set. If an invalid address was "
|
||||||
|
"chosen, an error message is shown in the status bar.");
|
||||||
|
QWhatsThis::add(TextLabelServerAddr, strServAddrH);
|
||||||
|
QWhatsThis::add(LineEditServerAddr, strServAddrH);
|
||||||
|
|
||||||
|
/* set text for version and application name */
|
||||||
|
TextLabelNameVersion->
|
||||||
|
setText(QString(APP_NAME) + tr(" client ") + QString(VERSION) +
|
||||||
|
" (" + QString().setNum(BLOCK_DURATION_MS) + " ms)");
|
||||||
|
|
||||||
|
/* init server address line edit */
|
||||||
|
LineEditServerAddr->setText ( pClient->strIPAddress.c_str () );
|
||||||
|
|
||||||
|
/* init status label */
|
||||||
|
OnTimerStatus ();
|
||||||
|
|
||||||
|
/* init sample rate offset label */
|
||||||
|
// TextSamRateOffsValue->setText ( "0 Hz" );
|
||||||
|
// FIXME disable sample rate estimation result label since estimation does not work
|
||||||
|
TextSamRateOffsValue->setText ( "---" );
|
||||||
|
|
||||||
|
/* init connection button text */
|
||||||
|
PushButtonConnect->setText ( CON_BUT_CONNECTTEXT );
|
||||||
|
|
||||||
|
/* Init timing jitter text label */
|
||||||
|
TextLabelStdDevTimer->setText ( "" );
|
||||||
|
|
||||||
|
/* init input level meter bars */
|
||||||
|
ProgressBarInputLevelL->setTotalSteps ( NUM_STEPS_INP_LEV_METER );
|
||||||
|
ProgressBarInputLevelL->setProgress ( 0 );
|
||||||
|
ProgressBarInputLevelR->setTotalSteps ( NUM_STEPS_INP_LEV_METER );
|
||||||
|
ProgressBarInputLevelR->setProgress ( 0 );
|
||||||
|
|
||||||
|
|
||||||
|
/* init slider controls --- */
|
||||||
|
/* sound buffer in */
|
||||||
|
SliderSndBufIn->setRange(2, AUD_SLIDER_LENGTH);
|
||||||
|
const int iCurNumInBuf = pClient->GetSndInterface()->GetInNumBuf();
|
||||||
|
SliderSndBufIn->setValue(iCurNumInBuf);
|
||||||
|
TextSndBufIn->setText("In: " + QString().setNum(iCurNumInBuf));
|
||||||
|
|
||||||
|
/* sound buffer out */
|
||||||
|
SliderSndBufOut->setRange(2, AUD_SLIDER_LENGTH);
|
||||||
|
const int iCurNumOutBuf = pClient->GetSndInterface()->GetOutNumBuf();
|
||||||
|
SliderSndBufOut->setValue(iCurNumOutBuf);
|
||||||
|
TextSndBufOut->setText("Out: " + QString().setNum(iCurNumOutBuf));
|
||||||
|
|
||||||
|
/* network buffer */
|
||||||
|
SliderNetBuf->setRange(1, MAX_NET_BUF_SIZE_NUM_BL);
|
||||||
|
const int iCurNumNetBuf = pClient->GetChannel()->GetSockBufSize();
|
||||||
|
SliderNetBuf->setValue(iCurNumNetBuf);
|
||||||
|
TextNetBuf->setText("Size: " + QString().setNum(iCurNumNetBuf));
|
||||||
|
|
||||||
|
/* audio in fader */
|
||||||
|
SliderAudInFader->setRange(0, AUD_FADER_IN_MAX);
|
||||||
|
const int iCurAudInFader = pClient->GetAudioInFader();
|
||||||
|
SliderAudInFader->setValue(iCurAudInFader);
|
||||||
|
SliderAudInFader->setTickInterval(AUD_FADER_IN_MAX / 9);
|
||||||
|
|
||||||
|
/* audio reverberation */
|
||||||
|
SliderAudReverb->setRange(0, AUD_REVERB_MAX);
|
||||||
|
const int iCurAudReverb = pClient->GetReverbLevel();
|
||||||
|
SliderAudReverb->setValue ( AUD_REVERB_MAX - iCurAudReverb );
|
||||||
|
SliderAudReverb->setTickInterval(AUD_REVERB_MAX / 9);
|
||||||
|
|
||||||
|
|
||||||
|
/* set radio buttons --- */
|
||||||
|
/* reverb channel */
|
||||||
|
if (pClient->IsReverbOnLeftChan())
|
||||||
|
RadioButtonRevSelL->setChecked(true);
|
||||||
|
else
|
||||||
|
RadioButtonRevSelR->setChecked(true);
|
||||||
|
|
||||||
|
|
||||||
|
/* Main menu bar -------------------------------------------------------- */
|
||||||
|
pMenu = new QMenuBar(this);
|
||||||
|
CHECK_PTR(pMenu);
|
||||||
|
pMenu->insertItem(tr("&?"), new CLlconHelpMenu(this));
|
||||||
|
pMenu->setSeparator(QMenuBar::InWindowsStyle);
|
||||||
|
|
||||||
|
/* Now tell the layout about the menu */
|
||||||
|
CLlconClientDlgBaseLayout->setMenuBar(pMenu);
|
||||||
|
|
||||||
|
|
||||||
|
/* connections ---------------------------------------------------------- */
|
||||||
|
/* push-buttons */
|
||||||
|
QObject::connect(PushButtonConnect, SIGNAL(clicked()),
|
||||||
|
this, SLOT(OnConnectDisconBut()));
|
||||||
|
|
||||||
|
/* timers */
|
||||||
|
QObject::connect(&TimerSigMet, SIGNAL(timeout()),
|
||||||
|
this, SLOT(OnTimerSigMet()));
|
||||||
|
QObject::connect(&TimerStatus, SIGNAL(timeout()),
|
||||||
|
this, SLOT(OnTimerStatus()));
|
||||||
|
|
||||||
|
/* sliders */
|
||||||
|
QObject::connect(SliderSndBufIn, SIGNAL(valueChanged(int)),
|
||||||
|
this, SLOT(OnSliderSndBufInChange(int)));
|
||||||
|
QObject::connect(SliderSndBufOut, SIGNAL(valueChanged(int)),
|
||||||
|
this, SLOT(OnSliderSndBufOutChange(int)));
|
||||||
|
QObject::connect(SliderNetBuf, SIGNAL(valueChanged(int)),
|
||||||
|
this, SLOT(OnSliderNetBuf(int)));
|
||||||
|
|
||||||
|
QObject::connect(SliderAudInFader, SIGNAL(valueChanged(int)),
|
||||||
|
this, SLOT(OnSliderAudInFader(int)));
|
||||||
|
QObject::connect(SliderAudReverb, SIGNAL(valueChanged(int)),
|
||||||
|
this, SLOT(OnSliderAudReverb(int)));
|
||||||
|
|
||||||
|
/* radio buttons */
|
||||||
|
QObject::connect(RadioButtonRevSelL, SIGNAL(clicked()),
|
||||||
|
this, SLOT(OnRevSelL()));
|
||||||
|
QObject::connect(RadioButtonRevSelR, SIGNAL(clicked()),
|
||||||
|
this, SLOT(OnRevSelR()));
|
||||||
|
|
||||||
|
|
||||||
|
/* timers --------------------------------------------------------------- */
|
||||||
|
/* start timer for status bar */
|
||||||
|
TimerStatus.start(STATUSBAR_UPDATE_TIME);
|
||||||
|
}
|
||||||
|
|
||||||
|
CLlconClientDlg::~CLlconClientDlg()
|
||||||
|
{
|
||||||
|
/* if connected, terminate connection */
|
||||||
|
if (pClient->IsRunning())
|
||||||
|
{
|
||||||
|
pClient->Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLlconClientDlg::closeEvent ( QCloseEvent * Event )
|
||||||
|
{
|
||||||
|
// store IP address
|
||||||
|
pClient->strIPAddress = LineEditServerAddr->text().latin1();
|
||||||
|
|
||||||
|
// default implementation of this event handler routine
|
||||||
|
Event->accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLlconClientDlg::OnConnectDisconBut()
|
||||||
|
{
|
||||||
|
/* start/stop client, set button text */
|
||||||
|
if (pClient->IsRunning())
|
||||||
|
{
|
||||||
|
pClient->Stop();
|
||||||
|
PushButtonConnect->setText(CON_BUT_CONNECTTEXT);
|
||||||
|
|
||||||
|
/* stop timer for level meter bars and reset them */
|
||||||
|
TimerSigMet.stop();
|
||||||
|
ProgressBarInputLevelL->setProgress(0);
|
||||||
|
ProgressBarInputLevelR->setProgress(0);
|
||||||
|
|
||||||
|
/* immediately update status bar */
|
||||||
|
OnTimerStatus();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* set address and check if address is valid */
|
||||||
|
if (pClient->SetServerAddr(LineEditServerAddr->text()))
|
||||||
|
{
|
||||||
|
pClient->start();
|
||||||
|
PushButtonConnect->setText(CON_BUT_DISCONNECTTEXT);
|
||||||
|
|
||||||
|
/* start timer for level meter bar */
|
||||||
|
TimerSigMet.start(LEVELMETER_UPDATE_TIME);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Restart timer to ensure that the text is visible at
|
||||||
|
least the time for one complete interval */
|
||||||
|
TimerStatus.changeInterval(STATUSBAR_UPDATE_TIME);
|
||||||
|
|
||||||
|
/* show the error in the status bar */
|
||||||
|
TextLabelStatus->setText(tr("invalid address"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLlconClientDlg::OnSliderSndBufInChange(int value)
|
||||||
|
{
|
||||||
|
pClient->GetSndInterface()->SetInNumBuf(value);
|
||||||
|
TextSndBufIn->setText("In: " + QString().setNum(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLlconClientDlg::OnSliderSndBufOutChange(int value)
|
||||||
|
{
|
||||||
|
pClient->GetSndInterface()->SetOutNumBuf(value);
|
||||||
|
TextSndBufOut->setText("Out: " + QString().setNum(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLlconClientDlg::OnSliderNetBuf(int value)
|
||||||
|
{
|
||||||
|
pClient->GetChannel()->SetSockBufSize ( MIN_BLOCK_SIZE_SAMPLES, value );
|
||||||
|
TextNetBuf->setText("Size: " + QString().setNum(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLlconClientDlg::OnTimerSigMet()
|
||||||
|
{
|
||||||
|
/* get current input levels */
|
||||||
|
double dCurSigLevelL = pClient->MicLevelL();
|
||||||
|
double dCurSigLevelR = pClient->MicLevelR();
|
||||||
|
|
||||||
|
/* linear transformation of the input level range to the progress-bar
|
||||||
|
range */
|
||||||
|
dCurSigLevelL -= LOW_BOUND_SIG_METER;
|
||||||
|
dCurSigLevelL *= NUM_STEPS_INP_LEV_METER /
|
||||||
|
(UPPER_BOUND_SIG_METER - LOW_BOUND_SIG_METER);
|
||||||
|
dCurSigLevelR -= LOW_BOUND_SIG_METER;
|
||||||
|
dCurSigLevelR *= NUM_STEPS_INP_LEV_METER /
|
||||||
|
(UPPER_BOUND_SIG_METER - LOW_BOUND_SIG_METER);
|
||||||
|
|
||||||
|
/* show current level */
|
||||||
|
ProgressBarInputLevelL->setProgress((int) ceil(dCurSigLevelL));
|
||||||
|
ProgressBarInputLevelR->setProgress((int) ceil(dCurSigLevelR));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLlconClientDlg::OnTimerStatus()
|
||||||
|
{
|
||||||
|
/* show connection status in status bar */
|
||||||
|
if (pClient->IsConnected() && pClient->IsRunning())
|
||||||
|
TextLabelStatus->setText(tr("connected"));
|
||||||
|
else
|
||||||
|
TextLabelStatus->setText(tr("disconnected"));
|
||||||
|
|
||||||
|
/* update sample rate offset label */
|
||||||
|
// FIXME disable sample rate estimation result label since estimation does not work
|
||||||
|
/*
|
||||||
|
QString strSamRaOffs;
|
||||||
|
|
||||||
|
// FIXME: sample rate estimation result must be corrected since we use
|
||||||
|
// smaller buffers in client now. Actual estimation should be fixed, not here
|
||||||
|
|
||||||
|
strSamRaOffs.setNum(pClient->GetChannel()->GetResampleOffset() *
|
||||||
|
MIN_BLOCK_DURATION_MS / BLOCK_DURATION_MS, 'f', 2);
|
||||||
|
TextSamRateOffsValue->setText(strSamRaOffs + " Hz");
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* response time */
|
||||||
|
TextLabelStdDevTimer->setText(QString().
|
||||||
|
setNum(pClient->GetTimingStdDev(), 'f', 2) + " ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLlconClientDlg::customEvent(QCustomEvent* Event)
|
||||||
|
{
|
||||||
|
if (Event->type() == QEvent::User + 11)
|
||||||
|
{
|
||||||
|
const int iMessType = ((CLlconEvent*) Event)->iMessType;
|
||||||
|
const int iStatus = ((CLlconEvent*) Event)->iStatus;
|
||||||
|
|
||||||
|
switch(iMessType)
|
||||||
|
{
|
||||||
|
case MS_SOUND_IN:
|
||||||
|
CLEDSoundIn->SetLight(iStatus);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MS_SOUND_OUT:
|
||||||
|
CLEDSoundOut->SetLight(iStatus);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MS_JIT_BUF_PUT:
|
||||||
|
CLEDNetwPut->SetLight(iStatus);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MS_JIT_BUF_GET:
|
||||||
|
CLEDNetwGet->SetLight(iStatus);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MS_RESET_ALL:
|
||||||
|
CLEDSoundIn->Reset();
|
||||||
|
CLEDSoundOut->Reset();
|
||||||
|
CLEDNetwPut->Reset();
|
||||||
|
CLEDNetwGet->Reset();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
96
src/llconclientdlg.h
Executable file
96
src/llconclientdlg.h
Executable file
|
@ -0,0 +1,96 @@
|
||||||
|
/******************************************************************************\
|
||||||
|
* Copyright (c) 2004-2006
|
||||||
|
*
|
||||||
|
* Author(s):
|
||||||
|
* Volker Fischer
|
||||||
|
*
|
||||||
|
******************************************************************************
|
||||||
|
*
|
||||||
|
* 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.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
\******************************************************************************/
|
||||||
|
|
||||||
|
#include <qlabel.h>
|
||||||
|
#include <qstring.h>
|
||||||
|
#include <qlineedit.h>
|
||||||
|
#include <qpushbutton.h>
|
||||||
|
#include <qprogressbar.h>
|
||||||
|
#include <qwhatsthis.h>
|
||||||
|
#include <qtimer.h>
|
||||||
|
#include <qslider.h>
|
||||||
|
#include <qradiobutton.h>
|
||||||
|
#include <qmenubar.h>
|
||||||
|
#include <qlayout.h>
|
||||||
|
#include "global.h"
|
||||||
|
#include "client.h"
|
||||||
|
#include "multicolorled.h"
|
||||||
|
#ifdef _WIN32
|
||||||
|
# include "../windows/moc/llconclientdlgbase.h"
|
||||||
|
#else
|
||||||
|
# include "moc/llconclientdlgbase.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Definitions ****************************************************************/
|
||||||
|
/* text strings for connection button for connect and disconnect */
|
||||||
|
#define CON_BUT_CONNECTTEXT "C&onnect"
|
||||||
|
#define CON_BUT_DISCONNECTTEXT "D&isconnect"
|
||||||
|
|
||||||
|
/* steps for input level meter */
|
||||||
|
#define NUM_STEPS_INP_LEV_METER 100
|
||||||
|
|
||||||
|
/* update time for GUI controls */
|
||||||
|
#define LEVELMETER_UPDATE_TIME 100 /* ms */
|
||||||
|
#define STATUSBAR_UPDATE_TIME 1000 /* ms */
|
||||||
|
|
||||||
|
/* range for signal level meter */
|
||||||
|
#define LOW_BOUND_SIG_METER ( -50.0 ) /* dB */
|
||||||
|
#define UPPER_BOUND_SIG_METER ( 0.0 ) /* dB */
|
||||||
|
|
||||||
|
|
||||||
|
/* Classes ********************************************************************/
|
||||||
|
class CLlconClientDlg : public CLlconClientDlgBase
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
CLlconClientDlg ( CClient* pNCliP, QWidget* parent = 0,
|
||||||
|
const char* name = 0, bool modal = FALSE, WFlags f = 0 );
|
||||||
|
|
||||||
|
virtual ~CLlconClientDlg ();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
CClient* pClient;
|
||||||
|
bool bConnected;
|
||||||
|
QTimer TimerSigMet;
|
||||||
|
QTimer TimerStatus;
|
||||||
|
virtual void customEvent ( QCustomEvent* Event );
|
||||||
|
virtual void closeEvent ( QCloseEvent * Event );
|
||||||
|
|
||||||
|
QMenuBar* pMenu;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void OnConnectDisconBut ();
|
||||||
|
void OnTimerSigMet ();
|
||||||
|
void OnTimerStatus ();
|
||||||
|
void OnSliderSndBufInChange ( int value );
|
||||||
|
void OnSliderSndBufOutChange ( int value );
|
||||||
|
void OnSliderNetBuf ( int value );
|
||||||
|
void OnSliderAudInFader ( int value ) { pClient->SetAudioInFader(value); }
|
||||||
|
void OnSliderAudReverb ( int value )
|
||||||
|
{ pClient->SetReverbLevel ( AUD_REVERB_MAX - value ); }
|
||||||
|
void OnRevSelL () { pClient->SetReverbOnLeftChan(true); }
|
||||||
|
void OnRevSelR () { pClient->SetReverbOnLeftChan(false); }
|
||||||
|
};
|
1125
src/llconclientdlgbase.ui
Executable file
1125
src/llconclientdlgbase.ui
Executable file
File diff suppressed because one or more lines are too long
177
src/llconserverdlg.cpp
Executable file
177
src/llconserverdlg.cpp
Executable file
|
@ -0,0 +1,177 @@
|
||||||
|
/******************************************************************************\
|
||||||
|
* Copyright (c) 2004-2006
|
||||||
|
*
|
||||||
|
* Author(s):
|
||||||
|
* Volker Fischer
|
||||||
|
*
|
||||||
|
******************************************************************************
|
||||||
|
*
|
||||||
|
* 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.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
\******************************************************************************/
|
||||||
|
|
||||||
|
#include "llconserverdlg.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Implementation *************************************************************/
|
||||||
|
CLlconServerDlg::CLlconServerDlg(QWidget* parent, const char* name, bool modal,
|
||||||
|
WFlags f) : CLlconServerDlgBase(parent, name, modal, f)
|
||||||
|
{
|
||||||
|
/* set text for version and application name */
|
||||||
|
TextLabelNameVersion->
|
||||||
|
setText(QString(APP_NAME) + tr(" server ") + QString(VERSION) +
|
||||||
|
" (" + QString().setNum(BLOCK_DURATION_MS) + " ms)");
|
||||||
|
|
||||||
|
/* Create bitmaps */
|
||||||
|
/* Define size of the bitmaps */
|
||||||
|
const int iXSize = 13;
|
||||||
|
const int iYSize = 13;
|
||||||
|
BitmCubeGreen.resize(iXSize, iYSize);
|
||||||
|
BitmCubeGreen.fill(QColor(0, 255, 0));
|
||||||
|
BitmCubeRed.resize(iXSize, iYSize);
|
||||||
|
BitmCubeRed.fill(QColor(255, 0, 0));
|
||||||
|
BitmCubeYellow.resize(iXSize, iYSize);
|
||||||
|
BitmCubeYellow.fill(QColor(255, 255, 0));
|
||||||
|
|
||||||
|
/* set up list view for connected clients (We assume that one column is
|
||||||
|
already there) */
|
||||||
|
ListViewClients->setColumnText(0, tr("Client IP : Port"));
|
||||||
|
ListViewClients->setColumnWidth(0, 170);
|
||||||
|
ListViewClients->addColumn(tr("Put"));
|
||||||
|
ListViewClients->setColumnAlignment(1, Qt::AlignCenter);
|
||||||
|
ListViewClients->addColumn(tr("Get"));
|
||||||
|
ListViewClients->setColumnAlignment(2, Qt::AlignCenter);
|
||||||
|
ListViewClients->addColumn(tr("Sample-rate offset [Hz]"));
|
||||||
|
ListViewClients->clear();
|
||||||
|
|
||||||
|
/* insert items in reverse order because in Windows all of them are
|
||||||
|
always visible -> put first item on the top */
|
||||||
|
vecpListViewItems.Init(MAX_NUM_CHANNELS);
|
||||||
|
for (int i = MAX_NUM_CHANNELS - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
vecpListViewItems[i] = new CServerListViewItem(ListViewClients);
|
||||||
|
#ifndef _WIN32
|
||||||
|
vecpListViewItems[i]->setVisible(false);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Init slider control */
|
||||||
|
SliderNetBuf->setRange(1, MAX_NET_BUF_SIZE_NUM_BL);
|
||||||
|
const int iCurNumNetBuf = Server.GetChannelSet()->GetSockBufSize();
|
||||||
|
SliderNetBuf->setValue(iCurNumNetBuf);
|
||||||
|
TextNetBuf->setText("Size: " + QString().setNum(iCurNumNetBuf));
|
||||||
|
|
||||||
|
/* start the server */
|
||||||
|
Server.Start();
|
||||||
|
|
||||||
|
/* Init timing jitter text label */
|
||||||
|
TextLabelResponseTime->setText("");
|
||||||
|
|
||||||
|
|
||||||
|
/* Main menu bar -------------------------------------------------------- */
|
||||||
|
pMenu = new QMenuBar(this);
|
||||||
|
CHECK_PTR(pMenu);
|
||||||
|
pMenu->insertItem(tr("&?"), new CLlconHelpMenu(this));
|
||||||
|
pMenu->setSeparator(QMenuBar::InWindowsStyle);
|
||||||
|
|
||||||
|
/* Now tell the layout about the menu */
|
||||||
|
CLlconServerDlgBaseLayout->setMenuBar(pMenu);
|
||||||
|
|
||||||
|
|
||||||
|
/* connections ---------------------------------------------------------- */
|
||||||
|
/* timers */
|
||||||
|
QObject::connect(&Timer, SIGNAL(timeout()), this, SLOT(OnTimer()));
|
||||||
|
|
||||||
|
/* sliders */
|
||||||
|
QObject::connect(SliderNetBuf, SIGNAL(valueChanged(int)),
|
||||||
|
this, SLOT(OnSliderNetBuf(int)));
|
||||||
|
|
||||||
|
|
||||||
|
/* timers --------------------------------------------------------------- */
|
||||||
|
/* start timer for GUI controls */
|
||||||
|
Timer.start(GUI_CONTRL_UPDATE_TIME);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLlconServerDlg::OnTimer()
|
||||||
|
{
|
||||||
|
CVector<CHostAddress> vecHostAddresses;
|
||||||
|
CVector<double> vecdSamOffs;
|
||||||
|
|
||||||
|
ListViewMutex.lock();
|
||||||
|
|
||||||
|
Server.GetConCliParam(vecHostAddresses, vecdSamOffs);
|
||||||
|
|
||||||
|
/* fill list with connected clients */
|
||||||
|
for (int i = 0; i < MAX_NUM_CHANNELS; i++)
|
||||||
|
{
|
||||||
|
if (!(vecHostAddresses[i].InetAddr == QHostAddress((Q_UINT32) 0)))
|
||||||
|
{
|
||||||
|
/* main text (IP, port number) */
|
||||||
|
vecpListViewItems[i]->setText(0, QString().sprintf("%s : %d",
|
||||||
|
vecHostAddresses[i].InetAddr.toString().latin1(),
|
||||||
|
vecHostAddresses[i].iPort) /* IP, port */);
|
||||||
|
|
||||||
|
/* sample rate offset */
|
||||||
|
// FIXME disable sample rate estimation result label since estimation does not work
|
||||||
|
// vecpListViewItems[i]->setText(3,
|
||||||
|
// QString().sprintf("%5.2f", vecdSamOffs[i]));
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
vecpListViewItems[i]->setVisible(true);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#ifndef _WIN32
|
||||||
|
else
|
||||||
|
vecpListViewItems[i]->setVisible(false);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
ListViewMutex.unlock();
|
||||||
|
|
||||||
|
/* response time */
|
||||||
|
TextLabelResponseTime->setText(QString().
|
||||||
|
setNum(Server.GetTimingStdDev(), 'f', 2) + " ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLlconServerDlg::OnSliderNetBuf(int value)
|
||||||
|
{
|
||||||
|
Server.GetChannelSet()->SetSockBufSize( BLOCK_SIZE_SAMPLES, value );
|
||||||
|
TextNetBuf->setText("Size: " + QString().setNum(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLlconServerDlg::customEvent(QCustomEvent* Event)
|
||||||
|
{
|
||||||
|
if (Event->type() == QEvent::User + 11)
|
||||||
|
{
|
||||||
|
ListViewMutex.lock();
|
||||||
|
|
||||||
|
const int iMessType = ((CLlconEvent*) Event)->iMessType;
|
||||||
|
const int iStatus = ((CLlconEvent*) Event)->iStatus;
|
||||||
|
const int iChanNum = ((CLlconEvent*) Event)->iChanNum;
|
||||||
|
|
||||||
|
switch(iMessType)
|
||||||
|
{
|
||||||
|
case MS_JIT_BUF_PUT:
|
||||||
|
vecpListViewItems[iChanNum]->SetLight(0, iStatus);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MS_JIT_BUF_GET:
|
||||||
|
vecpListViewItems[iChanNum]->SetLight(1, iStatus);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ListViewMutex.unlock();
|
||||||
|
}
|
||||||
|
}
|
78
src/llconserverdlg.h
Executable file
78
src/llconserverdlg.h
Executable file
|
@ -0,0 +1,78 @@
|
||||||
|
/******************************************************************************\
|
||||||
|
* Copyright (c) 2004-2006
|
||||||
|
*
|
||||||
|
* Author(s):
|
||||||
|
* Volker Fischer
|
||||||
|
*
|
||||||
|
******************************************************************************
|
||||||
|
*
|
||||||
|
* 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.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
\******************************************************************************/
|
||||||
|
|
||||||
|
#include <qlabel.h>
|
||||||
|
#include <qlistview.h>
|
||||||
|
#include <qtimer.h>
|
||||||
|
#include <qpixmap.h>
|
||||||
|
#include <qthread.h>
|
||||||
|
#include <qslider.h>
|
||||||
|
#include <qmenubar.h>
|
||||||
|
#include <qlayout.h>
|
||||||
|
#include "global.h"
|
||||||
|
#include "server.h"
|
||||||
|
#include "multicolorled.h"
|
||||||
|
#ifdef _WIN32
|
||||||
|
# include "../windows/moc/llconserverdlgbase.h"
|
||||||
|
#else
|
||||||
|
# include "moc/llconserverdlgbase.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Definitions ****************************************************************/
|
||||||
|
/* update time for GUI controls */
|
||||||
|
#define GUI_CONTRL_UPDATE_TIME 1000 /* ms */
|
||||||
|
|
||||||
|
|
||||||
|
/* Classes ********************************************************************/
|
||||||
|
class CLlconServerDlg : public CLlconServerDlgBase
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
CLlconServerDlg(QWidget* parent = 0, const char* name = 0,
|
||||||
|
bool modal = FALSE, WFlags f = 0);
|
||||||
|
|
||||||
|
virtual ~CLlconServerDlg() {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QTimer Timer;
|
||||||
|
CServer Server;
|
||||||
|
|
||||||
|
QPixmap BitmCubeGreen;
|
||||||
|
QPixmap BitmCubeYellow;
|
||||||
|
QPixmap BitmCubeRed;
|
||||||
|
|
||||||
|
CVector<CServerListViewItem*> vecpListViewItems;
|
||||||
|
QMutex ListViewMutex;
|
||||||
|
|
||||||
|
QMenuBar* pMenu;
|
||||||
|
|
||||||
|
virtual void customEvent(QCustomEvent* Event);
|
||||||
|
void UpdateSliderNetBuf();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void OnTimer();
|
||||||
|
void OnSliderNetBuf(int value);
|
||||||
|
};
|
272
src/llconserverdlgbase.ui
Executable file
272
src/llconserverdlgbase.ui
Executable file
|
@ -0,0 +1,272 @@
|
||||||
|
<!DOCTYPE UI><UI>
|
||||||
|
<class>CLlconServerDlgBase</class>
|
||||||
|
<widget>
|
||||||
|
<class>QDialog</class>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>name</name>
|
||||||
|
<cstring>CLlconServerDlgBase</cstring>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>geometry</name>
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>633</width>
|
||||||
|
<height>240</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>caption</name>
|
||||||
|
<string>llcon</string>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>icon</name>
|
||||||
|
<pixmap>image0</pixmap>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>sizeGripEnabled</name>
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<vbox>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>margin</name>
|
||||||
|
<number>11</number>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>spacing</name>
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<widget>
|
||||||
|
<class>QLayoutWidget</class>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>name</name>
|
||||||
|
<cstring>Layout4</cstring>
|
||||||
|
</property>
|
||||||
|
<hbox>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>margin</name>
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>spacing</name>
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<widget>
|
||||||
|
<class>QListView</class>
|
||||||
|
<column>
|
||||||
|
<property>
|
||||||
|
<name>text</name>
|
||||||
|
<string>Column 1</string>
|
||||||
|
</property>
|
||||||
|
<property>
|
||||||
|
<name>clickable</name>
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property>
|
||||||
|
<name>resizeable</name>
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
<item>
|
||||||
|
<property>
|
||||||
|
<name>text</name>
|
||||||
|
<string>New Item</string>
|
||||||
|
</property>
|
||||||
|
<property>
|
||||||
|
<name>pixmap</name>
|
||||||
|
<pixmap></pixmap>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>name</name>
|
||||||
|
<cstring>ListViewClients</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget>
|
||||||
|
<class>QGroupBox</class>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>name</name>
|
||||||
|
<cstring>GroupBox3</cstring>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>title</name>
|
||||||
|
<string>Jitter Buffer</string>
|
||||||
|
</property>
|
||||||
|
<vbox>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>margin</name>
|
||||||
|
<number>11</number>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>spacing</name>
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<widget>
|
||||||
|
<class>QLabel</class>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>name</name>
|
||||||
|
<cstring>TextNetBuf</cstring>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>text</name>
|
||||||
|
<string>Size</string>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>alignment</name>
|
||||||
|
<set>AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
<property>
|
||||||
|
<name>hAlign</name>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget>
|
||||||
|
<class>QSlider</class>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>name</name>
|
||||||
|
<cstring>SliderNetBuf</cstring>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>pageStep</name>
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>orientation</name>
|
||||||
|
<enum>Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>tickmarks</name>
|
||||||
|
<enum>Both</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</vbox>
|
||||||
|
</widget>
|
||||||
|
</hbox>
|
||||||
|
</widget>
|
||||||
|
<widget>
|
||||||
|
<class>QLayoutWidget</class>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>name</name>
|
||||||
|
<cstring>Layout3</cstring>
|
||||||
|
</property>
|
||||||
|
<hbox>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>margin</name>
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>spacing</name>
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<widget>
|
||||||
|
<class>QLabel</class>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>name</name>
|
||||||
|
<cstring>TextLabelNameVersion</cstring>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>text</name>
|
||||||
|
<string>TextLabelNameVersion</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<spacer>
|
||||||
|
<property>
|
||||||
|
<name>name</name>
|
||||||
|
<cstring>Horizontal Spacing2_2</cstring>
|
||||||
|
</property>
|
||||||
|
<property>
|
||||||
|
<name>orientation</name>
|
||||||
|
<enum>Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property>
|
||||||
|
<name>sizeType</name>
|
||||||
|
<enum>Fixed</enum>
|
||||||
|
</property>
|
||||||
|
<property>
|
||||||
|
<name>sizeHint</name>
|
||||||
|
<size>
|
||||||
|
<width>72</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
<widget>
|
||||||
|
<class>QLabel</class>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>name</name>
|
||||||
|
<cstring>TextLabel1</cstring>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>text</name>
|
||||||
|
<string>Timing Standard Deviation:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget>
|
||||||
|
<class>QLabel</class>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>name</name>
|
||||||
|
<cstring>TextLabelResponseTime</cstring>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>text</name>
|
||||||
|
<string>TextLabelResponseTime</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<spacer>
|
||||||
|
<property>
|
||||||
|
<name>name</name>
|
||||||
|
<cstring>Horizontal Spacing2</cstring>
|
||||||
|
</property>
|
||||||
|
<property>
|
||||||
|
<name>orientation</name>
|
||||||
|
<enum>Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property>
|
||||||
|
<name>sizeType</name>
|
||||||
|
<enum>Expanding</enum>
|
||||||
|
</property>
|
||||||
|
<property>
|
||||||
|
<name>sizeHint</name>
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
<widget>
|
||||||
|
<class>QPushButton</class>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>name</name>
|
||||||
|
<cstring>buttonOk</cstring>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>text</name>
|
||||||
|
<string>&OK</string>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>autoDefault</name>
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property stdset="1">
|
||||||
|
<name>default</name>
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</hbox>
|
||||||
|
</widget>
|
||||||
|
</vbox>
|
||||||
|
</widget>
|
||||||
|
<images>
|
||||||
|
<image>
|
||||||
|
<name>image0</name>
|
||||||
|
<data format="XPM.GZ" length="427">789cd3d7528808f055d0d2e72a2e492cc94c5648ce482c52d04a29cdcdad8c8eb5ade6523234530022130543251d2ea5248564056503300071f5205c0b2004719541dcb434986c22840b0260c56800454c9918b1c444e54454b1c4c4a424e5a4c4442431a0085008081231c4949511621021656565b042843a908032bade24a832547b21c6a1ba0f08d0fda18ccd6fd8c2009f58ad351700407358e1</data>
|
||||||
|
</image>
|
||||||
|
</images>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonOk</sender>
|
||||||
|
<signal>clicked()</signal>
|
||||||
|
<receiver>CLlconServerDlgBase</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</UI>
|
106
src/main.cpp
Executable file
106
src/main.cpp
Executable file
|
@ -0,0 +1,106 @@
|
||||||
|
/******************************************************************************\
|
||||||
|
* Copyright (c) 2004-2006
|
||||||
|
*
|
||||||
|
* Author(s):
|
||||||
|
* Volker Fischer
|
||||||
|
*
|
||||||
|
******************************************************************************
|
||||||
|
*
|
||||||
|
* 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.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
\******************************************************************************/
|
||||||
|
|
||||||
|
#include <qapplication.h>
|
||||||
|
#include "global.h"
|
||||||
|
#include "llconclientdlg.h"
|
||||||
|
#include "llconserverdlg.h"
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Implementation *************************************************************/
|
||||||
|
/* This pointer is only used for the post-event routine */
|
||||||
|
QApplication* pApp = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
/* Application object */
|
||||||
|
QApplication app(argc, argv);
|
||||||
|
|
||||||
|
/* check if server or client application shall be started */
|
||||||
|
bool bIsClient = true;
|
||||||
|
|
||||||
|
/* QT docu: argv()[0] is the program name, argv()[1] is the first
|
||||||
|
argument and argv()[argc()-1] is the last argument */
|
||||||
|
if (argc > 1)
|
||||||
|
{
|
||||||
|
/* only "-s" is supported right now */
|
||||||
|
std::string strShortOpt = "-s";
|
||||||
|
if (!strShortOpt.compare(argv[1]))
|
||||||
|
bIsClient = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bIsClient)
|
||||||
|
{
|
||||||
|
// actual client object
|
||||||
|
CClient Client;
|
||||||
|
|
||||||
|
// load settings from init-file
|
||||||
|
CSettings Settings ( &Client );
|
||||||
|
Settings.Load ();
|
||||||
|
|
||||||
|
/* client */
|
||||||
|
CLlconClientDlg ClientDlg ( &Client, 0, 0, FALSE, Qt::WStyle_MinMax );
|
||||||
|
|
||||||
|
/* Set main window */
|
||||||
|
app.setMainWidget ( &ClientDlg );
|
||||||
|
pApp = &app; /* Needed for post-event routine */
|
||||||
|
|
||||||
|
/* Show dialog */
|
||||||
|
ClientDlg.show ();
|
||||||
|
app.exec ();
|
||||||
|
|
||||||
|
/* Save settings to init-file */
|
||||||
|
Settings.Save ();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* server */
|
||||||
|
CLlconServerDlg ServerDlg ( 0, 0, FALSE, Qt::WStyle_MinMax );
|
||||||
|
|
||||||
|
/* Set main window */
|
||||||
|
app.setMainWidget ( &ServerDlg );
|
||||||
|
pApp = &app; /* Needed for post-event routine */
|
||||||
|
|
||||||
|
/* Show dialog */
|
||||||
|
ServerDlg.show ();
|
||||||
|
app.exec ();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostWinMessage ( const _MESSAGE_IDENT MessID, const int iMessageParam,
|
||||||
|
const int iChanNum )
|
||||||
|
{
|
||||||
|
/* In case of simulation no events should be generated */
|
||||||
|
if ( pApp != NULL )
|
||||||
|
{
|
||||||
|
CLlconEvent* LlconEv = new CLlconEvent ( MessID, iMessageParam, iChanNum );
|
||||||
|
|
||||||
|
/* Qt will delete the event object when done */
|
||||||
|
QThread::postEvent ( pApp->mainWidget (), LlconEv );
|
||||||
|
}
|
||||||
|
}
|
182
src/multicolorled.cpp
Executable file
182
src/multicolorled.cpp
Executable file
|
@ -0,0 +1,182 @@
|
||||||
|
/******************************************************************************\
|
||||||
|
* Copyright (c) 2004-2006
|
||||||
|
*
|
||||||
|
* Author(s):
|
||||||
|
* Volker Fischer
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Implements a multi-color LED
|
||||||
|
*
|
||||||
|
*
|
||||||
|
******************************************************************************
|
||||||
|
*
|
||||||
|
* 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.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
\******************************************************************************/
|
||||||
|
|
||||||
|
#include "multicolorled.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Implementation *************************************************************/
|
||||||
|
CMultiColorLEDbase::CMultiColorLEDbase()
|
||||||
|
{
|
||||||
|
/* Define size of the bitmaps */
|
||||||
|
const int iXSize = 13;
|
||||||
|
const int iYSize = 13;
|
||||||
|
|
||||||
|
/* Create bitmaps */
|
||||||
|
BitmCubeGreen.resize(iXSize, iYSize);
|
||||||
|
BitmCubeGreen.fill(QColor(0, 255, 0));
|
||||||
|
BitmCubeRed.resize(iXSize, iYSize);
|
||||||
|
BitmCubeRed.fill(QColor(255, 0, 0));
|
||||||
|
BitmCubeGrey.resize(iXSize, iYSize);
|
||||||
|
BitmCubeGrey.fill(QColor(192, 192, 192));
|
||||||
|
BitmCubeYellow.resize(iXSize, iYSize);
|
||||||
|
BitmCubeYellow.fill(QColor(255, 255, 0));
|
||||||
|
|
||||||
|
/* Init color flags */
|
||||||
|
Reset();
|
||||||
|
|
||||||
|
/* Set init-bitmap */
|
||||||
|
SetPixmap(BitmCubeGrey);
|
||||||
|
eColorFlag = RL_GREY;
|
||||||
|
|
||||||
|
/* Init update time */
|
||||||
|
iUpdateTime = DEFAULT_UPDATE_TIME;
|
||||||
|
|
||||||
|
/* Connect timer events to the desired slots */
|
||||||
|
connect(&TimerRedLight, SIGNAL(timeout()),
|
||||||
|
this, SLOT(OnTimerRedLight()));
|
||||||
|
connect(&TimerGreenLight, SIGNAL(timeout()),
|
||||||
|
this, SLOT(OnTimerGreenLight()));
|
||||||
|
connect(&TimerYellowLight, SIGNAL(timeout()),
|
||||||
|
this, SLOT(OnTimerYellowLight()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMultiColorLEDbase::Reset()
|
||||||
|
{
|
||||||
|
/* Reset color flags */
|
||||||
|
bFlagRedLi = false;
|
||||||
|
bFlagGreenLi = false;
|
||||||
|
bFlagYellowLi = false;
|
||||||
|
|
||||||
|
UpdateColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMultiColorLEDbase::OnTimerRedLight()
|
||||||
|
{
|
||||||
|
bFlagRedLi = false;
|
||||||
|
UpdateColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMultiColorLEDbase::OnTimerGreenLight()
|
||||||
|
{
|
||||||
|
bFlagGreenLi = false;
|
||||||
|
UpdateColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMultiColorLEDbase::OnTimerYellowLight()
|
||||||
|
{
|
||||||
|
bFlagYellowLi = false;
|
||||||
|
UpdateColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMultiColorLEDbase::UpdateColor()
|
||||||
|
{
|
||||||
|
/* Red light has highest priority, then comes yellow and at the end, we
|
||||||
|
decide to set green light. Allways check the current color of the
|
||||||
|
control before setting the color to prevent flicking */
|
||||||
|
if (bFlagRedLi)
|
||||||
|
{
|
||||||
|
if (eColorFlag != RL_RED)
|
||||||
|
{
|
||||||
|
SetPixmap(BitmCubeRed);
|
||||||
|
eColorFlag = RL_RED;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bFlagYellowLi)
|
||||||
|
{
|
||||||
|
if (eColorFlag != RL_YELLOW)
|
||||||
|
{
|
||||||
|
SetPixmap(BitmCubeYellow);
|
||||||
|
eColorFlag = RL_YELLOW;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bFlagGreenLi)
|
||||||
|
{
|
||||||
|
if (eColorFlag != RL_GREEN)
|
||||||
|
{
|
||||||
|
SetPixmap(BitmCubeGreen);
|
||||||
|
eColorFlag = RL_GREEN;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If no color is active, set control to grey light */
|
||||||
|
if (eColorFlag != RL_GREY)
|
||||||
|
{
|
||||||
|
SetPixmap(BitmCubeGrey);
|
||||||
|
eColorFlag = RL_GREY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMultiColorLEDbase::SetLight(int iNewStatus)
|
||||||
|
{
|
||||||
|
switch (iNewStatus)
|
||||||
|
{
|
||||||
|
case MUL_COL_LED_GREEN:
|
||||||
|
/* Green light */
|
||||||
|
bFlagGreenLi = true;
|
||||||
|
TimerGreenLight.changeInterval(iUpdateTime);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MUL_COL_LED_YELLOW:
|
||||||
|
/* Yellow light */
|
||||||
|
bFlagYellowLi = true;
|
||||||
|
TimerYellowLight.changeInterval(iUpdateTime);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MUL_COL_LED_RED:
|
||||||
|
/* Red light */
|
||||||
|
bFlagRedLi = true;
|
||||||
|
TimerRedLight.changeInterval(iUpdateTime);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMultiColorLEDbase::SetUpdateTime(int iNUTi)
|
||||||
|
{
|
||||||
|
/* Avoid too short intervals */
|
||||||
|
if (iNUTi < MIN_TIME_FOR_RED_LIGHT)
|
||||||
|
iUpdateTime = MIN_TIME_FOR_RED_LIGHT;
|
||||||
|
else
|
||||||
|
iUpdateTime = iNUTi;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CMultiColorLED::CMultiColorLED(QWidget* parent, const char* name, WFlags f) :
|
||||||
|
QLabel(parent, name, f)
|
||||||
|
{
|
||||||
|
/* Set modified style */
|
||||||
|
setFrameShape(QFrame::Panel);
|
||||||
|
setFrameShadow(QFrame::Sunken);
|
||||||
|
setIndent(0);
|
||||||
|
}
|
139
src/multicolorled.h
Executable file
139
src/multicolorled.h
Executable file
|
@ -0,0 +1,139 @@
|
||||||
|
/******************************************************************************\
|
||||||
|
* Copyright (c) 2004-2006
|
||||||
|
*
|
||||||
|
* Author(s):
|
||||||
|
* Volker Fischer
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
*
|
||||||
|
* SetLight():
|
||||||
|
* 0: Green
|
||||||
|
* 1: Yellow
|
||||||
|
* 2: Red
|
||||||
|
*
|
||||||
|
*
|
||||||
|
******************************************************************************
|
||||||
|
*
|
||||||
|
* 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.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
\******************************************************************************/
|
||||||
|
|
||||||
|
#if !defined(AFX_MULTCOLORLED_H__FD6B49B5_87DF_48DD_A873_804E1606C2AC__INCLUDED_)
|
||||||
|
#define AFX_MULTCOLORLED_H__FD6B49B5_87DF_48DD_A873_804E1606C2AC__INCLUDED_
|
||||||
|
|
||||||
|
#include <qlabel.h>
|
||||||
|
#include <qpixmap.h>
|
||||||
|
#include <qtimer.h>
|
||||||
|
#include <qlistview.h>
|
||||||
|
#include "global.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Definitions ****************************************************************/
|
||||||
|
#define DEFAULT_UPDATE_TIME 300
|
||||||
|
|
||||||
|
/* The red and yellow light should be on at least this interval */
|
||||||
|
#define MIN_TIME_FOR_RED_LIGHT 100
|
||||||
|
|
||||||
|
|
||||||
|
/* Classes ********************************************************************/
|
||||||
|
class CMultiColorLEDbase : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
CMultiColorLEDbase();
|
||||||
|
|
||||||
|
void Reset();
|
||||||
|
void SetUpdateTime(int iNUTi);
|
||||||
|
void SetLight(int iNewStatus);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
enum ELightColor {RL_GREY, RL_RED, RL_GREEN, RL_YELLOW};
|
||||||
|
ELightColor eColorFlag;
|
||||||
|
|
||||||
|
virtual void SetPixmap(QPixmap& NewBitmap) {} /* must be implemented in derived class! */
|
||||||
|
void UpdateColor();
|
||||||
|
|
||||||
|
QPixmap BitmCubeGreen;
|
||||||
|
QPixmap BitmCubeYellow;
|
||||||
|
QPixmap BitmCubeRed;
|
||||||
|
QPixmap BitmCubeGrey;
|
||||||
|
|
||||||
|
QTimer TimerRedLight;
|
||||||
|
QTimer TimerGreenLight;
|
||||||
|
QTimer TimerYellowLight;
|
||||||
|
|
||||||
|
int iUpdateTime;
|
||||||
|
|
||||||
|
bool bFlagRedLi;
|
||||||
|
bool bFlagGreenLi;
|
||||||
|
bool bFlagYellowLi;
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
void OnTimerRedLight();
|
||||||
|
void OnTimerGreenLight();
|
||||||
|
void OnTimerYellowLight();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class CMultiColorLED : public QLabel, public CMultiColorLEDbase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CMultiColorLED(QWidget* parent, const char* name = 0, WFlags f = 0);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void SetPixmap(QPixmap& NewBitmap) {setPixmap(NewBitmap);}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class CMultColLEDListViewItem : public CMultiColorLEDbase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CMultColLEDListViewItem(const int iNewCol) : iColumn(iNewCol),
|
||||||
|
pListViewItem(NULL) {}
|
||||||
|
|
||||||
|
void SetListViewItemPointer(QListViewItem* pNewListViewItem)
|
||||||
|
{pListViewItem = pNewListViewItem;}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void SetPixmap(QPixmap& NewBitmap)
|
||||||
|
{if (pListViewItem != NULL) pListViewItem->setPixmap(iColumn, NewBitmap);}
|
||||||
|
|
||||||
|
QListViewItem* pListViewItem;
|
||||||
|
int iColumn;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class CServerListViewItem : public QListViewItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CServerListViewItem(QListView* parent) : LED0(1), LED1(2),
|
||||||
|
QListViewItem(parent) {LED0.SetListViewItemPointer(this);
|
||||||
|
LED1.SetListViewItemPointer(this);}
|
||||||
|
|
||||||
|
void SetLight(int iWhichLED, int iNewStatus)
|
||||||
|
{
|
||||||
|
switch (iWhichLED) {
|
||||||
|
case 0: LED0.SetLight(iNewStatus); break;
|
||||||
|
case 1: LED1.SetLight(iNewStatus); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
CMultColLEDListViewItem LED0, LED1;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // AFX_MULTCOLORLED_H__FD6B49B5_87DF_48DD_A873_804E1606C2AC__INCLUDED_
|
184
src/resample.cpp
Executable file
184
src/resample.cpp
Executable file
|
@ -0,0 +1,184 @@
|
||||||
|
/******************************************************************************\
|
||||||
|
* Copyright (c) 2004-2006
|
||||||
|
*
|
||||||
|
* Author(s):
|
||||||
|
* Volker Fischer
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Resample routine for arbitrary sample-rate conversions in a low range (for
|
||||||
|
* frequency offset correction).
|
||||||
|
* The algorithm is based on a polyphase structure. We upsample the input
|
||||||
|
* signal with a factor INTERP_DECIM_I_D and calculate two successive samples
|
||||||
|
* whereby we perform a linear interpolation between these two samples to get
|
||||||
|
* an arbitraty sample grid.
|
||||||
|
* The polyphase filter is calculated with Matlab(TM), the associated file
|
||||||
|
* is ResampleFilter.m.
|
||||||
|
*
|
||||||
|
******************************************************************************
|
||||||
|
*
|
||||||
|
* 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.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
\******************************************************************************/
|
||||||
|
|
||||||
|
#include "resample.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Implementation *************************************************************/
|
||||||
|
int CResample::Resample(CVector<double>& vecdInput, CVector<double>& vecdOutput,
|
||||||
|
const double dRation)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* move old data from the end to the history part of the buffer and
|
||||||
|
add new data (shift register) */
|
||||||
|
/* Shift old values */
|
||||||
|
int iMovLen = iInputBlockSize;
|
||||||
|
for (i = 0; i < iHistorySize; i++)
|
||||||
|
{
|
||||||
|
vecdIntBuff[i] = vecdIntBuff[iMovLen++];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add new block of data */
|
||||||
|
int iBlockEnd = iHistorySize;
|
||||||
|
for (i = 0; i < iInputBlockSize; i++)
|
||||||
|
{
|
||||||
|
vecdIntBuff[iBlockEnd++] = vecdInput[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* sample-interval of new sample frequency in relation to interpolated
|
||||||
|
sample-interval */
|
||||||
|
dTStep = (double) INTERP_DECIM_I_D / dRation;
|
||||||
|
|
||||||
|
/* init output counter */
|
||||||
|
int im = 0;
|
||||||
|
|
||||||
|
/* main loop */
|
||||||
|
do
|
||||||
|
{
|
||||||
|
/* quantize output-time to interpolated time-index */
|
||||||
|
const int ik = (int) dtOut;
|
||||||
|
|
||||||
|
|
||||||
|
/* calculate convolutions for the two interpolation-taps ------------ */
|
||||||
|
/* phase for the linear interpolation-taps */
|
||||||
|
const int ip1 = ik % INTERP_DECIM_I_D;
|
||||||
|
const int ip2 = (ik + 1) % INTERP_DECIM_I_D;
|
||||||
|
|
||||||
|
/* sample positions in input vector */
|
||||||
|
const int in1 = (int) (ik / INTERP_DECIM_I_D);
|
||||||
|
const int in2 = (int) ((ik + 1) / INTERP_DECIM_I_D);
|
||||||
|
|
||||||
|
/* convolution */
|
||||||
|
double dy1 = 0.0;
|
||||||
|
double dy2 = 0.0;
|
||||||
|
for (int i = 0; i < NUM_TAPS_PER_PHASE; i++)
|
||||||
|
{
|
||||||
|
dy1 += fResTaps1To1[ip1][i] * vecdIntBuff[in1 - i];
|
||||||
|
dy2 += fResTaps1To1[ip2][i] * vecdIntBuff[in2 - i];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* linear interpolation --------------------------------------------- */
|
||||||
|
/* get numbers after the comma */
|
||||||
|
const double dxInt = dtOut - (int) dtOut;
|
||||||
|
vecdOutput[im] = (dy2 - dy1) * dxInt + dy1;
|
||||||
|
|
||||||
|
|
||||||
|
/* increase output counter */
|
||||||
|
im++;
|
||||||
|
|
||||||
|
/* increase output-time and index one step */
|
||||||
|
dtOut = dtOut + dTStep;
|
||||||
|
}
|
||||||
|
while (dtOut < dBlockDuration);
|
||||||
|
|
||||||
|
/* set rtOut back */
|
||||||
|
dtOut -= iInputBlockSize * INTERP_DECIM_I_D;
|
||||||
|
|
||||||
|
return im;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CResample::Init(const int iNewInputBlockSize)
|
||||||
|
{
|
||||||
|
iInputBlockSize = iNewInputBlockSize;
|
||||||
|
|
||||||
|
/* history size must be one sample larger, because we use always TWO
|
||||||
|
convolutions */
|
||||||
|
iHistorySize = NUM_TAPS_PER_PHASE + 1;
|
||||||
|
|
||||||
|
/* calculate block duration */
|
||||||
|
dBlockDuration = (iInputBlockSize + iHistorySize - 1) * INTERP_DECIM_I_D;
|
||||||
|
|
||||||
|
/* allocate memory for internal buffer, clear sample history */
|
||||||
|
vecdIntBuff.Init(iInputBlockSize + iHistorySize, 0.0);
|
||||||
|
|
||||||
|
/* init absolute time for output stream (at the end of the history part */
|
||||||
|
dtOut = (double) (iHistorySize - 1) * INTERP_DECIM_I_D;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CAudioResample::Resample(CVector<double>& vecdInput,
|
||||||
|
CVector<double>& vecdOutput)
|
||||||
|
{
|
||||||
|
int j;
|
||||||
|
|
||||||
|
if (dRation == 1.0)
|
||||||
|
{
|
||||||
|
/* if ratio is 1, no resampling is needed, just copy vector */
|
||||||
|
for (j = 0; j < iOutputBlockSize; j++)
|
||||||
|
vecdOutput[j] = vecdInput[j];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* move old data from the end to the history part of the buffer and
|
||||||
|
add new data (shift register) */
|
||||||
|
/* Shift old values */
|
||||||
|
int iMovLen = iInputBlockSize;
|
||||||
|
for (j = 0; j < NUM_TAPS_PER_PHASE; j++)
|
||||||
|
vecdIntBuff[j] = vecdIntBuff[iMovLen++];
|
||||||
|
|
||||||
|
/* Add new block of data */
|
||||||
|
int iBlockEnd = NUM_TAPS_PER_PHASE;
|
||||||
|
for (j = 0; j < iInputBlockSize; j++)
|
||||||
|
vecdIntBuff[iBlockEnd++] = vecdInput[j];
|
||||||
|
|
||||||
|
/* main loop */
|
||||||
|
for (j = 0; j < iOutputBlockSize; j++)
|
||||||
|
{
|
||||||
|
/* phase for the linear interpolation-taps */
|
||||||
|
const int ip =
|
||||||
|
(int) (j * INTERP_DECIM_I_D / dRation) % INTERP_DECIM_I_D;
|
||||||
|
|
||||||
|
/* sample position in input vector */
|
||||||
|
const int in = (int) (j / dRation) + NUM_TAPS_PER_PHASE;
|
||||||
|
|
||||||
|
/* convolution */
|
||||||
|
double dy = 0.0;
|
||||||
|
for (int i = 0; i < NUM_TAPS_PER_PHASE; i++)
|
||||||
|
dy += fResTaps1To1[ip][i] * vecdIntBuff[in - i];
|
||||||
|
|
||||||
|
vecdOutput[j] = dy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CAudioResample::Init(const int iNewInputBlockSize, const double dNewRation)
|
||||||
|
{
|
||||||
|
dRation = dNewRation;
|
||||||
|
iInputBlockSize = iNewInputBlockSize;
|
||||||
|
iOutputBlockSize = (int) (iInputBlockSize * dNewRation);
|
||||||
|
|
||||||
|
/* allocate memory for internal buffer, clear sample history */
|
||||||
|
vecdIntBuff.Init(iInputBlockSize + NUM_TAPS_PER_PHASE, 0.0);
|
||||||
|
}
|
75
src/resample.h
Executable file
75
src/resample.h
Executable file
|
@ -0,0 +1,75 @@
|
||||||
|
/******************************************************************************\
|
||||||
|
* Copyright (c) 2004-2006
|
||||||
|
*
|
||||||
|
* Author(s):
|
||||||
|
* Volker Fischer
|
||||||
|
*
|
||||||
|
******************************************************************************
|
||||||
|
*
|
||||||
|
* 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.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
\******************************************************************************/
|
||||||
|
|
||||||
|
#if !defined(RESAMPLE_H__3B0FEUFE7876F_FE8FE_CA63_4344_1912__INCLUDED_)
|
||||||
|
#define RESAMPLE_H__3B0FEUFE7876F_FE8FE_CA63_4344_1912__INCLUDED_
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
#include "resamplefilter.h"
|
||||||
|
#include "global.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Classes ********************************************************************/
|
||||||
|
class CResample
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CResample() {}
|
||||||
|
virtual ~CResample() {}
|
||||||
|
|
||||||
|
void Init(const int iNewInputBlockSize);
|
||||||
|
int Resample(CVector<double>& vecdInput, CVector<double>& vecdOutput,
|
||||||
|
const double dRation);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
double dTStep;
|
||||||
|
double dtOut;
|
||||||
|
double dBlockDuration;
|
||||||
|
|
||||||
|
CVector<double> vecdIntBuff;
|
||||||
|
int iHistorySize;
|
||||||
|
|
||||||
|
int iInputBlockSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CAudioResample
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CAudioResample() {}
|
||||||
|
virtual ~CAudioResample() {}
|
||||||
|
|
||||||
|
void Init(const int iNewInputBlockSize, const double dNewRation);
|
||||||
|
void Resample(CVector<double>& vecdInput, CVector<double>& vecdOutput);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
double dRation;
|
||||||
|
|
||||||
|
CVector<double> vecdIntBuff;
|
||||||
|
int iHistorySize;
|
||||||
|
|
||||||
|
int iInputBlockSize;
|
||||||
|
int iOutputBlockSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // !defined(RESAMPLE_H__3B0FEUFE7876F_FE8FE_CA63_4344_1912__INCLUDED_)
|
157
src/resamplefilter.h
Normal file
157
src/resamplefilter.h
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
/* Automatically generated file with MATLAB */
|
||||||
|
/* File name: "ResampleFilter.m" */
|
||||||
|
/* Filter taps in time-domain */
|
||||||
|
|
||||||
|
#ifndef _RESAMPLEFILTER_H_
|
||||||
|
#define _RESAMPLEFILTER_H_
|
||||||
|
|
||||||
|
#define NUM_TAPS_PER_PHASE 12
|
||||||
|
#define INTERP_DECIM_I_D 10
|
||||||
|
|
||||||
|
|
||||||
|
/* Filter for ratios close to 1 */
|
||||||
|
static float fResTaps1To1[INTERP_DECIM_I_D][NUM_TAPS_PER_PHASE] = {
|
||||||
|
{
|
||||||
|
-0.00129181992672801360f,
|
||||||
|
0.00561586829442904840f,
|
||||||
|
-0.01349857823816511800f,
|
||||||
|
0.02541150940858524100f,
|
||||||
|
-0.04267869501534898200f,
|
||||||
|
0.07724474282951483700f,
|
||||||
|
0.96609875058711103000f,
|
||||||
|
-0.01641812005088002400f,
|
||||||
|
-0.00427135103965109450f,
|
||||||
|
0.00726225824406205160f,
|
||||||
|
-0.00544188094946287510f,
|
||||||
|
0.00266742068076876060f
|
||||||
|
},
|
||||||
|
{
|
||||||
|
-0.00207886551285772290f,
|
||||||
|
0.00866090598717600930f,
|
||||||
|
-0.02161960909069559500f,
|
||||||
|
0.04383507935997314800f,
|
||||||
|
-0.08302470868585065700f,
|
||||||
|
0.18738870090358245000f,
|
||||||
|
0.93524350914423104000f,
|
||||||
|
-0.09031872116141286000f,
|
||||||
|
0.02909509423931267600f,
|
||||||
|
-0.00897188476756275060f,
|
||||||
|
0.00178311012364952820f,
|
||||||
|
0.00010586149691723067f
|
||||||
|
},
|
||||||
|
{
|
||||||
|
-0.00287519800425638110f,
|
||||||
|
0.01143197533872717000f,
|
||||||
|
-0.02889142869399521600f,
|
||||||
|
0.06060641890050100900f,
|
||||||
|
-0.12152802242786863000f,
|
||||||
|
0.30933747340895279000f,
|
||||||
|
0.87539536840978205000f,
|
||||||
|
-0.14271415809850990000f,
|
||||||
|
0.05516985095031713000f,
|
||||||
|
-0.02205265100214613000f,
|
||||||
|
0.00761119378345958850f,
|
||||||
|
-0.00187713739944610450f
|
||||||
|
},
|
||||||
|
{
|
||||||
|
-0.00354120720771153910f,
|
||||||
|
0.01351098086300389300f,
|
||||||
|
-0.03433664370844288100f,
|
||||||
|
0.07367662235517660800f,
|
||||||
|
-0.15398027155782226000f,
|
||||||
|
0.43728178746780866000f,
|
||||||
|
0.79013921003423337000f,
|
||||||
|
-0.17341770937821352000f,
|
||||||
|
0.07263788052016696700f,
|
||||||
|
-0.03120859084480779800f,
|
||||||
|
0.01170664402374247200f,
|
||||||
|
-0.00319259334815649940f
|
||||||
|
},
|
||||||
|
{
|
||||||
|
-0.00391755659664638590f,
|
||||||
|
0.01447751287549226700f,
|
||||||
|
-0.03701682481313090000f,
|
||||||
|
0.08107302414568577600f,
|
||||||
|
-0.17606165300033697000f,
|
||||||
|
0.56464344237183917000f,
|
||||||
|
0.68451472884717957000f,
|
||||||
|
-0.18369620562420094000f,
|
||||||
|
0.08111657494320076400f,
|
||||||
|
-0.03614676421513295800f,
|
||||||
|
0.01396276906259418800f,
|
||||||
|
-0.00384568128202934270f
|
||||||
|
},
|
||||||
|
{
|
||||||
|
-0.00384568128202934270f,
|
||||||
|
0.01396276906259418800f,
|
||||||
|
-0.03614676421513295800f,
|
||||||
|
0.08111657494320076400f,
|
||||||
|
-0.18369620562420094000f,
|
||||||
|
0.68451472884717957000f,
|
||||||
|
0.56464344237183917000f,
|
||||||
|
-0.17606165300033697000f,
|
||||||
|
0.08107302414568577600f,
|
||||||
|
-0.03701682481313090000f,
|
||||||
|
0.01447751287549226700f,
|
||||||
|
-0.00391755659664638590f
|
||||||
|
},
|
||||||
|
{
|
||||||
|
-0.00319259334815649940f,
|
||||||
|
0.01170664402374247200f,
|
||||||
|
-0.03120859084480779800f,
|
||||||
|
0.07263788052016696700f,
|
||||||
|
-0.17341770937821352000f,
|
||||||
|
0.79013921003423337000f,
|
||||||
|
0.43728178746780866000f,
|
||||||
|
-0.15398027155782226000f,
|
||||||
|
0.07367662235517660800f,
|
||||||
|
-0.03433664370844288100f,
|
||||||
|
0.01351098086300389300f,
|
||||||
|
-0.00354120720771153910f
|
||||||
|
},
|
||||||
|
{
|
||||||
|
-0.00187713739944610450f,
|
||||||
|
0.00761119378345958850f,
|
||||||
|
-0.02205265100214613000f,
|
||||||
|
0.05516985095031713000f,
|
||||||
|
-0.14271415809850990000f,
|
||||||
|
0.87539536840978205000f,
|
||||||
|
0.30933747340895279000f,
|
||||||
|
-0.12152802242786863000f,
|
||||||
|
0.06060641890050100900f,
|
||||||
|
-0.02889142869399521600f,
|
||||||
|
0.01143197533872717000f,
|
||||||
|
-0.00287519800425638110f
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0.00010586149691723067f,
|
||||||
|
0.00178311012364952820f,
|
||||||
|
-0.00897188476756275060f,
|
||||||
|
0.02909509423931267600f,
|
||||||
|
-0.09031872116141286000f,
|
||||||
|
0.93524350914423104000f,
|
||||||
|
0.18738870090358245000f,
|
||||||
|
-0.08302470868585065700f,
|
||||||
|
0.04383507935997314800f,
|
||||||
|
-0.02161960909069559500f,
|
||||||
|
0.00866090598717600930f,
|
||||||
|
-0.00207886551285772290f
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0.00266742068076876060f,
|
||||||
|
-0.00544188094946287510f,
|
||||||
|
0.00726225824406205160f,
|
||||||
|
-0.00427135103965109450f,
|
||||||
|
-0.01641812005088002400f,
|
||||||
|
0.96609875058711103000f,
|
||||||
|
0.07724474282951483700f,
|
||||||
|
-0.04267869501534898200f,
|
||||||
|
0.02541150940858524100f,
|
||||||
|
-0.01349857823816511800f,
|
||||||
|
0.00561586829442904840f,
|
||||||
|
-0.00129181992672801360f
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* _RESAMPLEFILTER_H_ */
|
50
src/resamplefilter.m
Executable file
50
src/resamplefilter.m
Executable file
|
@ -0,0 +1,50 @@
|
||||||
|
%/******************************************************************************\
|
||||||
|
% * Copyright (c) 2004-2006
|
||||||
|
% *
|
||||||
|
% * Author(s):
|
||||||
|
% * Volker Fischer
|
||||||
|
% *
|
||||||
|
%\******************************************************************************/
|
||||||
|
|
||||||
|
% Filter for ratios close to 1 -------------------------------------------------
|
||||||
|
% Fixed for sample-rate conversiones of R ~ 1
|
||||||
|
I = 10; % D = I
|
||||||
|
% Number of taps per poly-phase
|
||||||
|
NoTapsP = 12;
|
||||||
|
% Cut-off frequency
|
||||||
|
fc = 0.97 / I;
|
||||||
|
% MMSE filter-design and windowing
|
||||||
|
h = I * firls(I * NoTapsP - 1, [0 fc fc 1], [1 1 0 0]) .* kaiser(I * NoTapsP, 5)';
|
||||||
|
|
||||||
|
|
||||||
|
% Export coefficiants to file ****************************************
|
||||||
|
fid = fopen('resamplefilter.h', 'w');
|
||||||
|
|
||||||
|
fprintf(fid, '/* Automatically generated file with MATLAB */\n');
|
||||||
|
fprintf(fid, '/* File name: "ResampleFilter.m" */\n');
|
||||||
|
fprintf(fid, '/* Filter taps in time-domain */\n\n');
|
||||||
|
|
||||||
|
fprintf(fid, '#ifndef _RESAMPLEFILTER_H_\n');
|
||||||
|
fprintf(fid, '#define _RESAMPLEFILTER_H_\n\n');
|
||||||
|
|
||||||
|
fprintf(fid, '#define NUM_TAPS_PER_PHASE ');
|
||||||
|
fprintf(fid, int2str(NoTapsP));
|
||||||
|
fprintf(fid, '\n');
|
||||||
|
fprintf(fid, '#define INTERP_DECIM_I_D ');
|
||||||
|
fprintf(fid, int2str(I));
|
||||||
|
fprintf(fid, '\n\n\n');
|
||||||
|
|
||||||
|
% Write filter taps
|
||||||
|
fprintf(fid, '/* Filter for ratios close to 1 */\n');
|
||||||
|
fprintf(fid, 'static float fResTaps1To1[INTERP_DECIM_I_D][NUM_TAPS_PER_PHASE] = {\n');
|
||||||
|
for i = 1:I
|
||||||
|
hTemp = h(i:I:end) ;
|
||||||
|
fprintf(fid, '{\n');
|
||||||
|
fprintf(fid, ' %.20ff,\n', hTemp(1:end - 1));
|
||||||
|
fprintf(fid, ' %.20ff\n', hTemp(end));
|
||||||
|
fprintf(fid, '},\n');
|
||||||
|
end
|
||||||
|
fprintf(fid, '};\n\n\n');
|
||||||
|
|
||||||
|
fprintf(fid, '#endif /* _RESAMPLEFILTER_H_ */\n');
|
||||||
|
fclose(fid);
|
113
src/server.cpp
Executable file
113
src/server.cpp
Executable file
|
@ -0,0 +1,113 @@
|
||||||
|
/******************************************************************************\
|
||||||
|
* Copyright (c) 2004-2006
|
||||||
|
*
|
||||||
|
* Author(s):
|
||||||
|
* Volker Fischer
|
||||||
|
*
|
||||||
|
******************************************************************************
|
||||||
|
*
|
||||||
|
* 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.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
\******************************************************************************/
|
||||||
|
|
||||||
|
#include "server.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Implementation *************************************************************/
|
||||||
|
CServer::CServer() : Socket(&ChannelSet)
|
||||||
|
{
|
||||||
|
vecsSendData.Init(BLOCK_SIZE_SAMPLES);
|
||||||
|
|
||||||
|
/* init moving average buffer for response time evaluation */
|
||||||
|
RespTimeMoAvBuf.Init(LEN_MOV_AV_RESPONSE);
|
||||||
|
|
||||||
|
/* connect timer timeout signal */
|
||||||
|
QObject::connect(&Timer, SIGNAL(timeout()), this, SLOT(OnTimer()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CServer::Start()
|
||||||
|
{
|
||||||
|
/* start main timer */
|
||||||
|
Timer.start(BLOCK_DURATION_MS);
|
||||||
|
|
||||||
|
/* init time for response time evaluation */
|
||||||
|
TimeLastBlock = QTime::currentTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CServer::OnTimer()
|
||||||
|
{
|
||||||
|
CVector<int> vecChanID;
|
||||||
|
CVector<CVector<double> > vecvecdData(BLOCK_SIZE_SAMPLES);
|
||||||
|
|
||||||
|
/* get data from all connected clients */
|
||||||
|
ChannelSet.GetBlockAllConC(vecChanID, vecvecdData);
|
||||||
|
|
||||||
|
/* actual processing of audio data -> mix */
|
||||||
|
vecsSendData = ProcessData(vecvecdData);
|
||||||
|
|
||||||
|
/* get number of connected clients from vector size */
|
||||||
|
const int iNumClients = vecvecdData.Size();
|
||||||
|
|
||||||
|
/* send the same data to all connected clients */
|
||||||
|
for (int i = 0; i < iNumClients; i++)
|
||||||
|
{
|
||||||
|
Socket.SendPacket(ChannelSet.PrepSendPacket(vecChanID[i], vecsSendData),
|
||||||
|
ChannelSet.GetAddress(vecChanID[i]),
|
||||||
|
ChannelSet.GetTimeStampIdx(vecChanID[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* update response time measurement ------------------------------------- */
|
||||||
|
/* add time difference */
|
||||||
|
const QTime CurTime = QTime::currentTime();
|
||||||
|
|
||||||
|
/* we want to calculate the standard deviation (we assume that the mean is
|
||||||
|
correct at the block period time) */
|
||||||
|
const double dCurAddVal =
|
||||||
|
((double) TimeLastBlock.msecsTo(CurTime) - BLOCK_DURATION_MS);
|
||||||
|
|
||||||
|
RespTimeMoAvBuf.Add(dCurAddVal * dCurAddVal); /* add squared value */
|
||||||
|
|
||||||
|
/* store old time value */
|
||||||
|
TimeLastBlock = CurTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
CVector<short> CServer::ProcessData(CVector<CVector<double> >& vecvecdData)
|
||||||
|
{
|
||||||
|
CVector<short> vecsOutData;
|
||||||
|
vecsOutData.Init(BLOCK_SIZE_SAMPLES);
|
||||||
|
|
||||||
|
const int iNumClients = vecvecdData.Size();
|
||||||
|
|
||||||
|
/* we normalize with sqrt() of N to avoid that the level drops too much
|
||||||
|
in case that a new client connects */
|
||||||
|
const double dNorm = sqrt((double) iNumClients);
|
||||||
|
|
||||||
|
/* mix all audio data from all clients together */
|
||||||
|
for (int i = 0; i < BLOCK_SIZE_SAMPLES; i++)
|
||||||
|
{
|
||||||
|
double dMixedData = 0.0;
|
||||||
|
|
||||||
|
for (int j = 0; j < iNumClients; j++)
|
||||||
|
{
|
||||||
|
dMixedData += vecvecdData[j][i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* normalization and truncating to short */
|
||||||
|
vecsOutData[i] = Double2Short(dMixedData / dNorm);
|
||||||
|
}
|
||||||
|
|
||||||
|
return vecsOutData;
|
||||||
|
}
|
78
src/server.h
Executable file
78
src/server.h
Executable file
|
@ -0,0 +1,78 @@
|
||||||
|
/******************************************************************************\
|
||||||
|
* Copyright (c) 2004-2006
|
||||||
|
*
|
||||||
|
* Author(s):
|
||||||
|
* Volker Fischer
|
||||||
|
*
|
||||||
|
******************************************************************************
|
||||||
|
*
|
||||||
|
* 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.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
\******************************************************************************/
|
||||||
|
|
||||||
|
#if !defined(SERVER_HOIHGE7LOKIH83JH8_3_43445KJIUHF1912__INCLUDED_)
|
||||||
|
#define SERVER_HOIHGE7LOKIH83JH8_3_43445KJIUHF1912__INCLUDED_
|
||||||
|
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qtimer.h>
|
||||||
|
#include <qdatetime.h>
|
||||||
|
#include <qhostaddress.h>
|
||||||
|
#include <qdatetime.h>
|
||||||
|
#include "global.h"
|
||||||
|
#include "socket.h"
|
||||||
|
#include "channel.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Classes ********************************************************************/
|
||||||
|
class CServer : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
CServer();
|
||||||
|
virtual ~CServer() {}
|
||||||
|
|
||||||
|
void Start();
|
||||||
|
void GetConCliParam(CVector<CHostAddress>& vecHostAddresses,
|
||||||
|
CVector<double>& vecdSamOffs)
|
||||||
|
{ChannelSet.GetConCliParam(vecHostAddresses, vecdSamOffs);}
|
||||||
|
|
||||||
|
/* we want to return the standard deviation. For that we need to calculate
|
||||||
|
the sqaure root */
|
||||||
|
double GetTimingStdDev() {return sqrt(RespTimeMoAvBuf.GetAverage());}
|
||||||
|
|
||||||
|
CChannelSet* GetChannelSet() {return &ChannelSet;}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
CVector<short> ProcessData(CVector<CVector<double> >& vecvecdData);
|
||||||
|
|
||||||
|
QTimer Timer;
|
||||||
|
CVector<short> vecsSendData;
|
||||||
|
|
||||||
|
/* actual working objects */
|
||||||
|
CChannelSet ChannelSet;
|
||||||
|
CSocket Socket;
|
||||||
|
|
||||||
|
/* debugging, evaluating */
|
||||||
|
CMovingAv<double> RespTimeMoAvBuf;
|
||||||
|
QTime TimeLastBlock;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void OnTimer();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* !defined(SERVER_HOIHGE7LOKIH83JH8_3_43445KJIUHF1912__INCLUDED_) */
|
378
src/settings.cpp
Executable file
378
src/settings.cpp
Executable file
|
@ -0,0 +1,378 @@
|
||||||
|
/******************************************************************************\
|
||||||
|
* Copyright (c) 2004-2006
|
||||||
|
*
|
||||||
|
* Author(s):
|
||||||
|
* Volker Fischer, Robert Kesterson
|
||||||
|
*
|
||||||
|
******************************************************************************
|
||||||
|
*
|
||||||
|
* 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.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
\******************************************************************************/
|
||||||
|
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Implementation *************************************************************/
|
||||||
|
void CSettings::Load ()
|
||||||
|
{
|
||||||
|
/* load settings from init-file */
|
||||||
|
ReadIniFile ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSettings::Save()
|
||||||
|
{
|
||||||
|
/* write settings in init-file */
|
||||||
|
WriteIniFile ();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Read and write init-file ***************************************************/
|
||||||
|
void CSettings::ReadIniFile()
|
||||||
|
{
|
||||||
|
int iValue;
|
||||||
|
bool bValue;
|
||||||
|
|
||||||
|
/* Load data from init-file */
|
||||||
|
INIFile ini = LoadIni ( LLCON_INIT_FILE_NAME );
|
||||||
|
|
||||||
|
|
||||||
|
// IP address
|
||||||
|
pClient->strIPAddress = GetIniSetting ( ini, "Client", "ipaddress" );
|
||||||
|
|
||||||
|
// audio fader
|
||||||
|
if ( GetNumericIniSet(ini, "Client", "audfad", 0, AUD_FADER_IN_MAX, iValue ) == TRUE ) {
|
||||||
|
pClient->SetAudioInFader ( iValue );
|
||||||
|
}
|
||||||
|
|
||||||
|
// reverberation level
|
||||||
|
if ( GetNumericIniSet(ini, "Client", "revlev", 0, AUD_REVERB_MAX, iValue ) == TRUE ) {
|
||||||
|
pClient->SetReverbLevel ( iValue );
|
||||||
|
}
|
||||||
|
|
||||||
|
// reverberation channel assignment
|
||||||
|
if ( GetFlagIniSet(ini, "Client", "reverblchan", bValue ) == TRUE ) {
|
||||||
|
pClient->SetReverbOnLeftChan ( bValue );
|
||||||
|
}
|
||||||
|
|
||||||
|
// sound card in number of buffers
|
||||||
|
if ( GetNumericIniSet(ini, "Client", "audinbuf", 0, AUD_SLIDER_LENGTH, iValue ) == TRUE ) {
|
||||||
|
pClient->GetSndInterface()->SetInNumBuf( iValue );
|
||||||
|
}
|
||||||
|
|
||||||
|
// sound card out number of buffers
|
||||||
|
if ( GetNumericIniSet(ini, "Client", "audoutbuf", 0, AUD_SLIDER_LENGTH, iValue ) == TRUE ) {
|
||||||
|
pClient->GetSndInterface()->SetOutNumBuf ( iValue );
|
||||||
|
}
|
||||||
|
|
||||||
|
// network jitter buffer size
|
||||||
|
if ( GetNumericIniSet(ini, "Client", "jitbuf", 0, MAX_NET_BUF_SIZE_NUM_BL, iValue ) == TRUE ) {
|
||||||
|
pClient->GetChannel()->SetSockBufSize ( MIN_BLOCK_SIZE_SAMPLES, iValue );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSettings::WriteIniFile()
|
||||||
|
{
|
||||||
|
INIFile ini;
|
||||||
|
|
||||||
|
// IP address
|
||||||
|
PutIniSetting ( ini, "Client", "ipaddress", pClient->strIPAddress.c_str() );
|
||||||
|
|
||||||
|
// audio fader
|
||||||
|
SetNumericIniSet ( ini, "Client", "audfad", pClient->GetAudioInFader () );
|
||||||
|
|
||||||
|
// reverberation level
|
||||||
|
SetNumericIniSet ( ini, "Client", "revlev", pClient->GetReverbLevel () );
|
||||||
|
|
||||||
|
// reverberation channel assignment
|
||||||
|
SetFlagIniSet ( ini, "Client", "reverblchan", pClient->IsReverbOnLeftChan () );
|
||||||
|
|
||||||
|
// sound card in number of buffers
|
||||||
|
SetNumericIniSet ( ini, "Client", "audinbuf", pClient->GetSndInterface()->GetInNumBuf () );
|
||||||
|
|
||||||
|
// sound card out number of buffers
|
||||||
|
SetNumericIniSet ( ini, "Client", "audoutbuf", pClient->GetSndInterface()->GetOutNumBuf () );
|
||||||
|
|
||||||
|
// network jitter buffer size
|
||||||
|
SetNumericIniSet ( ini, "Client", "jitbuf", pClient->GetChannel()->GetSockBufSize () );
|
||||||
|
|
||||||
|
|
||||||
|
/* Save settings in init-file */
|
||||||
|
SaveIni ( ini, LLCON_INIT_FILE_NAME );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSettings::GetNumericIniSet ( INIFile& theINI, string strSection,
|
||||||
|
string strKey, int iRangeStart,
|
||||||
|
int iRangeStop, int& iValue )
|
||||||
|
{
|
||||||
|
/* Init return value */
|
||||||
|
bool bReturn = FALSE;
|
||||||
|
|
||||||
|
const string strGetIni =
|
||||||
|
GetIniSetting ( theINI, strSection.c_str (), strKey.c_str () );
|
||||||
|
|
||||||
|
/* Check if it is a valid parameter */
|
||||||
|
if ( !strGetIni.empty () )
|
||||||
|
{
|
||||||
|
iValue = atoi( strGetIni.c_str () );
|
||||||
|
|
||||||
|
/* Check range */
|
||||||
|
if ( ( iValue >= iRangeStart ) && ( iValue <= iRangeStop ) )
|
||||||
|
{
|
||||||
|
bReturn = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSettings::SetNumericIniSet ( INIFile& theINI, string strSection,
|
||||||
|
string strKey, int iValue )
|
||||||
|
{
|
||||||
|
char cString[256];
|
||||||
|
|
||||||
|
sprintf ( cString, "%d", iValue );
|
||||||
|
PutIniSetting ( theINI, strSection.c_str (), strKey.c_str (), cString );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSettings::GetFlagIniSet ( INIFile& theINI, string strSection,
|
||||||
|
string strKey, bool& bValue )
|
||||||
|
{
|
||||||
|
/* Init return value */
|
||||||
|
bool bReturn = FALSE;
|
||||||
|
|
||||||
|
const string strGetIni =
|
||||||
|
GetIniSetting ( theINI, strSection.c_str (), strKey.c_str () );
|
||||||
|
|
||||||
|
if ( !strGetIni.empty () )
|
||||||
|
{
|
||||||
|
if ( atoi ( strGetIni.c_str () ) )
|
||||||
|
{
|
||||||
|
bValue = TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bValue = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bReturn = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSettings::SetFlagIniSet ( INIFile& theINI, string strSection, string strKey,
|
||||||
|
bool bValue )
|
||||||
|
{
|
||||||
|
if ( bValue == TRUE )
|
||||||
|
{
|
||||||
|
PutIniSetting ( theINI, strSection.c_str (), strKey.c_str (), "1" );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PutIniSetting ( theINI, strSection.c_str (), strKey.c_str (), "0" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* INI File routines using the STL ********************************************/
|
||||||
|
/* The following code was taken from "INI File Tools (STLINI)" written by
|
||||||
|
Robert Kesterson in 1999. The original files are stlini.cpp and stlini.h.
|
||||||
|
The homepage is http://robertk.com/source
|
||||||
|
|
||||||
|
Copyright August 18, 1999 by Robert Kesterson */
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
/* These pragmas are to quiet VC++ about the expanded template identifiers
|
||||||
|
exceeding 255 chars. You won't be able to see those variables in a debug
|
||||||
|
session, but the code will run normally */
|
||||||
|
#pragma warning (push)
|
||||||
|
#pragma warning (disable : 4786 4503)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
string CSettings::GetIniSetting(CSettings::INIFile& theINI, const char* section,
|
||||||
|
const char* key, const char* defaultval)
|
||||||
|
{
|
||||||
|
string result(defaultval);
|
||||||
|
INIFile::iterator iSection = theINI.find(string(section));
|
||||||
|
|
||||||
|
if (iSection != theINI.end())
|
||||||
|
{
|
||||||
|
INISection::iterator apair = iSection->second.find(string(key));
|
||||||
|
|
||||||
|
if (apair != iSection->second.end())
|
||||||
|
result = apair->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSettings::PutIniSetting(CSettings::INIFile &theINI, const char *section,
|
||||||
|
const char *key, const char *value)
|
||||||
|
{
|
||||||
|
INIFile::iterator iniSection;
|
||||||
|
INISection::iterator apair;
|
||||||
|
|
||||||
|
if ((iniSection = theINI.find(string(section))) == theINI.end())
|
||||||
|
{
|
||||||
|
/* No such section? Then add one */
|
||||||
|
INISection newsection;
|
||||||
|
if (key)
|
||||||
|
{
|
||||||
|
newsection.insert(
|
||||||
|
std::pair<std::string, string>(string(key), string(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
theINI.insert(
|
||||||
|
std::pair<string, INISection>(string(section), newsection));
|
||||||
|
}
|
||||||
|
else if (key)
|
||||||
|
{
|
||||||
|
/* Found section, make sure key isn't in there already,
|
||||||
|
if it is, just drop and re-add */
|
||||||
|
apair = iniSection->second.find(string(key));
|
||||||
|
if (apair != iniSection->second.end())
|
||||||
|
iniSection->second.erase(apair);
|
||||||
|
|
||||||
|
iniSection->second.insert(
|
||||||
|
std::pair<string, string>(string(key), string(value)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CSettings::INIFile CSettings::LoadIni(const char* filename)
|
||||||
|
{
|
||||||
|
INIFile theINI;
|
||||||
|
char *value, *temp;
|
||||||
|
string section;
|
||||||
|
char buffer[MAX_INI_LINE];
|
||||||
|
std::fstream file(filename, std::ios::in);
|
||||||
|
|
||||||
|
while (file.good())
|
||||||
|
{
|
||||||
|
memset(buffer, 0, sizeof(buffer));
|
||||||
|
file.getline(buffer, sizeof(buffer));
|
||||||
|
|
||||||
|
if ((temp = strchr(buffer, '\n')))
|
||||||
|
*temp = '\0'; /* Cut off at newline */
|
||||||
|
|
||||||
|
if ((temp = strchr(buffer, '\r')))
|
||||||
|
*temp = '\0'; /* Cut off at linefeeds */
|
||||||
|
|
||||||
|
if ((buffer[0] == '[') && (temp = strrchr(buffer, ']')))
|
||||||
|
{ /* if line is like --> [section name] */
|
||||||
|
*temp = '\0'; /* Chop off the trailing ']' */
|
||||||
|
section = &buffer[1];
|
||||||
|
PutIniSetting(theINI, &buffer[1]); /* Start new section */
|
||||||
|
}
|
||||||
|
else if (buffer[0] && (value = strchr(buffer, '=')))
|
||||||
|
{
|
||||||
|
/* Assign whatever follows = sign to value, chop at "=" */
|
||||||
|
*value++ = '\0';
|
||||||
|
|
||||||
|
/* And add both sides to INISection */
|
||||||
|
PutIniSetting(theINI, section.c_str(), buffer, value);
|
||||||
|
}
|
||||||
|
else if (buffer[0])
|
||||||
|
{
|
||||||
|
/* Must be a comment or something */
|
||||||
|
PutIniSetting(theINI, section.c_str(), buffer, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return theINI;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSettings::SaveIni(CSettings::INIFile &theINI, const char* filename)
|
||||||
|
{
|
||||||
|
bool bFirstSection = TRUE; /* Init flag */
|
||||||
|
|
||||||
|
std::fstream file(filename, std::ios::out);
|
||||||
|
if(!file.good())
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Just iterate the hashes and values and dump them to a file */
|
||||||
|
INIFile::iterator section = theINI.begin();
|
||||||
|
while (section != theINI.end())
|
||||||
|
{
|
||||||
|
if (section->first > "")
|
||||||
|
{
|
||||||
|
if (bFirstSection == TRUE)
|
||||||
|
{
|
||||||
|
/* Don't put a newline at the beginning of the first section */
|
||||||
|
file << "[" << section->first << "]" << std::endl;
|
||||||
|
|
||||||
|
/* Reset flag */
|
||||||
|
bFirstSection = FALSE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
file << std::endl << "[" << section->first << "]" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
INISection::iterator pair = section->second.begin();
|
||||||
|
|
||||||
|
while (pair != section->second.end())
|
||||||
|
{
|
||||||
|
if (pair->second > "")
|
||||||
|
file << pair->first << "=" << pair->second << std::endl;
|
||||||
|
else
|
||||||
|
file << pair->first << "=" << std::endl;
|
||||||
|
pair++;
|
||||||
|
}
|
||||||
|
section++;
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return true or false depending on whether the first string is less than the
|
||||||
|
second */
|
||||||
|
bool CSettings::StlIniCompareStringNoCase::operator()(const string& x,
|
||||||
|
const string& y) const
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
return (stricmp(x.c_str(), y.c_str()) < 0) ? true : false;
|
||||||
|
#else
|
||||||
|
#ifdef strcasecmp
|
||||||
|
return (strcasecmp(x.c_str(), y.c_str()) < 0) ? true : false;
|
||||||
|
#else
|
||||||
|
unsigned nCount = 0;
|
||||||
|
int nResult = 0;
|
||||||
|
const char *p1 = x.c_str();
|
||||||
|
const char *p2 = y.c_str();
|
||||||
|
|
||||||
|
while (*p1 && *p2)
|
||||||
|
{
|
||||||
|
nResult = toupper(*p1) - toupper(*p2);
|
||||||
|
if (nResult != 0)
|
||||||
|
break;
|
||||||
|
p1++;
|
||||||
|
p2++;
|
||||||
|
nCount++;
|
||||||
|
}
|
||||||
|
if (nResult == 0)
|
||||||
|
{
|
||||||
|
if (*p1 && !*p2)
|
||||||
|
nResult = -1;
|
||||||
|
if (!*p1 && *p2)
|
||||||
|
nResult = 1;
|
||||||
|
}
|
||||||
|
if (nResult < 0)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
#endif /* strcasecmp */
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
88
src/settings.h
Executable file
88
src/settings.h
Executable file
|
@ -0,0 +1,88 @@
|
||||||
|
/******************************************************************************\
|
||||||
|
* Copyright (c) 2004-2006
|
||||||
|
*
|
||||||
|
* Author(s):
|
||||||
|
* Volker Fischer, Robert Kesterson
|
||||||
|
*
|
||||||
|
******************************************************************************
|
||||||
|
*
|
||||||
|
* 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.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
\******************************************************************************/
|
||||||
|
|
||||||
|
#if !defined(SETTINGS_H__3B0BA660_DGEG56G456G9876D31912__INCLUDED_)
|
||||||
|
#define SETTINGS_H__3B0BA660_DGEG56G456G9876D31912__INCLUDED_
|
||||||
|
|
||||||
|
#include "global.h"
|
||||||
|
#include "client.h"
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
|
||||||
|
/* Definitions ****************************************************************/
|
||||||
|
// name of the init-file
|
||||||
|
#define LLCON_INIT_FILE_NAME "llcon.ini"
|
||||||
|
|
||||||
|
/* change this if you expect to have huge lines in your INI files. Note that
|
||||||
|
this is the max size of a single line, NOT the max number of lines */
|
||||||
|
#define MAX_INI_LINE 500
|
||||||
|
|
||||||
|
|
||||||
|
/* Classes ********************************************************************/
|
||||||
|
class CSettings
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CSettings ( CClient* pNCliP ) : pClient ( pNCliP ) {}
|
||||||
|
|
||||||
|
void Load ();
|
||||||
|
void Save ();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void ReadIniFile ();
|
||||||
|
void WriteIniFile ();
|
||||||
|
|
||||||
|
/* Function declarations for stlini code written by Robert Kesterson */
|
||||||
|
struct StlIniCompareStringNoCase
|
||||||
|
{
|
||||||
|
bool operator () ( const std::string& x, const std::string& y ) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* These typedefs just make the code a bit more readable */
|
||||||
|
typedef std::map<string, string, StlIniCompareStringNoCase > INISection;
|
||||||
|
typedef std::map<string, INISection , StlIniCompareStringNoCase > INIFile;
|
||||||
|
|
||||||
|
string GetIniSetting( INIFile& theINI, const char* pszSection,
|
||||||
|
const char* pszKey, const char* pszDefaultVal = "" );
|
||||||
|
void PutIniSetting ( INIFile &theINI, const char *pszSection,
|
||||||
|
const char* pszKey = NULL, const char* pszValue = "" );
|
||||||
|
void SaveIni ( INIFile& theINI, const char* pszFilename );
|
||||||
|
INIFile LoadIni ( const char* pszFilename );
|
||||||
|
|
||||||
|
|
||||||
|
void SetNumericIniSet ( INIFile& theINI, string strSection, string strKey,
|
||||||
|
int iValue );
|
||||||
|
bool GetNumericIniSet ( INIFile& theINI, string strSection, string strKey,
|
||||||
|
int iRangeStart, int iRangeStop, int& iValue );
|
||||||
|
void SetFlagIniSet ( INIFile& theINI, string strSection, string strKey,
|
||||||
|
bool bValue );
|
||||||
|
bool GetFlagIniSet ( INIFile& theINI, string strSection, string strKey,
|
||||||
|
bool& bValue );
|
||||||
|
|
||||||
|
/* Pointer to the client object needed for the various settings */
|
||||||
|
CClient* pClient;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // !defined(SETTINGS_H__3B0BA660_DGEG56G456G9876D31912__INCLUDED_)
|
121
src/socket.cpp
Executable file
121
src/socket.cpp
Executable file
|
@ -0,0 +1,121 @@
|
||||||
|
/******************************************************************************\
|
||||||
|
* Copyright (c) 2004-2006
|
||||||
|
*
|
||||||
|
* Author(s):
|
||||||
|
* Volker Fischer
|
||||||
|
*
|
||||||
|
******************************************************************************
|
||||||
|
*
|
||||||
|
* 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.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
\******************************************************************************/
|
||||||
|
|
||||||
|
#include "socket.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Implementation *************************************************************/
|
||||||
|
void CSocket::Init()
|
||||||
|
{
|
||||||
|
/* allocate memory for network receive and send buffer in samples */
|
||||||
|
vecbyRecBuf.Init(MAX_SIZE_BYTES_NETW_BUF);
|
||||||
|
|
||||||
|
/* initialize the listening socket */
|
||||||
|
bool bSuccess = SocketDevice.bind(
|
||||||
|
QHostAddress((Q_UINT32) 0) /* INADDR_ANY */, LLCON_PORT_NUMBER);
|
||||||
|
|
||||||
|
if (bIsClient)
|
||||||
|
{
|
||||||
|
/* if no success, try if server is on same machine (only for client) */
|
||||||
|
if (!bSuccess)
|
||||||
|
{
|
||||||
|
/* if server and client is on same machine, decrease port number by
|
||||||
|
one by definition */
|
||||||
|
bSuccess =
|
||||||
|
SocketDevice.bind(QHostAddress((Q_UINT32) 0) /* INADDR_ANY */,
|
||||||
|
LLCON_PORT_NUMBER - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bSuccess)
|
||||||
|
{
|
||||||
|
/* show error message */
|
||||||
|
QMessageBox::critical(0, "Network Error", "Cannot bind the socket.",
|
||||||
|
QMessageBox::Ok, QMessageBox::NoButton);
|
||||||
|
|
||||||
|
/* exit application */
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
QSocketNotifier* pSocketNotivRead =
|
||||||
|
new QSocketNotifier(SocketDevice.socket(), QSocketNotifier::Read);
|
||||||
|
|
||||||
|
/* connect the "activated" signal */
|
||||||
|
QObject::connect(pSocketNotivRead, SIGNAL(activated(int)),
|
||||||
|
this, SLOT(OnDataReceived()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSocket::SendPacket(const CVector<unsigned char>& vecbySendBuf,
|
||||||
|
const CHostAddress& HostAddr, const int iTimeStampIdx)
|
||||||
|
{
|
||||||
|
const int iVecSizeOut = vecbySendBuf.Size();
|
||||||
|
|
||||||
|
if ( iVecSizeOut != 0 )
|
||||||
|
{
|
||||||
|
/* send packet through network */
|
||||||
|
SocketDevice.writeBlock ((const char*) &((CVector<unsigned char>) vecbySendBuf)[0],
|
||||||
|
iVecSizeOut, HostAddr.InetAddr, HostAddr.iPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* sent time stamp if required */
|
||||||
|
if (iTimeStampIdx != INVALID_TIME_STAMP_IDX)
|
||||||
|
{
|
||||||
|
/* Always one byte long */
|
||||||
|
SocketDevice.writeBlock((const char*) &iTimeStampIdx, 1,
|
||||||
|
HostAddr.InetAddr, HostAddr.iPort);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSocket::OnDataReceived()
|
||||||
|
{
|
||||||
|
/* read block from network interface */
|
||||||
|
const int iNumBytesRead = SocketDevice.readBlock((char*) &vecbyRecBuf[0],
|
||||||
|
MAX_SIZE_BYTES_NETW_BUF);
|
||||||
|
|
||||||
|
/* check if an error occurred */
|
||||||
|
if (iNumBytesRead < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* get host address of client */
|
||||||
|
CHostAddress RecHostAddr(SocketDevice.peerAddress(),
|
||||||
|
SocketDevice.peerPort());
|
||||||
|
|
||||||
|
if (bIsClient)
|
||||||
|
{
|
||||||
|
/* client */
|
||||||
|
/* check if packet comes from the server we want to connect */
|
||||||
|
if (!(pChannel->GetAddress() == RecHostAddr))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (pChannel->PutData(vecbyRecBuf, iNumBytesRead))
|
||||||
|
PostWinMessage(MS_JIT_BUF_PUT, MUL_COL_LED_GREEN);
|
||||||
|
else
|
||||||
|
PostWinMessage(MS_JIT_BUF_PUT, MUL_COL_LED_RED);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* server */
|
||||||
|
pChannelSet->PutData(vecbyRecBuf, iNumBytesRead, RecHostAddr);
|
||||||
|
}
|
||||||
|
}
|
78
src/socket.h
Executable file
78
src/socket.h
Executable file
|
@ -0,0 +1,78 @@
|
||||||
|
/******************************************************************************\
|
||||||
|
* Copyright (c) 2004-2006
|
||||||
|
*
|
||||||
|
* Author(s):
|
||||||
|
* Volker Fischer
|
||||||
|
*
|
||||||
|
******************************************************************************
|
||||||
|
*
|
||||||
|
* 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.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
\******************************************************************************/
|
||||||
|
|
||||||
|
#if !defined(SOCKET_HOIHGE76GEKJH98_3_4344_BB23945IUHF1912__INCLUDED_)
|
||||||
|
#define SOCKET_HOIHGE76GEKJH98_3_4344_BB23945IUHF1912__INCLUDED_
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qmessagebox.h>
|
||||||
|
#include <qsocket.h>
|
||||||
|
#include <qsocketdevice.h>
|
||||||
|
#include <qsocketnotifier.h>
|
||||||
|
#include "global.h"
|
||||||
|
#include "channel.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Definitions ****************************************************************/
|
||||||
|
/* maximum block size for network input buffer. Consider two bytes per sample */
|
||||||
|
#define MAX_SIZE_BYTES_NETW_BUF (BLOCK_SIZE_SAMPLES * 2)
|
||||||
|
|
||||||
|
|
||||||
|
/* Classes ********************************************************************/
|
||||||
|
class CSocket : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
CSocket::CSocket(CChannel* pNewChannel) : pChannel(pNewChannel),
|
||||||
|
SocketDevice(QSocketDevice::Datagram /* UDP */), bIsClient(true)
|
||||||
|
{Init();}
|
||||||
|
CSocket::CSocket(CChannelSet* pNewChannelSet) : pChannelSet(pNewChannelSet),
|
||||||
|
SocketDevice(QSocketDevice::Datagram /* UDP */), bIsClient(false)
|
||||||
|
{Init();}
|
||||||
|
virtual ~CSocket() {}
|
||||||
|
|
||||||
|
void SendPacket(const CVector<unsigned char>& vecbySendBuf,
|
||||||
|
const CHostAddress& HostAddr, const int iTimeStampIdx);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void Init();
|
||||||
|
|
||||||
|
QSocketDevice SocketDevice;
|
||||||
|
|
||||||
|
CVector<unsigned char> vecbyRecBuf;
|
||||||
|
CHostAddress RecHostAddr;
|
||||||
|
|
||||||
|
CChannel* pChannel; /* for client */
|
||||||
|
CChannelSet* pChannelSet; /* for server */
|
||||||
|
bool bIsClient;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void OnDataReceived();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* !defined(SOCKET_HOIHGE76GEKJH98_3_4344_BB23945IUHF1912__INCLUDED_) */
|
276
src/util.cpp
Executable file
276
src/util.cpp
Executable file
|
@ -0,0 +1,276 @@
|
||||||
|
/******************************************************************************\
|
||||||
|
* Copyright (c) 2004-2006
|
||||||
|
*
|
||||||
|
* Author(s):
|
||||||
|
* Volker Fischer
|
||||||
|
*
|
||||||
|
******************************************************************************
|
||||||
|
*
|
||||||
|
* 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.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
\******************************************************************************/
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Implementation *************************************************************/
|
||||||
|
/* Input level meter implementation ------------------------------------------ */
|
||||||
|
void CSignalLevelMeter::Update(CVector<double>& vecdAudio)
|
||||||
|
{
|
||||||
|
/* Do the update for entire vector, convert to floating-point */
|
||||||
|
const int iVecSize = vecdAudio.Size();
|
||||||
|
|
||||||
|
for (int i = 0; i < iVecSize; i++)
|
||||||
|
{
|
||||||
|
/* norm of current audio sample */
|
||||||
|
const double dCurSig = fabs(vecdAudio[i]);
|
||||||
|
|
||||||
|
/* search for maximum. Decrease this max with time */
|
||||||
|
/* decrease max with time */
|
||||||
|
if (dCurLevel >= METER_FLY_BACK)
|
||||||
|
dCurLevel -= METER_FLY_BACK;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((dCurLevel <= METER_FLY_BACK) && (dCurLevel > 1.0))
|
||||||
|
dCurLevel -= 2.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* search for max */
|
||||||
|
if (dCurSig > dCurLevel)
|
||||||
|
dCurLevel = dCurSig;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double CSignalLevelMeter::MicLevel()
|
||||||
|
{
|
||||||
|
const double dNormMicLevel = dCurLevel / _MAXSHORT;
|
||||||
|
|
||||||
|
/* logarithmic measure */
|
||||||
|
if (dNormMicLevel > 0)
|
||||||
|
return 20.0 * log10(dNormMicLevel);
|
||||||
|
else
|
||||||
|
return -100000.0; /* large negative value */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Global functions implementation ********************************************/
|
||||||
|
void DebugError(const char* pchErDescr, const char* pchPar1Descr,
|
||||||
|
const double dPar1, const char* pchPar2Descr,
|
||||||
|
const double dPar2)
|
||||||
|
{
|
||||||
|
FILE* pFile = fopen("DebugError.dat", "a");
|
||||||
|
fprintf(pFile, pchErDescr); fprintf(pFile, " ### ");
|
||||||
|
fprintf(pFile, pchPar1Descr); fprintf(pFile, ": ");
|
||||||
|
fprintf(pFile, "%e ### ", dPar1);
|
||||||
|
fprintf(pFile, pchPar2Descr); fprintf(pFile, ": ");
|
||||||
|
fprintf(pFile, "%e\n", dPar2);
|
||||||
|
fclose(pFile);
|
||||||
|
printf("\nDebug error! For more information see test/DebugError.dat\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************\
|
||||||
|
* Audio Reverberation *
|
||||||
|
\******************************************************************************/
|
||||||
|
/*
|
||||||
|
The following code is based on "JCRev: John Chowning's reverberator class"
|
||||||
|
by Perry R. Cook and Gary P. Scavone, 1995 - 2004
|
||||||
|
which is in "The Synthesis ToolKit in C++ (STK)"
|
||||||
|
http://ccrma.stanford.edu/software/stk
|
||||||
|
|
||||||
|
Original description:
|
||||||
|
This class is derived from the CLM JCRev function, which is based on the use
|
||||||
|
of networks of simple allpass and comb delay filters. This class implements
|
||||||
|
three series allpass units, followed by four parallel comb filters, and two
|
||||||
|
decorrelation delay lines in parallel at the output.
|
||||||
|
*/
|
||||||
|
CAudioReverb::CAudioReverb(const double rT60)
|
||||||
|
{
|
||||||
|
/* Delay lengths for 44100 Hz sample rate */
|
||||||
|
int lengths[9] = {1777, 1847, 1993, 2137, 389, 127, 43, 211, 179};
|
||||||
|
const double scaler = (double) SAMPLE_RATE / 44100.0;
|
||||||
|
|
||||||
|
int delay, i;
|
||||||
|
if (scaler != 1.0)
|
||||||
|
{
|
||||||
|
for (i = 0; i < 9; i++)
|
||||||
|
{
|
||||||
|
delay = (int) floor(scaler * lengths[i]);
|
||||||
|
|
||||||
|
if ((delay & 1) == 0)
|
||||||
|
delay++;
|
||||||
|
|
||||||
|
while (!isPrime(delay))
|
||||||
|
delay += 2;
|
||||||
|
|
||||||
|
lengths[i] = delay;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 3; i++)
|
||||||
|
allpassDelays_[i].Init(lengths[i + 4]);
|
||||||
|
|
||||||
|
for (i = 0; i < 4; i++)
|
||||||
|
combDelays_[i].Init(lengths[i]);
|
||||||
|
|
||||||
|
setT60(rT60);
|
||||||
|
allpassCoefficient_ = (double) 0.7;
|
||||||
|
Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CAudioReverb::isPrime(const int number)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Returns true if argument value is prime. Taken from "class Effect" in
|
||||||
|
"STK abstract effects parent class".
|
||||||
|
*/
|
||||||
|
if (number == 2)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (number & 1)
|
||||||
|
{
|
||||||
|
for (int i = 3; i < (int) sqrt((double) number) + 1; i += 2)
|
||||||
|
{
|
||||||
|
if ((number % i) == 0)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; /* prime */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false; /* even */
|
||||||
|
}
|
||||||
|
|
||||||
|
void CAudioReverb::Clear()
|
||||||
|
{
|
||||||
|
/* Reset and clear all internal state */
|
||||||
|
allpassDelays_[0].Reset(0);
|
||||||
|
allpassDelays_[1].Reset(0);
|
||||||
|
allpassDelays_[2].Reset(0);
|
||||||
|
combDelays_[0].Reset(0);
|
||||||
|
combDelays_[1].Reset(0);
|
||||||
|
combDelays_[2].Reset(0);
|
||||||
|
combDelays_[3].Reset(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CAudioReverb::setT60(const double rT60)
|
||||||
|
{
|
||||||
|
/* Set the reverberation T60 decay time */
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
combCoefficient_[i] = pow((double) 10.0, (double) (-3.0 *
|
||||||
|
combDelays_[i].Size() / (rT60 * SAMPLE_RATE)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double CAudioReverb::ProcessSample(const double input)
|
||||||
|
{
|
||||||
|
/* Compute one output sample */
|
||||||
|
double temp, temp0, temp1, temp2;
|
||||||
|
|
||||||
|
temp = allpassDelays_[0].Get();
|
||||||
|
temp0 = allpassCoefficient_ * temp;
|
||||||
|
temp0 += input;
|
||||||
|
allpassDelays_[0].Add((int) temp0);
|
||||||
|
temp0 = -(allpassCoefficient_ * temp0) + temp;
|
||||||
|
|
||||||
|
temp = allpassDelays_[1].Get();
|
||||||
|
temp1 = allpassCoefficient_ * temp;
|
||||||
|
temp1 += temp0;
|
||||||
|
allpassDelays_[1].Add((int) temp1);
|
||||||
|
temp1 = -(allpassCoefficient_ * temp1) + temp;
|
||||||
|
|
||||||
|
temp = allpassDelays_[2].Get();
|
||||||
|
temp2 = allpassCoefficient_ * temp;
|
||||||
|
temp2 += temp1;
|
||||||
|
allpassDelays_[2].Add((int) temp2);
|
||||||
|
temp2 = -(allpassCoefficient_ * temp2) + temp;
|
||||||
|
|
||||||
|
const double temp3 = temp2 + (combCoefficient_[0] * combDelays_[0].Get());
|
||||||
|
const double temp4 = temp2 + (combCoefficient_[1] * combDelays_[1].Get());
|
||||||
|
const double temp5 = temp2 + (combCoefficient_[2] * combDelays_[2].Get());
|
||||||
|
const double temp6 = temp2 + (combCoefficient_[3] * combDelays_[3].Get());
|
||||||
|
|
||||||
|
combDelays_[0].Add((int) temp3);
|
||||||
|
combDelays_[1].Add((int) temp4);
|
||||||
|
combDelays_[2].Add((int) temp5);
|
||||||
|
combDelays_[3].Add((int) temp6);
|
||||||
|
|
||||||
|
return (temp3 + temp4 + temp5 + temp6) * (double) 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************\
|
||||||
|
* GUI utilities *
|
||||||
|
\******************************************************************************/
|
||||||
|
/* About dialog ------------------------------------------------------------- */
|
||||||
|
CAboutDlg::CAboutDlg(QWidget* parent, const char* name, bool modal, WFlags f)
|
||||||
|
: CAboutDlgBase(parent, name, modal, f)
|
||||||
|
{
|
||||||
|
/* Set the text for the about dialog html text control */
|
||||||
|
TextViewCredits->setText(
|
||||||
|
"<p>" /* General description of llcon software */
|
||||||
|
"<big><b>llcon</b> " + tr("Client/Server communication tool to enable "
|
||||||
|
"musician to play together through a conventional broadband internet "
|
||||||
|
"connection (like DSL).") + "</big>"
|
||||||
|
"</p><br>"
|
||||||
|
"<p><font face=\"courier\">" /* GPL header text */
|
||||||
|
"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.<br>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.<br>You should have received a copy of the GNU General Public "
|
||||||
|
"License along with his program; if not, write to the Free Software "
|
||||||
|
"Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 "
|
||||||
|
"USA"
|
||||||
|
"</font></p><br>"
|
||||||
|
"<p>" /* Libraries used by this compilation of Dream */
|
||||||
|
"<b>" + tr("llcon uses the following libraries or code snippets:") +
|
||||||
|
"</b></p>"
|
||||||
|
"<ul>"
|
||||||
|
"<li>audio reverberation code: by Perry R. Cook and Gary P. Scavone, "
|
||||||
|
"1995 - 2004 (taken from \"The Synthesis ToolKit in C++ (STK)\")</li>"
|
||||||
|
"<li>IMA-ADPCM: by Erik de Castro Lopo</li>"
|
||||||
|
"<li>INI File Tools (STLINI): Robert Kesterson in 1999</li>"
|
||||||
|
"<li>Parts from Dream DRM Receiver by Volker Fischer and Alexander "
|
||||||
|
"Kurpiers</li>"
|
||||||
|
"</ul>"
|
||||||
|
"</center><br>");
|
||||||
|
|
||||||
|
/* Set version number in about dialog */
|
||||||
|
QString strVersionText;
|
||||||
|
strVersionText = "<center><b>" + tr("llcon, Version ");
|
||||||
|
strVersionText += VERSION;
|
||||||
|
strVersionText += "</b><br> " +
|
||||||
|
tr("llcon, Low-Latency (Internet) Connection") + "<br>";
|
||||||
|
strVersionText += tr("Under the GNU General Public License (GPL)") +
|
||||||
|
"</center>";
|
||||||
|
TextLabelVersion->setText(strVersionText);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Help menu ---------------------------------------------------------------- */
|
||||||
|
CLlconHelpMenu::CLlconHelpMenu ( QWidget* parent ) : QPopupMenu ( parent )
|
||||||
|
{
|
||||||
|
/* Standard help menu consists of about and what's this help */
|
||||||
|
insertItem ( tr ( "What's &This" ), this ,
|
||||||
|
SLOT ( OnHelpWhatsThis () ), SHIFT+Key_F1 );
|
||||||
|
insertSeparator();
|
||||||
|
insertItem ( tr ( "&About..." ), this, SLOT ( OnHelpAbout () ) );
|
||||||
|
}
|
381
src/util.h
Executable file
381
src/util.h
Executable file
|
@ -0,0 +1,381 @@
|
||||||
|
/******************************************************************************\
|
||||||
|
* Copyright (c) 2004-2006
|
||||||
|
*
|
||||||
|
* Author(s):
|
||||||
|
* Volker Fischer
|
||||||
|
*
|
||||||
|
******************************************************************************
|
||||||
|
*
|
||||||
|
* 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.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
\******************************************************************************/
|
||||||
|
|
||||||
|
#if !defined(UTIL_HOIH934256GEKJH98_3_43445KJIUHF1912__INCLUDED_)
|
||||||
|
#define UTIL_HOIH934256GEKJH98_3_43445KJIUHF1912__INCLUDED_
|
||||||
|
|
||||||
|
#include <qhostaddress.h>
|
||||||
|
#include <qpopupmenu.h>
|
||||||
|
#include <qwhatsthis.h>
|
||||||
|
#include <qtextview.h>
|
||||||
|
#include <qlabel.h>
|
||||||
|
#include <vector>
|
||||||
|
#include "global.h"
|
||||||
|
using namespace std; /* Because of the library: "vector" */
|
||||||
|
#ifdef _WIN32
|
||||||
|
# include "../windows/moc/aboutdlgbase.h"
|
||||||
|
#else
|
||||||
|
# include "moc/aboutdlgbase.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Definitions ****************************************************************/
|
||||||
|
#define METER_FLY_BACK 2
|
||||||
|
|
||||||
|
|
||||||
|
/* Global functions ***********************************************************/
|
||||||
|
/* Converting double to short */
|
||||||
|
inline short Double2Short(const double dInput)
|
||||||
|
{
|
||||||
|
/* Lower bound */
|
||||||
|
if (dInput < _MINSHORT)
|
||||||
|
return _MINSHORT;
|
||||||
|
|
||||||
|
/* Upper bound */
|
||||||
|
if (dInput > _MAXSHORT)
|
||||||
|
return _MAXSHORT;
|
||||||
|
|
||||||
|
return (short) dInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Debug error handling */
|
||||||
|
void DebugError(const char* pchErDescr, const char* pchPar1Descr,
|
||||||
|
const double dPar1, const char* pchPar2Descr,
|
||||||
|
const double dPar2);
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************\
|
||||||
|
* CVector base class *
|
||||||
|
\******************************************************************************/
|
||||||
|
template<class TData> class CVector : public std::vector<TData>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CVector() : iVectorSize(0) {pData = this->begin();}
|
||||||
|
CVector(const int iNeSi) {Init(iNeSi);}
|
||||||
|
CVector(const int iNeSi, const TData tInVa) {Init(iNeSi, tInVa);}
|
||||||
|
virtual ~CVector() {}
|
||||||
|
|
||||||
|
/* Copy constructor: The order of the initialization list must not be
|
||||||
|
changed. First, the base class must be initialized, then the pData
|
||||||
|
pointer must be set to the new data source. The bit access is, by
|
||||||
|
default, reset */
|
||||||
|
CVector(const CVector<TData>& vecI) :
|
||||||
|
std::vector<TData>(static_cast<const std::vector<TData>&>(vecI)),
|
||||||
|
iVectorSize(vecI.Size()), pData(this->begin()) {}
|
||||||
|
|
||||||
|
void Init(const int iNewSize);
|
||||||
|
|
||||||
|
/* Use this init to give all elements a defined value */
|
||||||
|
void Init(const int iNewSize, const TData tIniVal);
|
||||||
|
void Reset(const TData tResetVal);
|
||||||
|
|
||||||
|
void Enlarge(const int iAddedSize);
|
||||||
|
void Add(const TData& tI) {Enlarge(1); pData[iVectorSize - 1] = tI;}
|
||||||
|
|
||||||
|
inline int Size() const {return iVectorSize;}
|
||||||
|
|
||||||
|
/* This operator allows for a l-value assignment of this object:
|
||||||
|
CVector[x] = y is possible */
|
||||||
|
inline TData& operator[](const int iPos) {
|
||||||
|
#ifdef _DEBUG_
|
||||||
|
if ((iPos < 0) || (iPos > iVectorSize - 1))
|
||||||
|
{
|
||||||
|
DebugError("Writing vector out of bounds", "Vector size",
|
||||||
|
iVectorSize, "New parameter", iPos);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return pData[iPos];}
|
||||||
|
|
||||||
|
inline TData operator[](const int iPos) const {
|
||||||
|
#ifdef _DEBUG_
|
||||||
|
if ((iPos < 0) || (iPos > iVectorSize - 1))
|
||||||
|
{
|
||||||
|
DebugError("Reading vector out of bounds", "Vector size",
|
||||||
|
iVectorSize, "New parameter", iPos);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return pData[iPos];}
|
||||||
|
|
||||||
|
inline CVector<TData>& operator=(const CVector<TData>& vecI) {
|
||||||
|
#ifdef _DEBUG_
|
||||||
|
/* Vectors which shall be copied MUST have same size! (If this is
|
||||||
|
satisfied, the parameter "iVectorSize" must not be adjusted as
|
||||||
|
a side effect) */
|
||||||
|
if (vecI.Size() != iVectorSize)
|
||||||
|
{
|
||||||
|
DebugError("Vector operator=() different size", "Vector size",
|
||||||
|
iVectorSize, "New parameter", vecI.Size());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
vector<TData>::operator=(vecI);
|
||||||
|
|
||||||
|
/* Reset my data pointer in case, the operator=() of the base class
|
||||||
|
did change the actual memory */
|
||||||
|
pData = this->begin();
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
typename std::vector<TData>::iterator pData;
|
||||||
|
int iVectorSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* Implementation *************************************************************/
|
||||||
|
template<class TData> void CVector<TData>::Init(const int iNewSize)
|
||||||
|
{
|
||||||
|
iVectorSize = iNewSize;
|
||||||
|
|
||||||
|
/* Clear old buffer and reserve memory for new buffer, get iterator
|
||||||
|
for pointer operations */
|
||||||
|
this->clear();
|
||||||
|
this->resize(iNewSize);
|
||||||
|
pData = this->begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class TData> void CVector<TData>::Init(const int iNewSize,
|
||||||
|
const TData tIniVal)
|
||||||
|
{
|
||||||
|
/* Call actual init routine */
|
||||||
|
Init(iNewSize);
|
||||||
|
|
||||||
|
/* Set values */
|
||||||
|
Reset(tIniVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class TData> void CVector<TData>::Enlarge(const int iAddedSize)
|
||||||
|
{
|
||||||
|
iVectorSize += iAddedSize;
|
||||||
|
this->resize(iVectorSize);
|
||||||
|
|
||||||
|
/* We have to reset the pointer since it could be that the vector size was
|
||||||
|
zero before enlarging the vector */
|
||||||
|
pData = this->begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class TData> void CVector<TData>::Reset(const TData tResetVal)
|
||||||
|
{
|
||||||
|
/* Set all values to reset value */
|
||||||
|
for (int i = 0; i < iVectorSize; i++)
|
||||||
|
pData[i] = tResetVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************\
|
||||||
|
* CFIFO class (first in, first out) *
|
||||||
|
\******************************************************************************/
|
||||||
|
template<class TData> class CFIFO : public CVector<TData>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CFIFO() : CVector<TData>(), iCurIdx(0) {}
|
||||||
|
CFIFO(const int iNeSi) : CVector<TData>(iNeSi), iCurIdx(0) {}
|
||||||
|
CFIFO(const int iNeSi, const TData tInVa) :
|
||||||
|
CVector<TData>(iNeSi, tInVa), iCurIdx(0) {}
|
||||||
|
|
||||||
|
void Add(const TData tNewD);
|
||||||
|
inline TData Get() {return this->pData[iCurIdx];}
|
||||||
|
|
||||||
|
virtual void Init(const int iNewSize);
|
||||||
|
virtual void Init(const int iNewSize, const TData tIniVal);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int iCurIdx;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class TData> void CFIFO<TData>::Init(const int iNewSize)
|
||||||
|
{
|
||||||
|
iCurIdx = 0;
|
||||||
|
CVector<TData>::Init(iNewSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class TData> void CFIFO<TData>::Init(const int iNewSize,
|
||||||
|
const TData tIniVal)
|
||||||
|
{
|
||||||
|
iCurIdx = 0;
|
||||||
|
CVector<TData>::Init(iNewSize, tIniVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class TData> void CFIFO<TData>::Add(const TData tNewD)
|
||||||
|
{
|
||||||
|
this->pData[iCurIdx] = tNewD;
|
||||||
|
|
||||||
|
/* Increment index */
|
||||||
|
iCurIdx++;
|
||||||
|
if (iCurIdx >= this->iVectorSize)
|
||||||
|
iCurIdx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************\
|
||||||
|
* CMovingAv class (moving average) *
|
||||||
|
\******************************************************************************/
|
||||||
|
template<class TData> class CMovingAv : public CVector<TData>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CMovingAv() : CVector<TData>(), iCurIdx(0), iNorm(0),
|
||||||
|
tCurAvResult(TData(0)) {}
|
||||||
|
CMovingAv(const int iNeSi) : CVector<TData>(iNeSi), iCurIdx(0), iNorm(0),
|
||||||
|
tCurAvResult(TData(0)) {}
|
||||||
|
CMovingAv(const int iNeSi, const TData tInVa) :
|
||||||
|
CVector<TData>(iNeSi, tInVa), iCurIdx(0), iNorm(0),
|
||||||
|
tCurAvResult(TData(0)) {}
|
||||||
|
|
||||||
|
void Add(const TData tNewD);
|
||||||
|
inline TData GetAverage()
|
||||||
|
{
|
||||||
|
if (this->iNorm == 0) return TData(0);
|
||||||
|
else return tCurAvResult / this->iNorm;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void Init(const int iNewSize);
|
||||||
|
void InitVec(const int iNewSize, const int iNewVecSize);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int iCurIdx;
|
||||||
|
int iNorm;
|
||||||
|
TData tCurAvResult;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class TData> void CMovingAv<TData>::Init(const int iNewSize)
|
||||||
|
{
|
||||||
|
iNorm = 0;
|
||||||
|
iCurIdx = 0;
|
||||||
|
tCurAvResult = TData(0); /* Only for scalars! */
|
||||||
|
CVector<TData>::Init(iNewSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class TData> void CMovingAv<TData>::Add(const TData tNewD)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Optimized calculation of the moving average. We only add a new value and
|
||||||
|
subtract the old value from the result. We only need one addition and a
|
||||||
|
history buffer
|
||||||
|
*/
|
||||||
|
/* Subtract oldest value */
|
||||||
|
tCurAvResult -= this->pData[iCurIdx];
|
||||||
|
|
||||||
|
/* Add new value and write in memory */
|
||||||
|
tCurAvResult += tNewD;
|
||||||
|
this->pData[iCurIdx] = tNewD;
|
||||||
|
|
||||||
|
/* Increase position pointer and test if wrap */
|
||||||
|
iCurIdx++;
|
||||||
|
if (iCurIdx >= this->iVectorSize)
|
||||||
|
iCurIdx = 0;
|
||||||
|
|
||||||
|
/* take care of norm */
|
||||||
|
if (this->iNorm < this->iVectorSize)
|
||||||
|
this->iNorm++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************\
|
||||||
|
* GUI utilities *
|
||||||
|
\******************************************************************************/
|
||||||
|
/* About dialog ------------------------------------------------------------- */
|
||||||
|
class CAboutDlg : public CAboutDlgBase
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
CAboutDlg(QWidget* parent = 0, const char* name = 0, bool modal = FALSE,
|
||||||
|
WFlags f = 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* Help menu ---------------------------------------------------------------- */
|
||||||
|
class CLlconHelpMenu : public QPopupMenu
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
CLlconHelpMenu(QWidget* parent = 0);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
CAboutDlg AboutDlg;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void OnHelpWhatsThis () { QWhatsThis::enterWhatsThisMode (); }
|
||||||
|
void OnHelpAbout () { AboutDlg.exec(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* Other Classes **************************************************************/
|
||||||
|
/* Signal Level Meter ------------------------------------------------------- */
|
||||||
|
class CSignalLevelMeter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CSignalLevelMeter() : dCurLevel(0.0) {}
|
||||||
|
virtual ~CSignalLevelMeter() {}
|
||||||
|
|
||||||
|
void Update(CVector<double>& vecdAudio);
|
||||||
|
double MicLevel();
|
||||||
|
void Reset() {dCurLevel = 0.0;}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
double dCurLevel;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CHostAddress
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CHostAddress() : InetAddr((Q_UINT32) 0), iPort(0) {}
|
||||||
|
CHostAddress(const QHostAddress NInetAddr, const Q_UINT16 iNPort) :
|
||||||
|
InetAddr(NInetAddr), iPort(iNPort) {}
|
||||||
|
CHostAddress(const CHostAddress& NHAddr) :
|
||||||
|
InetAddr(NHAddr.InetAddr), iPort(NHAddr.iPort) {}
|
||||||
|
|
||||||
|
/* copy and compare operators */
|
||||||
|
CHostAddress& operator=(const CHostAddress& NHAddr)
|
||||||
|
{InetAddr = NHAddr.InetAddr; iPort = NHAddr.iPort; return *this;}
|
||||||
|
bool operator==(const CHostAddress& CompAddr) /* oompare operator */
|
||||||
|
{return ((CompAddr.InetAddr == InetAddr) && (CompAddr.iPort == iPort));}
|
||||||
|
|
||||||
|
QHostAddress InetAddr;
|
||||||
|
Q_UINT16 iPort;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* Audio Reverbration ------------------------------------------------------- */
|
||||||
|
class CAudioReverb
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CAudioReverb(const double rT60 = (double) 5.0);
|
||||||
|
|
||||||
|
void Clear();
|
||||||
|
double ProcessSample(const double input);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void setT60(const double rT60);
|
||||||
|
bool isPrime(const int number);
|
||||||
|
|
||||||
|
CFIFO<int> allpassDelays_[3];
|
||||||
|
CFIFO<int> combDelays_[4];
|
||||||
|
double allpassCoefficient_;
|
||||||
|
double combCoefficient_[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* !defined(UTIL_HOIH934256GEKJH98_3_43445KJIUHF1912__INCLUDED_) */
|
48
windows/MocQT.bat
Executable file
48
windows/MocQT.bat
Executable file
|
@ -0,0 +1,48 @@
|
||||||
|
rem/******************************************************************************\
|
||||||
|
rem * Copyright (c) 2004-2006
|
||||||
|
rem *
|
||||||
|
rem * Author(s):
|
||||||
|
rem * Volker Fischer
|
||||||
|
rem *
|
||||||
|
rem * Description:
|
||||||
|
rem * Script for compiling the QT resources under Windows (MOCing and UICing)
|
||||||
|
rem ******************************************************************************
|
||||||
|
rem *
|
||||||
|
rem * This program is free software; you can redistribute it and/or modify it under
|
||||||
|
rem * the terms of the GNU General Public License as published by the Free Software
|
||||||
|
rem * Foundation; either version 2 of the License, or (at your option) any later
|
||||||
|
rem * version.
|
||||||
|
rem *
|
||||||
|
rem * This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
rem * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
rem * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 1111
|
||||||
|
rem * details.
|
||||||
|
rem *
|
||||||
|
rem * You should have received a copy of the GNU General Public License along with
|
||||||
|
rem * this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
rem * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
rem *
|
||||||
|
rem\******************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
rem .h --------------
|
||||||
|
%qtdir%\bin\moc.exe ..\src\util.h -o moc\moc_util.cpp
|
||||||
|
%qtdir%\bin\moc.exe ..\src\multicolorled.h -o moc\moc_multicolorled.cpp
|
||||||
|
%qtdir%\bin\moc.exe ..\src\llconclientdlg.h -o moc\moc_llconclientdlg.cpp
|
||||||
|
%qtdir%\bin\moc.exe ..\src\llconserverdlg.h -o moc\moc_llconserverdlg.cpp
|
||||||
|
%qtdir%\bin\moc.exe ..\src\server.h -o moc\moc_server.cpp
|
||||||
|
%qtdir%\bin\moc.exe ..\src\socket.h -o moc\moc_socket.cpp
|
||||||
|
|
||||||
|
|
||||||
|
rem .ui -------------
|
||||||
|
%qtdir%\bin\uic.exe ..\src\aboutdlgbase.ui -o moc\aboutdlgbase.h
|
||||||
|
%qtdir%\bin\uic.exe ..\src\aboutdlgbase.ui -i aboutdlgbase.h -o moc\aboutdlgbase.cpp
|
||||||
|
%qtdir%\bin\moc.exe moc\aboutdlgbase.h -o moc\moc_aboutdlgbase.cpp
|
||||||
|
|
||||||
|
%qtdir%\bin\uic.exe ..\src\llconclientdlgbase.ui -o moc\llconclientdlgbase.h
|
||||||
|
%qtdir%\bin\uic.exe ..\src\llconclientdlgbase.ui -i llconclientdlgbase.h -o moc\llconclientdlgbase.cpp
|
||||||
|
%qtdir%\bin\moc.exe moc\llconclientdlgbase.h -o moc\moc_llconclientdlgbase.cpp
|
||||||
|
|
||||||
|
%qtdir%\bin\uic.exe ..\src\llconserverdlgbase.ui -o moc\llconserverdlgbase.h
|
||||||
|
%qtdir%\bin\uic.exe ..\src\llconserverdlgbase.ui -i llconserverdlgbase.h -o moc\llconserverdlgbase.cpp
|
||||||
|
%qtdir%\bin\moc.exe moc\llconserverdlgbase.h -o moc\moc_llconserverdlgbase.cpp
|
313
windows/llcon.dsp
Executable file
313
windows/llcon.dsp
Executable file
|
@ -0,0 +1,313 @@
|
||||||
|
# Microsoft Developer Studio Project File - Name="llcon" - Package Owner=<4>
|
||||||
|
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||||
|
# ** DO NOT EDIT **
|
||||||
|
|
||||||
|
# TARGTYPE "Win32 (x86) Application" 0x0101
|
||||||
|
|
||||||
|
CFG=llcon - Win32 Debug
|
||||||
|
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
||||||
|
!MESSAGE use the Export Makefile command and run
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE NMAKE /f "llcon.mak".
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE You can specify a configuration when running NMAKE
|
||||||
|
!MESSAGE by defining the macro CFG on the command line. For example:
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE NMAKE /f "llcon.mak" CFG="llcon - Win32 Debug"
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE Possible choices for configuration are:
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE "llcon - Win32 Release" (based on "Win32 (x86) Application")
|
||||||
|
!MESSAGE "llcon - Win32 Debug" (based on "Win32 (x86) Application")
|
||||||
|
!MESSAGE
|
||||||
|
|
||||||
|
# Begin Project
|
||||||
|
# PROP AllowPerConfigDependencies 0
|
||||||
|
# PROP Scc_ProjName ""
|
||||||
|
# PROP Scc_LocalPath ""
|
||||||
|
CPP=cl.exe
|
||||||
|
MTL=midl.exe
|
||||||
|
RSC=rc.exe
|
||||||
|
|
||||||
|
!IF "$(CFG)" == "llcon - Win32 Release"
|
||||||
|
|
||||||
|
# PROP BASE Use_MFC 0
|
||||||
|
# PROP BASE Use_Debug_Libraries 0
|
||||||
|
# PROP BASE Output_Dir "Release"
|
||||||
|
# PROP BASE Intermediate_Dir "Release"
|
||||||
|
# PROP BASE Target_Dir ""
|
||||||
|
# PROP Use_MFC 0
|
||||||
|
# PROP Use_Debug_Libraries 0
|
||||||
|
# PROP Output_Dir "Release"
|
||||||
|
# PROP Intermediate_Dir "Release"
|
||||||
|
# PROP Ignore_Export_Lib 0
|
||||||
|
# PROP Target_Dir ""
|
||||||
|
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /Yu"stdafx.h" /FD /c
|
||||||
|
# ADD CPP /nologo /W3 /GX /O2 /I "$(QTDIR)\include" /I "../src" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "QT_DLL" /D "QT_THREAD_SUPPORT" /FD /c
|
||||||
|
# SUBTRACT CPP /YX /Yc /Yu
|
||||||
|
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
||||||
|
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
||||||
|
# ADD BASE RSC /l 0x409 /d "NDEBUG"
|
||||||
|
# ADD RSC /l 0x409 /d "NDEBUG"
|
||||||
|
BSC32=bscmake.exe
|
||||||
|
# ADD BASE BSC32 /nologo
|
||||||
|
# ADD BSC32 /nologo
|
||||||
|
LINK32=link.exe
|
||||||
|
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386
|
||||||
|
# ADD LINK32 winmm.lib $(QTDIR)\lib\qt-mt230nc.lib $(QTDIR)\lib\qtmain.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386
|
||||||
|
|
||||||
|
!ELSEIF "$(CFG)" == "llcon - Win32 Debug"
|
||||||
|
|
||||||
|
# PROP BASE Use_MFC 0
|
||||||
|
# PROP BASE Use_Debug_Libraries 1
|
||||||
|
# PROP BASE Output_Dir "Debug"
|
||||||
|
# PROP BASE Intermediate_Dir "Debug"
|
||||||
|
# PROP BASE Target_Dir ""
|
||||||
|
# PROP Use_MFC 0
|
||||||
|
# PROP Use_Debug_Libraries 1
|
||||||
|
# PROP Output_Dir "Debug"
|
||||||
|
# PROP Intermediate_Dir "Debug"
|
||||||
|
# PROP Ignore_Export_Lib 0
|
||||||
|
# PROP Target_Dir ""
|
||||||
|
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c
|
||||||
|
# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "$(QTDIR)\include" /I "../src" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "QT_DLL" /D "QT_THREAD_SUPPORT" /FD /GZ /c
|
||||||
|
# SUBTRACT CPP /YX /Yc /Yu
|
||||||
|
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
|
||||||
|
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
|
||||||
|
# ADD BASE RSC /l 0x409 /d "_DEBUG"
|
||||||
|
# ADD RSC /l 0x409 /d "_DEBUG"
|
||||||
|
BSC32=bscmake.exe
|
||||||
|
# ADD BASE BSC32 /nologo
|
||||||
|
# ADD BSC32 /nologo
|
||||||
|
LINK32=link.exe
|
||||||
|
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
|
||||||
|
# ADD LINK32 winmm.lib $(QTDIR)\lib\qt-mt230nc.lib $(QTDIR)\lib\qtmain.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
|
||||||
|
|
||||||
|
!ENDIF
|
||||||
|
|
||||||
|
# Begin Target
|
||||||
|
|
||||||
|
# Name "llcon - Win32 Release"
|
||||||
|
# Name "llcon - Win32 Debug"
|
||||||
|
# Begin Group "Source Files"
|
||||||
|
|
||||||
|
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
|
||||||
|
# Begin Group "moc Files"
|
||||||
|
|
||||||
|
# PROP Default_Filter ""
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\moc\aboutdlgbase.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\moc\llconclientdlgbase.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\moc\llconserverdlgbase.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\moc\moc_aboutdlgbase.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\moc\moc_llconclientdlg.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\moc\moc_llconclientdlgbase.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\moc\moc_llconserverdlg.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\moc\moc_llconserverdlgbase.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\moc\moc_multicolorled.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\moc\moc_server.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\moc\moc_socket.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\moc\moc_util.cpp
|
||||||
|
# End Source File
|
||||||
|
# End Group
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\src\audiocompr.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\src\buffer.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\src\channel.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\src\client.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\src\llconclientdlg.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\src\llconserverdlg.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\src\main.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\src\multicolorled.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\src\resample.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\src\server.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\src\settings.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\src\socket.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\sound.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\src\util.cpp
|
||||||
|
# End Source File
|
||||||
|
# End Group
|
||||||
|
# Begin Group "Header Files"
|
||||||
|
|
||||||
|
# PROP Default_Filter "h;hpp;hxx;hm;inl"
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\src\audiocompr.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\src\buffer.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\src\channel.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\src\client.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\src\global.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\src\llconclientdlg.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\src\llconserverdlg.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\src\multicolorled.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\src\resample.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\src\resamplefilter.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\src\server.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\src\settings.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\src\socket.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\sound.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\src\util.h
|
||||||
|
# End Source File
|
||||||
|
# End Group
|
||||||
|
# Begin Group "Resource Files"
|
||||||
|
|
||||||
|
# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\llcon.rc
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\mainicon.ico
|
||||||
|
# End Source File
|
||||||
|
# End Group
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\AUTHORS
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\ChangeLog
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\COPYING
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\INSTALL
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\MocQT.bat
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\NEWS
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\README
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\TODO
|
||||||
|
# End Source File
|
||||||
|
# End Target
|
||||||
|
# End Project
|
29
windows/llcon.dsw
Executable file
29
windows/llcon.dsw
Executable file
|
@ -0,0 +1,29 @@
|
||||||
|
Microsoft Developer Studio Workspace File, Format Version 6.00
|
||||||
|
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
Project: "llcon"=.\llcon.dsp - Package Owner=<4>
|
||||||
|
|
||||||
|
Package=<5>
|
||||||
|
{{{
|
||||||
|
}}}
|
||||||
|
|
||||||
|
Package=<4>
|
||||||
|
{{{
|
||||||
|
}}}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
Global:
|
||||||
|
|
||||||
|
Package=<5>
|
||||||
|
{{{
|
||||||
|
}}}
|
||||||
|
|
||||||
|
Package=<3>
|
||||||
|
{{{
|
||||||
|
}}}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
|
72
windows/llcon.rc
Executable file
72
windows/llcon.rc
Executable file
|
@ -0,0 +1,72 @@
|
||||||
|
//Microsoft Developer Studio generated resource script.
|
||||||
|
//
|
||||||
|
#include "resource.h"
|
||||||
|
|
||||||
|
#define APSTUDIO_READONLY_SYMBOLS
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Generated from the TEXTINCLUDE 2 resource.
|
||||||
|
//
|
||||||
|
#include "afxres.h"
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
#undef APSTUDIO_READONLY_SYMBOLS
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
// English (U.S.) resources
|
||||||
|
|
||||||
|
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||||
|
#ifdef _WIN32
|
||||||
|
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||||
|
#pragma code_page(1252)
|
||||||
|
#endif //_WIN32
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Icon
|
||||||
|
//
|
||||||
|
|
||||||
|
// Icon with lowest ID value placed first to ensure application icon
|
||||||
|
// remains consistent on all systems.
|
||||||
|
IDI_MAINICON ICON DISCARDABLE "mainicon.ico"
|
||||||
|
|
||||||
|
#ifdef APSTUDIO_INVOKED
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// TEXTINCLUDE
|
||||||
|
//
|
||||||
|
|
||||||
|
1 TEXTINCLUDE DISCARDABLE
|
||||||
|
BEGIN
|
||||||
|
"resource.h\0"
|
||||||
|
END
|
||||||
|
|
||||||
|
2 TEXTINCLUDE DISCARDABLE
|
||||||
|
BEGIN
|
||||||
|
"#include ""afxres.h""\r\n"
|
||||||
|
"\0"
|
||||||
|
END
|
||||||
|
|
||||||
|
3 TEXTINCLUDE DISCARDABLE
|
||||||
|
BEGIN
|
||||||
|
"\r\n"
|
||||||
|
"\0"
|
||||||
|
END
|
||||||
|
|
||||||
|
#endif // APSTUDIO_INVOKED
|
||||||
|
|
||||||
|
#endif // English (U.S.) resources
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef APSTUDIO_INVOKED
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Generated from the TEXTINCLUDE 3 resource.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
#endif // not APSTUDIO_INVOKED
|
||||||
|
|
BIN
windows/mainicon.ico
Executable file
BIN
windows/mainicon.ico
Executable file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
16
windows/resource.h
Executable file
16
windows/resource.h
Executable file
|
@ -0,0 +1,16 @@
|
||||||
|
//{{NO_DEPENDENCIES}}
|
||||||
|
// Microsoft Developer Studio generated include file.
|
||||||
|
// Used by llcon.rc
|
||||||
|
//
|
||||||
|
#define IDI_MAINICON 101
|
||||||
|
|
||||||
|
// Next default values for new objects
|
||||||
|
//
|
||||||
|
#ifdef APSTUDIO_INVOKED
|
||||||
|
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||||
|
#define _APS_NEXT_RESOURCE_VALUE 105
|
||||||
|
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||||
|
#define _APS_NEXT_CONTROL_VALUE 1000
|
||||||
|
#define _APS_NEXT_SYMED_VALUE 101
|
||||||
|
#endif
|
||||||
|
#endif
|
601
windows/sound.cpp
Executable file
601
windows/sound.cpp
Executable file
|
@ -0,0 +1,601 @@
|
||||||
|
/******************************************************************************\
|
||||||
|
* Copyright (c) 2004-2006
|
||||||
|
*
|
||||||
|
* Author(s):
|
||||||
|
* Volker Fischer
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Sound card interface for Windows operating systems
|
||||||
|
*
|
||||||
|
******************************************************************************
|
||||||
|
*
|
||||||
|
* 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.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
\******************************************************************************/
|
||||||
|
|
||||||
|
#include "Sound.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Implementation *************************************************************/
|
||||||
|
/******************************************************************************\
|
||||||
|
* Wave in *
|
||||||
|
\******************************************************************************/
|
||||||
|
bool CSound::Read(CVector<short>& psData)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
bool bError;
|
||||||
|
|
||||||
|
/* Check if device must be opened or reinitialized */
|
||||||
|
if (bChangParamIn == TRUE)
|
||||||
|
{
|
||||||
|
OpenInDevice();
|
||||||
|
|
||||||
|
/* Reinit sound interface */
|
||||||
|
InitRecording(iBufferSizeIn, bBlockingRec);
|
||||||
|
|
||||||
|
/* Reset flag */
|
||||||
|
bChangParamIn = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait until data is available */
|
||||||
|
if (!(m_WaveInHeader[iWhichBufferIn].dwFlags & WHDR_DONE))
|
||||||
|
{
|
||||||
|
if (bBlockingRec == TRUE)
|
||||||
|
WaitForSingleObject(m_WaveInEvent, INFINITE);
|
||||||
|
else
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if buffers got lost */
|
||||||
|
int iNumInBufDone = 0;
|
||||||
|
for (i = 0; i < iCurNumSndBufIn; i++)
|
||||||
|
{
|
||||||
|
if (m_WaveInHeader[i].dwFlags & WHDR_DONE)
|
||||||
|
iNumInBufDone++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the number of done buffers equals the total number of buffers, it is
|
||||||
|
very likely that a buffer got lost -> set error flag */
|
||||||
|
if (iNumInBufDone == iCurNumSndBufIn)
|
||||||
|
bError = TRUE;
|
||||||
|
else
|
||||||
|
bError = FALSE;
|
||||||
|
|
||||||
|
/* Copy data from sound card in output buffer */
|
||||||
|
for (i = 0; i < iBufferSizeIn; i++)
|
||||||
|
psData[i] = psSoundcardBuffer[iWhichBufferIn][i];
|
||||||
|
|
||||||
|
/* Add the buffer so that it can be filled with new samples */
|
||||||
|
AddInBuffer();
|
||||||
|
|
||||||
|
/* In case more than one buffer was ready, reset event */
|
||||||
|
ResetEvent(m_WaveInEvent);
|
||||||
|
|
||||||
|
return bError;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSound::AddInBuffer()
|
||||||
|
{
|
||||||
|
/* Unprepare old wave-header */
|
||||||
|
waveInUnprepareHeader(
|
||||||
|
m_WaveIn, &m_WaveInHeader[iWhichBufferIn], sizeof(WAVEHDR));
|
||||||
|
|
||||||
|
/* Prepare buffers for sending to sound interface */
|
||||||
|
PrepareInBuffer(iWhichBufferIn);
|
||||||
|
|
||||||
|
/* Send buffer to driver for filling with new data */
|
||||||
|
waveInAddBuffer(m_WaveIn, &m_WaveInHeader[iWhichBufferIn], sizeof(WAVEHDR));
|
||||||
|
|
||||||
|
/* Toggle buffers */
|
||||||
|
iWhichBufferIn++;
|
||||||
|
if (iWhichBufferIn == iCurNumSndBufIn)
|
||||||
|
iWhichBufferIn = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSound::PrepareInBuffer(int iBufNum)
|
||||||
|
{
|
||||||
|
/* Set struct entries */
|
||||||
|
m_WaveInHeader[iBufNum].lpData = (LPSTR) &psSoundcardBuffer[iBufNum][0];
|
||||||
|
m_WaveInHeader[iBufNum].dwBufferLength = iBufferSizeIn * BYTES_PER_SAMPLE;
|
||||||
|
m_WaveInHeader[iBufNum].dwFlags = 0;
|
||||||
|
|
||||||
|
/* Prepare wave-header */
|
||||||
|
waveInPrepareHeader(m_WaveIn, &m_WaveInHeader[iBufNum], sizeof(WAVEHDR));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSound::InitRecording(int iNewBufferSize, bool bNewBlocking)
|
||||||
|
{
|
||||||
|
/* Check if device must be opened or reinitialized */
|
||||||
|
if (bChangParamIn == TRUE)
|
||||||
|
{
|
||||||
|
OpenInDevice();
|
||||||
|
|
||||||
|
/* Reset flag */
|
||||||
|
bChangParamIn = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set internal parameter */
|
||||||
|
iBufferSizeIn = iNewBufferSize;
|
||||||
|
bBlockingRec = bNewBlocking;
|
||||||
|
|
||||||
|
/* Reset interface so that all buffers are returned from the interface */
|
||||||
|
waveInReset(m_WaveIn);
|
||||||
|
waveInStop(m_WaveIn);
|
||||||
|
|
||||||
|
/* Reset current buffer ID (it is important to do this BEFORE calling
|
||||||
|
"AddInBuffer()" */
|
||||||
|
iWhichBufferIn = 0;
|
||||||
|
|
||||||
|
/* Create memory for sound card buffer */
|
||||||
|
for (int i = 0; i < iCurNumSndBufIn; i++)
|
||||||
|
{
|
||||||
|
/* Unprepare old wave-header in case that we "re-initialized" this
|
||||||
|
module. Calling "waveInUnprepareHeader()" with an unprepared
|
||||||
|
buffer (when the module is initialized for the first time) has
|
||||||
|
simply no effect */
|
||||||
|
waveInUnprepareHeader(m_WaveIn, &m_WaveInHeader[i], sizeof(WAVEHDR));
|
||||||
|
|
||||||
|
if (psSoundcardBuffer[i] != NULL)
|
||||||
|
delete[] psSoundcardBuffer[i];
|
||||||
|
|
||||||
|
psSoundcardBuffer[i] = new short[iBufferSizeIn];
|
||||||
|
|
||||||
|
|
||||||
|
/* Send all buffers to driver for filling the queue ----------------- */
|
||||||
|
/* Prepare buffers before sending them to the sound interface */
|
||||||
|
PrepareInBuffer(i);
|
||||||
|
|
||||||
|
AddInBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Notify that sound capturing can start now */
|
||||||
|
waveInStart(m_WaveIn);
|
||||||
|
|
||||||
|
/* This reset event is very important for initialization, otherwise we will
|
||||||
|
get errors! */
|
||||||
|
ResetEvent(m_WaveInEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSound::OpenInDevice()
|
||||||
|
{
|
||||||
|
/* Open wave-input and set call-back mechanism to event handle */
|
||||||
|
if (m_WaveIn != NULL)
|
||||||
|
{
|
||||||
|
waveInReset(m_WaveIn);
|
||||||
|
waveInClose(m_WaveIn);
|
||||||
|
}
|
||||||
|
|
||||||
|
MMRESULT result = waveInOpen(&m_WaveIn, iCurInDev, &sWaveFormatEx,
|
||||||
|
(DWORD) m_WaveInEvent, NULL, CALLBACK_EVENT);
|
||||||
|
|
||||||
|
if (result != MMSYSERR_NOERROR)
|
||||||
|
{
|
||||||
|
throw CGenErr("Sound Interface Start, waveInOpen() failed. This error "
|
||||||
|
"usually occurs if another application blocks the sound in.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSound::SetInDev(int iNewDev)
|
||||||
|
{
|
||||||
|
/* Set device to wave mapper if iNewDev is invalid */
|
||||||
|
if ((iNewDev >= iNumDevs) || (iNewDev < 0))
|
||||||
|
iNewDev = WAVE_MAPPER;
|
||||||
|
|
||||||
|
/* Change only in case new device id is not already active */
|
||||||
|
if (iNewDev != iCurInDev)
|
||||||
|
{
|
||||||
|
iCurInDev = iNewDev;
|
||||||
|
bChangParamIn = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSound::SetInNumBuf(int iNewNum)
|
||||||
|
{
|
||||||
|
/* check new parameter */
|
||||||
|
if ((iNewNum >= MAX_SND_BUF_IN) || (iNewNum < 1))
|
||||||
|
iNewNum = NUM_SOUND_BUFFERS_IN;
|
||||||
|
|
||||||
|
/* Change only if parameter is different */
|
||||||
|
if (iNewNum != iCurNumSndBufIn)
|
||||||
|
{
|
||||||
|
iCurNumSndBufIn = iNewNum;
|
||||||
|
bChangParamIn = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************\
|
||||||
|
* Wave out *
|
||||||
|
\******************************************************************************/
|
||||||
|
bool CSound::Write(CVector<short>& psData)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
int iCntPrepBuf;
|
||||||
|
int iIndexDoneBuf;
|
||||||
|
bool bError;
|
||||||
|
|
||||||
|
/* Check if device must be opened or reinitialized */
|
||||||
|
if (bChangParamOut == TRUE)
|
||||||
|
{
|
||||||
|
OpenOutDevice();
|
||||||
|
|
||||||
|
/* Reinit sound interface */
|
||||||
|
InitPlayback(iBufferSizeOut, bBlockingPlay);
|
||||||
|
|
||||||
|
/* Reset flag */
|
||||||
|
bChangParamOut = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get number of "done"-buffers and position of one of them */
|
||||||
|
GetDoneBuffer(iCntPrepBuf, iIndexDoneBuf);
|
||||||
|
|
||||||
|
/* Now check special cases (Buffer is full or empty) */
|
||||||
|
if (iCntPrepBuf == 0)
|
||||||
|
{
|
||||||
|
if (bBlockingPlay == TRUE)
|
||||||
|
{
|
||||||
|
/* Blocking wave out routine. Needed for transmitter. Always
|
||||||
|
ensure that the buffer is completely filled to avoid buffer
|
||||||
|
underruns */
|
||||||
|
while (iCntPrepBuf == 0)
|
||||||
|
{
|
||||||
|
WaitForSingleObject(m_WaveOutEvent, INFINITE);
|
||||||
|
|
||||||
|
GetDoneBuffer(iCntPrepBuf, iIndexDoneBuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* All buffers are filled, dump new block ----------------------- */
|
||||||
|
// It would be better to kill half of the buffer blocks to set the start
|
||||||
|
// back to the middle: TODO
|
||||||
|
return TRUE; /* An error occurred */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (iCntPrepBuf == iCurNumSndBufOut)
|
||||||
|
{
|
||||||
|
/* ---------------------------------------------------------------------
|
||||||
|
Buffer is empty -> send as many cleared blocks to the sound-
|
||||||
|
interface until half of the buffer size is reached */
|
||||||
|
/* Send half of the buffer size blocks to the sound-interface */
|
||||||
|
for (j = 0; j < iCurNumSndBufOut / 2; j++)
|
||||||
|
{
|
||||||
|
/* First, clear these buffers */
|
||||||
|
for (i = 0; i < iBufferSizeOut; i++)
|
||||||
|
psPlaybackBuffer[j][i] = 0;
|
||||||
|
|
||||||
|
/* Then send them to the interface */
|
||||||
|
AddOutBuffer(j);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set index for done buffer */
|
||||||
|
iIndexDoneBuf = iCurNumSndBufOut / 2;
|
||||||
|
|
||||||
|
bError = TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
bError = FALSE;
|
||||||
|
|
||||||
|
/* Copy stereo data from input in soundcard buffer */
|
||||||
|
for (i = 0; i < iBufferSizeOut; i++)
|
||||||
|
psPlaybackBuffer[iIndexDoneBuf][i] = psData[i];
|
||||||
|
|
||||||
|
/* Now, send the current block */
|
||||||
|
AddOutBuffer(iIndexDoneBuf);
|
||||||
|
|
||||||
|
return bError;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSound::GetDoneBuffer(int& iCntPrepBuf, int& iIndexDoneBuf)
|
||||||
|
{
|
||||||
|
/* Get number of "done"-buffers and position of one of them */
|
||||||
|
iCntPrepBuf = 0;
|
||||||
|
for (int i = 0; i < iCurNumSndBufOut; i++)
|
||||||
|
{
|
||||||
|
if (m_WaveOutHeader[i].dwFlags & WHDR_DONE)
|
||||||
|
{
|
||||||
|
iCntPrepBuf++;
|
||||||
|
iIndexDoneBuf = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSound::AddOutBuffer(int iBufNum)
|
||||||
|
{
|
||||||
|
/* Unprepare old wave-header */
|
||||||
|
waveOutUnprepareHeader(
|
||||||
|
m_WaveOut, &m_WaveOutHeader[iBufNum], sizeof(WAVEHDR));
|
||||||
|
|
||||||
|
/* Prepare buffers for sending to sound interface */
|
||||||
|
PrepareOutBuffer(iBufNum);
|
||||||
|
|
||||||
|
/* Send buffer to driver for filling with new data */
|
||||||
|
waveOutWrite(m_WaveOut, &m_WaveOutHeader[iBufNum], sizeof(WAVEHDR));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSound::PrepareOutBuffer(int iBufNum)
|
||||||
|
{
|
||||||
|
/* Set Header data */
|
||||||
|
m_WaveOutHeader[iBufNum].lpData = (LPSTR) &psPlaybackBuffer[iBufNum][0];
|
||||||
|
m_WaveOutHeader[iBufNum].dwBufferLength = iBufferSizeOut * BYTES_PER_SAMPLE;
|
||||||
|
m_WaveOutHeader[iBufNum].dwFlags = 0;
|
||||||
|
|
||||||
|
/* Prepare wave-header */
|
||||||
|
waveOutPrepareHeader(m_WaveOut, &m_WaveOutHeader[iBufNum], sizeof(WAVEHDR));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSound::InitPlayback(int iNewBufferSize, bool bNewBlocking)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
/* Check if device must be opened or reinitialized */
|
||||||
|
if (bChangParamOut == TRUE)
|
||||||
|
{
|
||||||
|
OpenOutDevice();
|
||||||
|
|
||||||
|
/* Reset flag */
|
||||||
|
bChangParamOut = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set internal parameters */
|
||||||
|
iBufferSizeOut = iNewBufferSize;
|
||||||
|
bBlockingPlay = bNewBlocking;
|
||||||
|
|
||||||
|
/* Reset interface */
|
||||||
|
waveOutReset(m_WaveOut);
|
||||||
|
|
||||||
|
for (j = 0; j < iCurNumSndBufOut; j++)
|
||||||
|
{
|
||||||
|
/* Unprepare old wave-header (in case header was not prepared before,
|
||||||
|
simply nothing happens with this function call */
|
||||||
|
waveOutUnprepareHeader(m_WaveOut, &m_WaveOutHeader[j], sizeof(WAVEHDR));
|
||||||
|
|
||||||
|
/* Create memory for playback buffer */
|
||||||
|
if (psPlaybackBuffer[j] != NULL)
|
||||||
|
delete[] psPlaybackBuffer[j];
|
||||||
|
|
||||||
|
psPlaybackBuffer[j] = new short[iBufferSizeOut];
|
||||||
|
|
||||||
|
/* Clear new buffer */
|
||||||
|
for (i = 0; i < iBufferSizeOut; i++)
|
||||||
|
psPlaybackBuffer[j][i] = 0;
|
||||||
|
|
||||||
|
/* Prepare buffer for sending to the sound interface */
|
||||||
|
PrepareOutBuffer(j);
|
||||||
|
|
||||||
|
/* Initially, send all buffers to the interface */
|
||||||
|
AddOutBuffer(j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSound::OpenOutDevice()
|
||||||
|
{
|
||||||
|
if (m_WaveOut != NULL)
|
||||||
|
{
|
||||||
|
waveOutReset(m_WaveOut);
|
||||||
|
waveOutClose(m_WaveOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
MMRESULT result = waveOutOpen(&m_WaveOut, iCurOutDev, &sWaveFormatEx,
|
||||||
|
(DWORD) m_WaveOutEvent, NULL, CALLBACK_EVENT);
|
||||||
|
|
||||||
|
if (result != MMSYSERR_NOERROR)
|
||||||
|
throw CGenErr("Sound Interface Start, waveOutOpen() failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSound::SetOutDev(int iNewDev)
|
||||||
|
{
|
||||||
|
/* Set device to wave mapper if iNewDev is invalid */
|
||||||
|
if ((iNewDev >= iNumDevs) || (iNewDev < 0))
|
||||||
|
iNewDev = WAVE_MAPPER;
|
||||||
|
|
||||||
|
/* Change only in case new device id is not already active */
|
||||||
|
if (iNewDev != iCurOutDev)
|
||||||
|
{
|
||||||
|
iCurOutDev = iNewDev;
|
||||||
|
bChangParamOut = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSound::SetOutNumBuf(int iNewNum)
|
||||||
|
{
|
||||||
|
/* check new parameter */
|
||||||
|
if ((iNewNum >= MAX_SND_BUF_OUT) || (iNewNum < 1))
|
||||||
|
iNewNum = NUM_SOUND_BUFFERS_OUT;
|
||||||
|
|
||||||
|
/* Change only if parameter is different */
|
||||||
|
if (iNewNum != iCurNumSndBufOut)
|
||||||
|
{
|
||||||
|
iCurNumSndBufOut = iNewNum;
|
||||||
|
bChangParamOut = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************\
|
||||||
|
* Common *
|
||||||
|
\******************************************************************************/
|
||||||
|
void CSound::Close()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
MMRESULT result;
|
||||||
|
|
||||||
|
/* Reset audio driver */
|
||||||
|
if (m_WaveOut != NULL)
|
||||||
|
{
|
||||||
|
result = waveOutReset(m_WaveOut);
|
||||||
|
if (result != MMSYSERR_NOERROR)
|
||||||
|
throw CGenErr("Sound Interface, waveOutReset() failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_WaveIn != NULL)
|
||||||
|
{
|
||||||
|
result = waveInReset(m_WaveIn);
|
||||||
|
if (result != MMSYSERR_NOERROR)
|
||||||
|
throw CGenErr("Sound Interface, waveInReset() failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set event to ensure that thread leaves the waiting function */
|
||||||
|
if (m_WaveInEvent != NULL)
|
||||||
|
SetEvent(m_WaveInEvent);
|
||||||
|
|
||||||
|
/* Wait for the thread to terminate */
|
||||||
|
Sleep(500);
|
||||||
|
|
||||||
|
/* Unprepare wave-headers */
|
||||||
|
if (m_WaveIn != NULL)
|
||||||
|
{
|
||||||
|
for (i = 0; i < iCurNumSndBufIn; i++)
|
||||||
|
{
|
||||||
|
result = waveInUnprepareHeader(
|
||||||
|
m_WaveIn, &m_WaveInHeader[i], sizeof(WAVEHDR));
|
||||||
|
|
||||||
|
if (result != MMSYSERR_NOERROR)
|
||||||
|
{
|
||||||
|
throw CGenErr("Sound Interface, waveInUnprepareHeader()"
|
||||||
|
" failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close the sound in device */
|
||||||
|
result = waveInClose(m_WaveIn);
|
||||||
|
if (result != MMSYSERR_NOERROR)
|
||||||
|
throw CGenErr("Sound Interface, waveInClose() failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_WaveOut != NULL)
|
||||||
|
{
|
||||||
|
for (i = 0; i < iCurNumSndBufOut; i++)
|
||||||
|
{
|
||||||
|
result = waveOutUnprepareHeader(
|
||||||
|
m_WaveOut, &m_WaveOutHeader[i], sizeof(WAVEHDR));
|
||||||
|
|
||||||
|
if (result != MMSYSERR_NOERROR)
|
||||||
|
{
|
||||||
|
throw CGenErr("Sound Interface, waveOutUnprepareHeader()"
|
||||||
|
" failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close the sound out device */
|
||||||
|
result = waveOutClose(m_WaveOut);
|
||||||
|
if (result != MMSYSERR_NOERROR)
|
||||||
|
throw CGenErr("Sound Interface, waveOutClose() failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set flag to open devices the next time it is initialized */
|
||||||
|
bChangParamIn = TRUE;
|
||||||
|
bChangParamOut = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSound::CSound()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* init number of sound buffers */
|
||||||
|
iCurNumSndBufIn = NUM_SOUND_BUFFERS_IN;
|
||||||
|
iCurNumSndBufOut = NUM_SOUND_BUFFERS_OUT;
|
||||||
|
|
||||||
|
/* Should be initialized because an error can occur during init */
|
||||||
|
m_WaveInEvent = NULL;
|
||||||
|
m_WaveOutEvent = NULL;
|
||||||
|
m_WaveIn = NULL;
|
||||||
|
m_WaveOut = NULL;
|
||||||
|
|
||||||
|
/* Init buffer pointer to zero */
|
||||||
|
for (i = 0; i < MAX_SND_BUF_IN; i++)
|
||||||
|
{
|
||||||
|
memset(&m_WaveInHeader[i], 0, sizeof(WAVEHDR));
|
||||||
|
psSoundcardBuffer[i] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_SND_BUF_OUT; i++)
|
||||||
|
{
|
||||||
|
memset(&m_WaveOutHeader[i], 0, sizeof(WAVEHDR));
|
||||||
|
psPlaybackBuffer[i] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Init wave-format structure */
|
||||||
|
sWaveFormatEx.wFormatTag = WAVE_FORMAT_PCM;
|
||||||
|
sWaveFormatEx.nChannels = NUM_IN_OUT_CHANNELS;
|
||||||
|
sWaveFormatEx.wBitsPerSample = BITS_PER_SAMPLE;
|
||||||
|
sWaveFormatEx.nSamplesPerSec = SND_CRD_SAMPLE_RATE;
|
||||||
|
sWaveFormatEx.nBlockAlign = sWaveFormatEx.nChannels *
|
||||||
|
sWaveFormatEx.wBitsPerSample / 8;
|
||||||
|
sWaveFormatEx.nAvgBytesPerSec = sWaveFormatEx.nBlockAlign *
|
||||||
|
sWaveFormatEx.nSamplesPerSec;
|
||||||
|
sWaveFormatEx.cbSize = 0;
|
||||||
|
|
||||||
|
/* Get the number of digital audio devices in this computer, check range */
|
||||||
|
iNumDevs = waveInGetNumDevs();
|
||||||
|
|
||||||
|
if (iNumDevs > MAX_NUMBER_SOUND_CARDS)
|
||||||
|
iNumDevs = MAX_NUMBER_SOUND_CARDS;
|
||||||
|
|
||||||
|
/* At least one device must exist in the system */
|
||||||
|
if (iNumDevs == 0)
|
||||||
|
throw CGenErr("No audio device found.");
|
||||||
|
|
||||||
|
/* Get info about the devices and store the names */
|
||||||
|
for (i = 0; i < iNumDevs; i++)
|
||||||
|
{
|
||||||
|
if (!waveInGetDevCaps(i, &m_WaveInDevCaps, sizeof(WAVEINCAPS)))
|
||||||
|
pstrDevices[i] = m_WaveInDevCaps.szPname;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We use an event controlled wave-in (wave-out) structure */
|
||||||
|
/* Create events */
|
||||||
|
m_WaveInEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||||
|
m_WaveOutEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||||
|
|
||||||
|
/* Set flag to open devices */
|
||||||
|
bChangParamIn = TRUE;
|
||||||
|
bChangParamOut = TRUE;
|
||||||
|
|
||||||
|
/* Default device number, "wave mapper" */
|
||||||
|
iCurInDev = WAVE_MAPPER;
|
||||||
|
iCurOutDev = WAVE_MAPPER;
|
||||||
|
|
||||||
|
/* Non-blocking wave out is default */
|
||||||
|
bBlockingPlay = FALSE;
|
||||||
|
|
||||||
|
/* Blocking wave in is default */
|
||||||
|
bBlockingRec = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSound::~CSound()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Delete allocated memory */
|
||||||
|
for (i = 0; i < iCurNumSndBufIn; i++)
|
||||||
|
{
|
||||||
|
if (psSoundcardBuffer[i] != NULL)
|
||||||
|
delete[] psSoundcardBuffer[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < iCurNumSndBufOut; i++)
|
||||||
|
{
|
||||||
|
if (psPlaybackBuffer[i] != NULL)
|
||||||
|
delete[] psPlaybackBuffer[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close the handle for the events */
|
||||||
|
if (m_WaveInEvent != NULL)
|
||||||
|
CloseHandle(m_WaveInEvent);
|
||||||
|
|
||||||
|
if (m_WaveOutEvent != NULL)
|
||||||
|
CloseHandle(m_WaveOutEvent);
|
||||||
|
}
|
119
windows/sound.h
Executable file
119
windows/sound.h
Executable file
|
@ -0,0 +1,119 @@
|
||||||
|
/******************************************************************************\
|
||||||
|
* Copyright (c) 2004-2006
|
||||||
|
*
|
||||||
|
* Author(s):
|
||||||
|
* Volker Fischer
|
||||||
|
*
|
||||||
|
*
|
||||||
|
******************************************************************************
|
||||||
|
*
|
||||||
|
* 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.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
\******************************************************************************/
|
||||||
|
|
||||||
|
#if !defined(AFX_SOUNDIN_H__9518A621_7F78_11D3_8C0D_EEBF182CF549__INCLUDED_)
|
||||||
|
#define AFX_SOUNDIN_H__9518A621_7F78_11D3_8C0D_EEBF182CF549__INCLUDED_
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <mmsystem.h>
|
||||||
|
#include <string>
|
||||||
|
#include "../src/util.h"
|
||||||
|
#include "../src/global.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Definitions ****************************************************************/
|
||||||
|
#define NUM_IN_OUT_CHANNELS 2 /* Stereo recording (but we only
|
||||||
|
use one channel for recording) */
|
||||||
|
#define BITS_PER_SAMPLE 16 /* Use all bits of the D/A-converter */
|
||||||
|
#define BYTES_PER_SAMPLE 2 /* Number of bytes per sample */
|
||||||
|
|
||||||
|
#define MAX_SND_BUF_IN 200
|
||||||
|
#define MAX_SND_BUF_OUT 200
|
||||||
|
|
||||||
|
#define NUM_SOUND_BUFFERS_IN (70 / BLOCK_DURATION_MS)
|
||||||
|
#define NUM_SOUND_BUFFERS_OUT (80 / BLOCK_DURATION_MS)
|
||||||
|
|
||||||
|
//#define NUM_SOUND_BUFFERS_IN 7//200 /* Number of sound card buffers */
|
||||||
|
//#define NUM_SOUND_BUFFERS_OUT 8//100//15 /* Number of sound card buffers */
|
||||||
|
|
||||||
|
/* Maximum number of recognized sound cards installed in the system */
|
||||||
|
#define MAX_NUMBER_SOUND_CARDS 10
|
||||||
|
|
||||||
|
|
||||||
|
/* Classes ********************************************************************/
|
||||||
|
class CSound
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CSound();
|
||||||
|
virtual ~CSound();
|
||||||
|
|
||||||
|
void InitRecording(int iNewBufferSize, bool bNewBlocking = TRUE);
|
||||||
|
void InitPlayback(int iNewBufferSize, bool bNewBlocking = FALSE);
|
||||||
|
bool Read(CVector<short>& psData);
|
||||||
|
bool Write(CVector<short>& psData);
|
||||||
|
|
||||||
|
int GetNumDev() {return iNumDevs;}
|
||||||
|
std::string GetDeviceName(int iDiD) {return pstrDevices[iDiD];}
|
||||||
|
void SetOutDev(int iNewDev);
|
||||||
|
int GetOutDev() {return iCurOutDev;}
|
||||||
|
void SetInDev(int iNewDev);
|
||||||
|
int GetInDev() {return iCurInDev;}
|
||||||
|
void SetOutNumBuf(int iNewNum);
|
||||||
|
int GetOutNumBuf() {return iCurNumSndBufOut;}
|
||||||
|
void SetInNumBuf(int iNewNum);
|
||||||
|
int GetInNumBuf() {return iCurNumSndBufIn;}
|
||||||
|
|
||||||
|
void Close();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void OpenInDevice();
|
||||||
|
void OpenOutDevice();
|
||||||
|
void PrepareInBuffer(int iBufNum);
|
||||||
|
void PrepareOutBuffer(int iBufNum);
|
||||||
|
void AddInBuffer();
|
||||||
|
void AddOutBuffer(int iBufNum);
|
||||||
|
void GetDoneBuffer(int& iCntPrepBuf, int& iIndexDoneBuf);
|
||||||
|
|
||||||
|
WAVEFORMATEX sWaveFormatEx;
|
||||||
|
UINT iNumDevs;
|
||||||
|
std::string pstrDevices[MAX_NUMBER_SOUND_CARDS];
|
||||||
|
UINT iCurInDev;
|
||||||
|
UINT iCurOutDev;
|
||||||
|
bool bChangParamIn;
|
||||||
|
bool bChangParamOut;
|
||||||
|
int iCurNumSndBufIn;
|
||||||
|
int iCurNumSndBufOut;
|
||||||
|
|
||||||
|
/* Wave in */
|
||||||
|
WAVEINCAPS m_WaveInDevCaps;
|
||||||
|
HWAVEIN m_WaveIn;
|
||||||
|
HANDLE m_WaveInEvent;
|
||||||
|
WAVEHDR m_WaveInHeader[MAX_SND_BUF_IN];
|
||||||
|
int iBufferSizeIn;
|
||||||
|
int iWhichBufferIn;
|
||||||
|
short* psSoundcardBuffer[MAX_SND_BUF_IN];
|
||||||
|
bool bBlockingRec;
|
||||||
|
|
||||||
|
/* Wave out */
|
||||||
|
int iBufferSizeOut;
|
||||||
|
HWAVEOUT m_WaveOut;
|
||||||
|
short* psPlaybackBuffer[MAX_SND_BUF_OUT];
|
||||||
|
WAVEHDR m_WaveOutHeader[MAX_SND_BUF_OUT];
|
||||||
|
HANDLE m_WaveOutEvent;
|
||||||
|
bool bBlockingPlay;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // !defined(AFX_SOUNDIN_H__9518A621_7F78_11D3_8C0D_EEBF182CF549__INCLUDED_)
|
Loading…
Reference in a new issue