Initial import

This commit is contained in:
Wim Taymans 2018-06-01 11:28:31 +02:00
commit faa5984fce
39 changed files with 9955 additions and 0 deletions

504
LICENSE Normal file
View file

@ -0,0 +1,504 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
(This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.)
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, 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 library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.
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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.
If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be 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.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.
9. 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 Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.
11. 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 Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.
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.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
{description}
Copyright (C) {year} {fullname}
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random
Hacker.
{signature of Ty Coon}, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

2
README.md Normal file
View file

@ -0,0 +1,2 @@
# pipewire-pulseaudio
PulseAudio client library for PipeWire

64
src/bitset.c Normal file
View file

@ -0,0 +1,64 @@
/***
This file is part of PulseAudio.
Copyright 2009 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio 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 Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string.h>
#include <stdbool.h>
#include "internal.h"
#include "bitset.h"
void pa_bitset_set(pa_bitset_t *b, unsigned k, bool v) {
pa_assert(b);
if (v)
b[k >> 5] |= 1 << (k & 31);
else
b[k >> 5] &= ~((uint32_t) (1 << (k & 31)));
}
bool pa_bitset_get(const pa_bitset_t *b, unsigned k) {
return !!(b[k >> 5] & (1 << (k & 31)));
}
bool pa_bitset_equals(const pa_bitset_t *b, unsigned n, ...) {
va_list ap;
pa_bitset_t *a;
bool equal;
a = alloca(PA_BITSET_ELEMENTS(n));
va_start(ap, n);
for (;;) {
int j = va_arg(ap, int);
if (j < 0)
break;
pa_bitset_set(a, j, true);
}
va_end(ap);
equal = memcmp(a, b, PA_BITSET_SIZE(n)) == 0;
return equal;
}

34
src/bitset.h Normal file
View file

@ -0,0 +1,34 @@
#ifndef foopulsecorebitsethfoo
#define foopulsecorebitsethfoo
/***
This file is part of PulseAudio.
Copyright 2009 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio 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 Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#include <inttypes.h>
#define PA_BITSET_ELEMENTS(n) (((n)+31)/32)
#define PA_BITSET_SIZE(n) (PA_BITSET_ELEMENTS(n)*4)
typedef uint32_t pa_bitset_t;
void pa_bitset_set(pa_bitset_t *b, unsigned k, bool v);
bool pa_bitset_get(const pa_bitset_t *b, unsigned k);
bool pa_bitset_equals(const pa_bitset_t *b, unsigned n, ...);
#endif

830
src/channelmap.c Normal file
View file

@ -0,0 +1,830 @@
/***
This file is part of PulseAudio.
Copyright 2005-2006 Lennart Poettering
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio 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 Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <pulse/xmalloc.h>
#include <pulse/channelmap.h>
#include "internal.h"
#include "bitset.h"
#include "sample-util.h"
const char *const table[PA_CHANNEL_POSITION_MAX] = {
[PA_CHANNEL_POSITION_MONO] = "mono",
[PA_CHANNEL_POSITION_FRONT_CENTER] = "front-center",
[PA_CHANNEL_POSITION_FRONT_LEFT] = "front-left",
[PA_CHANNEL_POSITION_FRONT_RIGHT] = "front-right",
[PA_CHANNEL_POSITION_REAR_CENTER] = "rear-center",
[PA_CHANNEL_POSITION_REAR_LEFT] = "rear-left",
[PA_CHANNEL_POSITION_REAR_RIGHT] = "rear-right",
[PA_CHANNEL_POSITION_LFE] = "lfe",
[PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = "front-left-of-center",
[PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = "front-right-of-center",
[PA_CHANNEL_POSITION_SIDE_LEFT] = "side-left",
[PA_CHANNEL_POSITION_SIDE_RIGHT] = "side-right",
[PA_CHANNEL_POSITION_AUX0] = "aux0",
[PA_CHANNEL_POSITION_AUX1] = "aux1",
[PA_CHANNEL_POSITION_AUX2] = "aux2",
[PA_CHANNEL_POSITION_AUX3] = "aux3",
[PA_CHANNEL_POSITION_AUX4] = "aux4",
[PA_CHANNEL_POSITION_AUX5] = "aux5",
[PA_CHANNEL_POSITION_AUX6] = "aux6",
[PA_CHANNEL_POSITION_AUX7] = "aux7",
[PA_CHANNEL_POSITION_AUX8] = "aux8",
[PA_CHANNEL_POSITION_AUX9] = "aux9",
[PA_CHANNEL_POSITION_AUX10] = "aux10",
[PA_CHANNEL_POSITION_AUX11] = "aux11",
[PA_CHANNEL_POSITION_AUX12] = "aux12",
[PA_CHANNEL_POSITION_AUX13] = "aux13",
[PA_CHANNEL_POSITION_AUX14] = "aux14",
[PA_CHANNEL_POSITION_AUX15] = "aux15",
[PA_CHANNEL_POSITION_AUX16] = "aux16",
[PA_CHANNEL_POSITION_AUX17] = "aux17",
[PA_CHANNEL_POSITION_AUX18] = "aux18",
[PA_CHANNEL_POSITION_AUX19] = "aux19",
[PA_CHANNEL_POSITION_AUX20] = "aux20",
[PA_CHANNEL_POSITION_AUX21] = "aux21",
[PA_CHANNEL_POSITION_AUX22] = "aux22",
[PA_CHANNEL_POSITION_AUX23] = "aux23",
[PA_CHANNEL_POSITION_AUX24] = "aux24",
[PA_CHANNEL_POSITION_AUX25] = "aux25",
[PA_CHANNEL_POSITION_AUX26] = "aux26",
[PA_CHANNEL_POSITION_AUX27] = "aux27",
[PA_CHANNEL_POSITION_AUX28] = "aux28",
[PA_CHANNEL_POSITION_AUX29] = "aux29",
[PA_CHANNEL_POSITION_AUX30] = "aux30",
[PA_CHANNEL_POSITION_AUX31] = "aux31",
[PA_CHANNEL_POSITION_TOP_CENTER] = "top-center",
[PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = "top-front-center",
[PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = "top-front-left",
[PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = "top-front-right",
[PA_CHANNEL_POSITION_TOP_REAR_CENTER] = "top-rear-center",
[PA_CHANNEL_POSITION_TOP_REAR_LEFT] = "top-rear-left",
[PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = "top-rear-right"
};
const char *const pretty_table[PA_CHANNEL_POSITION_MAX] = {
[PA_CHANNEL_POSITION_MONO] = N_("Mono"),
[PA_CHANNEL_POSITION_FRONT_CENTER] = N_("Front Center"),
[PA_CHANNEL_POSITION_FRONT_LEFT] = N_("Front Left"),
[PA_CHANNEL_POSITION_FRONT_RIGHT] = N_("Front Right"),
[PA_CHANNEL_POSITION_REAR_CENTER] = N_("Rear Center"),
[PA_CHANNEL_POSITION_REAR_LEFT] = N_("Rear Left"),
[PA_CHANNEL_POSITION_REAR_RIGHT] = N_("Rear Right"),
[PA_CHANNEL_POSITION_LFE] = N_("Subwoofer"),
[PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = N_("Front Left-of-center"),
[PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = N_("Front Right-of-center"),
[PA_CHANNEL_POSITION_SIDE_LEFT] = N_("Side Left"),
[PA_CHANNEL_POSITION_SIDE_RIGHT] = N_("Side Right"),
[PA_CHANNEL_POSITION_AUX0] = N_("Auxiliary 0"),
[PA_CHANNEL_POSITION_AUX1] = N_("Auxiliary 1"),
[PA_CHANNEL_POSITION_AUX2] = N_("Auxiliary 2"),
[PA_CHANNEL_POSITION_AUX3] = N_("Auxiliary 3"),
[PA_CHANNEL_POSITION_AUX4] = N_("Auxiliary 4"),
[PA_CHANNEL_POSITION_AUX5] = N_("Auxiliary 5"),
[PA_CHANNEL_POSITION_AUX6] = N_("Auxiliary 6"),
[PA_CHANNEL_POSITION_AUX7] = N_("Auxiliary 7"),
[PA_CHANNEL_POSITION_AUX8] = N_("Auxiliary 8"),
[PA_CHANNEL_POSITION_AUX9] = N_("Auxiliary 9"),
[PA_CHANNEL_POSITION_AUX10] = N_("Auxiliary 10"),
[PA_CHANNEL_POSITION_AUX11] = N_("Auxiliary 11"),
[PA_CHANNEL_POSITION_AUX12] = N_("Auxiliary 12"),
[PA_CHANNEL_POSITION_AUX13] = N_("Auxiliary 13"),
[PA_CHANNEL_POSITION_AUX14] = N_("Auxiliary 14"),
[PA_CHANNEL_POSITION_AUX15] = N_("Auxiliary 15"),
[PA_CHANNEL_POSITION_AUX16] = N_("Auxiliary 16"),
[PA_CHANNEL_POSITION_AUX17] = N_("Auxiliary 17"),
[PA_CHANNEL_POSITION_AUX18] = N_("Auxiliary 18"),
[PA_CHANNEL_POSITION_AUX19] = N_("Auxiliary 19"),
[PA_CHANNEL_POSITION_AUX20] = N_("Auxiliary 20"),
[PA_CHANNEL_POSITION_AUX21] = N_("Auxiliary 21"),
[PA_CHANNEL_POSITION_AUX22] = N_("Auxiliary 22"),
[PA_CHANNEL_POSITION_AUX23] = N_("Auxiliary 23"),
[PA_CHANNEL_POSITION_AUX24] = N_("Auxiliary 24"),
[PA_CHANNEL_POSITION_AUX25] = N_("Auxiliary 25"),
[PA_CHANNEL_POSITION_AUX26] = N_("Auxiliary 26"),
[PA_CHANNEL_POSITION_AUX27] = N_("Auxiliary 27"),
[PA_CHANNEL_POSITION_AUX28] = N_("Auxiliary 28"),
[PA_CHANNEL_POSITION_AUX29] = N_("Auxiliary 29"),
[PA_CHANNEL_POSITION_AUX30] = N_("Auxiliary 30"),
[PA_CHANNEL_POSITION_AUX31] = N_("Auxiliary 31"),
[PA_CHANNEL_POSITION_TOP_CENTER] = N_("Top Center"),
[PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = N_("Top Front Center"),
[PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = N_("Top Front Left"),
[PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = N_("Top Front Right"),
[PA_CHANNEL_POSITION_TOP_REAR_CENTER] = N_("Top Rear Center"),
[PA_CHANNEL_POSITION_TOP_REAR_LEFT] = N_("Top Rear Left"),
[PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = N_("Top Rear Right")
};
pa_channel_map* pa_channel_map_init(pa_channel_map *m) {
unsigned c;
pa_assert(m);
m->channels = 0;
for (c = 0; c < PA_CHANNELS_MAX; c++)
m->map[c] = PA_CHANNEL_POSITION_INVALID;
return m;
}
pa_channel_map* pa_channel_map_init_mono(pa_channel_map *m) {
pa_assert(m);
pa_channel_map_init(m);
m->channels = 1;
m->map[0] = PA_CHANNEL_POSITION_MONO;
return m;
}
pa_channel_map* pa_channel_map_init_stereo(pa_channel_map *m) {
pa_assert(m);
pa_channel_map_init(m);
m->channels = 2;
m->map[0] = PA_CHANNEL_POSITION_LEFT;
m->map[1] = PA_CHANNEL_POSITION_RIGHT;
return m;
}
pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def) {
pa_assert(m);
pa_assert(pa_channels_valid(channels));
pa_assert(def < PA_CHANNEL_MAP_DEF_MAX);
pa_channel_map_init(m);
m->channels = (uint8_t) channels;
switch (def) {
case PA_CHANNEL_MAP_AIFF:
/* This is somewhat compatible with RFC3551 */
switch (channels) {
case 1:
m->map[0] = PA_CHANNEL_POSITION_MONO;
return m;
case 6:
m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
m->map[1] = PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
m->map[3] = PA_CHANNEL_POSITION_FRONT_RIGHT;
m->map[4] = PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
m->map[5] = PA_CHANNEL_POSITION_REAR_CENTER;
return m;
case 5:
m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
m->map[3] = PA_CHANNEL_POSITION_REAR_LEFT;
m->map[4] = PA_CHANNEL_POSITION_REAR_RIGHT;
/* Fall through */
case 2:
m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
return m;
case 3:
m->map[0] = PA_CHANNEL_POSITION_LEFT;
m->map[1] = PA_CHANNEL_POSITION_RIGHT;
m->map[2] = PA_CHANNEL_POSITION_CENTER;
return m;
case 4:
m->map[0] = PA_CHANNEL_POSITION_LEFT;
m->map[1] = PA_CHANNEL_POSITION_CENTER;
m->map[2] = PA_CHANNEL_POSITION_RIGHT;
m->map[3] = PA_CHANNEL_POSITION_REAR_CENTER;
return m;
default:
return NULL;
}
case PA_CHANNEL_MAP_ALSA:
switch (channels) {
case 1:
m->map[0] = PA_CHANNEL_POSITION_MONO;
return m;
case 8:
m->map[6] = PA_CHANNEL_POSITION_SIDE_LEFT;
m->map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT;
/* Fall through */
case 6:
m->map[5] = PA_CHANNEL_POSITION_LFE;
/* Fall through */
case 5:
m->map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
/* Fall through */
case 4:
m->map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
m->map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
/* Fall through */
case 2:
m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
return m;
default:
return NULL;
}
case PA_CHANNEL_MAP_AUX: {
unsigned i;
for (i = 0; i < channels; i++)
m->map[i] = PA_CHANNEL_POSITION_AUX0 + i;
return m;
}
case PA_CHANNEL_MAP_WAVEEX:
/* Following http://www.microsoft.com/whdc/device/audio/multichaud.mspx#EKLAC */
switch (channels) {
case 1:
m->map[0] = PA_CHANNEL_POSITION_MONO;
return m;
case 18:
m->map[15] = PA_CHANNEL_POSITION_TOP_REAR_LEFT;
m->map[16] = PA_CHANNEL_POSITION_TOP_REAR_CENTER;
m->map[17] = PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
/* Fall through */
case 15:
m->map[12] = PA_CHANNEL_POSITION_TOP_FRONT_LEFT;
m->map[13] = PA_CHANNEL_POSITION_TOP_FRONT_CENTER;
m->map[14] = PA_CHANNEL_POSITION_TOP_FRONT_RIGHT;
/* Fall through */
case 12:
m->map[11] = PA_CHANNEL_POSITION_TOP_CENTER;
/* Fall through */
case 11:
m->map[9] = PA_CHANNEL_POSITION_SIDE_LEFT;
m->map[10] = PA_CHANNEL_POSITION_SIDE_RIGHT;
/* Fall through */
case 9:
m->map[8] = PA_CHANNEL_POSITION_REAR_CENTER;
/* Fall through */
case 8:
m->map[6] = PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
m->map[7] = PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
/* Fall through */
case 6:
m->map[4] = PA_CHANNEL_POSITION_REAR_LEFT;
m->map[5] = PA_CHANNEL_POSITION_REAR_RIGHT;
/* Fall through */
case 4:
m->map[3] = PA_CHANNEL_POSITION_LFE;
/* Fall through */
case 3:
m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
/* Fall through */
case 2:
m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
return m;
default:
return NULL;
}
case PA_CHANNEL_MAP_OSS:
switch (channels) {
case 1:
m->map[0] = PA_CHANNEL_POSITION_MONO;
return m;
case 8:
m->map[6] = PA_CHANNEL_POSITION_REAR_LEFT;
m->map[7] = PA_CHANNEL_POSITION_REAR_RIGHT;
/* Fall through */
case 6:
m->map[4] = PA_CHANNEL_POSITION_SIDE_LEFT;
m->map[5] = PA_CHANNEL_POSITION_SIDE_RIGHT;
/* Fall through */
case 4:
m->map[3] = PA_CHANNEL_POSITION_LFE;
/* Fall through */
case 3:
m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
/* Fall through */
case 2:
m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
return m;
default:
return NULL;
}
default:
pa_assert_not_reached();
}
}
pa_channel_map* pa_channel_map_init_extend(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def) {
unsigned c;
pa_assert(m);
pa_assert(pa_channels_valid(channels));
pa_assert(def < PA_CHANNEL_MAP_DEF_MAX);
pa_channel_map_init(m);
for (c = channels; c > 0; c--) {
if (pa_channel_map_init_auto(m, c, def)) {
unsigned i = 0;
for (; c < channels; c++) {
m->map[c] = PA_CHANNEL_POSITION_AUX0 + i;
i++;
}
m->channels = (uint8_t) channels;
return m;
}
}
return NULL;
}
const char* pa_channel_position_to_string(pa_channel_position_t pos) {
if (pos < 0 || pos >= PA_CHANNEL_POSITION_MAX)
return NULL;
return table[pos];
}
const char* pa_channel_position_to_pretty_string(pa_channel_position_t pos) {
if (pos < 0 || pos >= PA_CHANNEL_POSITION_MAX)
return NULL;
pa_init_i18n();
return _(pretty_table[pos]);
}
int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b) {
unsigned c;
pa_assert(a);
pa_assert(b);
pa_return_val_if_fail(pa_channel_map_valid(a), 0);
if (PA_UNLIKELY(a == b))
return 1;
pa_return_val_if_fail(pa_channel_map_valid(b), 0);
if (a->channels != b->channels)
return 0;
for (c = 0; c < a->channels; c++)
if (a->map[c] != b->map[c])
return 0;
return 1;
}
char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map) {
unsigned channel;
bool first = true;
char *e;
pa_assert(s);
pa_assert(l > 0);
pa_assert(map);
pa_init_i18n();
if (!pa_channel_map_valid(map)) {
pa_snprintf(s, l, _("(invalid)"));
return s;
}
*(e = s) = 0;
for (channel = 0; channel < map->channels && l > 1; channel++) {
l -= pa_snprintf(e, l, "%s%s",
first ? "" : ",",
pa_channel_position_to_string(map->map[channel]));
e = strchr(e, 0);
first = false;
}
return s;
}
pa_channel_position_t pa_channel_position_from_string(const char *p) {
pa_channel_position_t i;
pa_assert(p);
/* Some special aliases */
if (pa_streq(p, "left"))
return PA_CHANNEL_POSITION_LEFT;
else if (pa_streq(p, "right"))
return PA_CHANNEL_POSITION_RIGHT;
else if (pa_streq(p, "center"))
return PA_CHANNEL_POSITION_CENTER;
else if (pa_streq(p, "subwoofer"))
return PA_CHANNEL_POSITION_SUBWOOFER;
for (i = 0; i < PA_CHANNEL_POSITION_MAX; i++)
if (pa_streq(p, table[i]))
return i;
return PA_CHANNEL_POSITION_INVALID;
}
pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) {
pa_channel_map map;
char **tokens;
int i, n_tokens;
pa_assert(rmap);
pa_assert(s);
pa_channel_map_init(&map);
/* We don't need to match against the well known channel mapping
* "mono" here explicitly, because that can be understood as
* listing with one channel called "mono". */
if (pa_streq(s, "stereo")) {
map.channels = 2;
map.map[0] = PA_CHANNEL_POSITION_LEFT;
map.map[1] = PA_CHANNEL_POSITION_RIGHT;
goto finish;
} else if (pa_streq(s, "surround-21")) {
map.channels = 3;
map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
map.map[2] = PA_CHANNEL_POSITION_LFE;
goto finish;
} else if (pa_streq(s, "surround-40")) {
map.channels = 4;
map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
goto finish;
} else if (pa_streq(s, "surround-41")) {
map.channels = 5;
map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
map.map[4] = PA_CHANNEL_POSITION_LFE;
goto finish;
} else if (pa_streq(s, "surround-50")) {
map.channels = 5;
map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
goto finish;
} else if (pa_streq(s, "surround-51")) {
map.channels = 6;
map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
map.map[5] = PA_CHANNEL_POSITION_LFE;
goto finish;
} else if (pa_streq(s, "surround-71")) {
map.channels = 8;
map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
map.map[5] = PA_CHANNEL_POSITION_LFE;
map.map[6] = PA_CHANNEL_POSITION_SIDE_LEFT;
map.map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT;
goto finish;
}
map.channels = 0;
tokens = pw_split_strv(s, ",", -1, &n_tokens);
for (i = 0; i < n_tokens; i++) {
pa_channel_position_t f;
if (map.channels >= PA_CHANNELS_MAX) {
pw_free_strv(tokens);
return NULL;
}
if ((f = pa_channel_position_from_string(tokens[i])) == PA_CHANNEL_POSITION_INVALID) {
pw_free_strv(tokens);
return NULL;
}
map.map[map.channels++] = f;
}
pw_free_strv(tokens);
finish:
if (!pa_channel_map_valid(&map))
return NULL;
*rmap = map;
return rmap;
}
int pa_channel_map_valid(const pa_channel_map *map) {
unsigned c;
pa_assert(map);
if (!pa_channels_valid(map->channels))
return 0;
for (c = 0; c < map->channels; c++)
if (map->map[c] < 0 || map->map[c] >= PA_CHANNEL_POSITION_MAX)
return 0;
return 1;
}
int pa_channel_map_compatible(const pa_channel_map *map, const pa_sample_spec *ss) {
pa_assert(map);
pa_assert(ss);
pa_return_val_if_fail(pa_channel_map_valid(map), 0);
pa_return_val_if_fail(pa_sample_spec_valid(ss), 0);
return map->channels == ss->channels;
}
int pa_channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) {
pa_channel_position_mask_t am, bm;
pa_assert(a);
pa_assert(b);
pa_return_val_if_fail(pa_channel_map_valid(a), 0);
if (PA_UNLIKELY(a == b))
return 1;
pa_return_val_if_fail(pa_channel_map_valid(b), 0);
am = pa_channel_map_mask(a);
bm = pa_channel_map_mask(b);
return (bm & am) == bm;
}
int pa_channel_map_can_balance(const pa_channel_map *map) {
pa_channel_position_mask_t m;
pa_assert(map);
pa_return_val_if_fail(pa_channel_map_valid(map), 0);
m = pa_channel_map_mask(map);
return
(PA_CHANNEL_POSITION_MASK_LEFT & m) &&
(PA_CHANNEL_POSITION_MASK_RIGHT & m);
}
int pa_channel_map_can_fade(const pa_channel_map *map) {
pa_channel_position_mask_t m;
pa_assert(map);
pa_return_val_if_fail(pa_channel_map_valid(map), 0);
m = pa_channel_map_mask(map);
return
(PA_CHANNEL_POSITION_MASK_FRONT & m) &&
(PA_CHANNEL_POSITION_MASK_REAR & m);
}
int pa_channel_map_can_lfe_balance(const pa_channel_map *map) {
pa_channel_position_mask_t m;
pa_assert(map);
pa_return_val_if_fail(pa_channel_map_valid(map), 0);
m = pa_channel_map_mask(map);
return
(PA_CHANNEL_POSITION_MASK_LFE & m) &&
(PA_CHANNEL_POSITION_MASK_HFE & m);
}
const char* pa_channel_map_to_name(const pa_channel_map *map) {
pa_bitset_t in_map[PA_BITSET_ELEMENTS(PA_CHANNEL_POSITION_MAX)];
unsigned c;
pa_assert(map);
pa_return_val_if_fail(pa_channel_map_valid(map), NULL);
memset(in_map, 0, sizeof(in_map));
for (c = 0; c < map->channels; c++)
pa_bitset_set(in_map, map->map[c], true);
if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
PA_CHANNEL_POSITION_MONO, -1))
return "mono";
if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, -1))
return "stereo";
if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, -1))
return "surround-40";
if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
PA_CHANNEL_POSITION_LFE, -1))
return "surround-41";
if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
PA_CHANNEL_POSITION_FRONT_CENTER, -1))
return "surround-50";
if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE, -1))
return "surround-51";
if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT, -1))
return "surround-71";
return NULL;
}
const char* pa_channel_map_to_pretty_name(const pa_channel_map *map) {
pa_bitset_t in_map[PA_BITSET_ELEMENTS(PA_CHANNEL_POSITION_MAX)];
unsigned c;
pa_assert(map);
pa_return_val_if_fail(pa_channel_map_valid(map), NULL);
memset(in_map, 0, sizeof(in_map));
for (c = 0; c < map->channels; c++)
pa_bitset_set(in_map, map->map[c], true);
pa_init_i18n();
if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
PA_CHANNEL_POSITION_MONO, -1))
return _("Mono");
if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, -1))
return _("Stereo");
if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, -1))
return _("Surround 4.0");
if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
PA_CHANNEL_POSITION_LFE, -1))
return _("Surround 4.1");
if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
PA_CHANNEL_POSITION_FRONT_CENTER, -1))
return _("Surround 5.0");
if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE, -1))
return _("Surround 5.1");
if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT, -1))
return _("Surround 7.1");
return NULL;
}
int pa_channel_map_has_position(const pa_channel_map *map, pa_channel_position_t p) {
unsigned c;
pa_return_val_if_fail(pa_channel_map_valid(map), 0);
pa_return_val_if_fail(p < PA_CHANNEL_POSITION_MAX, 0);
for (c = 0; c < map->channels; c++)
if (map->map[c] == p)
return 1;
return 0;
}
pa_channel_position_mask_t pa_channel_map_mask(const pa_channel_map *map) {
unsigned c;
pa_channel_position_mask_t r = 0;
pa_return_val_if_fail(pa_channel_map_valid(map), 0);
for (c = 0; c < map->channels; c++)
r |= PA_CHANNEL_POSITION_MASK(map->map[c]);
return r;
}

453
src/context.c Normal file
View file

