initial version

This commit is contained in:
Volker Fischer 2006-01-28 11:29:22 +00:00
parent 58e127024e
commit f787f8dd5c
52 changed files with 9207 additions and 0 deletions

1
AUTHORS Normal file
View File

@ -0,0 +1 @@
Volker Fischer <corrados[at+]users[dot*]sourceforge[dot_]net>

280
COPYING Normal file
View 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
View File

54
INSTALL Executable file
View 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
View File

@ -0,0 +1,3 @@
AUTOMAKE_OPTIONS = foreign
SUBDIRS = linux

0
NEWS Normal file
View File

75
README Executable file
View 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
View File

@ -0,0 +1 @@

8
bootstrap Executable file
View 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
View 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
View 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
View 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
View 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
View 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>&amp;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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because one or more lines are too long

177
src/llconserverdlg.cpp Executable file
View 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
View 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
View 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>&amp;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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

16
windows/resource.h Executable file
View 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
View 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
View 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_)