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