@ -0,0 +1,453 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <errno.h>
#include <pipewire/log.h>
#include <pipewire/properties.h>
#include <pipewire/main-loop.h>
#include <pipewire/core.h>
#include <pipewire/remote.h>
#include <pulse/context.h>
#include "internal.h"
int pa_context_set_error(pa_context *c, int error) {
spa_assert(error >= 0);
spa_assert(error < PA_ERR_MAX);
if (c)
c->error = error;
return error;
}
static void context_unlink(pa_context *c)
{
pa_stream *s, *t;
spa_list_for_each_safe(s, t, &c->streams, link) {
pa_stream_set_state(s, c->state == PA_CONTEXT_FAILED ?
PA_STREAM_FAILED : PA_STREAM_TERMINATED);
pa_stream_unref(s);
}
while(!spa_list_is_empty(&c->operations))
pa_operation_cancel(spa_list_first(&c->operations, pa_operation, link));
}
void pa_context_set_state(pa_context *c, pa_context_state_t st) {
spa_assert(c);
spa_assert(c->refcount >= 1);
if (c->state == st)
return;
pa_context_ref(c);
c->state = st;
if (c->state_callback)
c->state_callback(c, c->state_userdata);
if (st == PA_CONTEXT_FAILED || st == PA_CONTEXT_TERMINATED)
context_unlink(c);
pa_context_unref(c);
}
static void context_fail(pa_context *c, int error) {
spa_assert(c);
spa_assert(c->refcount >= 1);
pa_context_set_error(c, error);
pa_context_set_state(c, PA_CONTEXT_FAILED);
}
pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name)
{
return pa_context_new_with_proplist(mainloop, name, NULL);
}
static void registry_event_global(void *data, uint32_t id, uint32_t parent_id,
uint32_t permissions, uint32_t type, uint32_t version,
const struct spa_dict *props)
{
pa_context *c = data;
struct global *g;
pw_log_debug("global %d", id);
g = calloc(1, sizeof(struct global));
g->id = id;
g->parent_id = parent_id;
g->type = type;
g->props = props ? pw_properties_new_dict(props) : NULL;
spa_list_append(&c->globals, &g->link);
}
struct global *pa_context_find_global(pa_context *c, uint32_t id)
{
struct global *g;
spa_list_for_each(g, &c->globals, link) {
if (g->id == id)
return g;
}
return NULL;
}
static void registry_event_global_remove(void *object, uint32_t id)
{
pa_context *c = object;
struct global *g;
pw_log_debug("remove %d", id);
if ((g = pa_context_find_global(c, id)) == NULL)
return;
spa_list_remove(&g->link);
pw_properties_free(g->props);
if (g->destroy && g->info)
g->destroy(g->info);
free(g);
}
static const struct pw_registry_proxy_events registry_events =
{
PW_VERSION_REGISTRY_PROXY_EVENTS,
.global = registry_event_global,
.global_remove = registry_event_global_remove,
};
struct ready_data
{
pa_context *context;
};
static void on_ready(pa_operation *o, void *userdata)
{
struct ready_data *d = userdata;
pa_context_set_state(d->context, PA_CONTEXT_READY);
}
static void remote_state_changed(void *data, enum pw_remote_state old,
enum pw_remote_state state, const char *error)
{
pa_context *c = data;
pa_operation *o;
struct ready_data *d;
switch(state) {
case PW_REMOTE_STATE_ERROR:
context_fail(c, PA_ERR_CONNECTIONTERMINATED);
break;
case PW_REMOTE_STATE_UNCONNECTED:
break;
case PW_REMOTE_STATE_CONNECTING:
pa_context_set_state(c, PA_CONTEXT_CONNECTING);
break;
case PW_REMOTE_STATE_CONNECTED:
pa_context_set_state(c, PA_CONTEXT_AUTHORIZING);
pa_context_set_state(c, PA_CONTEXT_SETTING_NAME);
c->core_proxy = pw_remote_get_core_proxy(c->remote);
c->registry_proxy = pw_core_proxy_get_registry(c->core_proxy,
c->t->registry,
PW_VERSION_REGISTRY, 0);
pw_registry_proxy_add_listener(c->registry_proxy,
&c->registry_listener,
&registry_events, c);
o = pa_operation_new(c, NULL, on_ready, sizeof(struct ready_data));
d = o->userdata;
d->context = c;
break;
}
}
static void remote_sync_reply(void *data, uint32_t seq)
{
pa_context *c = data;
pa_operation *o;
pw_log_debug("done %d", seq);
spa_list_for_each(o, &c->operations, link) {
if (o->seq != seq)
continue;
pa_operation_ref(o);
if (o->callback)
o->callback(o, o->userdata);
pa_operation_unref(o);
break;
}
}
static const struct pw_remote_events remote_events = {
PW_VERSION_REMOTE_EVENTS,
.state_changed = remote_state_changed,
.sync_reply = remote_sync_reply
};
pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *name, pa_proplist *p)
{
struct pw_core *core;
struct pw_loop *loop;
struct pw_remote *r;
struct pw_properties *props;
pa_context *c;
spa_assert(mainloop);
props = pw_properties_new(NULL, NULL);
if (name)
pw_properties_set(props, PA_PROP_APPLICATION_NAME, name);
loop = mainloop->userdata;
core = pw_core_new(loop, NULL);
r = pw_remote_new(core, props, sizeof(struct pa_context));
if (r == NULL)
return NULL;
c = pw_remote_get_user_data(r);
c->loop = loop;
c->core = core;
c->t = pw_core_get_type(core);
c->remote = r;
pw_remote_add_listener(r, &c->remote_listener, &remote_events, c);
c->proplist = p ? pa_proplist_copy(p) : pa_proplist_new();
c->refcount = 1;
c->client_index = PA_INVALID_INDEX;
if (name)
pa_proplist_sets(c->proplist, PA_PROP_APPLICATION_NAME, name);
c->mainloop = mainloop;
c->error = 0;
c->state = PA_CONTEXT_UNCONNECTED;
spa_list_init(&c->globals);
spa_list_init(&c->streams);
spa_list_init(&c->operations);
return c;
}
static void context_free(pa_context *c)
{
context_unlink(c);
if (c->proplist)
pa_proplist_free(c->proplist);
free(c);
}
void pa_context_unref(pa_context *c)
{
spa_assert(c);
spa_assert(c->refcount >= 1);
if (--c->refcount == 0)
context_free(c);
}
pa_context* pa_context_ref(pa_context *c)
{
spa_assert(c);
spa_assert(c->refcount >= 1);
c->refcount++;
return c;
}
void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, void *userdata)
{
spa_assert(c);
spa_assert(c->refcount >= 1);
if (c->state == PA_CONTEXT_TERMINATED || c->state == PA_CONTEXT_FAILED)
return;
c->state_callback = cb;
c->state_userdata = userdata;
}
void pa_context_set_event_callback(pa_context *c, pa_context_event_cb_t cb, void *userdata)
{
spa_assert(c);
spa_assert(c->refcount >= 1);
if (c->state == PA_CONTEXT_TERMINATED || c->state == PA_CONTEXT_FAILED)
return;
c->event_callback = cb;
c->event_userdata = userdata;
}
int pa_context_errno(pa_context *c)
{
if (!c)
return PA_ERR_INVALID;
spa_assert(c->refcount >= 1);
return c->error;
}
int pa_context_is_pending(pa_context *c)
{
pw_log_warn("Not Implemented");
return 0;
}
pa_context_state_t pa_context_get_state(pa_context *c)
{
spa_assert(c);
spa_assert(c->refcount >= 1);
return c->state;
}
int pa_context_connect(pa_context *c, const char *server, pa_context_flags_t flags, const pa_spawn_api *api)
{
int res;
spa_assert(c);
spa_assert(c->refcount >= 1);
PA_CHECK_VALIDITY(c, c->state == PA_CONTEXT_UNCONNECTED, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(c, !(flags & ~(PA_CONTEXT_NOAUTOSPAWN|PA_CONTEXT_NOFAIL)), PA_ERR_INVALID);
PA_CHECK_VALIDITY(c, !server || *server, PA_ERR_INVALID);
pa_context_ref(c);
c->no_fail = !!(flags & PA_CONTEXT_NOFAIL);
res = pw_remote_connect(c->remote);
pa_context_unref(c);
return res;
}
void pa_context_disconnect(pa_context *c)
{
spa_assert(c);
spa_assert(c->refcount >= 1);
pw_remote_disconnect(c->remote);
if (PA_CONTEXT_IS_GOOD(c->state))
pa_context_set_state(c, PA_CONTEXT_TERMINATED);
}
pa_operation* pa_context_drain(pa_context *c, pa_context_notify_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
int pa_context_is_local(pa_context *c)
{
pw_log_warn("Not Implemented");
return 0;
}
pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
const char* pa_context_get_server(pa_context *c)
{
pw_log_warn("Not Implemented");
return NULL;
}
uint32_t pa_context_get_protocol_version(pa_context *c)
{
return PA_PROTOCOL_VERSION;
}
uint32_t pa_context_get_server_protocol_version(pa_context *c)
{
return PA_PROTOCOL_VERSION;
}
pa_operation *pa_context_proplist_update(pa_context *c, pa_update_mode_t mode, pa_proplist *p, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation *pa_context_proplist_remove(pa_context *c, const char *const keys[], pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
uint32_t pa_context_get_index(pa_context *c)
{
return c->client_index;
}
pa_time_event* pa_context_rttime_new(pa_context *c, pa_usec_t usec, pa_time_event_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
void pa_context_rttime_restart(pa_context *c, pa_time_event *e, pa_usec_t usec)
{
pw_log_warn("Not Implemented");
}
size_t pa_context_get_tile_size(pa_context *c, const pa_sample_spec *ss)
{
pw_log_warn("Not Implemented");
return 1024;
}
int pa_context_load_cookie_from_file(pa_context *c, const char *cookie_file_path)
{
pw_log_warn("Not Implemented");
return -ENOTSUP;
}

241
src/core-format.c Normal file
View file

@ -0,0 +1,241 @@
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio 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 Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "core-format.h"
#include <pulse/def.h>
#include <pulse/xmalloc.h>
#include "internal.h"
int pa_format_info_get_sample_format(const pa_format_info *f, pa_sample_format_t *sf) {
int r;
char *sf_str;
pa_sample_format_t sf_local;
pa_assert(f);
pa_assert(sf);
r = pa_format_info_get_prop_string(f, PA_PROP_FORMAT_SAMPLE_FORMAT, &sf_str);
if (r < 0)
return r;
sf_local = pa_parse_sample_format(sf_str);
pa_xfree(sf_str);
if (!pa_sample_format_valid(sf_local)) {
pa_log_debug("Invalid sample format.");
return -PA_ERR_INVALID;
}
*sf = sf_local;
return 0;
}
int pa_format_info_get_rate(const pa_format_info *f, uint32_t *rate) {
int r;
int rate_local;
pa_assert(f);
pa_assert(rate);
r = pa_format_info_get_prop_int(f, PA_PROP_FORMAT_RATE, &rate_local);
if (r < 0)
return r;
if (!pa_sample_rate_valid(rate_local)) {
pa_log_debug("Invalid sample rate: %i", rate_local);
return -PA_ERR_INVALID;
}
*rate = rate_local;
return 0;
}
int pa_format_info_get_channels(const pa_format_info *f, uint8_t *channels) {
int r;
int channels_local;
pa_assert(f);
pa_assert(channels);
r = pa_format_info_get_prop_int(f, PA_PROP_FORMAT_CHANNELS, &channels_local);
if (r < 0)
return r;
if (!pa_channels_valid(channels_local)) {
pa_log_debug("Invalid channel count: %i", channels_local);
return -PA_ERR_INVALID;
}
*channels = channels_local;
return 0;
}
int pa_format_info_get_channel_map(const pa_format_info *f, pa_channel_map *map) {
int r;
char *map_str;
pa_assert(f);
pa_assert(map);
r = pa_format_info_get_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, &map_str);
if (r < 0)
return r;
map = pa_channel_map_parse(map, map_str);
pa_xfree(map_str);
if (!map) {
pa_log_debug("Failed to parse channel map.");
return -PA_ERR_INVALID;
}
return 0;
}
pa_format_info *pa_format_info_from_sample_spec2(const pa_sample_spec *ss, const pa_channel_map *map, bool set_format,
bool set_rate, bool set_channels) {
pa_format_info *format = NULL;
pa_assert(ss);
format = pa_format_info_new();
format->encoding = PA_ENCODING_PCM;
if (set_format)
pa_format_info_set_sample_format(format, ss->format);
if (set_rate)
pa_format_info_set_rate(format, ss->rate);
if (set_channels) {
pa_format_info_set_channels(format, ss->channels);
if (map) {
if (map->channels != ss->channels) {
pa_log_debug("Channel map is incompatible with the sample spec.");
goto fail;
}
pa_format_info_set_channel_map(format, map);
}
}
return format;
fail:
if (format)
pa_format_info_free(format);
return NULL;
}
int pa_format_info_to_sample_spec2(const pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map,
const pa_sample_spec *fallback_ss, const pa_channel_map *fallback_map) {
int r, r2;
pa_sample_spec ss_local;
pa_channel_map map_local;
pa_assert(f);
pa_assert(ss);
pa_assert(map);
pa_assert(fallback_ss);
pa_assert(fallback_map);
if (!pa_format_info_is_pcm(f))
return pa_format_info_to_sample_spec_fake(f, ss, map);
r = pa_format_info_get_sample_format(f, &ss_local.format);
if (r == -PA_ERR_NOENTITY)
ss_local.format = fallback_ss->format;
else if (r < 0)
return r;
pa_assert(pa_sample_format_valid(ss_local.format));
r = pa_format_info_get_rate(f, &ss_local.rate);
if (r == -PA_ERR_NOENTITY)
ss_local.rate = fallback_ss->rate;
else if (r < 0)
return r;
pa_assert(pa_sample_rate_valid(ss_local.rate));
r = pa_format_info_get_channels(f, &ss_local.channels);
r2 = pa_format_info_get_channel_map(f, &map_local);
if (r == -PA_ERR_NOENTITY && r2 >= 0)
ss_local.channels = map_local.channels;
else if (r == -PA_ERR_NOENTITY)
ss_local.channels = fallback_ss->channels;
else if (r < 0)
return r;
pa_assert(pa_channels_valid(ss_local.channels));
if (r2 >= 0 && map_local.channels != ss_local.channels) {
pa_log_debug("Channel map is not compatible with the sample spec.");
return -PA_ERR_INVALID;
}
if (r2 == -PA_ERR_NOENTITY) {
if (fallback_map->channels == ss_local.channels)
map_local = *fallback_map;
else
pa_channel_map_init_extend(&map_local, ss_local.channels, PA_CHANNEL_MAP_DEFAULT);
} else if (r2 < 0)
return r2;
pa_assert(pa_channel_map_valid(&map_local));
pa_assert(ss_local.channels == map_local.channels);
*ss = ss_local;
*map = map_local;
return 0;
}
int pa_format_info_to_sample_spec_fake(const pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map) {
int rate;
pa_assert(f);
pa_assert(ss);
/* Note: When we add support for non-IEC61937 encapsulated compressed
* formats, this function should return a non-zero values for these. */
ss->format = PA_SAMPLE_S16LE;
ss->channels = 2;
if (map)
pa_channel_map_init_stereo(map);
pa_return_val_if_fail(pa_format_info_get_prop_int(f, PA_PROP_FORMAT_RATE, &rate) == 0, -PA_ERR_INVALID);
ss->rate = (uint32_t) rate;
if (f->encoding == PA_ENCODING_EAC3_IEC61937)
ss->rate *= 4;
return 0;
}

79
src/core-format.h Normal file
View file

@ -0,0 +1,79 @@
#ifndef foocoreformathfoo
#define foocoreformathfoo
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio 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 Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#include <pulse/format.h>
#include <stdbool.h>
/* Gets the sample format stored in the format info. Returns a negative error
* code on failure. If the sample format property is not set at all, returns
* -PA_ERR_NOENTITY. */
int pa_format_info_get_sample_format(const pa_format_info *f, pa_sample_format_t *sf);
/* Gets the sample rate stored in the format info. Returns a negative error
* code on failure. If the sample rate property is not set at all, returns
* -PA_ERR_NOENTITY. */
int pa_format_info_get_rate(const pa_format_info *f, uint32_t *rate);
/* Gets the channel count stored in the format info. Returns a negative error
* code on failure. If the channels property is not set at all, returns
* -PA_ERR_NOENTITY. */
int pa_format_info_get_channels(const pa_format_info *f, uint8_t *channels);
/* Gets the channel map stored in the format info. Returns a negative error
* code on failure. If the channel map property is not set at all, returns
* -PA_ERR_NOENTITY. */
int pa_format_info_get_channel_map(const pa_format_info *f, pa_channel_map *map);
/* Convert a sample spec and an optional channel map to a new PCM format info
* object (remember to free it). If map is NULL, then the channel map will be
* left unspecified. If some fields of the sample spec should be ignored, pass
* false for set_format, set_rate and set_channels as appropriate, then those
* fields will be left unspecified. This function returns NULL if the input is
* invalid (for example, setting the sample rate was requested, but the rate
* in ss is invalid).
*
* pa_format_info_from_sample_spec() exists too. This "version 2" was created,
* because the original function doesn't provide the possibility of ignoring
* some of the sample spec fields. That functionality can't be added to the
* original function, because the function is a part of the public API and
* adding parameters to it would break the API. */
pa_format_info *pa_format_info_from_sample_spec2(const pa_sample_spec *ss, const pa_channel_map *map, bool set_format,
bool set_rate, bool set_channels);
/* Convert the format info into a sample spec and a channel map. If the format
* info doesn't contain some information, the fallback sample spec and channel
* map are used to populate the output.
*
* pa_format_info_to_sample_spec() exists too. This "version 2" was created,
* because the original function doesn't provide the possibility of specifying
* a fallback sample spec and channel map. That functionality can't be added to
* the original function, because the function is part of the public API and
* adding parameters to it would break the API. */
int pa_format_info_to_sample_spec2(const pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map,
const pa_sample_spec *fallback_ss, const pa_channel_map *fallback_map);
/* For compressed formats. Converts the format info into a sample spec and a
* channel map that an ALSA device can use as its configuration parameters when
* playing back the compressed data. That is, the returned sample spec doesn't
* describe the audio content, but the device parameters. */
int pa_format_info_to_sample_spec_fake(const pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map);
#endif

47
src/direction.c Normal file
View file

@ -0,0 +1,47 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <errno.h>
#include <pulse/direction.h>
#define pa_init_i18n()
#define _(String) (String)
int pa_direction_valid(pa_direction_t direction)
{
if (direction != PA_DIRECTION_INPUT
&& direction != PA_DIRECTION_OUTPUT
&& direction != (PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT))
return 0;
return 1;
}
const char *pa_direction_to_string(pa_direction_t direction) {
pa_init_i18n();
if (direction == PA_DIRECTION_INPUT)
return _("input");
if (direction == PA_DIRECTION_OUTPUT)
return _("output");
if (direction == (PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT))
return _("bidirectional");
return _("invalid");
}

74
src/error.c Normal file
View file

@ -0,0 +1,74 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <unistd.h>
#include <errno.h>
#include <spa/utils/defs.h>
#include <pulse/def.h>
#include <pulse/error.h>
#define N_(String) (String)
#define _(String) (String)
#define pa_init_i18n()
const char*pa_strerror(int error)
{
static const char* const errortab[PA_ERR_MAX] = {
[PA_OK] = N_("OK"),
[PA_ERR_ACCESS] = N_("Access denied"),
[PA_ERR_COMMAND] = N_("Unknown command"),
[PA_ERR_INVALID] = N_("Invalid argument"),
[PA_ERR_EXIST] = N_("Entity exists"),
[PA_ERR_NOENTITY] = N_("No such entity"),
[PA_ERR_CONNECTIONREFUSED] = N_("Connection refused"),
[PA_ERR_PROTOCOL] = N_("Protocol error"),
[PA_ERR_TIMEOUT] = N_("Timeout"),
[PA_ERR_AUTHKEY] = N_("No authentication key"),
[PA_ERR_INTERNAL] = N_("Internal error"),
[PA_ERR_CONNECTIONTERMINATED] = N_("Connection terminated"),
[PA_ERR_KILLED] = N_("Entity killed"),
[PA_ERR_INVALIDSERVER] = N_("Invalid server"),
[PA_ERR_MODINITFAILED] = N_("Module initialization failed"),
[PA_ERR_BADSTATE] = N_("Bad state"),
[PA_ERR_NODATA] = N_("No data"),
[PA_ERR_VERSION] = N_("Incompatible protocol version"),
[PA_ERR_TOOLARGE] = N_("Too large"),
[PA_ERR_NOTSUPPORTED] = N_("Not supported"),
[PA_ERR_UNKNOWN] = N_("Unknown error code"),
[PA_ERR_NOEXTENSION] = N_("No such extension"),
[PA_ERR_OBSOLETE] = N_("Obsolete functionality"),
[PA_ERR_NOTIMPLEMENTED] = N_("Missing implementation"),
[PA_ERR_FORKED] = N_("Client forked"),
[PA_ERR_IO] = N_("Input/Output error"),
[PA_ERR_BUSY] = N_("Device or resource busy")
};
pa_init_i18n();
if (error < 0)
error = -error;
if (error >= PA_ERR_MAX)
return NULL;
return _(errortab[error]);
}

105
src/ext-device-manager.c Normal file
View file

@ -0,0 +1,105 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <errno.h>
#include <pipewire/log.h>
#include <pulse/ext-device-manager.h>
#include "internal.h"
pa_operation *pa_ext_device_manager_test(
pa_context *c,
pa_ext_device_manager_test_cb_t cb,
void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation *pa_ext_device_manager_read(
pa_context *c,
pa_ext_device_manager_read_cb_t cb,
void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation *pa_ext_device_manager_set_device_description(
pa_context *c,
const char* device,
const char* description,
pa_context_success_cb_t cb,
void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation *pa_ext_device_manager_delete(
pa_context *c,
const char *const s[],
pa_context_success_cb_t cb,
void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation *pa_ext_device_manager_enable_role_device_priority_routing(
pa_context *c,
int enable,
pa_context_success_cb_t cb,
void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation *pa_ext_device_manager_reorder_devices_for_role(
pa_context *c,
const char* role,
const char** devices,
pa_context_success_cb_t cb,
void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation *pa_ext_device_manager_subscribe(
pa_context *c,
int enable,
pa_context_success_cb_t cb,
void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
void pa_ext_device_manager_set_subscribe_cb(
pa_context *c,
pa_ext_device_manager_subscribe_cb_t cb,
void *userdata)
{
pw_log_warn("Not Implemented");
}

84
src/ext-device-restore.c Normal file
View file

@ -0,0 +1,84 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <pipewire/log.h>
#include <pulse/ext-device-restore.h>
#include "internal.h"
pa_operation *pa_ext_device_restore_test(
pa_context *c,
pa_ext_device_restore_test_cb_t cb,
void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation *pa_ext_device_restore_subscribe(
pa_context *c,
int enable,
pa_context_success_cb_t cb,
void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
void pa_ext_device_restore_set_subscribe_cb(
pa_context *c,
pa_ext_device_restore_subscribe_cb_t cb,
void *userdata)
{
pw_log_warn("Not Implemented");
}
pa_operation *pa_ext_device_restore_read_formats_all(
pa_context *c,
pa_ext_device_restore_read_device_formats_cb_t cb,
void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation *pa_ext_device_restore_read_formats(
pa_context *c,
pa_device_type_t type,
uint32_t idx,
pa_ext_device_restore_read_device_formats_cb_t cb,
void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation *pa_ext_device_restore_save_formats(
pa_context *c,
pa_device_type_t type,
uint32_t idx,
uint8_t n_formats,
pa_format_info **formats,
pa_context_success_cb_t cb,
void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}

679
src/format.c Normal file
View file

@ -0,0 +1,679 @@
/***
This file is part of PulseAudio.
Copyright 2011 Intel Corporation
Copyright 2011 Collabora Multimedia
Copyright 2011 Arun Raghavan <arun.raghavan@collabora.co.uk>
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio 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 Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <pulse/xmalloc.h>
#include <pulse/format.h>
#include "core-format.h"
#include "json.h"
#include "internal.h"
#include "strbuf.h"
#define PA_JSON_MIN_KEY "min"
#define PA_JSON_MAX_KEY "max"
static int pa_format_info_prop_compatible(const char *one, const char *two);
static const char* const _encoding_str_table[]= {
[PA_ENCODING_PCM] = "pcm",
[PA_ENCODING_AC3_IEC61937] = "ac3-iec61937",
[PA_ENCODING_EAC3_IEC61937] = "eac3-iec61937",
[PA_ENCODING_MPEG_IEC61937] = "mpeg-iec61937",
[PA_ENCODING_DTS_IEC61937] = "dts-iec61937",
[PA_ENCODING_MPEG2_AAC_IEC61937] = "mpeg2-aac-iec61937",
[PA_ENCODING_ANY] = "any",
};
const char *pa_encoding_to_string(pa_encoding_t e) {
if (e < 0 || e >= PA_ENCODING_MAX)
return NULL;
return _encoding_str_table[e];
}
pa_encoding_t pa_encoding_from_string(const char *encoding) {
pa_encoding_t e;
for (e = PA_ENCODING_ANY; e < PA_ENCODING_MAX; e++)
if (pa_streq(_encoding_str_table[e], encoding))
return e;
return PA_ENCODING_INVALID;
}
pa_format_info* pa_format_info_new(void) {
pa_format_info *f = pa_xnew(pa_format_info, 1);
f->encoding = PA_ENCODING_INVALID;
f->plist = pa_proplist_new();
return f;
}
pa_format_info* pa_format_info_copy(const pa_format_info *src) {
pa_format_info *dest;
pa_assert(src);
dest = pa_xnew(pa_format_info, 1);
dest->encoding = src->encoding;
if (src->plist)
dest->plist = pa_proplist_copy(src->plist);
else
dest->plist = NULL;
return dest;
}
void pa_format_info_free(pa_format_info *f) {
pa_assert(f);
pa_proplist_free(f->plist);
pa_xfree(f);
}
int pa_format_info_valid(const pa_format_info *f) {
return (f->encoding >= 0 && f->encoding < PA_ENCODING_MAX && f->plist != NULL);
}
int pa_format_info_is_pcm(const pa_format_info *f) {
return f->encoding == PA_ENCODING_PCM;
}
char *pa_format_info_snprint(char *s, size_t l, const pa_format_info *f) {
char *tmp;
pa_assert(s);
pa_assert(l > 0);
pa_assert(f);
pa_init_i18n();
if (!pa_format_info_valid(f))
pa_snprintf(s, l, _("(invalid)"));
else {
tmp = pa_proplist_to_string_sep(f->plist, " ");
if (tmp[0])
pa_snprintf(s, l, "%s, %s", pa_encoding_to_string(f->encoding), tmp);
else
pa_snprintf(s, l, "%s", pa_encoding_to_string(f->encoding));
pa_xfree(tmp);
}
return s;
}
pa_format_info* pa_format_info_from_string(const char *str) {
pa_format_info *f = pa_format_info_new();
char *encoding = NULL, *properties = NULL;
size_t pos;
pos = strcspn(str, ",");
encoding = pa_xstrndup(str, pos);
f->encoding = pa_encoding_from_string(pa_strip(encoding));
if (f->encoding == PA_ENCODING_INVALID)
goto error;
if (pos != strlen(str)) {
pa_proplist *plist;
properties = pa_xstrdup(&str[pos+1]);
plist = pa_proplist_from_string(properties);
if (!plist)
goto error;
pa_proplist_free(f->plist);
f->plist = plist;
}
out:
if (encoding)
pa_xfree(encoding);
if (properties)
pa_xfree(properties);
return f;
error:
pa_format_info_free(f);
f = NULL;
goto out;
}
int pa_format_info_is_compatible(const pa_format_info *first, const pa_format_info *second) {
const char *key;
void *state = NULL;
pa_assert(first);
pa_assert(second);
if (first->encoding != second->encoding)
return false;
while ((key = pa_proplist_iterate(first->plist, &state))) {
const char *value_one, *value_two;
value_one = pa_proplist_gets(first->plist, key);
value_two = pa_proplist_gets(second->plist, key);
if (!value_two || !pa_format_info_prop_compatible(value_one, value_two))
return false;
}
return true;
}
pa_format_info* pa_format_info_from_sample_spec(const pa_sample_spec *ss, const pa_channel_map *map) {
char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
pa_format_info *f;
pa_assert(ss && pa_sample_spec_valid(ss));
pa_assert(!map || pa_channel_map_valid(map));
f = pa_format_info_new();
f->encoding = PA_ENCODING_PCM;
pa_format_info_set_sample_format(f, ss->format);
pa_format_info_set_rate(f, ss->rate);
pa_format_info_set_channels(f, ss->channels);
if (map) {
pa_channel_map_snprint(cm, sizeof(cm), map);
pa_format_info_set_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, cm);
}
return f;
}
/* For PCM streams */
int pa_format_info_to_sample_spec(const pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map) {
pa_assert(f);
pa_assert(ss);
if (!pa_format_info_is_pcm(f))
return pa_format_info_to_sample_spec_fake(f, ss, map);
if (pa_format_info_get_sample_format(f, &ss->format) < 0)
return -PA_ERR_INVALID;
if (pa_format_info_get_rate(f, &ss->rate) < 0)
return -PA_ERR_INVALID;
if (pa_format_info_get_channels(f, &ss->channels) < 0)
return -PA_ERR_INVALID;
if (map && pa_format_info_get_channel_map(f, map) < 0)
return -PA_ERR_INVALID;
return 0;
}
pa_prop_type_t pa_format_info_get_prop_type(const pa_format_info *f, const char *key) {
const char *str;
pa_json_object *o;
const pa_json_object *o1;
pa_prop_type_t type;
pa_assert(f);
pa_assert(key);
str = pa_proplist_gets(f->plist, key);
if (!str)
return PA_PROP_TYPE_INVALID;
o = pa_json_parse(str);
if (!o)
return PA_PROP_TYPE_INVALID;
switch (pa_json_object_get_type(o)) {
case PA_JSON_TYPE_INT:
type = PA_PROP_TYPE_INT;
break;
case PA_JSON_TYPE_STRING:
type = PA_PROP_TYPE_STRING;
break;
case PA_JSON_TYPE_ARRAY:
if (pa_json_object_get_array_length(o) == 0) {
/* Unlikely, but let's account for this anyway. We need at
* least one element to figure out the array type. */
type = PA_PROP_TYPE_INVALID;
break;
}
o1 = pa_json_object_get_array_member(o, 0);
if (pa_json_object_get_type(o1) == PA_JSON_TYPE_INT)
type = PA_PROP_TYPE_INT_ARRAY;
else if (pa_json_object_get_type(o1) == PA_JSON_TYPE_STRING)
type = PA_PROP_TYPE_STRING_ARRAY;
else
type = PA_PROP_TYPE_INVALID;
break;
case PA_JSON_TYPE_OBJECT:
/* We actually know at this point that it's a int range, but let's
* confirm. */
if (!pa_json_object_get_object_member(o, PA_JSON_MIN_KEY)) {
type = PA_PROP_TYPE_INVALID;
break;
}
if (!pa_json_object_get_object_member(o, PA_JSON_MAX_KEY)) {
type = PA_PROP_TYPE_INVALID;
break;
}
type = PA_PROP_TYPE_INT_RANGE;
break;
default:
type = PA_PROP_TYPE_INVALID;
break;
}
pa_json_object_free(o);
return type;
}
int pa_format_info_get_prop_int(const pa_format_info *f, const char *key, int *v) {
const char *str;
pa_json_object *o;
pa_assert(f);
pa_assert(key);
pa_assert(v);
str = pa_proplist_gets(f->plist, key);
if (!str)
return -PA_ERR_NOENTITY;
o = pa_json_parse(str);
if (!o) {
pa_log_debug("Failed to parse format info property '%s'.", key);
return -PA_ERR_INVALID;
}
if (pa_json_object_get_type(o) != PA_JSON_TYPE_INT) {
pa_log_debug("Format info property '%s' type is not int.", key);
pa_json_object_free(o);
return -PA_ERR_INVALID;
}
*v = pa_json_object_get_int(o);
pa_json_object_free(o);
return 0;
}
int pa_format_info_get_prop_int_range(const pa_format_info *f, const char *key, int *min, int *max) {
const char *str;
pa_json_object *o;
const pa_json_object *o1;
int ret = -PA_ERR_INVALID;
pa_assert(f);
pa_assert(key);
pa_assert(min);
pa_assert(max);
str = pa_proplist_gets(f->plist, key);
if (!str)
return -PA_ERR_NOENTITY;
o = pa_json_parse(str);
if (!o) {
pa_log_debug("Failed to parse format info property '%s'.", key);
return -PA_ERR_INVALID;
}
if (pa_json_object_get_type(o) != PA_JSON_TYPE_OBJECT)
goto out;
if (!(o1 = pa_json_object_get_object_member(o, PA_JSON_MIN_KEY)) ||
(pa_json_object_get_type(o1) != PA_JSON_TYPE_INT))
goto out;
*min = pa_json_object_get_int(o1);
if (!(o1 = pa_json_object_get_object_member(o, PA_JSON_MAX_KEY)) ||
(pa_json_object_get_type(o1) != PA_JSON_TYPE_INT))
goto out;
*max = pa_json_object_get_int(o1);
ret = 0;
out:
if (ret < 0)
pa_log_debug("Format info property '%s' is not a valid int range.", key);
pa_json_object_free(o);
return ret;
}
int pa_format_info_get_prop_int_array(const pa_format_info *f, const char *key, int **values, int *n_values) {
const char *str;
pa_json_object *o;
const pa_json_object *o1;
int i, ret = -PA_ERR_INVALID;
pa_assert(f);
pa_assert(key);
pa_assert(values);
pa_assert(n_values);
str = pa_proplist_gets(f->plist, key);
if (!str)
return -PA_ERR_NOENTITY;
o = pa_json_parse(str);
if (!o) {
pa_log_debug("Failed to parse format info property '%s'.", key);
return -PA_ERR_INVALID;
}
if (pa_json_object_get_type(o) != PA_JSON_TYPE_ARRAY)
goto out;
*n_values = pa_json_object_get_array_length(o);
*values = pa_xnew(int, *n_values);
for (i = 0; i < *n_values; i++) {
o1 = pa_json_object_get_array_member(o, i);
if (pa_json_object_get_type(o1) != PA_JSON_TYPE_INT) {
goto out;
}
(*values)[i] = pa_json_object_get_int(o1);
}
ret = 0;
out:
if (ret < 0)
pa_log_debug("Format info property '%s' is not a valid int array.", key);
pa_json_object_free(o);
return ret;
}
int pa_format_info_get_prop_string(const pa_format_info *f, const char *key, char **v) {
const char *str = NULL;
pa_json_object *o;
pa_assert(f);
pa_assert(key);
pa_assert(v);
str = pa_proplist_gets(f->plist, key);
if (!str)
return -PA_ERR_NOENTITY;
o = pa_json_parse(str);
if (!o) {
pa_log_debug("Failed to parse format info property '%s'.", key);
return -PA_ERR_INVALID;
}
if (pa_json_object_get_type(o) != PA_JSON_TYPE_STRING) {
pa_log_debug("Format info property '%s' type is not string.", key);
pa_json_object_free(o);
return -PA_ERR_INVALID;
}
*v = pa_xstrdup(pa_json_object_get_string(o));
pa_json_object_free(o);
return 0;
}
int pa_format_info_get_prop_string_array(const pa_format_info *f, const char *key, char ***values, int *n_values) {
const char *str;
pa_json_object *o;
const pa_json_object *o1;
int i, ret = -PA_ERR_INVALID;
pa_assert(f);
pa_assert(key);
pa_assert(values);
pa_assert(n_values);
str = pa_proplist_gets(f->plist, key);
if (!str)
return -PA_ERR_NOENTITY;
o = pa_json_parse(str);
if (!o) {
pa_log_debug("Failed to parse format info property '%s'.", key);
return -PA_ERR_INVALID;
}
if (pa_json_object_get_type(o) != PA_JSON_TYPE_ARRAY)
goto out;
*n_values = pa_json_object_get_array_length(o);
*values = pa_xnew(char *, *n_values);
for (i = 0; i < *n_values; i++) {
o1 = pa_json_object_get_array_member(o, i);
if (pa_json_object_get_type(o1) != PA_JSON_TYPE_STRING) {
goto out;
}
(*values)[i] = pa_xstrdup(pa_json_object_get_string(o1));
}
ret = 0;
out:
if (ret < 0)
pa_log_debug("Format info property '%s' is not a valid string array.", key);
pa_json_object_free(o);
return ret;
}
void pa_format_info_free_string_array(char **values, int n_values) {
int i;
for (i = 0; i < n_values; i++)
pa_xfree(values[i]);
pa_xfree(values);
}
void pa_format_info_set_sample_format(pa_format_info *f, pa_sample_format_t sf) {
pa_format_info_set_prop_string(f, PA_PROP_FORMAT_SAMPLE_FORMAT, pa_sample_format_to_string(sf));
}
void pa_format_info_set_rate(pa_format_info *f, int rate) {
pa_format_info_set_prop_int(f, PA_PROP_FORMAT_RATE, rate);
}
void pa_format_info_set_channels(pa_format_info *f, int channels) {
pa_format_info_set_prop_int(f, PA_PROP_FORMAT_CHANNELS, channels);
}
void pa_format_info_set_channel_map(pa_format_info *f, const pa_channel_map *map) {
char map_str[PA_CHANNEL_MAP_SNPRINT_MAX];
pa_channel_map_snprint(map_str, sizeof(map_str), map);
pa_format_info_set_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, map_str);
}
void pa_format_info_set_prop_int(pa_format_info *f, const char *key, int value) {
pa_assert(f);
pa_assert(key);
pa_proplist_setf(f->plist, key, "%d", value);
}
void pa_format_info_set_prop_int_array(pa_format_info *f, const char *key, const int *values, int n_values) {
pa_strbuf *buf;
char *str;
int i;
pa_assert(f);
pa_assert(key);
pa_assert(n_values > 0);
buf = pa_strbuf_new();
pa_strbuf_printf(buf, "[ %d", values[0]);
for (i = 1; i < n_values; i++)
pa_strbuf_printf(buf, ", %d", values[i]);
pa_strbuf_printf(buf, " ]");
str = pa_strbuf_to_string_free(buf);
pa_proplist_sets(f->plist, key, str);
pa_xfree (str);
}
void pa_format_info_set_prop_int_range(pa_format_info *f, const char *key, int min, int max) {
pa_assert(f);
pa_assert(key);
pa_proplist_setf(f->plist, key, "{ \"" PA_JSON_MIN_KEY "\": %d, \"" PA_JSON_MAX_KEY "\": %d }",
min, max);
}
void pa_format_info_set_prop_string(pa_format_info *f, const char *key, const char *value) {
pa_assert(f);
pa_assert(key);
pa_proplist_setf(f->plist, key, "\"%s\"", value);
}
void pa_format_info_set_prop_string_array(pa_format_info *f, const char *key, const char **values, int n_values) {
pa_strbuf *buf;
char *str;
int i;
pa_assert(f);
pa_assert(key);
buf = pa_strbuf_new();
pa_strbuf_printf(buf, "[ \"%s\"", values[0]);
for (i = 1; i < n_values; i++)
pa_strbuf_printf(buf, ", \"%s\"", values[i]);
pa_strbuf_printf(buf, " ]");
str = pa_strbuf_to_string_free(buf);
pa_proplist_sets(f->plist, key, str);
pa_xfree (str);
}
static bool pa_json_is_fixed_type(pa_json_object *o) {
switch(pa_json_object_get_type(o)) {
case PA_JSON_TYPE_OBJECT:
case PA_JSON_TYPE_ARRAY:
return false;
default:
return true;
}
}
static int pa_format_info_prop_compatible(const char *one, const char *two) {
pa_json_object *o1 = NULL, *o2 = NULL;
int i, ret = 0;
o1 = pa_json_parse(one);
if (!o1)
goto out;
o2 = pa_json_parse(two);
if (!o2)
goto out;
/* We don't deal with both values being non-fixed - just because there is no immediate need (FIXME) */
pa_return_val_if_fail(pa_json_is_fixed_type(o1) || pa_json_is_fixed_type(o2), false);
if (pa_json_is_fixed_type(o1) && pa_json_is_fixed_type(o2)) {
ret = pa_json_object_equal(o1, o2);
goto out;
}
if (pa_json_is_fixed_type(o1)) {
pa_json_object *tmp = o2;
o2 = o1;
o1 = tmp;
}
/* o2 is now a fixed type, and o1 is not */
if (pa_json_object_get_type(o1) == PA_JSON_TYPE_ARRAY) {
for (i = 0; i < pa_json_object_get_array_length(o1); i++) {
if (pa_json_object_equal(pa_json_object_get_array_member(o1, i), o2)) {
ret = 1;
break;
}
}
} else if (pa_json_object_get_type(o1) == PA_JSON_TYPE_OBJECT) {
/* o1 should be a range type */
int min, max, v;
const pa_json_object *o_min = NULL, *o_max = NULL;
if (pa_json_object_get_type(o2) != PA_JSON_TYPE_INT) {
/* We don't support non-integer ranges */
goto out;
}
if (!(o_min = pa_json_object_get_object_member(o1, PA_JSON_MIN_KEY)) ||
pa_json_object_get_type(o_min) != PA_JSON_TYPE_INT)
goto out;
if (!(o_max = pa_json_object_get_object_member(o1, PA_JSON_MAX_KEY)) ||
pa_json_object_get_type(o_max) != PA_JSON_TYPE_INT)
goto out;
v = pa_json_object_get_int(o2);
min = pa_json_object_get_int(o_min);
max = pa_json_object_get_int(o_max);
ret = v >= min && v <= max;
} else {
pa_log_warn("Got a format type that we don't support");
}
out:
if (o1)
pa_json_object_free(o1);
if (o2)
pa_json_object_free(o2);
return ret;
}

348
src/hashmap.c Normal file
View file

@ -0,0 +1,348 @@
/***
This file is part of PulseAudio.
Copyright 2004-2008 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio 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 Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <pulse/xmalloc.h>
#include "internal.h"
#include "hashmap.h"
#define NBUCKETS 127
struct hashmap_entry {
void *key;
void *value;
struct hashmap_entry *bucket_next, *bucket_previous;
struct hashmap_entry *iterate_next, *iterate_previous;
};
struct pa_hashmap {
pa_hash_func_t hash_func;
pa_compare_func_t compare_func;
pa_free_cb_t key_free_func;
pa_free_cb_t value_free_func;
struct hashmap_entry *iterate_list_head, *iterate_list_tail;
unsigned n_entries;
};
#define BY_HASH(h) ((struct hashmap_entry**) ((uint8_t*) (h) + PA_ALIGN(sizeof(pa_hashmap))))
PA_STATIC_FLIST_DECLARE(entries, 0, pa_xfree);
static unsigned pa_idxset_trivial_hash_func(const void *p) {
return PA_PTR_TO_UINT(p);
}
static int pa_idxset_trivial_compare_func(const void *a, const void *b) {
return a < b ? -1 : (a > b ? 1 : 0);
}
pa_hashmap *pa_hashmap_new_full(pa_hash_func_t hash_func, pa_compare_func_t compare_func, pa_free_cb_t key_free_func, pa_free_cb_t value_free_func) {
pa_hashmap *h;
h = pa_xmalloc0(PA_ALIGN(sizeof(pa_hashmap)) + NBUCKETS*sizeof(struct hashmap_entry*));
h->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func;
h->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func;
h->key_free_func = key_free_func;
h->value_free_func = value_free_func;
h->n_entries = 0;
h->iterate_list_head = h->iterate_list_tail = NULL;
return h;
}
pa_hashmap *pa_hashmap_new(pa_hash_func_t hash_func, pa_compare_func_t compare_func) {
return pa_hashmap_new_full(hash_func, compare_func, NULL, NULL);
}
static void remove_entry(pa_hashmap *h, struct hashmap_entry *e) {
pa_assert(h);
pa_assert(e);
/* Remove from iteration list */
if (e->iterate_next)
e->iterate_next->iterate_previous = e->iterate_previous;
else
h->iterate_list_tail = e->iterate_previous;
if (e->iterate_previous)
e->iterate_previous->iterate_next = e->iterate_next;
else
h->iterate_list_head = e->iterate_next;
/* Remove from hash table bucket list */
if (e->bucket_next)
e->bucket_next->bucket_previous = e->bucket_previous;
if (e->bucket_previous)
e->bucket_previous->bucket_next = e->bucket_next;
else {
unsigned hash = h->hash_func(e->key) % NBUCKETS;
BY_HASH(h)[hash] = e->bucket_next;
}
if (h->key_free_func)
h->key_free_func(e->key);
if (pa_flist_push(PA_STATIC_FLIST_GET(entries), e) < 0)
pa_xfree(e);
pa_assert(h->n_entries >= 1);
h->n_entries--;
}
void pa_hashmap_free(pa_hashmap *h) {
pa_assert(h);
pa_hashmap_remove_all(h);
pa_xfree(h);
}
static struct hashmap_entry *hash_scan(pa_hashmap *h, unsigned hash, const void *key) {
struct hashmap_entry *e;
pa_assert(h);
pa_assert(hash < NBUCKETS);
for (e = BY_HASH(h)[hash]; e; e = e->bucket_next)
if (h->compare_func(e->key, key) == 0)
return e;
return NULL;
}
int pa_hashmap_put(pa_hashmap *h, void *key, void *value) {
struct hashmap_entry *e;
unsigned hash;
pa_assert(h);
hash = h->hash_func(key) % NBUCKETS;
if (hash_scan(h, hash, key))
return -1;
if (!(e = pa_flist_pop(PA_STATIC_FLIST_GET(entries))))
e = pa_xnew(struct hashmap_entry, 1);
e->key = key;
e->value = value;
/* Insert into hash table */
e->bucket_next = BY_HASH(h)[hash];
e->bucket_previous = NULL;
if (BY_HASH(h)[hash])
BY_HASH(h)[hash]->bucket_previous = e;
BY_HASH(h)[hash] = e;
/* Insert into iteration list */
e->iterate_previous = h->iterate_list_tail;
e->iterate_next = NULL;
if (h->iterate_list_tail) {
pa_assert(h->iterate_list_head);
h->iterate_list_tail->iterate_next = e;
} else {
pa_assert(!h->iterate_list_head);
h->iterate_list_head = e;
}
h->iterate_list_tail = e;
h->n_entries++;
pa_assert(h->n_entries >= 1);
return 0;
}
void* pa_hashmap_get(pa_hashmap *h, const void *key) {
unsigned hash;
struct hashmap_entry *e;
pa_assert(h);
hash = h->hash_func(key) % NBUCKETS;
if (!(e = hash_scan(h, hash, key)))
return NULL;
return e->value;
}
void* pa_hashmap_remove(pa_hashmap *h, const void *key) {
struct hashmap_entry *e;
unsigned hash;
void *data;
pa_assert(h);
hash = h->hash_func(key) % NBUCKETS;
if (!(e = hash_scan(h, hash, key)))
return NULL;
data = e->value;
remove_entry(h, e);
return data;
}
int pa_hashmap_remove_and_free(pa_hashmap *h, const void *key) {
void *data;
pa_assert(h);
data = pa_hashmap_remove(h, key);
if (data && h->value_free_func)
h->value_free_func(data);
return data ? 0 : -1;
}
void pa_hashmap_remove_all(pa_hashmap *h) {
pa_assert(h);
while (h->iterate_list_head) {
void *data;
data = h->iterate_list_head->value;
remove_entry(h, h->iterate_list_head);
if (h->value_free_func)
h->value_free_func(data);
}
}
void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void **key) {
struct hashmap_entry *e;
pa_assert(h);
pa_assert(state);
if (*state == (void*) -1)
goto at_end;
if (!*state && !h->iterate_list_head)
goto at_end;
e = *state ? *state : h->iterate_list_head;
if (e->iterate_next)
*state = e->iterate_next;
else
*state = (void*) -1;
if (key)
*key = e->key;
return e->value;
at_end:
*state = (void *) -1;
if (key)
*key = NULL;
return NULL;
}
void *pa_hashmap_iterate_backwards(pa_hashmap *h, void **state, const void **key) {
struct hashmap_entry *e;
pa_assert(h);
pa_assert(state);
if (*state == (void*) -1)
goto at_beginning;
if (!*state && !h->iterate_list_tail)
goto at_beginning;
e = *state ? *state : h->iterate_list_tail;
if (e->iterate_previous)
*state = e->iterate_previous;
else
*state = (void*) -1;
if (key)
*key = e->key;
return e->value;
at_beginning:
*state = (void *) -1;
if (key)
*key = NULL;
return NULL;
}
void* pa_hashmap_first(pa_hashmap *h) {
pa_assert(h);
if (!h->iterate_list_head)
return NULL;
return h->iterate_list_head->value;
}
void* pa_hashmap_last(pa_hashmap *h) {
pa_assert(h);
if (!h->iterate_list_tail)
return NULL;
return h->iterate_list_tail->value;
}
void* pa_hashmap_steal_first(pa_hashmap *h) {
void *data;
pa_assert(h);
if (!h->iterate_list_head)
return NULL;
data = h->iterate_list_head->value;
remove_entry(h, h->iterate_list_head);
return data;
}
unsigned pa_hashmap_size(pa_hashmap *h) {
pa_assert(h);
return h->n_entries;
}
bool pa_hashmap_isempty(pa_hashmap *h) {
pa_assert(h);
return h->n_entries == 0;
}

105
src/hashmap.h Normal file
View file

@ -0,0 +1,105 @@
#ifndef foopulsecorehashmaphfoo
#define foopulsecorehashmaphfoo
/***
This file is part of PulseAudio.
Copyright 2004-2008 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio 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 Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#include <spa/utils/defs.h>
#include <pulse/def.h>
typedef unsigned (*pa_hash_func_t)(const void *p);
typedef int (*pa_compare_func_t)(const void *a, const void *b);
typedef void *(*pa_copy_func_t)(const void *p);
/* Simple Implementation of a hash table. Memory management is the
* user's job. It's a good idea to have the key pointer point to a
* string in the value data. The insertion order is preserved when
* iterating. */
typedef struct pa_hashmap pa_hashmap;
/* Create a new hashmap. Use the specified functions for hashing and comparing objects in the map */
pa_hashmap *pa_hashmap_new(pa_hash_func_t hash_func, pa_compare_func_t compare_func);
/* Create a new hashmap. Use the specified functions for hashing and comparing objects in the map, and functions to free the key
* and value (either or both can be NULL). */
pa_hashmap *pa_hashmap_new_full(pa_hash_func_t hash_func, pa_compare_func_t compare_func, pa_free_cb_t key_free_func, pa_free_cb_t value_free_func);
/* Free the hash table. */
void pa_hashmap_free(pa_hashmap*);
/* Add an entry to the hashmap. Returns non-zero when the entry already exists */
int pa_hashmap_put(pa_hashmap *h, void *key, void *value);
/* Return an entry from the hashmap */
void* pa_hashmap_get(pa_hashmap *h, const void *key);
/* Returns the data of the entry while removing */
void* pa_hashmap_remove(pa_hashmap *h, const void *key);
/* Removes the entry and frees the entry data. Returns a negative value if the
* entry is not found. FIXME: This function shouldn't be needed.
* pa_hashmap_remove() should free the entry data, and the current semantics of
* pa_hashmap_remove() should be implemented by a function called
* pa_hashmap_steal(). */
int pa_hashmap_remove_and_free(pa_hashmap *h, const void *key);
/* Remove all entries but don't free the hashmap */
void pa_hashmap_remove_all(pa_hashmap *h);
/* Return the current number of entries of the hashmap */
unsigned pa_hashmap_size(pa_hashmap *h);
/* Return true if the hashmap is empty */
bool pa_hashmap_isempty(pa_hashmap *h);
/* May be used to iterate through the hashmap. Initially the opaque
pointer *state has to be set to NULL. The hashmap may not be
modified during iteration -- except for deleting the current entry
via pa_hashmap_remove(). The key of the entry is returned in *key,
if key is non-NULL. After the last entry in the hashmap NULL is
returned. */
void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void**key);
/* Same as pa_hashmap_iterate() but goes backwards */
void *pa_hashmap_iterate_backwards(pa_hashmap *h, void **state, const void**key);
/* Remove the oldest entry in the hashmap and return it */
void *pa_hashmap_steal_first(pa_hashmap *h);
/* Return the oldest entry in the hashmap */
void* pa_hashmap_first(pa_hashmap *h);
/* Return the newest entry in the hashmap */
void* pa_hashmap_last(pa_hashmap *h);
/* A macro to ease iteration through all entries */
#define PA_HASHMAP_FOREACH(e, h, state) \
for ((state) = NULL, (e) = pa_hashmap_iterate((h), &(state), NULL); (e); (e) = pa_hashmap_iterate((h), &(state), NULL))
/* A macro to ease itration through all key, value pairs */
#define PA_HASHMAP_FOREACH_KV(k, e, h, state) \
for ((state) = NULL, (e) = pa_hashmap_iterate((h), &(state), (const void **) &(k)); (e); (e) = pa_hashmap_iterate((h), &(state), (const void **) &(k)))
/* A macro to ease iteration through all entries, backwards */
#define PA_HASHMAP_FOREACH_BACKWARDS(e, h, state) \
for ((state) = NULL, (e) = pa_hashmap_iterate_backwards((h), &(state), NULL); (e); (e) = pa_hashmap_iterate_backwards((h), &(state), NULL))
#endif

306
src/internal.h Normal file
View file

@ -0,0 +1,306 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __PIPEWIRE_PULSEAUDIO_INTERNAL_H__
#define __PIPEWIRE_PULSEAUDIO_INTERNAL_H__
#include <string.h>
#include <spa/utils/defs.h>
#include <spa/utils/hook.h>
#include <spa/param/format-utils.h>
#include <spa/param/audio/format-utils.h>
#include <pulse/stream.h>
#include <pulse/format.h>
#include <pipewire/utils.h>
#include <pipewire/interfaces.h>
#include <pipewire/log.h>
#define PA_MAX_FORMATS (PA_ENCODING_MAX)
#ifdef __cplusplus
extern "C" {
#endif
#define pa_streq(a,b) (!strcmp((a),(b)))
#define pa_strneq(a,b,n) (!strncmp((a),(b),(n)))
#define PA_UNLIKELY SPA_UNLIKELY
#define PA_MIN SPA_MIN
#define PA_MAX SPA_MAX
#define pa_assert spa_assert
#define pa_return_val_if_fail spa_return_val_if_fail
#define pa_assert_not_reached spa_assert_not_reached
#define PA_USEC_PER_MSEC SPA_USEC_PER_MSEC
#ifdef __GNUC__
#define PA_CLAMP_UNLIKELY(x, low, high) \
__extension__ ({ \
typeof(x) _x = (x); \
typeof(low) _low = (low); \
typeof(high) _high = (high); \
(PA_UNLIKELY(_x > _high) ? _high : (PA_UNLIKELY(_x < _low) ? _low : _x)); \
})
#else
#define PA_CLAMP_UNLIKELY(x, low, high) (PA_UNLIKELY((x) > (high)) ? (high) : (PA_UNLIKELY((x) < (low)) ? (low) : (x)))
#endif
#define pa_init_i18n()
#define _(String) (String)
#define N_(String) (String)
#define pa_snprintf snprintf
#define pa_strip(n) pw_strip(n,"\n\r \t")
#define pa_log pw_log_info
#define pa_log_debug pw_log_debug
#define pa_log_warn pw_log_warn
static inline void* PA_ALIGN_PTR(const void *p) {
return (void*) (((size_t) p) & ~(sizeof(void*) - 1));
}
/* Rounds up */
static inline size_t PA_ALIGN(size_t l) {
return ((l + sizeof(void*) - 1) & ~(sizeof(void*) - 1));
}
static inline const char *pa_strnull(const char *x) {
return x ? x : "(null)";
}
int pa_context_set_error(pa_context *c, int error);
#define PA_CHECK_VALIDITY(context, expression, error) \
do { \
if (!(expression)) \
return -pa_context_set_error((context), (error)); \
} while(false)
#define PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, value) \
do { \
if (!(expression)) { \
pa_context_set_error((context), (error)); \
return value; \
} \
} while(false)
#define PA_CHECK_VALIDITY_RETURN_NULL(context, expression, error) \
PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, NULL)
#define PA_FAIL(context, error) \
do { \
return -pa_context_set_error((context), (error)); \
} while(false)
#define PA_FAIL_RETURN_ANY(context, error, value) \
do { \
pa_context_set_error((context), (error)); \
return value; \
} while(false)
#define PA_FAIL_RETURN_NULL(context, error) \
PA_FAIL_RETURN_ANY(context, error, NULL)
struct pa_proplist {
struct pw_properties *props;
};
pa_proplist* pa_proplist_new_props(struct pw_properties *props);
pa_proplist* pa_proplist_new_dict(struct spa_dict *dict);
struct pa_mainloop {
struct pw_loop *loop;
struct spa_source *event;
pa_mainloop_api api;
bool quit;
int retval;
int timeout;
int n_events;
};
struct global {
struct spa_list link;
uint32_t id;
uint32_t parent_id;
uint32_t type;
struct pw_properties *props;
void *info;
pw_destroy_t destroy;
struct pw_proxy *proxy;
struct spa_hook proxy_listener;
struct spa_hook proxy_proxy_listener;
};
struct pa_context {
int refcount;
struct pw_loop *loop;
struct pw_core *core;
struct pw_type *t;
struct pw_remote *remote;
struct spa_hook remote_listener;
struct pw_core_proxy *core_proxy;
struct pw_registry_proxy *registry_proxy;
struct spa_hook registry_listener;
pa_proplist *proplist;
pa_mainloop_api *mainloop;
uint32_t seq;
int error;
pa_context_state_t state;
pa_context_notify_cb_t state_callback;
void *state_userdata;
pa_context_event_cb_t event_callback;
void *event_userdata;
bool no_fail;
uint32_t client_index;
struct spa_list globals;
struct spa_list streams;
struct spa_list operations;
};
struct global *pa_context_find_global(pa_context *c, uint32_t id);
struct type {
struct spa_type_media_type media_type;
struct spa_type_media_subtype media_subtype;
struct spa_type_format_audio format_audio;
struct spa_type_audio_format audio_format;
};
static inline void init_type(struct type *type, struct spa_type_map *map)
{
spa_type_media_type_map(map, &type->media_type);
spa_type_media_subtype_map(map, &type->media_subtype);
spa_type_format_audio_map(map, &type->format_audio);
spa_type_audio_format_map(map, &type->audio_format);
}
struct pa_stream {
struct spa_list link;
int refcount;
struct pw_stream *stream;
struct spa_hook stream_listener;
struct type type;
pa_context *context;
pa_proplist *proplist;
pa_stream_direction_t direction;
pa_stream_state_t state;
pa_stream_flags_t flags;
pa_sample_spec sample_spec;
pa_channel_map channel_map;
uint8_t n_formats;
pa_format_info *req_formats[PA_MAX_FORMATS];
pa_format_info *format;
uint32_t stream_index;
pa_buffer_attr buffer_attr;
uint32_t device_index;
char *device_name;
pa_timing_info timing_info;
uint32_t direct_on_input;
bool suspended:1;
bool corked:1;
bool timing_info_valid:1;
pa_stream_notify_cb_t state_callback;
void *state_userdata;
pa_stream_request_cb_t read_callback;
void *read_userdata;
pa_stream_request_cb_t write_callback;
void *write_userdata;
pa_stream_notify_cb_t overflow_callback;
void *overflow_userdata;
pa_stream_notify_cb_t underflow_callback;
void *underflow_userdata;
pa_stream_notify_cb_t latency_update_callback;
void *latency_update_userdata;
pa_stream_notify_cb_t moved_callback;
void *moved_userdata;
pa_stream_notify_cb_t suspended_callback;
void *suspended_userdata;
pa_stream_notify_cb_t started_callback;
void *started_userdata;
pa_stream_event_cb_t event_callback;
void *event_userdata;
pa_stream_notify_cb_t buffer_attr_callback;
void *buffer_attr_userdata;
struct pw_buffer *buffer;
};
void pa_stream_set_state(pa_stream *s, pa_stream_state_t st);
typedef void (*pa_operation_cb_t)(pa_operation *o, void *userdata);
struct pa_operation
{
struct spa_list link;
int refcount;
pa_context *context;
pa_stream *stream;
uint32_t seq;
pa_operation_state_t state;
pa_operation_cb_t callback;
void *userdata;
pa_operation_notify_cb_t state_callback;
void *state_userdata;
};
pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t cb, size_t userdata_size);
void pa_operation_done(pa_operation *o);
#ifdef __cplusplus
}
#endif
#endif /* __PIPEWIRE_PULSEAUDIO_INTERNAL_H__ */

839
src/introspect.c Normal file
View file

@ -0,0 +1,839 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <errno.h>
#include <pipewire/log.h>
#include <pulse/introspect.h>
#include "internal.h"
typedef int (*global_filter_t)(pa_context *c, struct global *g);
static void node_event_info(void *object, struct pw_node_info *info)
{
struct global *g = object;
pw_log_debug("update");
g->info = pw_node_info_update(g->info, info);
}
static const struct pw_node_proxy_events node_events = {
PW_VERSION_NODE_PROXY_EVENTS,
.info = node_event_info,
};
static void module_event_info(void *object, struct pw_module_info *info)
{
struct global *g = object;
pw_log_debug("update");
g->info = pw_module_info_update(g->info, info);
}
static const struct pw_module_proxy_events module_events = {
PW_VERSION_MODULE_PROXY_EVENTS,
.info = module_event_info,
};
static void client_event_info(void *object, struct pw_client_info *info)
{
struct global *g = object;
pw_log_debug("update");
g->info = pw_client_info_update(g->info, info);
}
static const struct pw_client_proxy_events client_events = {
PW_VERSION_CLIENT_PROXY_EVENTS,
.info = client_event_info,
};
static int ensure_global(pa_context *c, struct global *g)
{
uint32_t client_version;
const void *events;
pw_destroy_t destroy;
struct pw_type *t = c->t;
if (g->proxy != NULL)
return 0;
if (g->type == t->node) {
events = &node_events;
client_version = PW_VERSION_NODE;
destroy = (pw_destroy_t) pw_node_info_free;
}
else if (g->type == t->module) {
events = &module_events;
client_version = PW_VERSION_MODULE;
destroy = (pw_destroy_t) pw_module_info_free;
}
else if (g->type == t->client) {
events = &client_events;
client_version = PW_VERSION_CLIENT;
destroy = (pw_destroy_t) pw_client_info_free;
}
else
return -EINVAL;
g->proxy = pw_registry_proxy_bind(c->registry_proxy, g->id, g->type,
client_version, 0);
if (g->proxy == NULL)
return -ENOMEM;
pw_proxy_add_proxy_listener(g->proxy, &g->proxy_proxy_listener, events, g);
g->destroy = destroy;
return 0;
}
static void ensure_types(pa_context *c, uint32_t type, global_filter_t filter)
{
struct global *g;
spa_list_for_each(g, &c->globals, link) {
if (!filter(c, g))
continue;
ensure_global(c, g);
}
}
struct sink_data {
pa_context *context;
pa_sink_info_cb_t cb;
void *userdata;
struct global *global;
};
static void sink_callback(struct sink_data *d)
{
struct global *g = d->global;
struct pw_node_info *info = g->info;
pa_sink_info i;
spa_zero(i);
i.index = g->id;
i.name = info->name;
i.proplist = pa_proplist_new_dict(info->props);
i.owner_module = g->parent_id;
i.base_volume = PA_VOLUME_NORM;
i.n_volume_steps = PA_VOLUME_NORM+1;
d->cb(d->context, &i, 0, d->userdata);
}
static void sink_info(pa_operation *o, void *userdata)
{
struct sink_data *d = userdata;
sink_callback(d);
d->cb(d->context, NULL, 1, d->userdata);
}
static int sink_filter(pa_context *c, struct global *g)
{
const char *str;
if (g->type != c->t->node)
return 0;
if (g->props == NULL)
return 0;
if ((str = pw_properties_get(g->props, "media.class")) == NULL)
return 0;
if (strcmp(str, "Audio/Sink") != 0)
return 0;
return 1;
}
pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, pa_sink_info_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t idx, pa_sink_info_cb_t cb, void *userdata)
{
pa_operation *o;
struct global *g;
struct sink_data *d;
pa_assert(c);
pa_assert(c->refcount >= 1);
pa_assert(cb);
PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
if ((g = pa_context_find_global(c, idx)) == NULL)
return NULL;
if (!sink_filter(c, g))
return NULL;
ensure_global(c, g);
o = pa_operation_new(c, NULL, sink_info, sizeof(struct sink_data));
d = o->userdata;
d->global = g;
return o;
}
static void sink_info_list(pa_operation *o, void *userdata)
{
struct sink_data *d = userdata;
pa_context *c = d->context;
struct global *g;
spa_list_for_each(g, &c->globals, link) {
if (!sink_filter(c, g))
continue;
d->global = g;
sink_callback(d);
}
d->cb(c, NULL, 1, d->userdata);
}
pa_operation* pa_context_get_sink_info_list(pa_context *c, pa_sink_info_cb_t cb, void *userdata)
{
pa_operation *o;
struct sink_data *d;
pa_assert(c);
pa_assert(c->refcount >= 1);
pa_assert(cb);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
ensure_types(c, c->t->node, sink_filter);
o = pa_operation_new(c, NULL, sink_info_list, sizeof(struct sink_data));
d = o->userdata;
d->context = c;
d->cb = cb;
d->userdata = userdata;
return o;
}
pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_suspend_sink_by_name(pa_context *c, const char *sink_name, int suspend, pa_context_success_cb_t cb, void* userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_suspend_sink_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_sink_port_by_index(pa_context *c, uint32_t idx, const char*port, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_sink_port_by_name(pa_context *c, const char*name, const char*port, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
struct source_data {
pa_context *context;
pa_source_info_cb_t cb;
void *userdata;
struct global *global;
};
static void source_callback(struct source_data *d)
{
struct global *g = d->global;
struct pw_node_info *info = g->info;
pa_source_info i;
spa_zero(i);
i.index = g->id;
i.name = info->name;
i.proplist = pa_proplist_new_dict(info->props);
i.owner_module = g->parent_id;
i.base_volume = PA_VOLUME_NORM;
i.n_volume_steps = PA_VOLUME_NORM+1;
d->cb(d->context, &i, 0, d->userdata);
}
static void source_info(pa_operation *o, void *userdata)
{
struct source_data *d = userdata;
source_callback(d);
d->cb(d->context, NULL, 1, d->userdata);
}
static int source_filter(pa_context *c, struct global *g)
{
const char *str;
if (g->type != c->t->node)
return 0;
if (g->props == NULL)
return 0;
if ((str = pw_properties_get(g->props, "media.class")) == NULL)
return 0;
if (strcmp(str, "Audio/Source") != 0)
return 0;
return 1;
}
pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name, pa_source_info_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t idx, pa_source_info_cb_t cb, void *userdata)
{
pa_operation *o;
struct global *g;
struct source_data *d;
pa_assert(c);
pa_assert(c->refcount >= 1);
pa_assert(cb);
PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
if ((g = pa_context_find_global(c, idx)) == NULL)
return NULL;
if (!source_filter(c, g))
return NULL;
ensure_global(c, g);
o = pa_operation_new(c, NULL, source_info, sizeof(struct source_data));
d = o->userdata;
d->global = g;
return o;
}
static void source_info_list(pa_operation *o, void *userdata)
{
struct source_data *d = userdata;
pa_context *c = d->context;
struct global *g;
spa_list_for_each(g, &c->globals, link) {
if (!source_filter(c, g))
continue;
d->global = g;
source_callback(d);
}
d->cb(c, NULL, 1, d->userdata);
}
pa_operation* pa_context_get_source_info_list(pa_context *c, pa_source_info_cb_t cb, void *userdata)
{
pa_operation *o;
struct source_data *d;
pa_assert(c);
pa_assert(c->refcount >= 1);
pa_assert(cb);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
ensure_types(c, c->t->node, source_filter);
o = pa_operation_new(c, NULL, source_info_list, sizeof(struct source_data));
d = o->userdata;
d->context = c;
d->cb = cb;
d->userdata = userdata;
return o;
}
pa_operation* pa_context_set_source_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_source_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_source_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_suspend_source_by_name(pa_context *c, const char *source_name, int suspend, pa_context_success_cb_t cb, void* userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_suspend_source_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_source_port_by_index(pa_context *c, uint32_t idx, const char*port, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_source_port_by_name(pa_context *c, const char*name, const char*port, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_get_server_info(pa_context *c, pa_server_info_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
struct module_data {
pa_context *context;
pa_module_info_cb_t cb;
void *userdata;
struct global *global;
};
static void module_callback(struct module_data *d)
{
struct global *g = d->global;
struct pw_module_info *info = g->info;
pa_module_info i;
spa_zero(i);
i.proplist = pa_proplist_new_dict(info->props);
i.index = g->id;
i.name = info->name;
i.argument = info->args;
i.n_used = -1;
i.auto_unload = false;
d->cb(d->context, &i, 0, d->userdata);
}
static void module_info(pa_operation *o, void *userdata)
{
struct module_data *d = userdata;
module_callback(d);
d->cb(d->context, NULL, 1, d->userdata);
}
static int module_filter(pa_context *c, struct global *g)
{
if (g->type != c->t->module)
return 0;
return 1;
}
pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, pa_module_info_cb_t cb, void *userdata)
{
pa_operation *o;
struct global *g;
struct module_data *d;
pa_assert(c);
pa_assert(c->refcount >= 1);
pa_assert(cb);
PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
if ((g = pa_context_find_global(c, idx)) == NULL)
return NULL;
if (!module_filter(c, g))
return NULL;
ensure_global(c, g);
o = pa_operation_new(c, NULL, module_info, sizeof(struct module_data));
d = o->userdata;
d->global = g;
return o;
}
static void module_info_list(pa_operation *o, void *userdata)
{
struct module_data *d = userdata;
pa_context *c = d->context;
struct global *g;
spa_list_for_each(g, &c->globals, link) {
if (!module_filter(c, g))
continue;
d->global = g;
module_callback(d);
}
d->cb(c, NULL, 1, d->userdata);
}
pa_operation* pa_context_get_module_info_list(pa_context *c, pa_module_info_cb_t cb, void *userdata)
{
pa_operation *o;
struct module_data *d;
pa_assert(c);
pa_assert(c->refcount >= 1);
pa_assert(cb);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
ensure_types(c, c->t->module, module_filter);
o = pa_operation_new(c, NULL, module_info_list, sizeof(struct module_data));
d = o->userdata;
d->context = c;
d->cb = cb;
d->userdata = userdata;
return o;
}
pa_operation* pa_context_load_module(pa_context *c, const char*name, const char *argument, pa_context_index_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
struct client_data {
pa_context *context;
pa_client_info_cb_t cb;
void *userdata;
struct global *global;
};
static void client_callback(struct client_data *d)
{
struct global *g = d->global;
struct pw_client_info *info = g->info;
pa_client_info i;
spa_zero(i);
i.proplist = pa_proplist_new_dict(info->props);
i.index = g->id;
i.name = info->props ?
spa_dict_lookup(info->props, "application.prgname") : NULL;
i.owner_module = g->parent_id;
i.driver = info->props ?
spa_dict_lookup(info->props, PW_CLIENT_PROP_PROTOCOL) : NULL;
d->cb(d->context, &i, 0, d->userdata);
}
static void client_info(pa_operation *o, void *userdata)
{
struct client_data *d = userdata;
client_callback(d);
d->cb(d->context, NULL, 1, d->userdata);
}
static int client_filter(pa_context *c, struct global *g)
{
if (g->type != c->t->client)
return 0;
return 1;
}
pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, pa_client_info_cb_t cb, void *userdata)
{
pa_operation *o;
struct global *g;
struct client_data *d;
pa_assert(c);
pa_assert(c->refcount >= 1);
pa_assert(cb);
PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
if ((g = pa_context_find_global(c, idx)) == NULL)
return NULL;
if (!client_filter(c, g))
return NULL;
ensure_global(c, g);
o = pa_operation_new(c, NULL, client_info, sizeof(struct client_data));
d = o->userdata;
d->global = g;
return o;
}
static void client_info_list(pa_operation *o, void *userdata)
{
struct client_data *d = userdata;
pa_context *c = d->context;
struct global *g;
spa_list_for_each(g, &c->globals, link) {
if (!client_filter(c, g))
continue;
d->global = g;
client_callback(d);
}
d->cb(c, NULL, 1, d->userdata);
}
pa_operation* pa_context_get_client_info_list(pa_context *c, pa_client_info_cb_t cb, void *userdata)
{
pa_operation *o;
struct client_data *d;
pa_assert(c);
pa_assert(c->refcount >= 1);
pa_assert(cb);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
ensure_types(c, c->t->client, client_filter);
o = pa_operation_new(c, NULL, client_info_list, sizeof(struct client_data));
d = o->userdata;
d->context = c;
d->cb = cb;
d->userdata = userdata;
return o;
}
pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_get_card_info_by_index(pa_context *c, uint32_t idx, pa_card_info_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_get_card_info_by_name(pa_context *c, const char *name, pa_card_info_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_get_card_info_list(pa_context *c, pa_card_info_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_card_profile_by_index(pa_context *c, uint32_t idx, const char*profile, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_card_profile_by_name(pa_context *c, const char*name, const char*profile, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_port_latency_offset(pa_context *c, const char *card_name, const char *port_name, int64_t offset, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, pa_sink_input_info_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_get_sink_input_info_list(pa_context *c, pa_sink_input_info_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_move_sink_input_by_name(pa_context *c, uint32_t idx, const char *sink_name, pa_context_success_cb_t cb, void* userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_move_sink_input_by_index(pa_context *c, uint32_t idx, uint32_t sink_idx, pa_context_success_cb_t cb, void* userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_get_source_output_info(pa_context *c, uint32_t idx, pa_source_output_info_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_get_source_output_info_list(pa_context *c, pa_source_output_info_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_move_source_output_by_name(pa_context *c, uint32_t idx, const char *source_name, pa_context_success_cb_t cb, void* userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_move_source_output_by_index(pa_context *c, uint32_t idx, uint32_t source_idx, pa_context_success_cb_t cb, void* userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_source_output_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_source_output_mute(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_stat(pa_context *c, pa_stat_info_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_get_sample_info_by_name(pa_context *c, const char *name, pa_sample_info_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_get_sample_info_by_index(pa_context *c, uint32_t idx, pa_sample_info_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_get_sample_info_list(pa_context *c, pa_sample_info_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_autoload_info_cb_t cb, void *userdata)
{
pw_log_warn("Deprecated: Not Implemented");
return NULL;
}
pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, pa_autoload_info_cb_t cb, void *userdata)
{
pw_log_warn("Deprecated: Not Implemented");
return NULL;
}
pa_operation* pa_context_get_autoload_info_list(pa_context *c, pa_autoload_info_cb_t cb, void *userdata)
{
pw_log_warn("Deprecated: Not Implemented");
return NULL;
}
pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, pa_context_index_cb_t cb, void* userdata)
{
pw_log_warn("Deprecated: Not Implemented");
return NULL;
}
pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_context_success_cb_t cb, void* userdata)
{
pw_log_warn("Deprecated: Not Implemented");
return NULL;
}
pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void* userdata)
{
pw_log_warn("Deprecated: Not Implemented");
return NULL;
}

648
src/json.c Normal file
View file

@ -0,0 +1,648 @@
/***
This file is part of PulseAudio.
Copyright 2016 Arun Raghavan <mail@arunraghavan.net>
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio 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 Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <math.h>
#include <pulse/xmalloc.h>
#include <pipewire/array.h>
#include <pipewire/properties.h>
#include "internal.h"
#include "json.h"
#include "strbuf.h"
#define MAX_NESTING_DEPTH 20 /* Arbitrary number to make sure we don't have a stack overflow */
typedef struct pa_json_item {
char *key;
pa_json_object *value;
} pa_json_item;
struct pa_json_object {
pa_json_type type;
union {
int int_value;
double double_value;
bool bool_value;
char *string_value;
struct pw_array values; /* objects */
};
};
static void clear_array(struct pw_array *array)
{
pa_json_object *value;
pw_array_for_each(value, array)
pa_json_object_free(value);
pw_array_clear(array);
}
static void clear_item(pa_json_item *item)
{
free(item->key);
pa_json_object_free(item->value);
}
static void clear_object(struct pw_array *array)
{
pa_json_item *item;
pw_array_for_each(item, array)
clear_item(item);
pw_array_clear(array);
}
static const char* parse_value(const char *str, const char *end, pa_json_object **obj, unsigned int depth);
static pa_json_object* json_object_new(void) {
pa_json_object *obj;
obj = pa_xnew0(pa_json_object, 1);
return obj;
}
static bool is_whitespace(char c) {
return c == '\t' || c == '\n' || c == '\r' || c == ' ';
}
static bool is_digit(char c) {
return c >= '0' && c <= '9';
}
static bool is_end(const char c, const char *end) {
if (!end)
return c == '\0';
else {
while (*end) {
if (c == *end)
return true;
end++;
}
}
return false;
}
static const char* consume_string(const char *str, const char *expect) {
while (*expect) {
if (*str != *expect)
return NULL;
str++;
expect++;
}
return str;
}
static const char* parse_null(const char *str, pa_json_object *obj) {
str = consume_string(str, "null");
if (str)
obj->type = PA_JSON_TYPE_NULL;
return str;
}
static const char* parse_boolean(const char *str, pa_json_object *obj) {
const char *tmp;
tmp = consume_string(str, "true");
if (tmp) {
obj->type = PA_JSON_TYPE_BOOL;
obj->bool_value = true;
} else {
tmp = consume_string(str, "false");
if (str) {
obj->type = PA_JSON_TYPE_BOOL;
obj->bool_value = false;
}
}
return tmp;
}
static const char* parse_string(const char *str, pa_json_object *obj) {
pa_strbuf *buf = pa_strbuf_new();
str++; /* Consume leading '"' */
while (*str && *str != '"') {
if (*str != '\\') {
/* We only accept ASCII printable characters. */
if (*str < 0x20 || *str > 0x7E) {
pa_log("Invalid non-ASCII character: 0x%x", (unsigned int) *str);
goto error;
}
/* Normal character, juts consume */
pa_strbuf_putc(buf, *str);
} else {
/* Need to unescape */
str++;
switch (*str) {
case '"':
case '\\':
case '/':
pa_strbuf_putc(buf, *str);
break;
case 'b':
pa_strbuf_putc(buf, '\b' /* backspace */);
break;
case 'f':
pa_strbuf_putc(buf, '\f' /* form feed */);
break;
case 'n':
pa_strbuf_putc(buf, '\n' /* new line */);
break;
case 'r':
pa_strbuf_putc(buf, '\r' /* carriage return */);
break;
case 't':
pa_strbuf_putc(buf, '\t' /* horizontal tab */);
break;
case 'u':
pa_log("Unicode code points are currently unsupported");
goto error;
default:
pa_log("Unexepcted escape value: %c", *str);
goto error;
}
}
str++;
}
if (*str != '"') {
pa_log("Failed to parse remainder of string: %s", str);
goto error;
}
str++;
obj->type = PA_JSON_TYPE_STRING;
obj->string_value = pa_strbuf_to_string_free(buf);
return str;
error:
pa_strbuf_free(buf);
return NULL;
}
static const char* parse_number(const char *str, pa_json_object *obj) {
bool negative = false, has_fraction = false, has_exponent = false, valid = false;
unsigned int integer = 0;
unsigned int fraction = 0;
unsigned int fraction_digits = 0;
int exponent = 0;
if (*str == '-') {
negative = true;
str++;
}
if (*str == '0') {
valid = true;
str++;
goto fraction;
}
while (is_digit(*str)) {
valid = true;
if (integer > ((negative ? INT_MAX : UINT_MAX) / 10)) {
pa_log("Integer overflow while parsing number");
goto error;
}
integer = (integer * 10) + (*str - '0');
str++;
}
fraction:
if (!valid) {
pa_log("Missing digits while parsing number");
goto error;
}
if (*str == '.') {
has_fraction = true;
str++;
valid = false;
while (is_digit(*str)) {
valid = true;
if (fraction > (UINT_MAX / 10)) {
pa_log("Integer overflow while parsing fractional part of number");
goto error;
}
fraction = (fraction * 10) + (*str - '0');
fraction_digits++;
str++;
}
if (!valid) {
pa_log("No digit after '.' while parsing fraction");
goto error;
}
}
if (*str == 'e' || *str == 'E') {
bool exponent_negative = false;
has_exponent = true;
str++;
valid = false;
if (*str == '-') {
exponent_negative = true;
str++;
} else if (*str == '+')
str++;
while (is_digit(*str)) {
valid = true;
if (exponent > (INT_MAX / 10)) {
pa_log("Integer overflow while parsing exponent part of number");
goto error;
}
exponent = (exponent * 10) + (*str - '0');
str++;
}
if (!valid) {
pa_log("No digit in exponent while parsing fraction");
goto error;
}
if (exponent_negative)
exponent *= -1;
}
if (has_fraction || has_exponent) {
obj->type = PA_JSON_TYPE_DOUBLE;
obj->double_value =
(negative ? -1.0 : 1.0) * (integer + (double) fraction / pow(10, fraction_digits)) * pow(10, exponent);
} else {
obj->type = PA_JSON_TYPE_INT;
obj->int_value = (negative ? -1 : 1) * integer;
}
return str;
error:
return NULL;
}
static const char *parse_object(const char *str, pa_json_object *obj, unsigned int depth) {
pa_json_object *name = NULL, *value = NULL;
pa_json_item *item;
obj->values = PW_ARRAY_INIT(64);
while (*str != '}') {
str++; /* Consume leading '{' or ',' */
str = parse_value(str, ":", &name, depth + 1);
if (!str || pa_json_object_get_type(name) != PA_JSON_TYPE_STRING) {
pa_log("Could not parse key for object");
goto error;
}
/* Consume the ':' */
str++;
str = parse_value(str, ",}", &value, depth + 1);
if (!str) {
pa_log("Could not parse value for object");
goto error;
}
item = pw_array_add(&obj->values, sizeof(pa_json_item));
item->key = strdup(pa_json_object_get_string(name));
item->value = value;
pa_json_object_free(name);
name = NULL;
value = NULL;
}
/* Drop trailing '}' */
str++;
/* We now know the value was correctly parsed */
obj->type = PA_JSON_TYPE_OBJECT;
return str;
error:
clear_object(&obj->values);
if (name)
pa_json_object_free(name);
if (value)
pa_json_object_free(value);
return NULL;
}
static const char *parse_array(const char *str, pa_json_object *obj, unsigned int depth) {
pa_json_object *value;
obj->values = PW_ARRAY_INIT(64);
while (*str != ']') {
str++; /* Consume leading '[' or ',' */
/* Need to chew up whitespaces as a special case to deal with the
* possibility of an empty array */
while (is_whitespace(*str))
str++;
if (*str == ']')
break;
str = parse_value(str, ",]", &value, depth + 1);
if (!str) {
pa_log("Could not parse value for array");
goto error;
}
pw_array_add_ptr(&obj->values, value);
}
/* Drop trailing ']' */
str++;
/* We now know the value was correctly parsed */
obj->type = PA_JSON_TYPE_ARRAY;
return str;
error:
clear_array(&obj->values);
return NULL;
}
typedef enum {
JSON_PARSER_STATE_INIT,
JSON_PARSER_STATE_FINISH,
} json_parser_state;
static const char* parse_value(const char *str, const char *end, pa_json_object **obj, unsigned int depth) {
json_parser_state state = JSON_PARSER_STATE_INIT;
pa_json_object *o;
pa_assert(str != NULL);
o = json_object_new();
if (depth > MAX_NESTING_DEPTH) {
pa_log("Exceeded maximum permitted nesting depth of objects (%u)", MAX_NESTING_DEPTH);
goto error;
}
while (!is_end(*str, end)) {
switch (state) {
case JSON_PARSER_STATE_INIT:
if (is_whitespace(*str)) {
str++;
} else if (*str == 'n') {
str = parse_null(str, o);
state = JSON_PARSER_STATE_FINISH;
} else if (*str == 't' || *str == 'f') {
str = parse_boolean(str, o);
state = JSON_PARSER_STATE_FINISH;
} else if (*str == '"') {
str = parse_string(str, o);
state = JSON_PARSER_STATE_FINISH;
} else if (is_digit(*str) || *str == '-') {
str = parse_number(str, o);
state = JSON_PARSER_STATE_FINISH;
} else if (*str == '{') {
str = parse_object(str, o, depth);
state = JSON_PARSER_STATE_FINISH;
} else if (*str == '[') {
str = parse_array(str, o, depth);
state = JSON_PARSER_STATE_FINISH;
} else {
pa_log("Invalid JSON string: %s", str);
goto error;
}
if (!str)
goto error;
break;
case JSON_PARSER_STATE_FINISH:
/* Consume trailing whitespaces */
if (is_whitespace(*str)) {
str++;
} else {
goto error;
}
}
}
if (pa_json_object_get_type(o) == PA_JSON_TYPE_INIT) {
/* We didn't actually get any data */
pa_log("No data while parsing json string: '%s' till '%s'", str, pa_strnull(end));
goto error;
}
*obj = o;
return str;
error:
pa_json_object_free(o);
return NULL;
}
pa_json_object* pa_json_parse(const char *str) {
pa_json_object *obj;
str = parse_value(str, NULL, &obj, 0);
if (!str) {
pa_log("JSON parsing failed");
return NULL;
}
if (*str != '\0') {
pa_log("Unable to parse complete JSON string, remainder is: %s", str);
pa_json_object_free(obj);
return NULL;
}
return obj;
}
pa_json_type pa_json_object_get_type(const pa_json_object *obj) {
return obj->type;
}
void pa_json_object_free(pa_json_object *obj) {
switch (pa_json_object_get_type(obj)) {
case PA_JSON_TYPE_INIT:
case PA_JSON_TYPE_INT:
case PA_JSON_TYPE_DOUBLE:
case PA_JSON_TYPE_BOOL:
case PA_JSON_TYPE_NULL:
break;
case PA_JSON_TYPE_STRING:
pa_xfree(obj->string_value);
break;
case PA_JSON_TYPE_OBJECT:
clear_object(&obj->values);
break;
case PA_JSON_TYPE_ARRAY:
clear_array(&obj->values);
break;
default:
pa_assert_not_reached();
}
pa_xfree(obj);
}
int pa_json_object_get_int(const pa_json_object *o) {
pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_INT);
return o->int_value;
}
double pa_json_object_get_double(const pa_json_object *o) {
pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_DOUBLE);
return o->double_value;
}
bool pa_json_object_get_bool(const pa_json_object *o) {
pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_BOOL);
return o->bool_value;
}
const char* pa_json_object_get_string(const pa_json_object *o) {
pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_STRING);
return o->string_value;
}
const pa_json_object* pa_json_object_get_object_member(const pa_json_object *o, const char *name) {
pa_json_item *item;
pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_OBJECT);
pw_array_for_each(item, &o->values) {
if (pa_streq(item->key, name))
return item->value;
}
return NULL;
}
int pa_json_object_get_array_length(const pa_json_object *o) {
pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY);
return pw_array_get_len(&o->values, const pa_json_object*);
}
const pa_json_object* pa_json_object_get_array_member(const pa_json_object *o, int index) {
pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY);
return pw_array_get_unchecked_s(&o->values, index, sizeof(pa_json_object*),
const pa_json_object);
}
bool pa_json_object_equal(const pa_json_object *o1, const pa_json_object *o2) {
int i;
if (pa_json_object_get_type(o1) != pa_json_object_get_type(o2))
return false;
switch (pa_json_object_get_type(o1)) {
case PA_JSON_TYPE_NULL:
return true;
case PA_JSON_TYPE_BOOL:
return o1->bool_value == o2->bool_value;
case PA_JSON_TYPE_INT:
return o1->int_value == o2->int_value;
case PA_JSON_TYPE_DOUBLE:
return PA_DOUBLE_IS_EQUAL(o1->double_value, o2->double_value);
case PA_JSON_TYPE_STRING:
return pa_streq(o1->string_value, o2->string_value);
case PA_JSON_TYPE_ARRAY:
if (pa_json_object_get_array_length(o1) != pa_json_object_get_array_length(o2))
return false;
for (i = 0; i < pa_json_object_get_array_length(o1); i++) {
if (!pa_json_object_equal(pa_json_object_get_array_member(o1, i),
pa_json_object_get_array_member(o2, i)))
return false;
}
return true;
case PA_JSON_TYPE_OBJECT: {
const pa_json_object *value;
const pa_json_item *item;
if (o1->values.size != o2->values.size)
return false;
pw_array_for_each(item, &o1->values) {
value = pa_json_object_get_object_member(o2, item->key);
if (!value || !pa_json_object_equal(item->value, value))
return false;
}
return true;
}
default:
pa_assert_not_reached();
}
}

53
src/json.h Normal file
View file

@ -0,0 +1,53 @@
/***
This file is part of PulseAudio.
Copyright 2016 Arun Raghavan <mail@arunraghavan.net>
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio 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 Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#include <stdbool.h>
#define PA_DOUBLE_IS_EQUAL(x, y) (((x) - (y)) < 0.000001 && ((x) - (y)) > -0.000001)
typedef enum {
PA_JSON_TYPE_INIT = 0,
PA_JSON_TYPE_NULL,
PA_JSON_TYPE_INT,
PA_JSON_TYPE_DOUBLE,
PA_JSON_TYPE_BOOL,
PA_JSON_TYPE_STRING,
PA_JSON_TYPE_ARRAY,
PA_JSON_TYPE_OBJECT,
} pa_json_type;
typedef struct pa_json_object pa_json_object;
pa_json_object* pa_json_parse(const char *str);
pa_json_type pa_json_object_get_type(const pa_json_object *obj);
void pa_json_object_free(pa_json_object *obj);
/* All pointer members that are returned are valid while the corresponding object is valid */
int pa_json_object_get_int(const pa_json_object *o);
double pa_json_object_get_double(const pa_json_object *o);
bool pa_json_object_get_bool(const pa_json_object *o);
const char* pa_json_object_get_string(const pa_json_object *o);
const pa_json_object* pa_json_object_get_object_member(const pa_json_object *o, const char *name);
int pa_json_object_get_array_length(const pa_json_object *o);
const pa_json_object* pa_json_object_get_array_member(const pa_json_object *o, int index);
bool pa_json_object_equal(const pa_json_object *o1, const pa_json_object *o2);

109
src/mainloop-signal.c Normal file
View file

@ -0,0 +1,109 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <errno.h>
#include <spa/utils/list.h>
#include <spa/support/loop.h>
#include <pipewire/log.h>
#include <pipewire/loop.h>
#include <pulse/mainloop-signal.h>
#include "internal.h"
static pa_mainloop_api *api = NULL;
static struct spa_list signals;
static struct pw_loop *loop = NULL;
struct pa_signal_event {
struct spa_list link;
struct spa_source *source;
pa_signal_cb_t callback;
pa_signal_destroy_cb_t destroy;
void *userdata;
};
int pa_signal_init(pa_mainloop_api *a)
{
pa_assert(a);
pa_assert(!api);
api = a;
spa_list_init(&signals);
loop = a->userdata;
return 0;
}
void pa_signal_done(void)
{
pa_signal_event *ev, *t;
pa_assert(api);
spa_list_for_each_safe(ev, t, &signals, link)
pa_signal_free(ev);
spa_list_init(&signals);
api = NULL;
}
static void source_signal_func (void *data, int signal_number)
{
pa_signal_event *ev = data;
if (ev->callback)
ev->callback(api, ev, signal_number, ev->userdata);
}
pa_signal_event* pa_signal_new(int sig, pa_signal_cb_t callback, void *userdata)
{
pa_signal_event *ev;
pa_assert(sig > 0);
pa_assert(callback);
ev = calloc(1, sizeof(pa_signal_event));
ev->source = spa_loop_utils_add_signal(loop->utils, sig, source_signal_func, ev);
ev->callback = callback;
ev->userdata = userdata;
spa_list_append(&signals, &ev->link);
return ev;
}
void pa_signal_free(pa_signal_event *e)
{
pa_assert(e);
spa_list_remove(&e->link);
spa_loop_utils_destroy_source(loop->utils, e->source);
if (e->destroy)
e->destroy(api, e, e->userdata);
free(e);
}
void pa_signal_set_destroy(pa_signal_event *e, pa_signal_destroy_cb_t callback)
{
pa_assert(e);
e->destroy = callback;
}

251
src/mainloop.c Normal file
View file

@ -0,0 +1,251 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <errno.h>
#include <pipewire/log.h>
#include <pipewire/loop.h>
#include <pulse/mainloop.h>
#include "internal.h"
static void do_stop(void *data, uint64_t count)
{
struct pa_mainloop *this = data;
this->quit = true;
}
static pa_io_event* api_io_new(pa_mainloop_api*a, int fd, pa_io_event_flags_t events, pa_io_event_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
static void api_io_enable(pa_io_event* e, pa_io_event_flags_t events)
{
pw_log_warn("Not Implemented");
}
static void api_io_free(pa_io_event* e)
{
pw_log_warn("Not Implemented");
}
static void api_io_set_destroy(pa_io_event *e, pa_io_event_destroy_cb_t cb)
{
pw_log_warn("Not Implemented");
}
static pa_time_event* api_time_new(pa_mainloop_api*a, const struct timeval *tv, pa_time_event_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
static void api_time_restart(pa_time_event* e, const struct timeval *tv)
{
pw_log_warn("Not Implemented");
}
static void api_time_free(pa_time_event* e)
{
pw_log_warn("Not Implemented");
}
static void api_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb_t cb)
{
pw_log_warn("Not Implemented");
}
static pa_defer_event* api_defer_new(pa_mainloop_api*a, pa_defer_event_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
static void api_defer_enable(pa_defer_event* e, int b)
{
pw_log_warn("Not Implemented");
}
static void api_defer_free(pa_defer_event* e)
{
pw_log_warn("Not Implemented");
}
static void api_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy_cb_t cb)
{
pw_log_warn("Not Implemented");
}
static void api_quit(pa_mainloop_api*a, int retval)
{
pa_mainloop *m = SPA_CONTAINER_OF(a, pa_mainloop, api);
m->quit = true;
m->retval = retval;
pa_mainloop_wakeup(m);
}
static const pa_mainloop_api api =
{
.io_new = api_io_new,
.io_enable = api_io_enable,
.io_free = api_io_free,
.io_set_destroy = api_io_set_destroy,
.time_new = api_time_new,
.time_restart = api_time_restart,
.time_free = api_time_free,
.time_set_destroy = api_time_set_destroy,
.defer_new = api_defer_new,
.defer_enable = api_defer_enable,
.defer_free = api_defer_free,
.defer_set_destroy = api_defer_set_destroy,
.quit = api_quit,
};
pa_mainloop *pa_mainloop_new(void)
{
pa_mainloop *loop;
loop = calloc(1, sizeof(pa_mainloop));
if (loop == NULL)
return NULL;
loop->loop = pw_loop_new(NULL);
if (loop->loop == NULL)
goto no_loop;
loop->event = pw_loop_add_event(loop->loop, do_stop, loop);
loop->api = api;
loop->api.userdata = loop->loop;
return loop;
no_loop:
free(loop);
return NULL;
}
void pa_mainloop_free(pa_mainloop* m)
{
pw_loop_destroy(m->loop);
free(m);
}
int pa_mainloop_prepare(pa_mainloop *m, int timeout)
{
if (m->quit)
return -2;
m->timeout = timeout;
m->n_events = -EIO;
return 0;
}
/** Execute the previously prepared poll. Returns a negative value on error.*/
int pa_mainloop_poll(pa_mainloop *m)
{
if (m->quit)
return -2;
return m->n_events = pw_loop_iterate(m->loop, m->timeout);
}
int pa_mainloop_dispatch(pa_mainloop *m)
{
if (m->quit)
return -2;
return m->n_events;
}
int pa_mainloop_get_retval(pa_mainloop *m)
{
return m->retval;
}
/** Run a single iteration of the main loop. This is a convenience function
for pa_mainloop_prepare(), pa_mainloop_poll() and pa_mainloop_dispatch().
Returns a negative value on error or exit request. If block is nonzero,
block for events if none are queued. Optionally return the return value as
specified with the main loop's quit() routine in the integer variable retval points
to. On success returns the number of sources dispatched in this iteration. */
int pa_mainloop_iterate(pa_mainloop *m, int block, int *retval)
{
int r;
pa_assert(m);
if ((r = pa_mainloop_prepare(m, block ? -1 : 0)) < 0)
goto quit;
if ((r = pa_mainloop_poll(m)) < 0)
goto quit;
if ((r = pa_mainloop_dispatch(m)) < 0)
goto quit;
return r;
quit:
if ((r == -2) && retval)
*retval = pa_mainloop_get_retval(m);
return r;
}
int pa_mainloop_run(pa_mainloop *m, int *retval)
{
int r;
while ((r = pa_mainloop_iterate(m, 1, retval)) >= 0)
;
if (r == -2)
return 1;
else
return -1;
}
pa_mainloop_api* pa_mainloop_get_api(pa_mainloop *m)
{
pa_assert(m);
return &m->api;
}
void pa_mainloop_quit(pa_mainloop *m, int retval)
{
pa_assert(m);
m->api.quit(&m->api, retval);
}
void pa_mainloop_wakeup(pa_mainloop *m)
{
pa_assert(m);
pw_loop_signal_event(m->loop, m->event);
}
void pa_mainloop_set_poll_func(pa_mainloop *m, pa_poll_func poll_func, void *userdata)
{
pw_log_warn("Not Implemented");
}

386
src/map-file Normal file
View file

@ -0,0 +1,386 @@
PULSE_0 {
global:
pa_ascii_filter;
pa_ascii_valid;
pa_bytes_per_second;
pa_bytes_snprint;
pa_bytes_to_usec;
pa_channel_map_can_balance;
pa_channel_map_can_fade;
pa_channel_map_can_lfe_balance;
pa_channel_map_compatible;
pa_channel_map_equal;
pa_channel_map_has_position;
pa_channel_map_init;
pa_channel_map_init_auto;
pa_channel_map_init_extend;
pa_channel_map_init_mono;
pa_channel_map_init_stereo;
pa_channel_map_mask;
pa_channel_map_parse;
pa_channel_map_snprint;
pa_channel_map_superset;
pa_channel_map_to_name;
pa_channel_map_to_pretty_name;
pa_channel_map_valid;
pa_channel_position_from_string;
pa_channel_position_to_pretty_string;
pa_channel_position_to_string;
pa_channels_valid;
pa_context_add_autoload;
pa_context_connect;
pa_context_disconnect;
pa_context_drain;
pa_context_errno;
pa_context_exit_daemon;
pa_context_get_autoload_info_by_index;
pa_context_get_autoload_info_by_name;
pa_context_get_autoload_info_list;
pa_context_get_card_info_by_index;
pa_context_get_card_info_by_name;
pa_context_get_card_info_list;
pa_context_get_client_info;
pa_context_get_client_info_list;
pa_context_get_index;
pa_context_get_module_info;
pa_context_get_module_info_list;
pa_context_get_protocol_version;
pa_context_get_sample_info_by_index;
pa_context_get_sample_info_by_name;
pa_context_get_sample_info_list;
pa_context_get_server;
pa_context_get_server_info;
pa_context_get_server_protocol_version;
pa_context_get_sink_info_by_index;
pa_context_get_sink_info_by_name;
pa_context_get_sink_info_list;
pa_context_get_sink_input_info;
pa_context_get_sink_input_info_list;
pa_context_get_source_info_by_index;
pa_context_get_source_info_by_name;
pa_context_get_source_info_list;
pa_context_get_source_output_info;
pa_context_get_source_output_info_list;
pa_context_set_port_latency_offset;
pa_context_get_state;
pa_context_get_tile_size;
pa_context_is_local;
pa_context_is_pending;
pa_context_kill_client;
pa_context_kill_sink_input;
pa_context_kill_source_output;
pa_context_load_cookie_from_file;
pa_context_load_module;
pa_context_move_sink_input_by_index;
pa_context_move_sink_input_by_name;
pa_context_move_source_output_by_index;
pa_context_move_source_output_by_name;
pa_context_new;
pa_context_new_with_proplist;
pa_context_play_sample;
pa_context_play_sample_with_proplist;
pa_context_proplist_remove;
pa_context_proplist_update;
pa_context_ref;
pa_context_remove_autoload_by_index;
pa_context_remove_autoload_by_name;
pa_context_remove_sample;
pa_context_rttime_new;
pa_context_rttime_restart;
pa_context_set_card_profile_by_index;
pa_context_set_card_profile_by_name;
pa_context_set_default_sink;
pa_context_set_default_source;
pa_context_set_event_callback;
pa_context_set_name;
pa_context_set_sink_input_mute;
pa_context_set_sink_input_volume;
pa_context_set_sink_mute_by_index;
pa_context_set_sink_mute_by_name;
pa_context_set_sink_port_by_index;
pa_context_set_sink_port_by_name;
pa_context_set_sink_volume_by_index;
pa_context_set_sink_volume_by_name;
pa_context_set_source_output_mute;
pa_context_set_source_output_volume;
pa_context_set_source_mute_by_index;
pa_context_set_source_mute_by_name;
pa_context_set_source_port_by_index;
pa_context_set_source_port_by_name;
pa_context_set_source_volume_by_index;
pa_context_set_source_volume_by_name;
pa_context_set_state_callback;
pa_context_set_subscribe_callback;
pa_context_stat;
pa_context_subscribe;
pa_context_suspend_sink_by_index;
pa_context_suspend_sink_by_name;
pa_context_suspend_source_by_index;
pa_context_suspend_source_by_name;
pa_context_unload_module;
pa_context_unref;
pa_cvolume_avg;
pa_cvolume_avg_mask;
pa_cvolume_channels_equal_to;
pa_cvolume_compatible;
pa_cvolume_compatible_with_channel_map;
pa_cvolume_dec;
pa_cvolume_equal;
pa_cvolume_get_balance;
pa_cvolume_get_fade;
pa_cvolume_get_lfe_balance;
pa_cvolume_get_position;
pa_cvolume_inc;
pa_cvolume_inc_clamp;
pa_cvolume_init;
pa_cvolume_max;
pa_cvolume_max_mask;
pa_cvolume_merge;
pa_cvolume_min;
pa_cvolume_min_mask;
pa_cvolume_remap;
pa_cvolume_scale;
pa_cvolume_scale_mask;
pa_cvolume_set;
pa_cvolume_set_balance;
pa_cvolume_set_fade;
pa_cvolume_set_lfe_balance;
pa_cvolume_set_position;
pa_cvolume_snprint;
pa_cvolume_snprint_verbose;
pa_cvolume_valid;
pa_direction_to_string;
pa_direction_valid;
pa_encoding_from_string;
pa_encoding_to_string;
pa_ext_device_manager_delete;
pa_ext_device_manager_enable_role_device_priority_routing;
pa_ext_device_manager_read;
pa_ext_device_manager_reorder_devices_for_role;
pa_ext_device_manager_set_device_description;
pa_ext_device_manager_set_subscribe_cb;
pa_ext_device_manager_subscribe;
pa_ext_device_manager_test;
pa_ext_device_restore_read_formats;
pa_ext_device_restore_read_formats_all;
pa_ext_device_restore_save_formats;
pa_ext_device_restore_set_subscribe_cb;
pa_ext_device_restore_subscribe;
pa_ext_device_restore_test;
pa_ext_stream_restore_delete;
pa_ext_stream_restore_read;
pa_ext_stream_restore_set_subscribe_cb;
pa_ext_stream_restore_subscribe;
pa_ext_stream_restore_test;
pa_ext_stream_restore_write;
pa_format_info_copy;
pa_format_info_free;
pa_format_info_from_string;
pa_format_info_from_sample_spec;
pa_format_info_get_prop_type;
pa_format_info_get_prop_int;
pa_format_info_get_prop_int_range;
pa_format_info_get_prop_int_array;
pa_format_info_get_prop_string;
pa_format_info_get_prop_string_array;
pa_format_info_free_string_array;
pa_format_info_is_compatible;
pa_format_info_is_pcm;
pa_format_info_new;
pa_format_info_set_channel_map;
pa_format_info_set_channels;
pa_format_info_set_prop_int;
pa_format_info_set_prop_int_array;
pa_format_info_set_prop_int_range;
pa_format_info_set_prop_string;
pa_format_info_set_prop_string_array;
pa_format_info_set_rate;
pa_format_info_set_sample_format;
pa_format_info_snprint;
pa_format_info_to_sample_spec;
pa_format_info_valid;
pa_frame_size;
pa_get_binary_name;
pa_get_fqdn;
pa_get_home_dir;
pa_get_host_name;
pa_get_library_version;
pa_gettimeofday;
pa_get_user_name;
pa_glib_mainloop_free;
pa_glib_mainloop_get_api;
pa_glib_mainloop_new;
pa_locale_to_utf8;
pa_mainloop_api_once;
pa_mainloop_dispatch;
pa_mainloop_free;
pa_mainloop_get_api;
pa_mainloop_get_retval;
pa_mainloop_iterate;
pa_mainloop_new;
pa_mainloop_poll;
pa_mainloop_prepare;
pa_mainloop_quit;
pa_mainloop_run;
pa_mainloop_set_poll_func;
pa_mainloop_wakeup;
pa_msleep;
pa_operation_cancel;
pa_operation_get_state;
pa_operation_ref;
pa_operation_set_state_callback;
pa_operation_unref;
pa_parse_sample_format;
pa_path_get_filename;
pa_proplist_clear;
pa_proplist_contains;
pa_proplist_copy;
pa_proplist_equal;
pa_proplist_free;
pa_proplist_from_string;
pa_proplist_get;
pa_proplist_gets;
pa_proplist_isempty;
pa_proplist_iterate;
pa_proplist_key_valid;
pa_proplist_new;
pa_proplist_set;
pa_proplist_setf;
pa_proplist_setp;
pa_proplist_sets;
pa_proplist_size;
pa_proplist_to_string;
pa_proplist_to_string_sep;
pa_proplist_unset;
pa_proplist_unset_many;
pa_proplist_update;
pa_rtclock_now;
pa_sample_format_is_be;
pa_sample_format_is_le;
pa_sample_format_to_string;
pa_sample_format_valid;
pa_sample_rate_valid;
pa_sample_size;
pa_sample_size_of_format;
pa_sample_spec_equal;
pa_sample_spec_init;
pa_sample_spec_snprint;
pa_sample_spec_valid;
pa_signal_done;
pa_signal_free;
pa_signal_init;
pa_signal_new;
pa_signal_set_destroy;
pa_simple_drain;
pa_simple_flush;
pa_simple_free;
pa_simple_get_latency;
pa_simple_new;
pa_simple_read;
pa_simple_write;
pa_stream_begin_write;
pa_stream_cancel_write;
pa_stream_connect_playback;
pa_stream_connect_record;
pa_stream_connect_upload;
pa_stream_cork;
pa_stream_disconnect;
pa_stream_drain;
pa_stream_drop;
pa_stream_finish_upload;
pa_stream_flush;
pa_stream_get_buffer_attr;
pa_stream_get_channel_map;
pa_stream_get_context;
pa_stream_get_device_index;
pa_stream_get_device_name;
pa_stream_get_format_info;
pa_stream_get_index;
pa_stream_get_latency;
pa_stream_get_monitor_stream;
pa_stream_get_sample_spec;
pa_stream_get_state;
pa_stream_get_time;
pa_stream_get_timing_info;
pa_stream_get_underflow_index;
pa_stream_is_corked;
pa_stream_is_suspended;
pa_stream_new;
pa_stream_new_extended;
pa_stream_new_with_proplist;
pa_stream_peek;
pa_stream_prebuf;
pa_stream_proplist_remove;
pa_stream_proplist_update;
pa_stream_readable_size;
pa_stream_ref;
pa_stream_set_buffer_attr;
pa_stream_set_buffer_attr_callback;
pa_stream_set_event_callback;
pa_stream_set_latency_update_callback;
pa_stream_set_monitor_stream;
pa_stream_set_moved_callback;
pa_stream_set_name;
pa_stream_set_overflow_callback;
pa_stream_set_read_callback;
pa_stream_set_started_callback;
pa_stream_set_state_callback;
pa_stream_set_suspended_callback;
pa_stream_set_underflow_callback;
pa_stream_set_write_callback;
pa_stream_trigger;
pa_stream_unref;
pa_stream_update_sample_rate;
pa_stream_update_timing_info;
pa_stream_writable_size;
pa_stream_write;
pa_stream_write_ext_free;
pa_strerror;
pa_sw_cvolume_divide;
pa_sw_cvolume_divide_scalar;
pa_sw_cvolume_multiply;
pa_sw_cvolume_multiply_scalar;
pa_sw_cvolume_snprint_dB;
pa_sw_volume_divide;
pa_sw_volume_from_dB;
pa_sw_volume_from_linear;
pa_sw_volume_multiply;
pa_sw_volume_snprint_dB;
pa_sw_volume_to_dB;
pa_sw_volume_to_linear;
pa_threaded_mainloop_accept;
pa_threaded_mainloop_free;
pa_threaded_mainloop_get_api;
pa_threaded_mainloop_get_retval;
pa_threaded_mainloop_in_thread;
pa_threaded_mainloop_lock;
pa_threaded_mainloop_new;
pa_threaded_mainloop_set_name;
pa_threaded_mainloop_signal;
pa_threaded_mainloop_start;
pa_threaded_mainloop_stop;
pa_threaded_mainloop_unlock;
pa_threaded_mainloop_wait;
pa_timeval_add;
pa_timeval_age;
pa_timeval_cmp;
pa_timeval_diff;
pa_timeval_load;
pa_timeval_store;
pa_timeval_sub;
pa_usec_to_bytes;
pa_utf8_filter;
pa_utf8_to_locale;
pa_utf8_valid;
pa_volume_snprint;
pa_volume_snprint_verbose;
pa_xfree;
pa_xmalloc;
pa_xmalloc0;
pa_xmemdup;
pa_xrealloc;
pa_xstrdup;
pa_xstrndup;
local:
*;
};

47
src/meson.build Normal file
View file

@ -0,0 +1,47 @@
pipewire_pulseaudio_sources = [
'bitset.c',
'channelmap.c',
'context.c',
'core-format.c',
'direction.c',
'error.c',
'ext-device-manager.c',
'ext-device-restore.c',
'format.c',
'introspect.c',
'json.c',
'mainloop.c',
'mainloop-signal.c',
'operation.c',
'proplist.c',
'rtclock.c',
'sample.c',
'scache.c',
'stream.c',
'strbuf.c',
'subscribe.c',
'utf8.c',
'util.c',
'version.c',
'volume.c',
'xmalloc.c',
'pipewire-pulseaudio.c',
]
pipewire_pulseaudio_c_args = [
'-DHAVE_CONFIG_H',
'-DPIC',
]
mapfile = 'map-file'
vflag = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), mapfile)
pipewire_pulseaudio = shared_library('pulse',
pipewire_pulseaudio_sources,
soversion : '0',
c_args : pipewire_pulseaudio_c_args,
link_args : vflag,
include_directories : [configinc],
dependencies : [pipewire_dep, pulseaudio_dep, mathlib],
install : false,
)

153
src/operation.c Normal file
View file

@ -0,0 +1,153 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <errno.h>
#include <spa/utils/list.h>
#include <pipewire/log.h>
#include <pulse/operation.h>
#include "internal.h"
pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t cb, size_t userdata_size) {
pa_operation *o;
pa_assert(c);
o = calloc(1, sizeof(pa_operation) + userdata_size);
o->refcount = 1;
o->context = c;
o->stream = s;
o->seq = ++c->seq;
o->state = PA_OPERATION_RUNNING;
o->callback = cb;
o->userdata = SPA_MEMBER(o, sizeof(pa_operation), void);
spa_list_append(&c->operations, &o->link);
pa_operation_ref(o);
pw_log_debug("new %p %d", o, o->seq);
pw_core_proxy_sync(c->core_proxy, o->seq);
return o;
}
pa_operation *pa_operation_ref(pa_operation *o)
{
pa_assert(o);
pa_assert(o->refcount >= 1);
o->refcount++;
return o;
}
static void operation_free(pa_operation *o)
{
pa_assert(!o->context);
pa_assert(!o->stream);
pw_log_debug("%p %d", o, o->seq);
free(o);
}
static void operation_unlink(pa_operation *o) {
pa_assert(o);
pw_log_debug("%p %d", o, o->seq);
if (o->context) {
pa_assert(o->refcount >= 2);
spa_list_remove(&o->link);
pa_operation_unref(o);
o->context = NULL;
}
o->stream = NULL;
o->callback = NULL;
o->userdata = NULL;
o->state_callback = NULL;
o->state_userdata = NULL;
}
void pa_operation_unref(pa_operation *o)
{
pa_assert(o);
pa_assert(o->refcount >= 1);
if (--o->refcount == 0)
operation_free(o);
}
static void operation_set_state(pa_operation *o, pa_operation_state_t st) {
pa_assert(o);
pa_assert(o->refcount >= 1);
if (st == o->state)
return;
pa_operation_ref(o);
pw_log_debug("new state %p %d %d", o, o->seq, st);
o->state = st;
if (o->state_callback)
o->state_callback(o, o->state_userdata);
if ((o->state == PA_OPERATION_DONE) || (o->state == PA_OPERATION_CANCELED))
operation_unlink(o);
pa_operation_unref(o);
}
void pa_operation_cancel(pa_operation *o)
{
pa_assert(o);
pa_assert(o->refcount >= 1);
operation_set_state(o, PA_OPERATION_CANCELED);
}
void pa_operation_done(pa_operation *o) {
pa_assert(o);
pa_assert(o->refcount >= 1);
operation_set_state(o, PA_OPERATION_DONE);
}
pa_operation_state_t pa_operation_get_state(pa_operation *o)
{
pa_assert(o);
pa_assert(o->refcount >= 1);
return o->state;
}
void pa_operation_set_state_callback(pa_operation *o, pa_operation_notify_cb_t cb, void *userdata)
{
pa_assert(o);
pa_assert(o->refcount >= 1);
if (o->state == PA_OPERATION_DONE || o->state == PA_OPERATION_CANCELED)
return;
o->state_callback = cb;
o->state_userdata = userdata;
}

30
src/pipewire-pulseaudio.c Normal file
View file

@ -0,0 +1,30 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <pipewire/pipewire.h>
static void reg(void) __attribute__ ((constructor));
static void reg(void)
{
pw_init(NULL, NULL);
}

271
src/proplist.c Normal file
View file

@ -0,0 +1,271 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <string.h>
#include <pipewire/log.h>
#include <pipewire/properties.h>
#include <pulse/proplist.h>
#include "internal.h"
#include "strbuf.h"
pa_proplist* pa_proplist_new_dict(struct spa_dict *dict)
{
pa_proplist *p;
p = calloc(1, sizeof(struct pa_proplist));
if (p == NULL)
return NULL;
if (dict)
p->props = pw_properties_new_dict(dict);
else
p->props = pw_properties_new(NULL, NULL);
return p;
}
pa_proplist* pa_proplist_new_props(struct pw_properties *props)
{
return pa_proplist_new_dict(&props->dict);
}
pa_proplist* pa_proplist_new(void)
{
return pa_proplist_new_dict(NULL);
}
void pa_proplist_free(pa_proplist* p)
{
pw_properties_free(p->props);
free(p);
}
int pa_proplist_key_valid(const char *key)
{
const char *p;
for (p = key; *p; p++)
if ((unsigned char) *p >= 128)
return 0;
if (strlen(key) < 1)
return 0;
return 1;
}
int pa_proplist_sets(pa_proplist *p, const char *key, const char *value)
{
pw_properties_set(p->props, key, value);
return 0;
}
int pa_proplist_setp(pa_proplist *p, const char *pair)
{
pw_log_warn("Not Implemented");
return -1;
}
int pa_proplist_setf(pa_proplist *p, const char *key, const char *format, ...)
{
pw_log_warn("Not Implemented");
return -1;
}
int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nbytes)
{
pw_log_warn("Not Implemented");
return -1;
}
const char *pa_proplist_gets(pa_proplist *p, const char *key)
{
return pw_properties_get(p->props, key);
}
int pa_proplist_get(pa_proplist *p, const char *key, const void **data, size_t *nbytes)
{
const char *val;
spa_assert(p);
spa_assert(key);
val = pw_properties_get(p->props, key);
*data = val;
*nbytes = val ? strlen(val) : 0;
return 0;
}
void pa_proplist_update(pa_proplist *p, pa_update_mode_t mode, const pa_proplist *other)
{
spa_assert(p);
pw_log_warn("Not Implemented");
}
int pa_proplist_unset(pa_proplist *p, const char *key)
{
spa_assert(p);
spa_assert(key);
if (!pa_proplist_key_valid(key))
return -1;
return pw_properties_set(p->props, key, NULL);
}
int pa_proplist_unset_many(pa_proplist *p, const char * const keys[])
{
const char * const * k;
int n = 0;
spa_assert(p);
spa_assert(keys);
for (k = keys; *k; k++)
if (!pa_proplist_key_valid(*k))
return -1;
for (k = keys; *k; k++)
if (pa_proplist_unset(p, *k) >= 0)
n++;
return n;
}
const char *pa_proplist_iterate(pa_proplist *p, void **state)
{
spa_assert(p);
spa_assert(state);
return pw_properties_iterate(p->props, state);
}
char *pa_proplist_to_string(pa_proplist *p)
{
spa_assert(p);
pw_log_warn("Not Implemented");
return NULL;
}
char *pa_proplist_to_string_sep(pa_proplist *p, const char *sep)
{
const char *key;
void *state = NULL;
pa_strbuf *buf;
spa_assert(p);
spa_assert(sep);
buf = pa_strbuf_new();
while ((key = pa_proplist_iterate(p, &state))) {
const char *v;
const char *t;
if (!pa_strbuf_isempty(buf))
pa_strbuf_puts(buf, sep);
if ((v = pa_proplist_gets(p, key)) == NULL)
continue;
pa_strbuf_printf(buf, "%s = \"", key);
for (t = v;;) {
size_t h;
h = strcspn(t, "\"");
if (h > 0)
pa_strbuf_putsn(buf, t, h);
t += h;
if (*t == 0)
break;
pa_assert(*t == '"');
pa_strbuf_puts(buf, "\\\"");
t++;
}
pa_strbuf_puts(buf, "\"");
}
return pa_strbuf_to_string_free(buf);
}
pa_proplist *pa_proplist_from_string(const char *str)
{
spa_assert(str);
pw_log_warn("Not Implemented");
return NULL;
}
int pa_proplist_contains(pa_proplist *p, const char *key)
{
spa_assert(p);
spa_assert(key);
if (!pa_proplist_key_valid(key))
return -1;
if (pw_properties_get(p->props, key) == NULL)
return 0;
return 1;
}
void pa_proplist_clear(pa_proplist *p)
{
spa_assert(p);
pw_log_warn("Not Implemented");
}
pa_proplist* pa_proplist_copy(const pa_proplist *p)
{
pa_proplist *c;
spa_assert(p);
c = calloc(1, sizeof(struct pa_proplist));
if (c == NULL)
return NULL;
c->props = pw_properties_copy(p->props);
return c;
}
unsigned pa_proplist_size(pa_proplist *p)
{
spa_assert(p);
return p->props->dict.n_items;
}
int pa_proplist_isempty(pa_proplist *p)
{
spa_assert(p);
return p->props->dict.n_items == 0 ? 1 : 0;
}
int pa_proplist_equal(pa_proplist *a, pa_proplist *b)
{
spa_assert(a);
spa_assert(b);
pw_log_warn("Not Implemented");
return 0;
}

34
src/rtclock.c Normal file
View file

@ -0,0 +1,34 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <time.h>
#include <spa/utils/defs.h>
#include <pulse/rtclock.h>
pa_usec_t pa_rtclock_now(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec * SPA_USEC_PER_SEC +
ts.tv_nsec * SPA_NSEC_PER_USEC;
}

134
src/sample-util.h Normal file
View file

@ -0,0 +1,134 @@
#ifndef foosampleutilhfoo
#define foosampleutilhfoo
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio 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 Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#include <inttypes.h>
#include <limits.h>
#include <pulse/gccmacro.h>
#include <pulse/sample.h>
#include <pulse/volume.h>
#include <pulse/channelmap.h>
size_t pa_frame_align(size_t l, const pa_sample_spec *ss) PA_GCC_PURE;
bool pa_frame_aligned(size_t l, const pa_sample_spec *ss) PA_GCC_PURE;
void pa_interleave(const void *src[], unsigned channels, void *dst, size_t ss, unsigned n);
void pa_deinterleave(const void *src, void *dst[], unsigned channels, size_t ss, unsigned n);
void pa_sample_clamp(pa_sample_format_t format, void *dst, size_t dstr, const void *src, size_t sstr, unsigned n);
static inline int32_t pa_mult_s16_volume(int16_t v, int32_t cv) {
#ifdef HAVE_FAST_64BIT_OPERATIONS
/* Multiply with 64 bit integers on 64 bit platforms */
return (v * (int64_t) cv) >> 16;
#else
/* Multiplying the 32 bit volume factor with the
* 16 bit sample might result in an 48 bit value. We
* want to do without 64 bit integers and hence do
* the multiplication independently for the HI and
* LO part of the volume. */
int32_t hi = cv >> 16;
int32_t lo = cv & 0xFFFF;
return ((v * lo) >> 16) + (v * hi);
#endif
}
pa_usec_t pa_bytes_to_usec_round_up(uint64_t length, const pa_sample_spec *spec);
size_t pa_usec_to_bytes_round_up(pa_usec_t t, const pa_sample_spec *spec);
typedef void (*pa_do_volume_func_t) (void *samples, const void *volumes, unsigned channels, unsigned length);
pa_do_volume_func_t pa_get_volume_func(pa_sample_format_t f);
void pa_set_volume_func(pa_sample_format_t f, pa_do_volume_func_t func);
size_t pa_convert_size(size_t size, const pa_sample_spec *from, const pa_sample_spec *to);
#define PA_CHANNEL_POSITION_MASK_LEFT \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_LEFT)) \
#define PA_CHANNEL_POSITION_MASK_RIGHT \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_RIGHT))
#define PA_CHANNEL_POSITION_MASK_CENTER \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_CENTER))
#define PA_CHANNEL_POSITION_MASK_FRONT \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_CENTER))
#define PA_CHANNEL_POSITION_MASK_REAR \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_CENTER))
#define PA_CHANNEL_POSITION_MASK_LFE \
PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE)
#define PA_CHANNEL_POSITION_MASK_HFE \
(PA_CHANNEL_POSITION_MASK_REAR | PA_CHANNEL_POSITION_MASK_FRONT \
| PA_CHANNEL_POSITION_MASK_LEFT | PA_CHANNEL_POSITION_MASK_RIGHT \
| PA_CHANNEL_POSITION_MASK_CENTER)
#define PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_CENTER))
#define PA_CHANNEL_POSITION_MASK_TOP \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_CENTER))
#define PA_CHANNEL_POSITION_MASK_ALL \
((pa_channel_position_mask_t) (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_MAX)-1))
#endif

319
src/sample.c Normal file
View file

@ -0,0 +1,319 @@
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
Copyright 2018 Wim Taymans <wim.taymans@gmail.com>
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio 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 Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <string.h>
#include <spa/utils/defs.h>
#include <pulse/timeval.h>
#include <pulse/sample.h>
#define pa_init_i18n()
#define _(String) (String)
static const size_t size_table[] = {
[PA_SAMPLE_U8] = 1,
[PA_SAMPLE_ULAW] = 1,
[PA_SAMPLE_ALAW] = 1,
[PA_SAMPLE_S16LE] = 2,
[PA_SAMPLE_S16BE] = 2,
[PA_SAMPLE_FLOAT32LE] = 4,
[PA_SAMPLE_FLOAT32BE] = 4,
[PA_SAMPLE_S32LE] = 4,
[PA_SAMPLE_S32BE] = 4,
[PA_SAMPLE_S24LE] = 3,
[PA_SAMPLE_S24BE] = 3,
[PA_SAMPLE_S24_32LE] = 4,
[PA_SAMPLE_S24_32BE] = 4
};
size_t pa_sample_size_of_format(pa_sample_format_t f)
{
spa_assert(pa_sample_format_valid(f));
return size_table[f];
}
size_t pa_sample_size(const pa_sample_spec * spec)
{
spa_assert(spec);
spa_assert(pa_sample_spec_valid(spec));
return size_table[spec->format];
}
size_t pa_frame_size(const pa_sample_spec * spec)
{
spa_assert(spec);
spa_assert(pa_sample_spec_valid(spec));
return size_table[spec->format] * spec->channels;
}
size_t pa_bytes_per_second(const pa_sample_spec * spec)
{
spa_assert(spec);
spa_assert(pa_sample_spec_valid(spec));
return spec->rate * size_table[spec->format] * spec->channels;
}
pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec * spec)
{
spa_assert(spec);
spa_assert(pa_sample_spec_valid(spec));
return (((pa_usec_t)
(length / (size_table[spec->format] * spec->channels))
* PA_USEC_PER_SEC) / spec->rate);
}
size_t pa_usec_to_bytes(pa_usec_t t, const pa_sample_spec * spec)
{
spa_assert(spec);
spa_assert(pa_sample_spec_valid(spec));
return (size_t) (((t * spec->rate) / PA_USEC_PER_SEC)) *
(size_table[spec->format] * spec->channels);
}
pa_sample_spec *pa_sample_spec_init(pa_sample_spec * spec)
{
spa_assert(spec);
spec->format = PA_SAMPLE_INVALID;
spec->rate = 0;
spec->channels = 0;
return spec;
}
int pa_sample_format_valid(unsigned format)
{
return format < PA_SAMPLE_MAX;
}
int pa_sample_rate_valid(uint32_t rate)
{
/* The extra 1% is due to module-loopback: it temporarily sets
* a higher-than-nominal rate to get rid of excessive buffer
* latency */
return rate > 0 && rate <= PA_RATE_MAX * 101 / 100;
}
int pa_channels_valid(uint8_t channels)
{
return channels > 0 && channels <= PA_CHANNELS_MAX;
}
int pa_sample_spec_valid(const pa_sample_spec * spec)
{
spa_assert(spec);
if (SPA_UNLIKELY(!pa_sample_rate_valid(spec->rate) ||
!pa_channels_valid(spec->channels) ||
!pa_sample_format_valid(spec->format)))
return 0;
return 1;
}
int pa_sample_spec_equal(const pa_sample_spec * a, const pa_sample_spec * b)
{
spa_assert(a);
spa_assert(b);
spa_return_val_if_fail(pa_sample_spec_valid(a), 0);
if (SPA_UNLIKELY(a == b))
return 1;
spa_return_val_if_fail(pa_sample_spec_valid(b), 0);
return
(a->format == b->format) &&
(a->rate == b->rate) && (a->channels == b->channels);
}
const char *pa_sample_format_to_string(pa_sample_format_t f)
{
static const char *const table[] = {
[PA_SAMPLE_U8] = "u8",
[PA_SAMPLE_ALAW] = "aLaw",
[PA_SAMPLE_ULAW] = "uLaw",
[PA_SAMPLE_S16LE] = "s16le",
[PA_SAMPLE_S16BE] = "s16be",
[PA_SAMPLE_FLOAT32LE] = "float32le",
[PA_SAMPLE_FLOAT32BE] = "float32be",
[PA_SAMPLE_S32LE] = "s32le",
[PA_SAMPLE_S32BE] = "s32be",
[PA_SAMPLE_S24LE] = "s24le",
[PA_SAMPLE_S24BE] = "s24be",
[PA_SAMPLE_S24_32LE] = "s24-32le",
[PA_SAMPLE_S24_32BE] = "s24-32be",
};
if (!pa_sample_format_valid(f))
return NULL;
return table[f];
}
char *pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec * spec)
{
spa_assert(s);
spa_assert(l > 0);
spa_assert(spec);
pa_init_i18n();
if (!pa_sample_spec_valid(spec))
snprintf(s, l, _("(invalid)"));
else
snprintf(s, l, _("%s %uch %uHz"),
pa_sample_format_to_string(spec->format),
spec->channels, spec->rate);
return s;
}
char *pa_bytes_snprint(char *s, size_t l, unsigned v)
{
spa_assert(s);
spa_assert(l > 0);
pa_init_i18n();
if (v >= ((unsigned)1024) * 1024 * 1024)
snprintf(s, l, _("%0.1f GiB"),
((double)v) / 1024 / 1024 / 1024);
else if (v >= ((unsigned)1024) * 1024)
snprintf(s, l, _("%0.1f MiB"), ((double)v) / 1024 / 1024);
else if (v >= (unsigned)1024)
snprintf(s, l, _("%0.1f KiB"), ((double)v) / 1024);
else
snprintf(s, l, _("%u B"), (unsigned)v);
return s;
}
pa_sample_format_t pa_parse_sample_format(const char *format)
{
spa_assert(format);
if (strcasecmp(format, "s16le") == 0)
return PA_SAMPLE_S16LE;
else if (strcasecmp(format, "s16be") == 0)
return PA_SAMPLE_S16BE;
else if (strcasecmp(format, "s16ne") == 0
|| strcasecmp(format, "s16") == 0
|| strcasecmp(format, "16") == 0)
return PA_SAMPLE_S16NE;
else if (strcasecmp(format, "s16re") == 0)
return PA_SAMPLE_S16RE;
else if (strcasecmp(format, "u8") == 0 || strcasecmp(format, "8") == 0)
return PA_SAMPLE_U8;
else if (strcasecmp(format, "float32") == 0
|| strcasecmp(format, "float32ne") == 0
|| strcasecmp(format, "float") == 0)
return PA_SAMPLE_FLOAT32NE;
else if (strcasecmp(format, "float32re") == 0)
return PA_SAMPLE_FLOAT32RE;
else if (strcasecmp(format, "float32le") == 0)
return PA_SAMPLE_FLOAT32LE;
else if (strcasecmp(format, "float32be") == 0)
return PA_SAMPLE_FLOAT32BE;
else if (strcasecmp(format, "ulaw") == 0
|| strcasecmp(format, "mulaw") == 0)
return PA_SAMPLE_ULAW;
else if (strcasecmp(format, "alaw") == 0)
return PA_SAMPLE_ALAW;
else if (strcasecmp(format, "s32le") == 0)
return PA_SAMPLE_S32LE;
else if (strcasecmp(format, "s32be") == 0)
return PA_SAMPLE_S32BE;
else if (strcasecmp(format, "s32ne") == 0
|| strcasecmp(format, "s32") == 0
|| strcasecmp(format, "32") == 0)
return PA_SAMPLE_S32NE;
else if (strcasecmp(format, "s32re") == 0)
return PA_SAMPLE_S24RE;
else if (strcasecmp(format, "s24le") == 0)
return PA_SAMPLE_S24LE;
else if (strcasecmp(format, "s24be") == 0)
return PA_SAMPLE_S24BE;
else if (strcasecmp(format, "s24ne") == 0
|| strcasecmp(format, "s24") == 0
|| strcasecmp(format, "24") == 0)
return PA_SAMPLE_S24NE;
else if (strcasecmp(format, "s24re") == 0)
return PA_SAMPLE_S24RE;
else if (strcasecmp(format, "s24-32le") == 0)
return PA_SAMPLE_S24_32LE;
else if (strcasecmp(format, "s24-32be") == 0)
return PA_SAMPLE_S24_32BE;
else if (strcasecmp(format, "s24-32ne") == 0
|| strcasecmp(format, "s24-32") == 0)
return PA_SAMPLE_S24_32NE;
else if (strcasecmp(format, "s24-32re") == 0)
return PA_SAMPLE_S24_32RE;
return PA_SAMPLE_INVALID;
}
int pa_sample_format_is_le(pa_sample_format_t f)
{
spa_assert(pa_sample_format_valid(f));
switch (f) {
case PA_SAMPLE_S16LE:
case PA_SAMPLE_S24LE:
case PA_SAMPLE_S32LE:
case PA_SAMPLE_S24_32LE:
case PA_SAMPLE_FLOAT32LE:
return 1;
case PA_SAMPLE_S16BE:
case PA_SAMPLE_S24BE:
case PA_SAMPLE_S32BE:
case PA_SAMPLE_S24_32BE:
case PA_SAMPLE_FLOAT32BE:
return 0;
default:
return -1;
}
}
int pa_sample_format_is_be(pa_sample_format_t f)
{
int r;
if ((r = pa_sample_format_is_le(f)) < 0)
return r;
return !r;
}

57
src/scache.c Normal file
View file

@ -0,0 +1,57 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <pipewire/log.h>
#include <pulse/scache.h>
#include "internal.h"
int pa_stream_connect_upload(pa_stream *s, size_t length)
{
pw_log_warn("Not Implemented");
return 0;
}
int pa_stream_finish_upload(pa_stream *s)
{
pw_log_warn("Not Implemented");
return 0;
}
pa_operation* pa_context_remove_sample(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_play_sample(pa_context *c, const char *name, const char *dev,
pa_volume_t volume, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_play_sample_with_proplist(pa_context *c, const char *name,
const char *dev, pa_volume_t volume, pa_proplist *proplist,
pa_context_play_sample_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}

193
src/strbuf.c Normal file
View file

@ -0,0 +1,193 @@
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio 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 Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <pulse/xmalloc.h>
#include "internal.h"
#include "strbuf.h"
/* A chunk of the linked list that makes up the string */
struct chunk {
struct chunk *next;
size_t length;
};
#define CHUNK_TO_TEXT(c) ((char*) (c) + PA_ALIGN(sizeof(struct chunk)))
struct pa_strbuf {
size_t length;
struct chunk *head, *tail;
};
pa_strbuf *pa_strbuf_new(void) {
pa_strbuf *sb;
sb = pa_xnew(pa_strbuf, 1);
sb->length = 0;
sb->head = sb->tail = NULL;
return sb;
}
void pa_strbuf_free(pa_strbuf *sb) {
pa_assert(sb);
while (sb->head) {
struct chunk *c = sb->head;
sb->head = sb->head->next;
pa_xfree(c);
}
pa_xfree(sb);
}
/* Make a C string from the string buffer. The caller has to free
* string with pa_xfree(). */
char *pa_strbuf_to_string(pa_strbuf *sb) {
char *t, *e;
struct chunk *c;
pa_assert(sb);
e = t = pa_xmalloc(sb->length+1);
for (c = sb->head; c; c = c->next) {
pa_assert((size_t) (e-t) <= sb->length);
memcpy(e, CHUNK_TO_TEXT(c), c->length);
e += c->length;
}
/* Trailing NUL */
*e = 0;
pa_assert(e == t+sb->length);
return t;
}
/* Combination of pa_strbuf_free() and pa_strbuf_to_string() */
char *pa_strbuf_to_string_free(pa_strbuf *sb) {
char *t;
pa_assert(sb);
t = pa_strbuf_to_string(sb);
pa_strbuf_free(sb);
return t;
}
/* Append a string to the string buffer */
void pa_strbuf_puts(pa_strbuf *sb, const char *t) {
pa_assert(sb);
pa_assert(t);
pa_strbuf_putsn(sb, t, strlen(t));
}
/* Append a character to the string buffer */
void pa_strbuf_putc(pa_strbuf *sb, char c) {
pa_assert(sb);
pa_strbuf_putsn(sb, &c, 1);
}
/* Append a new chunk to the linked list */
static void append(pa_strbuf *sb, struct chunk *c) {
pa_assert(sb);
pa_assert(c);
if (sb->tail) {
pa_assert(sb->head);
sb->tail->next = c;
} else {
pa_assert(!sb->head);
sb->head = c;
}
sb->tail = c;
sb->length += c->length;
c->next = NULL;
}
/* Append up to l bytes of a string to the string buffer */
void pa_strbuf_putsn(pa_strbuf *sb, const char *t, size_t l) {
struct chunk *c;
pa_assert(sb);
pa_assert(t);
if (!l)
return;
c = pa_xmalloc(PA_ALIGN(sizeof(struct chunk)) + l);
c->length = l;
memcpy(CHUNK_TO_TEXT(c), t, l);
append(sb, c);
}
/* Append a printf() style formatted string to the string buffer. */
/* The following is based on an example from the GNU libc documentation */
size_t pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) {
size_t size = 100;
struct chunk *c = NULL;
pa_assert(sb);
pa_assert(format);
for(;;) {
va_list ap;
int r;
c = pa_xrealloc(c, PA_ALIGN(sizeof(struct chunk)) + size);
va_start(ap, format);
r = vsnprintf(CHUNK_TO_TEXT(c), size, format, ap);
CHUNK_TO_TEXT(c)[size-1] = 0;
va_end(ap);
if (r > -1 && (size_t) r < size) {
c->length = (size_t) r;
append(sb, c);
return (size_t) r;
}
if (r > -1) /* glibc 2.1 */
size = (size_t) r+1;
else /* glibc 2.0 */
size *= 2;
}
}
bool pa_strbuf_isempty(pa_strbuf *sb) {
pa_assert(sb);
return sb->length <= 0;
}

39
src/strbuf.h Normal file
View file

@ -0,0 +1,39 @@
#ifndef foostrbufhfoo
#define foostrbufhfoo
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio 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 Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#include <spa/utils/defs.h>
typedef struct pa_strbuf pa_strbuf;
pa_strbuf *pa_strbuf_new(void);
void pa_strbuf_free(pa_strbuf *sb);
char *pa_strbuf_to_string(pa_strbuf *sb);
char *pa_strbuf_to_string_free(pa_strbuf *sb);
size_t pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3);
void pa_strbuf_puts(pa_strbuf *sb, const char *t);
void pa_strbuf_putsn(pa_strbuf *sb, const char *t, size_t m);
void pa_strbuf_putc(pa_strbuf *sb, char c);
bool pa_strbuf_isempty(pa_strbuf *sb);
#endif

908
src/stream.c Normal file
View file

@ -0,0 +1,908 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <unistd.h>
#include <errno.h>
#include <spa/utils/defs.h>
#include <pulse/stream.h>
#include <pipewire/stream.h>
#include "internal.h"
static void stream_state_changed(void *data, enum pw_stream_state old,
enum pw_stream_state state, const char *error)
{
pa_stream *s = data;
switch(state) {
case PW_STREAM_STATE_ERROR:
pa_stream_set_state(s, PA_STREAM_FAILED);
break;
case PW_STREAM_STATE_UNCONNECTED:
pa_stream_set_state(s, PA_STREAM_UNCONNECTED);
break;
case PW_STREAM_STATE_CONNECTING:
pa_stream_set_state(s, PA_STREAM_CREATING);
break;
case PW_STREAM_STATE_CONFIGURE:
case PW_STREAM_STATE_READY:
break;
case PW_STREAM_STATE_PAUSED:
pa_stream_set_state(s, PA_STREAM_READY);
break;
case PW_STREAM_STATE_STREAMING:
break;
}
}
static void stream_format_changed(void *data, const struct spa_pod *format)
{
}
static void stream_process(void *data)
{
pa_stream *s = data;
if (s->direction == PA_STREAM_PLAYBACK) {
if (s->write_callback)
s->write_callback(s, 4096, s->write_userdata);
}
else {
if (s->read_callback)
s->read_callback(s, 4096, s->read_userdata);
}
}
static const struct pw_stream_events stream_events =
{
PW_VERSION_STREAM_EVENTS,
.state_changed = stream_state_changed,
.format_changed = stream_format_changed,
.process = stream_process,
};
pa_stream* stream_new(pa_context *c, const char *name,
const pa_sample_spec *ss, const pa_channel_map *map,
pa_format_info * const * formats, unsigned int n_formats,
pa_proplist *p)
{
pa_stream *s;
int i;
spa_assert(c);
spa_assert(c->refcount >= 1);
pa_assert((ss == NULL && map == NULL) || (formats == NULL && n_formats == 0));
pa_assert(n_formats < PA_MAX_FORMATS);
PA_CHECK_VALIDITY_RETURN_NULL(c, name ||
(p && pa_proplist_contains(p, PA_PROP_MEDIA_NAME)), PA_ERR_INVALID);
s = calloc(1, sizeof(pa_stream));
if (s == NULL)
return NULL;
s->stream = pw_stream_new(c->remote, name, NULL);
s->refcount = 1;
s->context = c;
init_type(&s->type, pw_core_get_type(c->core)->map);
pw_stream_add_listener(s->stream, &s->stream_listener, &stream_events, s);
s->direction = PA_STREAM_NODIRECTION;
s->state = PA_STREAM_UNCONNECTED;
s->flags = 0;
if (ss)
s->sample_spec = *ss;
else
pa_sample_spec_init(&s->sample_spec);
if (map)
s->channel_map = *map;
else
pa_channel_map_init(&s->channel_map);
s->n_formats = 0;
if (formats) {
s->n_formats = n_formats;
for (i = 0; i < n_formats; i++)
s->req_formats[i] = pa_format_info_copy(formats[i]);
}
s->format = NULL;
s->direct_on_input = PA_INVALID_INDEX;
s->proplist = p ? pa_proplist_copy(p) : pa_proplist_new();
if (name)
pa_proplist_sets(s->proplist, PA_PROP_MEDIA_NAME, name);
s->stream_index = PA_INVALID_INDEX;
s->buffer_attr.maxlength = (uint32_t) -1;
if (ss)
s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, ss); /* 250ms of buffering */
else {
/* FIXME: We assume a worst-case compressed format corresponding to
* 48000 Hz, 2 ch, S16 PCM, but this can very well be incorrect */
pa_sample_spec tmp_ss = {
.format = PA_SAMPLE_S16NE,
.rate = 48000,
.channels = 2,
};
s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, &tmp_ss); /* 250ms of buffering */
}
s->buffer_attr.minreq = (uint32_t) -1;
s->buffer_attr.prebuf = (uint32_t) -1;
s->buffer_attr.fragsize = (uint32_t) -1;
s->device_index = PA_INVALID_INDEX;
s->timing_info_valid = false;
spa_list_append(&c->streams, &s->link);
pa_stream_ref(s);
return s;
}
pa_stream* pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss,
const pa_channel_map *map)
{
return stream_new(c, name, ss, map, NULL, 0, NULL);
}
pa_stream* pa_stream_new_with_proplist(pa_context *c, const char *name,
const pa_sample_spec *ss, const pa_channel_map *map, pa_proplist *p)
{
return stream_new(c, name, ss, map, NULL, 0, p);
}
pa_stream *pa_stream_new_extended(pa_context *c, const char *name,
pa_format_info * const * formats, unsigned int n_formats, pa_proplist *p)
{
return stream_new(c, name, NULL, NULL, formats, n_formats, p);
}
static void stream_unlink(pa_stream *s)
{
spa_list_remove(&s->link);
}
static void stream_free(pa_stream *s)
{
int i;
if (s->proplist)
pa_proplist_free(s->proplist);
for (i = 0; i < s->n_formats; i++)
pa_format_info_free(s->req_formats[i]);
if (s->format)
pa_format_info_free(s->format);
free(s->device_name);
free(s);
}
void pa_stream_unref(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
if (--s->refcount == 0)
stream_free(s);
}
pa_stream *pa_stream_ref(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
s->refcount++;
return s;
}
pa_stream_state_t pa_stream_get_state(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
return s->state;
}
pa_context* pa_stream_get_context(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
return s->context;
}
uint32_t pa_stream_get_index(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
PA_CHECK_VALIDITY_RETURN_ANY(s->context,
s->state == PA_STREAM_READY, PA_ERR_BADSTATE, PA_INVALID_INDEX);
return s->stream_index;
}
void pa_stream_set_state(pa_stream *s, pa_stream_state_t st) {
spa_assert(s);
spa_assert(s->refcount >= 1);
if (s->state == st)
return;
pa_stream_ref(s);
s->state = st;
if (s->state_callback)
s->state_callback(s, s->state_userdata);
if ((st == PA_STREAM_FAILED || st == PA_STREAM_TERMINATED))
stream_unlink(s);
pa_stream_unref(s);
}
uint32_t pa_stream_get_device_index(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY,
PA_ERR_BADSTATE, PA_INVALID_INDEX);
PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction != PA_STREAM_UPLOAD,
PA_ERR_BADSTATE, PA_INVALID_INDEX);
PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->device_index != PA_INVALID_INDEX,
PA_ERR_BADSTATE, PA_INVALID_INDEX);
return s->device_index;
}
const char *pa_stream_get_device_name(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->device_name, PA_ERR_BADSTATE);
return s->device_name;
}
int pa_stream_is_suspended(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
return s->suspended;
}
int pa_stream_is_corked(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
return s->corked;
}
static int create_stream(pa_stream_direction_t direction,
pa_stream *s,
const char *dev,
const pa_buffer_attr *attr,
pa_stream_flags_t flags,
const pa_cvolume *volume,
pa_stream *sync_stream)
{
int res;
enum pw_stream_flags fl;
const struct spa_pod *params[1];
uint8_t buffer[1024];
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
struct pw_type *t;
spa_assert(s);
spa_assert(s->refcount >= 1);
s->direction = direction;
t = pw_core_get_type(s->context->core);
pa_stream_set_state(s, PA_STREAM_CREATING);
fl = PW_STREAM_FLAG_AUTOCONNECT |
PW_STREAM_FLAG_MAP_BUFFERS |
PW_STREAM_FLAG_RT_PROCESS;
if (flags & PA_STREAM_PASSTHROUGH)
fl |= PW_STREAM_FLAG_EXCLUSIVE;
params[0] = spa_pod_builder_object(&b,
t->param.idEnumFormat, t->spa_format,
"I", s->type.media_type.audio,
"I", s->type.media_subtype.raw,
":", s->type.format_audio.format, "I", s->type.audio_format.S16,
":", s->type.format_audio.layout, "i", SPA_AUDIO_LAYOUT_INTERLEAVED,
":", s->type.format_audio.channels, "i", 2,
":", s->type.format_audio.rate, "i", 44100);
res = pw_stream_connect(s->stream,
direction == PA_STREAM_PLAYBACK ?
PW_DIRECTION_OUTPUT :
PW_DIRECTION_INPUT,
dev,
fl,
params, 1);
return res;
}
int pa_stream_connect_playback(
pa_stream *s,
const char *dev,
const pa_buffer_attr *attr,
pa_stream_flags_t flags,
const pa_cvolume *volume,
pa_stream *sync_stream)
{
return create_stream(PA_STREAM_PLAYBACK, s, dev, attr, flags, volume, sync_stream);
}
int pa_stream_connect_record(
pa_stream *s,
const char *dev,
const pa_buffer_attr *attr,
pa_stream_flags_t flags)
{
return create_stream(PA_STREAM_RECORD, s, dev, attr, flags, NULL, NULL);
}
int pa_stream_disconnect(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
PA_CHECK_VALIDITY(s->context, s->context->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
pa_stream_set_state(s, PA_STREAM_TERMINATED);
return 0;
}
int pa_stream_begin_write(
pa_stream *s,
void **data,
size_t *nbytes)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK ||
s->direction == PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, data, PA_ERR_INVALID);
PA_CHECK_VALIDITY(s->context, nbytes && *nbytes != 0, PA_ERR_INVALID);
if (s->buffer == NULL) {
s->buffer = pw_stream_dequeue_buffer(s->stream);
}
if (s->buffer == NULL) {
*data = NULL;
*nbytes = 0;
}
else {
*data = s->buffer->buffer->datas[0].data;
*nbytes = s->buffer->buffer->datas[0].maxsize;
}
return 0;
}
int pa_stream_cancel_write(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK ||
s->direction == PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
pw_log_warn("Not Implemented");
return 0;
}
int pa_stream_write(pa_stream *s,
const void *data,
size_t nbytes,
pa_free_cb_t free_cb,
int64_t offset,
pa_seek_mode_t seek)
{
return pa_stream_write_ext_free(s, data, nbytes, free_cb, (void*) data, offset, seek);
}
int pa_stream_write_ext_free(pa_stream *s,
const void *data,
size_t nbytes,
pa_free_cb_t free_cb,
void *free_cb_data,
int64_t offset,
pa_seek_mode_t seek)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
spa_assert(data);
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK ||
s->direction == PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, seek <= PA_SEEK_RELATIVE_END, PA_ERR_INVALID);
PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK ||
(seek == PA_SEEK_RELATIVE && offset == 0), PA_ERR_INVALID);
PA_CHECK_VALIDITY(s->context, offset % pa_frame_size(&s->sample_spec) == 0, PA_ERR_INVALID);
PA_CHECK_VALIDITY(s->context, nbytes % pa_frame_size(&s->sample_spec) == 0, PA_ERR_INVALID);
if (s->buffer == NULL) {
pw_log_warn("Not Implemented");
return PA_ERR_INVALID;
}
else {
s->buffer->buffer->datas[0].chunk->offset = 0;
s->buffer->buffer->datas[0].chunk->size = nbytes;
}
pw_stream_queue_buffer(s->stream, s->buffer);
return 0;
}
int pa_stream_peek(pa_stream *s,
const void **data,
size_t *nbytes)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
spa_assert(data);
spa_assert(nbytes);
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE);
pw_log_warn("Not Implemented");
return 0;
}
int pa_stream_drop(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE);
pw_log_warn("Not Implemented");
return 0;
}
size_t pa_stream_writable_size(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY,
PA_ERR_BADSTATE, (size_t) -1);
PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction != PA_STREAM_RECORD,
PA_ERR_BADSTATE, (size_t) -1);
pw_log_warn("Not Implemented");
return 0;
}
size_t pa_stream_readable_size(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY,
PA_ERR_BADSTATE, (size_t) -1);
PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction == PA_STREAM_RECORD,
PA_ERR_BADSTATE, (size_t) -1);
pw_log_warn("Not Implemented");
return 0;
}
pa_operation* pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE);
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_stream_update_timing_info(pa_stream *s, pa_stream_success_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
pw_log_warn("Not Implemented");
return NULL;
}
void pa_stream_set_state_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
return;
s->state_callback = cb;
s->state_userdata = userdata;
}
void pa_stream_set_write_callback(pa_stream *s, pa_stream_request_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
return;
s->write_callback = cb;
s->write_userdata = userdata;
}
void pa_stream_set_read_callback(pa_stream *s, pa_stream_request_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
return;
s->read_callback = cb;
s->read_userdata = userdata;
}
void pa_stream_set_overflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
return;
s->overflow_callback = cb;
s->overflow_userdata = userdata;
}
int64_t pa_stream_get_underflow_index(pa_stream *s)
{
pw_log_warn("Not Implemented");
return 0;
}
void pa_stream_set_underflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
return;
s->underflow_callback = cb;
s->underflow_userdata = userdata;
}
void pa_stream_set_started_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
return;
s->started_callback = cb;
s->started_userdata = userdata;
}
void pa_stream_set_latency_update_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
return;
s->latency_update_callback = cb;
s->latency_update_userdata = userdata;
}
void pa_stream_set_moved_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
return;
s->moved_callback = cb;
s->moved_userdata = userdata;
}
void pa_stream_set_suspended_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
return;
s->suspended_callback = cb;
s->suspended_userdata = userdata;
}
void pa_stream_set_event_callback(pa_stream *s, pa_stream_event_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
return;
s->event_callback = cb;
s->event_userdata = userdata;
}
void pa_stream_set_buffer_attr_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
return;
s->buffer_attr_callback = cb;
s->buffer_attr_userdata = userdata;
}
pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
s->corked = b;
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE);
return NULL;
}
pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE);
return NULL;
}
pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_success_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
spa_assert(name);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
return NULL;
}
int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, s->timing_info_valid, PA_ERR_NODATA);
return 0;
}
int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
spa_assert(r_usec);
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, s->timing_info_valid, PA_ERR_NODATA);
return 0;
}
const pa_timing_info* pa_stream_get_timing_info(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->timing_info_valid, PA_ERR_NODATA);
return &s->timing_info;
}
const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
return &s->sample_spec;
}
const pa_channel_map* pa_stream_get_channel_map(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
return &s->channel_map;
}
const pa_format_info* pa_stream_get_format_info(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
return s->format;
}
const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
return &s->buffer_attr;
}
pa_operation *pa_stream_set_buffer_attr(pa_stream *s, const pa_buffer_attr *attr, pa_stream_success_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
spa_assert(attr);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
return NULL;
}
pa_operation *pa_stream_update_sample_rate(pa_stream *s, uint32_t rate, pa_stream_success_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, pa_sample_rate_valid(rate), PA_ERR_INVALID);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->flags & PA_STREAM_VARIABLE_RATE, PA_ERR_BADSTATE);
return NULL;
}
pa_operation *pa_stream_proplist_update(pa_stream *s, pa_update_mode_t mode, pa_proplist *p, pa_stream_success_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, mode == PA_UPDATE_SET ||
mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE, PA_ERR_INVALID);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
return NULL;
}
pa_operation *pa_stream_proplist_remove(pa_stream *s, const char *const keys[], pa_stream_success_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, keys && keys[0], PA_ERR_INVALID);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
return NULL;
}
int pa_stream_set_monitor_stream(pa_stream *s, uint32_t sink_input_idx)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
PA_CHECK_VALIDITY(s->context, sink_input_idx != PA_INVALID_INDEX, PA_ERR_INVALID);
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE);
s->direct_on_input = sink_input_idx;
return 0;
}
uint32_t pa_stream_get_monitor_stream(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direct_on_input != PA_INVALID_INDEX,
PA_ERR_BADSTATE, PA_INVALID_INDEX);
return s->direct_on_input;
}

35
src/subscribe.c Normal file
View file

@ -0,0 +1,35 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <pipewire/log.h>
#include <pulse/subscribe.h>
#include "internal.h"
pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
void pa_context_set_subscribe_callback(pa_context *c, pa_context_subscribe_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
}

287
src/utf8.c Normal file
View file

@ -0,0 +1,287 @@
/***
This file is part of PulseAudio.
Copyright 2006 Lennart Poettering
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
PulseAudio 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
/* This file is based on the GLIB utf8 validation functions. The
* original license text follows. */
/* gutf8.c - Operations on UTF-8 strings.
*
* Copyright (C) 1999 Tom Tromey
* Copyright (C) 2000 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <errno.h>
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>
#ifdef HAVE_ICONV
#include <iconv.h>
#endif
#include <pulse/utf8.h>
#include <pulse/xmalloc.h>
#include "internal.h"
#define FILTER_CHAR '_'
static inline bool is_unicode_valid(uint32_t ch) {
if (ch >= 0x110000) /* End of unicode space */
return false;
if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */
return false;
if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) /* Reserved */
return false;
if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */
return false;
return true;
}
static inline bool is_continuation_char(uint8_t ch) {
if ((ch & 0xc0) != 0x80) /* 10xxxxxx */
return false;
return true;
}
static inline void merge_continuation_char(uint32_t *u_ch, uint8_t ch) {
*u_ch <<= 6;
*u_ch |= ch & 0x3f;
}
static char* utf8_validate(const char *str, char *output) {
uint32_t val = 0;
uint32_t min = 0;
const uint8_t *p, *last;
int size;
uint8_t *o;
pa_assert(str);
o = (uint8_t*) output;
for (p = (const uint8_t*) str; *p; p++) {
if (*p < 128) {
if (o)
*o = *p;
} else {
last = p;
if ((*p & 0xe0) == 0xc0) { /* 110xxxxx two-char seq. */
size = 2;
min = 128;
val = (uint32_t) (*p & 0x1e);
goto ONE_REMAINING;
} else if ((*p & 0xf0) == 0xe0) { /* 1110xxxx three-char seq.*/
size = 3;
min = (1 << 11);
val = (uint32_t) (*p & 0x0f);
goto TWO_REMAINING;
} else if ((*p & 0xf8) == 0xf0) { /* 11110xxx four-char seq */
size = 4;
min = (1 << 16);
val = (uint32_t) (*p & 0x07);
} else
goto error;
p++;
if (!is_continuation_char(*p))
goto error;
merge_continuation_char(&val, *p);
TWO_REMAINING:
p++;
if (!is_continuation_char(*p))
goto error;
merge_continuation_char(&val, *p);
ONE_REMAINING:
p++;
if (!is_continuation_char(*p))
goto error;
merge_continuation_char(&val, *p);
if (val < min)
goto error;
if (!is_unicode_valid(val))
goto error;
if (o) {
memcpy(o, last, (size_t) size);
o += size;
}
continue;
error:
if (o) {
*o = FILTER_CHAR;
p = last; /* We retry at the next character */
} else
goto failure;
}
if (o)
o++;
}
if (o) {
*o = '\0';
return output;
}
return (char*) str;
failure:
return NULL;
}
char* pa_utf8_valid (const char *str) {
return utf8_validate(str, NULL);
}
char* pa_utf8_filter (const char *str) {
char *new_str;
pa_assert(str);
new_str = pa_xmalloc(strlen(str) + 1);
return utf8_validate(str, new_str);
}
#ifdef HAVE_ICONV
static char* iconv_simple(const char *str, const char *to, const char *from) {
char *new_str;
size_t len, inlen;
iconv_t cd;
ICONV_CONST char *inbuf;
char *outbuf;
size_t res, inbytes, outbytes;
pa_assert(str);
pa_assert(to);
pa_assert(from);
cd = iconv_open(to, from);
if (cd == (iconv_t)-1)
return NULL;
inlen = len = strlen(str) + 1;
new_str = pa_xmalloc(len);
for (;;) {
inbuf = (ICONV_CONST char*) str; /* Brain dead prototype for iconv() */
inbytes = inlen;
outbuf = new_str;
outbytes = len;
res = iconv(cd, &inbuf, &inbytes, &outbuf, &outbytes);
if (res != (size_t)-1)
break;
if (errno != E2BIG) {
pa_xfree(new_str);
new_str = NULL;
break;
}
pa_assert(inbytes != 0);
len += inbytes;
new_str = pa_xrealloc(new_str, len);
}
iconv_close(cd);
return new_str;
}
char* pa_utf8_to_locale (const char *str) {
return iconv_simple(str, "", "UTF-8");
}
char* pa_locale_to_utf8 (const char *str) {
return iconv_simple(str, "UTF-8", "");
}
#else
char* pa_utf8_to_locale (const char *str) {
pa_assert(str);
return pa_ascii_filter(str);
}
char* pa_locale_to_utf8 (const char *str) {
pa_assert(str);
if (pa_utf8_valid(str))
return pa_xstrdup(str);
return NULL;
}
#endif
char *pa_ascii_valid(const char *str) {
const char *p;
pa_assert(str);
for (p = str; *p; p++)
if ((unsigned char) *p >= 128)
return NULL;
return (char*) str;
}
char *pa_ascii_filter(const char *str) {
char *r, *s, *d;
pa_assert(str);
r = pa_xstrdup(str);
for (s = r, d = r; *s; s++)
if ((unsigned char) *s < 128)
*(d++) = *s;
*d = 0;
return r;
}

77
src/util.c Normal file
View file

@ -0,0 +1,77 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <time.h>
#include <pipewire/log.h>
#include <pipewire/pipewire.h>
#include <pulse/util.h>
#define PA_PATH_SEP_CHAR '/'
char *pa_get_user_name(char *s, size_t l)
{
return strncpy(s, pw_get_user_name(), l);
}
char *pa_get_host_name(char *s, size_t l)
{
return strncpy(s, pw_get_host_name(), l);
}
char *pa_get_fqdn(char *s, size_t l)
{
return strncpy(s, pw_get_host_name(), l);
}
char *pa_get_home_dir(char *s, size_t l)
{
pw_log_warn("Not Implemented");
return NULL;
}
char *pa_get_binary_name(char *s, size_t l)
{
return strncpy(s, pw_get_prgname(), l);
}
char *pa_path_get_filename(const char *p)
{
char *fn;
if (!p)
return NULL;
if ((fn = strrchr(p, PA_PATH_SEP_CHAR)))
return fn+1;
return (char*) p;
}
int pa_msleep(unsigned long t)
{
struct timespec ts;
ts.tv_sec = (time_t) (t / SPA_MSEC_PER_SEC);
ts.tv_nsec = (long) ((t % SPA_MSEC_PER_SEC) * SPA_NSEC_PER_MSEC);
return nanosleep(&ts, NULL);
}

26
src/version.c Normal file
View file

@ -0,0 +1,26 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <pulse/version.h>
const char* pa_get_library_version(void)
{
return pa_get_headers_version();
}

989
src/volume.c Normal file
View file

@ -0,0 +1,989 @@
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio 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 Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <pulse/volume.h>
#include "internal.h"
#include "sample-util.h"
int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) {
int i;
pa_assert(a);
pa_assert(b);
pa_return_val_if_fail(pa_cvolume_valid(a), 0);
if (PA_UNLIKELY(a == b))
return 1;
pa_return_val_if_fail(pa_cvolume_valid(b), 0);
if (a->channels != b->channels)
return 0;
for (i = 0; i < a->channels; i++)
if (a->values[i] != b->values[i])
return 0;
return 1;
}
pa_cvolume* pa_cvolume_init(pa_cvolume *a) {
unsigned c;
pa_assert(a);
a->channels = 0;
for (c = 0; c < PA_CHANNELS_MAX; c++)
a->values[c] = PA_VOLUME_INVALID;
return a;
}
pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v) {
int i;
pa_assert(a);
pa_assert(pa_channels_valid(channels));
a->channels = (uint8_t) channels;
for (i = 0; i < a->channels; i++)
/* Clamp in case there is stale data that exceeds the current
* PA_VOLUME_MAX */
a->values[i] = PA_CLAMP_VOLUME(v);
return a;
}
pa_volume_t pa_cvolume_avg(const pa_cvolume *a) {
uint64_t sum = 0;
unsigned c;
pa_assert(a);
pa_return_val_if_fail(pa_cvolume_valid(a), PA_VOLUME_MUTED);
for (c = 0; c < a->channels; c++)
sum += a->values[c];
sum /= a->channels;
return (pa_volume_t) sum;
}
pa_volume_t pa_cvolume_avg_mask(const pa_cvolume *a, const pa_channel_map *cm, pa_channel_position_mask_t mask) {
uint64_t sum = 0;
unsigned c, n;
pa_assert(a);
if (!cm)
return pa_cvolume_avg(a);
pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(a, cm), PA_VOLUME_MUTED);
for (c = n = 0; c < a->channels; c++) {
if (!(PA_CHANNEL_POSITION_MASK(cm->map[c]) & mask))
continue;
sum += a->values[c];
n ++;
}
if (n > 0)
sum /= n;
return (pa_volume_t) sum;
}
pa_volume_t pa_cvolume_max(const pa_cvolume *a) {
pa_volume_t m = PA_VOLUME_MUTED;
unsigned c;
pa_assert(a);
pa_return_val_if_fail(pa_cvolume_valid(a), PA_VOLUME_MUTED);
for (c = 0; c < a->channels; c++)
if (a->values[c] > m)
m = a->values[c];
return m;
}
pa_volume_t pa_cvolume_min(const pa_cvolume *a) {
pa_volume_t m = PA_VOLUME_MAX;
unsigned c;
pa_assert(a);
pa_return_val_if_fail(pa_cvolume_valid(a), PA_VOLUME_MUTED);
for (c = 0; c < a->channels; c++)
if (a->values[c] < m)
m = a->values[c];
return m;
}
pa_volume_t pa_cvolume_max_mask(const pa_cvolume *a, const pa_channel_map *cm, pa_channel_position_mask_t mask) {
pa_volume_t m = PA_VOLUME_MUTED;
unsigned c;
pa_assert(a);
if (!cm)
return pa_cvolume_max(a);
pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(a, cm), PA_VOLUME_MUTED);
for (c = 0; c < a->channels; c++) {
if (!(PA_CHANNEL_POSITION_MASK(cm->map[c]) & mask))
continue;
if (a->values[c] > m)
m = a->values[c];
}
return m;
}
pa_volume_t pa_cvolume_min_mask(const pa_cvolume *a, const pa_channel_map *cm, pa_channel_position_mask_t mask) {
pa_volume_t m = PA_VOLUME_MAX;
unsigned c;
pa_assert(a);
if (!cm)
return pa_cvolume_min(a);
pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(a, cm), PA_VOLUME_MUTED);
for (c = 0; c < a->channels; c++) {
if (!(PA_CHANNEL_POSITION_MASK(cm->map[c]) & mask))
continue;
if (a->values[c] < m)
m = a->values[c];
}
return m;
}
pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b) {
uint64_t result;
pa_return_val_if_fail(PA_VOLUME_IS_VALID(a), PA_VOLUME_INVALID);
pa_return_val_if_fail(PA_VOLUME_IS_VALID(b), PA_VOLUME_INVALID);
/* cbrt((a/PA_VOLUME_NORM)^3*(b/PA_VOLUME_NORM)^3)*PA_VOLUME_NORM = a*b/PA_VOLUME_NORM */
result = ((uint64_t) a * (uint64_t) b + (uint64_t) PA_VOLUME_NORM / 2ULL) / (uint64_t) PA_VOLUME_NORM;
if (result > (uint64_t)PA_VOLUME_MAX)
pa_log_warn("pa_sw_volume_multiply: Volume exceeds maximum allowed value and will be clipped. Please check your volume settings.");
return (pa_volume_t) PA_CLAMP_VOLUME(result);
}
pa_volume_t pa_sw_volume_divide(pa_volume_t a, pa_volume_t b) {
uint64_t result;
pa_return_val_if_fail(PA_VOLUME_IS_VALID(a), PA_VOLUME_INVALID);
pa_return_val_if_fail(PA_VOLUME_IS_VALID(b), PA_VOLUME_INVALID);
if (b <= PA_VOLUME_MUTED)
return 0;
result = ((uint64_t) a * (uint64_t) PA_VOLUME_NORM + (uint64_t) b / 2ULL) / (uint64_t) b;
if (result > (uint64_t)PA_VOLUME_MAX)
pa_log_warn("pa_sw_volume_divide: Volume exceeds maximum allowed value and will be clipped. Please check your volume settings.");
return (pa_volume_t) PA_CLAMP_VOLUME(result);
}
/* Amplitude, not power */
static double linear_to_dB(double v) {
return 20.0 * log10(v);
}
static double dB_to_linear(double v) {
return pow(10.0, v / 20.0);
}
pa_volume_t pa_sw_volume_from_dB(double dB) {
if (isinf(dB) < 0 || dB <= PA_DECIBEL_MININFTY)
return PA_VOLUME_MUTED;
return pa_sw_volume_from_linear(dB_to_linear(dB));
}
double pa_sw_volume_to_dB(pa_volume_t v) {
pa_return_val_if_fail(PA_VOLUME_IS_VALID(v), PA_DECIBEL_MININFTY);
if (v <= PA_VOLUME_MUTED)
return PA_DECIBEL_MININFTY;
return linear_to_dB(pa_sw_volume_to_linear(v));
}
pa_volume_t pa_sw_volume_from_linear(double v) {
if (v <= 0.0)
return PA_VOLUME_MUTED;
/*
* We use a cubic mapping here, as suggested and discussed here:
*
* http://www.robotplanet.dk/audio/audio_gui_design/
* http://lists.linuxaudio.org/pipermail/linux-audio-dev/2009-May/thread.html#23151
*
* We make sure that the conversion to linear and back yields the
* same volume value! That's why we need the lround() below!
*/
return (pa_volume_t) PA_CLAMP_VOLUME((uint64_t) lround(cbrt(v) * PA_VOLUME_NORM));
}
double pa_sw_volume_to_linear(pa_volume_t v) {
double f;
pa_return_val_if_fail(PA_VOLUME_IS_VALID(v), 0.0);
if (v <= PA_VOLUME_MUTED)
return 0.0;
if (v == PA_VOLUME_NORM)
return 1.0;
f = ((double) v / PA_VOLUME_NORM);
return f*f*f;
}
char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c) {
unsigned channel;
bool first = true;
char *e;
pa_assert(s);
pa_assert(l > 0);
pa_assert(c);
pa_init_i18n();
if (!pa_cvolume_valid(c)) {
pa_snprintf(s, l, _("(invalid)"));
return s;
}
*(e = s) = 0;
for (channel = 0; channel < c->channels && l > 1; channel++) {
l -= pa_snprintf(e, l, "%s%u: %3u%%",
first ? "" : " ",
channel,
(unsigned)(((uint64_t)c->values[channel] * 100 + (uint64_t)PA_VOLUME_NORM / 2) / (uint64_t)PA_VOLUME_NORM));
e = strchr(e, 0);
first = false;
}
return s;
}
char *pa_volume_snprint(char *s, size_t l, pa_volume_t v) {
pa_assert(s);
pa_assert(l > 0);
pa_init_i18n();
if (!PA_VOLUME_IS_VALID(v)) {
pa_snprintf(s, l, _("(invalid)"));
return s;
}
pa_snprintf(s, l, "%3u%%", (unsigned)(((uint64_t)v * 100 + (uint64_t)PA_VOLUME_NORM / 2) / (uint64_t)PA_VOLUME_NORM));
return s;
}
char *pa_sw_cvolume_snprint_dB(char *s, size_t l, const pa_cvolume *c) {
unsigned channel;
bool first = true;
char *e;
pa_assert(s);
pa_assert(l > 0);
pa_assert(c);
pa_init_i18n();
if (!pa_cvolume_valid(c)) {
pa_snprintf(s, l, _("(invalid)"));
return s;
}
*(e = s) = 0;
for (channel = 0; channel < c->channels && l > 1; channel++) {
double f = pa_sw_volume_to_dB(c->values[channel]);
l -= pa_snprintf(e, l, "%s%u: %0.2f dB",
first ? "" : " ",
channel,
isinf(f) < 0 || f <= PA_DECIBEL_MININFTY ? -INFINITY : f);
e = strchr(e, 0);
first = false;
}
return s;
}
char *pa_cvolume_snprint_verbose(char *s, size_t l, const pa_cvolume *c, const pa_channel_map *map, int print_dB) {
char *current = s;
bool first = true;
pa_assert(s);
pa_assert(l > 0);
pa_assert(c);
pa_init_i18n();
if (!pa_cvolume_valid(c)) {
pa_snprintf(s, l, _("(invalid)"));
return s;
}
pa_assert(!map || (map->channels == c->channels));
pa_assert(!map || pa_channel_map_valid(map));
current[0] = 0;
for (unsigned channel = 0; channel < c->channels && l > 1; channel++) {
char channel_position[32];
size_t bytes_printed;
char buf[PA_VOLUME_SNPRINT_VERBOSE_MAX];
if (map)
pa_snprintf(channel_position, sizeof(channel_position), "%s", pa_channel_position_to_string(map->map[channel]));
else
pa_snprintf(channel_position, sizeof(channel_position), "%u", channel);
bytes_printed = pa_snprintf(current, l, "%s%s: %s",
first ? "" : ", ",
channel_position,
pa_volume_snprint_verbose(buf, sizeof(buf), c->values[channel], print_dB));
l -= bytes_printed;
current += bytes_printed;
first = false;
}
return s;
}
char *pa_sw_volume_snprint_dB(char *s, size_t l, pa_volume_t v) {
double f;
pa_assert(s);
pa_assert(l > 0);
pa_init_i18n();
if (!PA_VOLUME_IS_VALID(v)) {
pa_snprintf(s, l, _("(invalid)"));
return s;
}
f = pa_sw_volume_to_dB(v);
pa_snprintf(s, l, "%0.2f dB", isinf(f) < 0 || f <= PA_DECIBEL_MININFTY ? -INFINITY : f);
return s;
}
char *pa_volume_snprint_verbose(char *s, size_t l, pa_volume_t v, int print_dB) {
char dB[PA_SW_VOLUME_SNPRINT_DB_MAX];
pa_assert(s);
pa_assert(l > 0);
pa_init_i18n();
if (!PA_VOLUME_IS_VALID(v)) {
pa_snprintf(s, l, _("(invalid)"));
return s;
}
pa_snprintf(s, l, "%" PRIu32 " / %3u%%%s%s",
v,
(unsigned)(((uint64_t)v * 100 + (uint64_t)PA_VOLUME_NORM / 2) / (uint64_t)PA_VOLUME_NORM),
print_dB ? " / " : "",
print_dB ? pa_sw_volume_snprint_dB(dB, sizeof(dB), v) : "");
return s;
}
int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) {
unsigned c;
pa_assert(a);
pa_return_val_if_fail(pa_cvolume_valid(a), 0);
pa_return_val_if_fail(PA_VOLUME_IS_VALID(v), 0);
for (c = 0; c < a->channels; c++)
if (a->values[c] != v)
return 0;
return 1;
}
pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) {
unsigned i;
pa_assert(dest);
pa_assert(a);
pa_assert(b);
pa_return_val_if_fail(pa_cvolume_valid(a), NULL);
pa_return_val_if_fail(pa_cvolume_valid(b), NULL);
dest->channels = PA_MIN(a->channels, b->channels);
for (i = 0; i < dest->channels; i++)
dest->values[i] = pa_sw_volume_multiply(a->values[i], b->values[i]);
return dest;
}
pa_cvolume *pa_sw_cvolume_multiply_scalar(pa_cvolume *dest, const pa_cvolume *a, pa_volume_t b) {
unsigned i;
pa_assert(dest);
pa_assert(a);
pa_return_val_if_fail(pa_cvolume_valid(a), NULL);
pa_return_val_if_fail(PA_VOLUME_IS_VALID(b), NULL);
for (i = 0; i < a->channels; i++)
dest->values[i] = pa_sw_volume_multiply(a->values[i], b);
dest->channels = (uint8_t) i;
return dest;
}
pa_cvolume *pa_sw_cvolume_divide(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) {
unsigned i;
pa_assert(dest);
pa_assert(a);
pa_assert(b);
pa_return_val_if_fail(pa_cvolume_valid(a), NULL);
pa_return_val_if_fail(pa_cvolume_valid(b), NULL);
dest->channels = PA_MIN(a->channels, b->channels);
for (i = 0; i < dest->channels; i++)
dest->values[i] = pa_sw_volume_divide(a->values[i], b->values[i]);
return dest;
}
pa_cvolume *pa_sw_cvolume_divide_scalar(pa_cvolume *dest, const pa_cvolume *a, pa_volume_t b) {
unsigned i;
pa_assert(dest);
pa_assert(a);
pa_return_val_if_fail(pa_cvolume_valid(a), NULL);
pa_return_val_if_fail(PA_VOLUME_IS_VALID(b), NULL);
for (i = 0; i < a->channels; i++)
dest->values[i] = pa_sw_volume_divide(a->values[i], b);
dest->channels = (uint8_t) i;
return dest;
}
int pa_cvolume_valid(const pa_cvolume *v) {
unsigned c;
pa_assert(v);
if (!pa_channels_valid(v->channels))
return 0;
for (c = 0; c < v->channels; c++)
if (!PA_VOLUME_IS_VALID(v->values[c]))
return 0;
return 1;
}
static bool on_left(pa_channel_position_t p) {
return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_LEFT);
}
static bool on_right(pa_channel_position_t p) {
return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_RIGHT);
}
static bool on_center(pa_channel_position_t p) {
return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_CENTER);
}
static bool on_hfe(pa_channel_position_t p) {
return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_HFE);
}
static bool on_lfe(pa_channel_position_t p) {
return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_LFE);
}
static bool on_front(pa_channel_position_t p) {
return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_FRONT);
}
static bool on_rear(pa_channel_position_t p) {
return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_REAR);
}
pa_cvolume *pa_cvolume_remap(pa_cvolume *v, const pa_channel_map *from, const pa_channel_map *to) {
int a, b;
pa_cvolume result;
pa_assert(v);
pa_assert(from);
pa_assert(to);
pa_return_val_if_fail(pa_channel_map_valid(to), NULL);
pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, from), NULL);
if (pa_channel_map_equal(from, to))
return v;
result.channels = to->channels;
for (b = 0; b < to->channels; b++) {
pa_volume_t k = 0;
int n = 0;
for (a = 0; a < from->channels; a++)
if (from->map[a] == to->map[b]) {
k += v->values[a];
n ++;
}
if (n <= 0) {
for (a = 0; a < from->channels; a++)
if ((on_left(from->map[a]) && on_left(to->map[b])) ||
(on_right(from->map[a]) && on_right(to->map[b])) ||
(on_center(from->map[a]) && on_center(to->map[b])) ||
(on_lfe(from->map[a]) && on_lfe(to->map[b]))) {
k += v->values[a];
n ++;
}
}
if (n <= 0)
k = pa_cvolume_avg(v);
else
k /= n;
result.values[b] = k;
}
*v = result;
return v;
}
int pa_cvolume_compatible(const pa_cvolume *v, const pa_sample_spec *ss) {
pa_assert(v);
pa_assert(ss);
pa_return_val_if_fail(pa_cvolume_valid(v), 0);
pa_return_val_if_fail(pa_sample_spec_valid(ss), 0);
return v->channels == ss->channels;
}
int pa_cvolume_compatible_with_channel_map(const pa_cvolume *v, const pa_channel_map *cm) {
pa_assert(v);
pa_assert(cm);
pa_return_val_if_fail(pa_cvolume_valid(v), 0);
pa_return_val_if_fail(pa_channel_map_valid(cm), 0);
return v->channels == cm->channels;
}
/*
* Returns the average volume of l and r, where l and r are two disjoint sets of channels
* (e g left and right, or front and rear).
*/
static void get_avg(const pa_channel_map *map, const pa_cvolume *v, pa_volume_t *l, pa_volume_t *r,
bool (*on_l)(pa_channel_position_t), bool (*on_r)(pa_channel_position_t)) {
int c;
pa_volume_t left = 0, right = 0;
unsigned n_left = 0, n_right = 0;
pa_assert(v);
pa_assert(map);
pa_assert(map->channels == v->channels);
pa_assert(l);
pa_assert(r);
for (c = 0; c < map->channels; c++) {
if (on_l(map->map[c])) {
left += v->values[c];
n_left++;
} else if (on_r(map->map[c])) {
right += v->values[c];
n_right++;
}
}
if (n_left <= 0)
*l = PA_VOLUME_NORM;
else
*l = left / n_left;
if (n_right <= 0)
*r = PA_VOLUME_NORM;
else
*r = right / n_right;
}
float pa_cvolume_get_balance(const pa_cvolume *v, const pa_channel_map *map) {
pa_volume_t left, right;
pa_assert(v);
pa_assert(map);
pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), 0.0f);
if (!pa_channel_map_can_balance(map))
return 0.0f;
get_avg(map, v, &left, &right, on_left, on_right);
if (left == right)
return 0.0f;
/* 1.0, 0.0 => -1.0
0.0, 1.0 => 1.0
0.0, 0.0 => 0.0
0.5, 0.5 => 0.0
1.0, 0.5 => -0.5
1.0, 0.25 => -0.75
0.75, 0.25 => -0.66
0.5, 0.25 => -0.5 */
if (left > right)
return -1.0f + ((float) right / (float) left);
else
return 1.0f - ((float) left / (float) right);
}
static pa_cvolume* set_balance(pa_cvolume *v, const pa_channel_map *map, float new_balance,
bool (*on_l)(pa_channel_position_t), bool (*on_r)(pa_channel_position_t)) {
pa_volume_t left, nleft, right, nright, m;
unsigned c;
get_avg(map, v, &left, &right, on_l, on_r);
m = PA_MAX(left, right);
if (new_balance <= 0) {
nright = (new_balance + 1.0f) * m;
nleft = m;
} else {
nleft = (1.0f - new_balance) * m;
nright = m;
}
for (c = 0; c < map->channels; c++) {
if (on_l(map->map[c])) {
if (left == 0)
v->values[c] = nleft;
else
v->values[c] = (pa_volume_t) PA_CLAMP_VOLUME(((uint64_t) v->values[c] * (uint64_t) nleft) / (uint64_t) left);
} else if (on_r(map->map[c])) {
if (right == 0)
v->values[c] = nright;
else
v->values[c] = (pa_volume_t) PA_CLAMP_VOLUME(((uint64_t) v->values[c] * (uint64_t) nright) / (uint64_t) right);
}
}
return v;
}
pa_cvolume* pa_cvolume_set_balance(pa_cvolume *v, const pa_channel_map *map, float new_balance) {
pa_assert(map);
pa_assert(v);
pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), NULL);
pa_return_val_if_fail(new_balance >= -1.0f, NULL);
pa_return_val_if_fail(new_balance <= 1.0f, NULL);
if (!pa_channel_map_can_balance(map))
return v;
return set_balance(v, map, new_balance, on_left, on_right);
}
pa_cvolume* pa_cvolume_scale(pa_cvolume *v, pa_volume_t max) {
unsigned c;
pa_volume_t t = 0;
pa_assert(v);
pa_return_val_if_fail(pa_cvolume_valid(v), NULL);
pa_return_val_if_fail(PA_VOLUME_IS_VALID(max), NULL);
t = pa_cvolume_max(v);
if (t <= PA_VOLUME_MUTED)
return pa_cvolume_set(v, v->channels, max);
for (c = 0; c < v->channels; c++)
v->values[c] = (pa_volume_t) PA_CLAMP_VOLUME(((uint64_t) v->values[c] * (uint64_t) max) / (uint64_t) t);
return v;
}
pa_cvolume* pa_cvolume_scale_mask(pa_cvolume *v, pa_volume_t max, pa_channel_map *cm, pa_channel_position_mask_t mask) {
unsigned c;
pa_volume_t t = 0;
pa_assert(v);
pa_return_val_if_fail(PA_VOLUME_IS_VALID(max), NULL);
if (!cm)
return pa_cvolume_scale(v, max);
pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, cm), NULL);
t = pa_cvolume_max_mask(v, cm, mask);
if (t <= PA_VOLUME_MUTED)
return pa_cvolume_set(v, v->channels, max);
for (c = 0; c < v->channels; c++)
v->values[c] = (pa_volume_t) PA_CLAMP_VOLUME(((uint64_t) v->values[c] * (uint64_t) max) / (uint64_t) t);
return v;
}
float pa_cvolume_get_fade(const pa_cvolume *v, const pa_channel_map *map) {
pa_volume_t rear, front;
pa_assert(v);
pa_assert(map);
pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), 0.0f);
if (!pa_channel_map_can_fade(map))
return 0.0f;
get_avg(map, v, &rear, &front, on_rear, on_front);
if (front == rear)
return 0.0f;
if (rear > front)
return -1.0f + ((float) front / (float) rear);
else
return 1.0f - ((float) rear / (float) front);
}
pa_cvolume* pa_cvolume_set_fade(pa_cvolume *v, const pa_channel_map *map, float new_fade) {
pa_assert(map);
pa_assert(v);
pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), NULL);
pa_return_val_if_fail(new_fade >= -1.0f, NULL);
pa_return_val_if_fail(new_fade <= 1.0f, NULL);
if (!pa_channel_map_can_fade(map))
return v;
return set_balance(v, map, new_fade, on_rear, on_front);
}
float pa_cvolume_get_lfe_balance(const pa_cvolume *v, const pa_channel_map *map) {
pa_volume_t hfe, lfe;
pa_assert(v);
pa_assert(map);
pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), 0.0f);
if (!pa_channel_map_can_lfe_balance(map))
return 0.0f;
get_avg(map, v, &hfe, &lfe, on_hfe, on_lfe);
if (hfe == lfe)
return 0.0f;
if (hfe > lfe)
return -1.0f + ((float) lfe / (float) hfe);
else
return 1.0f - ((float) hfe / (float) lfe);
}
pa_cvolume* pa_cvolume_set_lfe_balance(pa_cvolume *v, const pa_channel_map *map, float new_balance) {
pa_assert(map);
pa_assert(v);
pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), NULL);
pa_return_val_if_fail(new_balance >= -1.0f, NULL);
pa_return_val_if_fail(new_balance <= 1.0f, NULL);
if (!pa_channel_map_can_lfe_balance(map))
return v;
return set_balance(v, map, new_balance, on_hfe, on_lfe);
}
pa_cvolume* pa_cvolume_set_position(
pa_cvolume *cv,
const pa_channel_map *map,
pa_channel_position_t t,
pa_volume_t v) {
unsigned c;
bool good = false;
pa_assert(cv);
pa_assert(map);
pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(cv, map), NULL);
pa_return_val_if_fail(t < PA_CHANNEL_POSITION_MAX, NULL);
pa_return_val_if_fail(PA_VOLUME_IS_VALID(v), NULL);
for (c = 0; c < map->channels; c++)
if (map->map[c] == t) {
cv->values[c] = v;
good = true;
}
return good ? cv : NULL;
}
pa_volume_t pa_cvolume_get_position(
pa_cvolume *cv,
const pa_channel_map *map,
pa_channel_position_t t) {
unsigned c;
pa_volume_t v = PA_VOLUME_MUTED;
pa_assert(cv);
pa_assert(map);
pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(cv, map), PA_VOLUME_MUTED);
pa_return_val_if_fail(t < PA_CHANNEL_POSITION_MAX, PA_VOLUME_MUTED);
for (c = 0; c < map->channels; c++)
if (map->map[c] == t)
if (cv->values[c] > v)
v = cv->values[c];
return v;
}
pa_cvolume* pa_cvolume_merge(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) {
unsigned i;
pa_assert(dest);
pa_assert(a);
pa_assert(b);
pa_return_val_if_fail(pa_cvolume_valid(a), NULL);
pa_return_val_if_fail(pa_cvolume_valid(b), NULL);
dest->channels = PA_MIN(a->channels, b->channels);
for (i = 0; i < dest->channels; i++)
dest->values[i] = PA_MAX(a->values[i], b->values[i]);
return dest;
}
pa_cvolume* pa_cvolume_inc_clamp(pa_cvolume *v, pa_volume_t inc, pa_volume_t limit) {
pa_volume_t m;
pa_assert(v);
pa_return_val_if_fail(pa_cvolume_valid(v), NULL);
pa_return_val_if_fail(PA_VOLUME_IS_VALID(inc), NULL);
m = pa_cvolume_max(v);
if (m >= limit - inc)
m = limit;
else
m += inc;
return pa_cvolume_scale(v, m);
}
pa_cvolume* pa_cvolume_inc(pa_cvolume *v, pa_volume_t inc) {
return pa_cvolume_inc_clamp(v, inc, PA_VOLUME_MAX);
}
pa_cvolume* pa_cvolume_dec(pa_cvolume *v, pa_volume_t dec) {
pa_volume_t m;
pa_assert(v);
pa_return_val_if_fail(pa_cvolume_valid(v), NULL);
pa_return_val_if_fail(PA_VOLUME_IS_VALID(dec), NULL);
m = pa_cvolume_max(v);
if (m <= PA_VOLUME_MUTED + dec)
m = PA_VOLUME_MUTED;
else
m -= dec;
return pa_cvolume_scale(v, m);
}

115
src/xmalloc.c Normal file
View file

@ -0,0 +1,115 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <unistd.h>
#include <errno.h>
#include <spa/utils/defs.h>
#include <pulse/xmalloc.h>
#define MAX_ALLOC_SIZE (1024*1024*96) /* 96MB */
static void oom(void) {
static const char e[] = "Not enough memory\n";
write(STDERR_FILENO, e, sizeof(e)-1);
#ifdef SIGQUIT
raise(SIGQUIT);
#endif
_exit(1);
}
void* pa_xmalloc(size_t l)
{
void *p;
spa_assert(l > 0);
spa_assert(l < MAX_ALLOC_SIZE);
if (!(p = malloc(l)))
oom();
return p;
}
void *pa_xmalloc0(size_t l)
{
void *p;
spa_assert(l > 0);
spa_assert(l < MAX_ALLOC_SIZE);
if (!(p = calloc(1, l)))
oom();
return p;
}
void *pa_xrealloc(void *ptr, size_t size)
{
void *p;
spa_assert(size > 0);
spa_assert(size < MAX_ALLOC_SIZE);
if (!(p = realloc(ptr, size)))
oom();
return p;
}
void pa_xfree(void *p)
{
int saved_errno;
if (!p)
return;
saved_errno = errno;
free(p);
errno = saved_errno;
}
char *pa_xstrdup(const char *s)
{
if (!s)
return NULL;
return pa_xmemdup(s, strlen(s)+1);
}
char *pa_xstrndup(const char *s, size_t l)
{
char *e, *r;
if (!s)
return NULL;
if ((e = memchr(s, 0, l)))
return pa_xmemdup(s, (size_t) (e-s+1));
r = pa_xmalloc(l+1);
memcpy(r, s, l);
r[l] = 0;
return r;
}
void* pa_xmemdup(const void *p, size_t l)
{
if (!p)
return NULL;
else {
char *r = pa_xmalloc(l);
memcpy(r, p, l);
return r;
}
}