From 93c0e6aad00892a04a8b23f48d5d8aec2ea68b69 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 9 Feb 2018 18:23:36 +0100 Subject: [PATCH 001/133] Initial commit --- LICENSE | 504 +++++++++++++++++++ README.md | 2 + src/meson.build | 16 + src/pipewire-jack.c | 1159 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1681 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 src/meson.build create mode 100644 src/pipewire-jack.c diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..19e307187 --- /dev/null +++ b/LICENSE @@ -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! diff --git a/README.md b/README.md new file mode 100644 index 000000000..928ce3357 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# pipewire-jack +JACK client library for PipeWire diff --git a/src/meson.build b/src/meson.build new file mode 100644 index 000000000..e297c9efd --- /dev/null +++ b/src/meson.build @@ -0,0 +1,16 @@ +pipewire_jack_sources = [ + 'pipewire-jack.c', +] + +pipewire_jack_c_args = [ + '-DHAVE_CONFIG_H', + '-DPIC', +] + +pipewire_jack = shared_library('jack-pipewire', + pipewire_jack_sources, + c_args : pipewire_jack_c_args, + include_directories : [configinc], + dependencies : [pipewire_dep, jack_dep], + install : false, +) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c new file mode 100644 index 000000000..111929542 --- /dev/null +++ b/src/pipewire-jack.c @@ -0,0 +1,1159 @@ +/* PipeWire + * Copyright (C) 2018 Wim Taymans + * + * 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 + +#include + +#include +#include + +#include + +#include "extensions/client-node.h" + +#define JACK_CLIENT_NAME_SIZE 64 +#define JACK_PORT_NAME_SIZE 256 +#define JACK_PORT_MAX 4096 +#define JACK_PORT_TYPE_SIZE 32 + +#define REAL_JACK_PORT_NAME_SIZE JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE + +struct type { + uint32_t client_node; + 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) +{ + type->client_node = spa_type_map_get_id(map, PW_TYPE_INTERFACE__ClientNode); + 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 global { + bool valid; + uint32_t type; + void *data; +}; + +struct node { + bool valid; + uint32_t id; + char name[JACK_CLIENT_NAME_SIZE]; +}; + +struct port { + bool valid; + uint32_t id; + uint32_t parent_id; + unsigned long flags; + char name[REAL_JACK_PORT_NAME_SIZE]; + enum spa_direction direction; + const char *type; + uint32_t type_id; +}; + +struct link { + bool valid; + uint32_t id; + uint32_t src; + uint32_t dst; +}; + +struct context { + struct pw_main_loop *loop; + struct pw_core *core; + struct pw_type *t; + + struct pw_map globals; + struct pw_array nodes; + struct pw_array ports; + struct pw_array links; +}; + +struct client { + struct type type; + + char name[JACK_CLIENT_NAME_SIZE]; + + struct context context; + + struct pw_remote *remote; + struct spa_hook remote_listener; + + struct pw_core_proxy *core_proxy; + uint32_t last_sync; + bool error; + + struct pw_registry_proxy *registry_proxy; + struct spa_hook registry_listener; + + struct pw_client_node_proxy *node_proxy; + struct spa_hook node_listener; + struct spa_hook proxy_listener; + + struct port ports[1024]; + uint32_t n_ports; +}; + +static uint32_t alloc_port(struct client *client) +{ + int i; + + for (i = 0; i < client->n_ports; i++) { + if (!client->ports[i].valid) + break; + } + if (i >= 1024) + return SPA_ID_INVALID; + + client->ports[i].valid = true; + client->n_ports = SPA_MAX(client->n_ports, i + 1); + return i; +} + +static struct port *find_port(struct client *client, const char *name) +{ + struct port *p; + + pw_array_for_each(p, &client->context.ports) { + if (!p->valid) + continue; + pw_log_debug("%s", p->name); + if (!strcmp(p->name, name)) + return p; + } + return NULL; +} + +void jack_get_version(int *major_ptr, int *minor_ptr, int *micro_ptr, int *proto_ptr) +{ + *major_ptr = 0; + *minor_ptr = 0; + *micro_ptr = 0; + *proto_ptr = 0; +} + +const char * +jack_get_version_string(void) +{ + return "0.0.0.0"; +} + +static void on_sync_reply(void *data, uint32_t seq) +{ + struct client *client = data; + client->last_sync = seq; + pw_main_loop_quit(client->context.loop); +} + +static void on_state_changed(void *data, enum pw_remote_state old, + enum pw_remote_state state, const char *error) +{ + struct client *client = data; + + switch (state) { + case PW_REMOTE_STATE_ERROR: + client->error = true; + case PW_REMOTE_STATE_CONNECTED: + pw_main_loop_quit(client->context.loop); + break; + default: + break; + } +} + +static const struct pw_remote_events remote_events = { + PW_VERSION_REMOTE_EVENTS, + .sync_reply = on_sync_reply, + .state_changed = on_state_changed, +}; + +static int do_sync(struct client *client) +{ + uint32_t seq = client->last_sync + 1; + + pw_core_proxy_sync(client->core_proxy, seq); + + while (true) { + pw_main_loop_run(client->context.loop); + + if (client->error) + return -1; + + if (client->last_sync == seq) + break; + } + return 0; +} + +static void on_node_proxy_destroy(void *data) +{ + struct client *client = data; + + client->node_proxy = NULL; + spa_hook_remove(&client->proxy_listener); + +} + +static const struct pw_proxy_events proxy_events = { + PW_VERSION_PROXY_EVENTS, + .destroy = on_node_proxy_destroy, +}; + +static void client_node_add_mem(void *object, + uint32_t mem_id, + uint32_t type, + int memfd, + uint32_t flags) +{ +} + +static void client_node_transport(void *object, + uint32_t node_id, + int readfd, + int writefd, + struct pw_client_node_transport *transport) +{ +} + + +static void client_node_set_param(void *object, uint32_t seq, + uint32_t id, uint32_t flags, + const struct spa_pod *param) +{ +} + +static void client_node_event(void *object, const struct spa_event *event) +{ +} + +static void client_node_command(void *object, uint32_t seq, const struct spa_command *command) +{ +} + +static void client_node_add_port(void *object, + uint32_t seq, + enum spa_direction direction, + uint32_t port_id) +{ +} + +static void client_node_remove_port(void *object, + uint32_t seq, + enum spa_direction direction, + uint32_t port_id) +{ +} + +static void client_node_port_set_param(void *object, + uint32_t seq, + enum spa_direction direction, + uint32_t port_id, + uint32_t id, uint32_t flags, + const struct spa_pod *param) +{ +} +static void client_node_port_use_buffers(void *object, + uint32_t seq, + enum spa_direction direction, + uint32_t port_id, + uint32_t n_buffers, + struct pw_client_node_buffer *buffers) +{ +} +static void client_node_port_command(void *object, + enum spa_direction direction, + uint32_t port_id, + const struct spa_command *command) +{ +} + +static void client_node_port_set_io(void *object, + uint32_t seq, + enum spa_direction direction, + uint32_t port_id, + uint32_t id, + uint32_t mem_id, + uint32_t offset, + uint32_t size) +{ +} + + + +static const struct pw_client_node_proxy_events client_node_events = { + PW_VERSION_CLIENT_NODE_PROXY_EVENTS, + .add_mem = client_node_add_mem, + .transport = client_node_transport, + .set_param = client_node_set_param, + .event = client_node_event, + .command = client_node_command, + .add_port = client_node_add_port, + .remove_port = client_node_remove_port, + .port_set_param = client_node_port_set_param, + .port_use_buffers = client_node_port_use_buffers, + .port_command = client_node_port_command, + .port_set_io = client_node_port_set_io, +}; + +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) +{ + struct client *c = (struct client *) data; + struct pw_type *t = c->context.t; + struct global *g, *gt; + const char *str; + size_t size; + struct node *n; + struct port *p; + + if (props == NULL) + return; + + pw_log_debug("added: %u", id); + + g = pw_array_add(&c->context.nodes, sizeof(struct global)); + g->valid = true; + g->type = type; + + if (type == t->node) { + if ((str = spa_dict_lookup(props, "node.name")) == NULL) + return; + + n = pw_array_add(&c->context.nodes, sizeof(struct node)); + n->valid = true; + n->id = id; + strncpy(n->name, str, sizeof(n->name)); + + g->data = n; + } + else if (type == t->port) { + const struct spa_dict_item *item; + + if ((str = spa_dict_lookup(props, "port.name")) == NULL) + return; + + if ((gt = pw_map_lookup(&c->context.globals, parent_id)) == NULL) + return; + + n = gt->data; + + p = pw_array_add(&c->context.ports, sizeof(struct port)); + p->valid = true; + p->id = id; + p->parent_id = parent_id; + snprintf(p->name, sizeof(p->name), "%s:%s", n->name, str); + + spa_dict_for_each(item, props) { + if (!strcmp(item->key, "port.direction")) { + if (!strcmp(item->value, "in")) + p->flags |= JackPortIsInput; + else if (!strcmp(item->value, "out")) + p->flags |= JackPortIsOutput; + } + else if (!strcmp(item->key, "port.physical")) { + if (pw_properties_parse_bool(item->value)) + p->flags |= JackPortIsPhysical; + } + else if (!strcmp(item->key, "port.terminal")) { + if (pw_properties_parse_bool(item->value)) + p->flags |= JackPortIsTerminal; + } + } + + g->data = n; + } + else if (type == t->link) { + } + else + return; + + size = pw_map_get_size(&c->context.globals); + while (id > size) + pw_map_insert_at(&c->context.globals, size++, NULL); + pw_map_insert_at(&c->context.globals, id, g); +} + +static void registry_event_global_remove(void *object, uint32_t id) +{ + struct client *c = (struct client *) object; + + pw_log_debug("removed: %u", id); + + pw_map_insert_at(&c->context.globals, id, NULL); +} + +static const struct pw_registry_proxy_events registry_events = { + PW_VERSION_REGISTRY_PROXY_EVENTS, + .global = registry_event_global, + .global_remove = registry_event_global_remove, +}; + +jack_client_t * jack_client_open (const char *client_name, + jack_options_t options, + jack_status_t *status, ...) +{ + struct client *client; + bool busy = true; + struct spa_dict props; + struct spa_dict_item items[2]; + + pw_log_debug("client open %s %d", client_name, options); + + client = calloc(1, sizeof(struct client)); + if (client == NULL) + goto init_failed; + + strncpy(client->name, client_name, JACK_CLIENT_NAME_SIZE); + client->context.loop = pw_main_loop_new(NULL); + client->context.core = pw_core_new(pw_main_loop_get_loop(client->context.loop), NULL); + client->context.t = pw_core_get_type(client->context.core); + init_type(&client->type, client->context.t->map); + + pw_map_init(&client->context.globals, 64, 64); + pw_array_init(&client->context.nodes, 64); + pw_array_init(&client->context.ports, 64); + pw_array_init(&client->context.links, 64); + + client->remote = pw_remote_new(client->context.core, + pw_properties_new( + "client.name", client_name, + NULL), + 0); + + pw_remote_add_listener(client->remote, &client->remote_listener, &remote_events, client); + pw_remote_connect(client->remote); + + while (busy) { + const char *error = NULL; + + pw_main_loop_run(client->context.loop); + + switch (pw_remote_get_state(client->remote, &error)) { + case PW_REMOTE_STATE_ERROR: + goto server_failed; + + case PW_REMOTE_STATE_CONNECTED: + busy = false; + break; + + default: + break; + } + } + client->core_proxy = pw_remote_get_core_proxy(client->remote); + client->registry_proxy = pw_core_proxy_get_registry(client->core_proxy, + client->context.t->registry, + PW_VERSION_REGISTRY, 0); + pw_registry_proxy_add_listener(client->registry_proxy, + &client->registry_listener, + ®istry_events, client); + + + props = SPA_DICT_INIT(items, 0); + items[props.n_items++] = SPA_DICT_ITEM_INIT("node.name", client_name); + + client->node_proxy = pw_core_proxy_create_object(client->core_proxy, + "client-node", + client->type.client_node, + PW_VERSION_CLIENT_NODE, + &props, + 0); + if (client->node_proxy == NULL) + goto init_failed; + + pw_client_node_proxy_add_listener(client->node_proxy, + &client->node_listener, &client_node_events, client); + pw_proxy_add_listener((struct pw_proxy*)client->node_proxy, + &client->proxy_listener, &proxy_events, client); + + pw_client_node_proxy_update(client->node_proxy, + PW_CLIENT_NODE_UPDATE_MAX_INPUTS | + PW_CLIENT_NODE_UPDATE_MAX_OUTPUTS, + 0, 0, 0, NULL); + + if (do_sync(client) < 0) + goto init_failed; + + *status = 0; + + return (jack_client_t *)client; + + init_failed: + *status = JackFailure | JackInitFailure; + return NULL; + server_failed: + *status = JackFailure | JackServerFailed; + return NULL; +} + +jack_client_t * jack_client_new (const char *client_name) +{ + jack_options_t options = JackUseExactName; + jack_status_t status; + + if (getenv("JACK_START_SERVER") == NULL) + options |= JackNoStartServer; + + return jack_client_open(client_name, options, &status, NULL); +} + +int jack_client_close (jack_client_t *client) +{ + struct client *c = (struct client *) client; + + pw_log_debug("client %p: close", client); + + pw_core_destroy(c->context.core); + pw_main_loop_destroy(c->context.loop); + free(c); + + return 0; +} + +int jack_client_name_size (void) +{ + return JACK_CLIENT_NAME_SIZE; +} + +char * jack_get_client_name (jack_client_t *client) +{ + struct client *c = (struct client *) client; + return c->name; +} + +char *jack_get_uuid_for_client_name (jack_client_t *client, + const char *client_name) +{ + return NULL; +} + +char *jack_get_client_name_by_uuid (jack_client_t *client, + const char *client_uuid ) +{ + return NULL; +} + +int jack_internal_client_new (const char *client_name, + const char *load_name, + const char *load_init) +{ + return 0; +} + +void jack_internal_client_close (const char *client_name) +{ +} + +int jack_activate (jack_client_t *client) +{ + struct client *c = (struct client *) client; + + pw_client_node_proxy_done(c->node_proxy, 0, 0); + pw_client_node_proxy_set_active(c->node_proxy, true); + + if (do_sync(c) < 0) + return -1; + + return 0; +} + +int jack_deactivate (jack_client_t *client) +{ + return 0; +} + +int jack_get_client_pid (const char *name) +{ + return 0; +} + +jack_native_thread_t jack_client_thread_id (jack_client_t *client) +{ + return 0; +} + +int jack_is_realtime (jack_client_t *client) +{ + return 0; +} + +jack_nframes_t jack_thread_wait (jack_client_t *client, int status) +{ + return 0; +} + +jack_nframes_t jack_cycle_wait (jack_client_t* client) +{ + return 0; +} + +void jack_cycle_signal (jack_client_t* client, int status) +{ +} + +int jack_set_process_thread(jack_client_t* client, JackThreadCallback thread_callback, void *arg) +{ + return 0; +} + +int jack_set_thread_init_callback (jack_client_t *client, + JackThreadInitCallback thread_init_callback, + void *arg) +{ + return 0; +} + +void jack_on_shutdown (jack_client_t *client, + JackShutdownCallback shutdown_callback, void *arg) +{ +} + +void jack_on_info_shutdown (jack_client_t *client, + JackInfoShutdownCallback shutdown_callback, void *arg) +{ +} + +int jack_set_process_callback (jack_client_t *client, + JackProcessCallback process_callback, + void *arg) +{ + return 0; +} + +int jack_set_freewheel_callback (jack_client_t *client, + JackFreewheelCallback freewheel_callback, + void *arg) +{ + return 0; +} + +int jack_set_buffer_size_callback (jack_client_t *client, + JackBufferSizeCallback bufsize_callback, + void *arg) +{ + return 0; +} + +int jack_set_sample_rate_callback (jack_client_t *client, + JackSampleRateCallback srate_callback, + void *arg) +{ + return 0; +} + +int jack_set_client_registration_callback (jack_client_t *client, + JackClientRegistrationCallback + registration_callback, void *arg) +{ + return 0; +} + +int jack_set_port_registration_callback (jack_client_t *client, + JackPortRegistrationCallback + registration_callback, void *arg) +{ + return 0; +} + +int jack_set_port_connect_callback (jack_client_t *client, + JackPortConnectCallback + connect_callback, void *arg) +{ + return 0; +} + +int jack_set_port_rename_callback (jack_client_t *client, + JackPortRenameCallback + rename_callback, void *arg) +{ + return 0; +} + +int jack_set_graph_order_callback (jack_client_t *client, + JackGraphOrderCallback graph_callback, + void *data) +{ + return 0; +} + +int jack_set_xrun_callback (jack_client_t *client, + JackXRunCallback xrun_callback, void *arg) +{ + return 0; +} + +int jack_set_latency_callback (jack_client_t *client, + JackLatencyCallback latency_callback, + void *data) +{ + return 0; +} + +int jack_set_freewheel(jack_client_t* client, int onoff) +{ + return 0; +} + +int jack_set_buffer_size (jack_client_t *client, jack_nframes_t nframes) +{ + return 0; +} + +jack_nframes_t jack_get_sample_rate (jack_client_t *client) +{ + return 0; +} + +jack_nframes_t jack_get_buffer_size (jack_client_t *client) +{ + return 0; +} + +int jack_engine_takeover_timebase (jack_client_t *client) +{ + return 0; +} + +float jack_cpu_load (jack_client_t *client) +{ + return 0.0; +} + +jack_port_t * jack_port_register (jack_client_t *client, + const char *port_name, + const char *port_type, + unsigned long flags, + unsigned long buffer_size) +{ + struct client *c = (struct client *) client; + enum spa_direction direction; + struct spa_port_info port_info = { 0, }; + struct spa_dict dict; + struct spa_dict_item items[10]; + uint8_t buffer[1024]; + struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); + struct pw_type *t = c->context.t; + struct spa_pod *params[4]; + uint32_t port_id; + struct port *p; + + pw_log_debug("client %p: port register \"%s\" \"%s\" %ld %ld", + c, port_name, port_type, flags, buffer_size); + + if (flags & JackPortIsInput) + direction = PW_DIRECTION_INPUT; + else if (flags & JackPortIsOutput) + direction = PW_DIRECTION_OUTPUT; + else + return NULL; + + if ((port_id = alloc_port(c)) == SPA_ID_INVALID) + return NULL; + + p = &c->ports[port_id]; + p->id = port_id; + snprintf(p->name, sizeof(p->name), "%s:%s", c->name, port_name); + p->direction = direction; + p->type = port_type; + + if (strcmp(port_type, JACK_DEFAULT_AUDIO_TYPE) == 0) + p->type_id = 0; + else if (strcmp(port_type, JACK_DEFAULT_MIDI_TYPE) == 0) + p->type_id = 1; + else + return NULL; + + port_info.flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS | + SPA_PORT_INFO_FLAG_NO_REF; + + port_info.props = &dict; + dict = SPA_DICT_INIT(items, 0); + items[dict.n_items++] = SPA_DICT_ITEM_INIT("port.name", port_name); + items[dict.n_items++] = SPA_DICT_ITEM_INIT("port.type", port_type); + + params[0] = spa_pod_builder_object(&b, + t->param.idEnumFormat, t->spa_format, + "I", c->type.media_type.audio, + "I", c->type.media_subtype.raw, + ":", c->type.format_audio.format, "I", c->type.audio_format.F32, + ":", c->type.format_audio.channels, "i", 1, + ":", c->type.format_audio.rate, "iru", 44100, 2, 1, INT32_MAX); + + params[1] = spa_pod_builder_object(&b, + t->param.idBuffers, t->param_buffers.Buffers, + ":", t->param_buffers.size, "isu", 128, 3, 4, INT32_MAX, 4, + ":", t->param_buffers.stride, "i", 4, + ":", t->param_buffers.buffers, "iru", 1, 2, 1, 2, + ":", t->param_buffers.align, "i", 16); + + pw_client_node_proxy_port_update(c->node_proxy, + direction, + port_id, + PW_CLIENT_NODE_PORT_UPDATE_PARAMS | + PW_CLIENT_NODE_PORT_UPDATE_INFO , + 2, + (const struct spa_pod **) params, + &port_info); + + if (do_sync(c) < 0) + return NULL; + + return (jack_port_t *) &c->ports[port_id]; +} + +int jack_port_unregister (jack_client_t *client, jack_port_t *port) +{ + struct client *c = (struct client *) client; + struct port *p = (struct port *) port; + + pw_log_debug("client %p: port unregister %p", client, port); + + p->valid = false; + + pw_client_node_proxy_port_update(c->node_proxy, + p->direction, + p->id, + 0, 0, NULL, NULL); + + if (do_sync(c) < 0) + return -1; + + return 0; +} + +void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) +{ + return 0; +} + +jack_uuid_t jack_port_uuid (const jack_port_t *port) +{ + return 0; +} + +const char * jack_port_name (const jack_port_t *port) +{ + struct port *p = (struct port *) port; + return p->name; +} + +const char * jack_port_short_name (const jack_port_t *port) +{ + struct port *p = (struct port *) port; + return strchr(p->name, ':') + 1; +} + +int jack_port_flags (const jack_port_t *port) +{ + struct port *p = (struct port *) port; + return p->flags; +} + +const char * jack_port_type (const jack_port_t *port) +{ + struct port *p = (struct port *) port; + return p->type; +} + +jack_port_type_id_t jack_port_type_id (const jack_port_t *port) +{ + struct port *p = (struct port *) port; + return p->type_id; +} + +int jack_port_is_mine (const jack_client_t *client, const jack_port_t *port) +{ + return 0; +} + +int jack_port_connected (const jack_port_t *port) +{ + return 0; +} + +int jack_port_connected_to (const jack_port_t *port, + const char *port_name) +{ + return 0; +} + +const char ** jack_port_get_connections (const jack_port_t *port) +{ + return NULL; +} + +const char ** jack_port_get_all_connections (const jack_client_t *client, + const jack_port_t *port) +{ + return NULL; +} + +int jack_port_tie (jack_port_t *src, jack_port_t *dst) +{ + return 0; +} + +int jack_port_untie (jack_port_t *port) +{ + return 0; +} + +int jack_port_set_name (jack_port_t *port, const char *port_name) +{ + return 0; +} + +int jack_port_set_alias (jack_port_t *port, const char *alias) +{ + return 0; +} + +int jack_port_unset_alias (jack_port_t *port, const char *alias) +{ + return 0; +} + +int jack_port_get_aliases (const jack_port_t *port, char* const aliases[2]) +{ + return 0; +} + +int jack_port_request_monitor (jack_port_t *port, int onoff) +{ + return 0; +} + +int jack_port_request_monitor_by_name (jack_client_t *client, + const char *port_name, int onoff) +{ + return 0; +} + +int jack_port_ensure_monitor (jack_port_t *port, int onoff) +{ + return 0; +} + +int jack_port_monitoring_input (jack_port_t *port) +{ + return 0; +} + +int jack_connect (jack_client_t *client, + const char *source_port, + const char *destination_port) +{ + struct client *c = (struct client *) client; + struct port *src, *dst; + struct pw_properties *props; + + pw_log_debug("client %p: connect %s %s", client, source_port, destination_port); + + src = find_port(c, source_port); + dst = find_port(c, destination_port); + + if (src == NULL || dst == NULL) + return -1; + + props = pw_properties_new(NULL, NULL); + pw_properties_setf(props, PW_LINK_OUTPUT_NODE_ID, "%d", src->parent_id); + pw_properties_setf(props, PW_LINK_OUTPUT_PORT_ID, "%d", src->id); + pw_properties_setf(props, PW_LINK_INPUT_NODE_ID, "%d", dst->parent_id); + pw_properties_setf(props, PW_LINK_INPUT_PORT_ID, "%d", dst->id); + + pw_core_proxy_create_object(c->core_proxy, + "link-factory", + c->context.t->link, + PW_VERSION_LINK, + &props->dict, + 0); + + pw_properties_free(props); + + if (do_sync(c) < 0) + return -1; + + return 0; +} + +int jack_disconnect (jack_client_t *client, + const char *source_port, + const char *destination_port) +{ + pw_log_debug("client %p: connect %s %s", client, source_port, destination_port); + + return 0; +} + +int jack_port_disconnect (jack_client_t *client, jack_port_t *port) +{ + return 0; +} + +int jack_port_name_size(void) +{ + return REAL_JACK_PORT_NAME_SIZE; +} + +int jack_port_type_size(void) +{ + return JACK_PORT_TYPE_SIZE; +} + +size_t jack_port_type_get_buffer_size (jack_client_t *client, const char *port_type) +{ + return 0; +} + +void jack_port_set_latency (jack_port_t *port, jack_nframes_t frames) +{ +} + +void jack_port_get_latency_range (jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range) +{ +} + +void jack_port_set_latency_range (jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range) +{ +} + +int jack_recompute_total_latencies (jack_client_t *client) +{ + return 0; +} + +jack_nframes_t jack_port_get_latency (jack_port_t *port) +{ + return 0; +} + +jack_nframes_t jack_port_get_total_latency (jack_client_t *client, + jack_port_t *port) +{ + return 0; +} + +int jack_recompute_total_latency (jack_client_t *client, jack_port_t* port) +{ + return 0; +} + +const char ** jack_get_ports (jack_client_t *client, + const char *port_name_pattern, + const char *type_name_pattern, + unsigned long flags) +{ + struct client *c = (struct client *) client; + const char **res = malloc(sizeof(char*) * JACK_PORT_MAX); + int count = 0; + struct port *p; + + pw_array_for_each(p, &c->context.ports) { + if (!p->valid) + continue; + if (!SPA_FLAG_CHECK(p->flags, flags)) + continue; + + res[count++] = p->name; + } + res[count] = NULL; + + return res; +} + +jack_port_t * jack_port_by_name (jack_client_t *client, const char *port_name) +{ + return NULL; +} + +jack_port_t * jack_port_by_id (jack_client_t *client, + jack_port_id_t port_id) +{ + return NULL; +} + +jack_nframes_t jack_frames_since_cycle_start (const jack_client_t *client) +{ + return 0; +} + +jack_nframes_t jack_frame_time (const jack_client_t *client) +{ + return 0; +} + +jack_nframes_t jack_last_frame_time (const jack_client_t *client) +{ + return 0; +} + +int jack_get_cycle_times(const jack_client_t *client, + jack_nframes_t *current_frames, + jack_time_t *current_usecs, + jack_time_t *next_usecs, + float *period_usecs) +{ + return 0; +} + +jack_time_t jack_frames_to_time(const jack_client_t *client, jack_nframes_t frames) +{ + return 0; +} + +jack_nframes_t jack_time_to_frames(const jack_client_t *client, jack_time_t usecs) +{ + return 0; +} + +jack_time_t jack_get_time() +{ + return 0; +} + +void jack_set_error_function (void (*func)(const char *)) +{ +} + +void jack_set_info_function (void (*func)(const char *)) +{ +} + +void jack_free(void* ptr) +{ +} + +static void reg(void) __attribute__ ((constructor)); +static void reg(void) +{ + pw_init(NULL, NULL); +} From 8b982d4bf5cac5c366d784bbc1ec95d34a949408 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 13 Feb 2018 18:05:55 +0100 Subject: [PATCH 002/133] jack: more stuff manage mem and buffers Use threaded main loop Handle transport handle activate/deactivate trigger the process callback --- src/pipewire-jack.c | 813 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 758 insertions(+), 55 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 111929542..8ead28a8e 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -22,6 +22,8 @@ #endif #include +#include +#include #include @@ -29,6 +31,7 @@ #include #include +#include #include "extensions/client-node.h" @@ -36,9 +39,20 @@ #define JACK_PORT_NAME_SIZE 256 #define JACK_PORT_MAX 4096 #define JACK_PORT_TYPE_SIZE 32 +#define PORT_NUM_FOR_CLIENT 1024 + +#define MAX_PORTS (PORT_NUM_FOR_CLIENT/2) +#define MAX_BUFFERS 64 +#define MAX_BUFFER_DATAS 4 +#define MAX_BUFFER_MEMS 4 + #define REAL_JACK_PORT_NAME_SIZE JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE +#define NAME "jack-client" + +struct client; + struct type { uint32_t client_node; struct spa_type_media_type media_type; @@ -68,15 +82,49 @@ struct node { char name[JACK_CLIENT_NAME_SIZE]; }; +struct mem { + uint32_t id; + int fd; + uint32_t flags; + uint32_t ref; + struct pw_map_range map; + void *ptr; +}; + +struct buffer { + struct spa_list link; +#define BUFFER_FLAG_OUT (1<<0) +#define BUFFER_FLAG_MAPPED (1<<1) + uint32_t flags; + uint32_t id; + void *ptr; + struct pw_map_range map; + + struct spa_data datas[MAX_BUFFER_DATAS]; + uint32_t n_datas; + + struct mem *mem[MAX_BUFFER_DATAS+1]; + uint32_t n_mem; +}; + struct port { bool valid; + + struct client *client; + + enum spa_direction direction; uint32_t id; uint32_t parent_id; unsigned long flags; char name[REAL_JACK_PORT_NAME_SIZE]; - enum spa_direction direction; + const char *type; uint32_t type_id; + + struct buffer buffers[MAX_BUFFERS]; + uint32_t n_buffers; + + struct spa_list queue; }; struct link { @@ -87,7 +135,8 @@ struct link { }; struct context { - struct pw_main_loop *loop; + struct pw_main_loop *main; + struct pw_thread_loop *loop; struct pw_core *core; struct pw_type *t; @@ -97,6 +146,10 @@ struct context { struct pw_array links; }; +#define GET_IN_PORT(c,p) (&c->in_ports[p]) +#define GET_OUT_PORT(c,p) (&c->out_ports[p]) +#define GET_PORT(c,d,p) (d == SPA_DIRECTION_INPUT ? GET_IN_PORT(c,p) : GET_OUT_PORT(c,p)) + struct client { struct type type; @@ -118,40 +171,114 @@ struct client { struct spa_hook node_listener; struct spa_hook proxy_listener; - struct port ports[1024]; - uint32_t n_ports; + uint32_t node_id; + struct pw_client_node_transport *trans; + int writefd; + struct spa_source *socket_source; + + bool active; + + JackThreadCallback thread_callback; + void *thread_arg; + JackProcessCallback process_callback; + void *process_arg; + + uint32_t sample_rate; + uint32_t buffer_size; + + struct port in_ports[MAX_PORTS]; + uint32_t n_in_ports; + uint32_t last_in_port; + struct port out_ports[MAX_PORTS]; + uint32_t n_out_ports; + uint32_t last_out_port; + + struct pw_array mems; }; -static uint32_t alloc_port(struct client *client) +static struct port * alloc_port(struct client *c, enum spa_direction direction) { int i; + struct port *p; - for (i = 0; i < client->n_ports; i++) { - if (!client->ports[i].valid) - break; + if (direction == SPA_DIRECTION_INPUT) { + for (i = 0; i < c->n_in_ports; i++) { + if (!c->in_ports[i].valid) + break; + } + if (i >= 1024) + return NULL; + + p = GET_IN_PORT(c, i); + c->n_in_ports++; + c->last_in_port = SPA_MAX(c->last_in_port, i + 1); + } else { + for (i = 0; i < c->n_out_ports; i++) { + if (!c->out_ports[i].valid) + break; + } + if (i >= 1024) + return NULL; + + p = GET_OUT_PORT(c, i); + c->n_out_ports++; + c->last_out_port = SPA_MAX(c->last_out_port, i + 1); } - if (i >= 1024) - return SPA_ID_INVALID; + p->valid = true; + p->client = c; + p->direction = direction; + p->id = i; - client->ports[i].valid = true; - client->n_ports = SPA_MAX(client->n_ports, i + 1); - return i; + pw_log_debug("port %p: dir %d, id %d", p, p->direction, p->id); + + return p; } -static struct port *find_port(struct client *client, const char *name) +static void free_port(struct client *c, struct port *p) +{ + if (!p->valid) + return; + + if (p->direction == SPA_DIRECTION_INPUT) { + c->n_in_ports--; + while (c->last_in_port > 0 && !c->in_ports[c->last_in_port - 1].valid) + c->last_in_port--; + } else { + c->n_out_ports--; + while (c->last_out_port > 0 && !c->out_ports[c->last_out_port - 1].valid) + c->last_out_port--; + } + p->valid = false; +} + +static struct port *find_port(struct client *c, const char *name) { struct port *p; - pw_array_for_each(p, &client->context.ports) { + pw_array_for_each(p, &c->context.ports) { if (!p->valid) continue; - pw_log_debug("%s", p->name); + pw_log_debug("\"%s\" <-> \"%s\"", p->name, name); if (!strcmp(p->name, name)) return p; } return NULL; } +static struct buffer *dequeue_buffer(struct port *p) +{ + struct buffer *b; + + if (spa_list_is_empty(&p->queue)) + return NULL; + + b = spa_list_first(&p->queue, struct buffer, link); + spa_list_remove(&b->link); + SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT); + + return b; +} + void jack_get_version(int *major_ptr, int *minor_ptr, int *micro_ptr, int *proto_ptr) { *major_ptr = 0; @@ -170,7 +297,7 @@ static void on_sync_reply(void *data, uint32_t seq) { struct client *client = data; client->last_sync = seq; - pw_main_loop_quit(client->context.loop); + pw_thread_loop_signal(client->context.loop, false); } static void on_state_changed(void *data, enum pw_remote_state old, @@ -182,7 +309,7 @@ static void on_state_changed(void *data, enum pw_remote_state old, case PW_REMOTE_STATE_ERROR: client->error = true; case PW_REMOTE_STATE_CONNECTED: - pw_main_loop_quit(client->context.loop); + pw_thread_loop_signal(client->context.loop, false); break; default: break; @@ -202,7 +329,7 @@ static int do_sync(struct client *client) pw_core_proxy_sync(client->core_proxy, seq); while (true) { - pw_main_loop_run(client->context.loop); + pw_thread_loop_wait(client->context.loop); if (client->error) return -1; @@ -227,12 +354,247 @@ static const struct pw_proxy_events proxy_events = { .destroy = on_node_proxy_destroy, }; +static struct mem *find_mem(struct pw_array *mems, uint32_t id) +{ + struct mem *m; + + pw_array_for_each(m, mems) { + if (m->id == id) + return m; + } + return NULL; +} + +static void *mem_map(struct client *c, struct mem *m, uint32_t offset, uint32_t size) +{ + if (m->ptr == NULL) { + pw_map_range_init(&m->map, offset, size, c->context.core->sc_pagesize); + + m->ptr = mmap(NULL, m->map.size, PROT_READ|PROT_WRITE, + MAP_SHARED, m->fd, m->map.offset); + + if (m->ptr == MAP_FAILED) { + pw_log_error(NAME" %p: Failed to mmap memory %d %p: %m", c, size, m); + m->ptr = NULL; + return NULL; + } + } + return SPA_MEMBER(m->ptr, m->map.start, void); +} + +static void mem_unmap(struct client *c, struct mem *m) +{ + if (m->ptr != NULL) { + if (munmap(m->ptr, m->map.size) < 0) + pw_log_warn(NAME" %p: failed to unmap: %m", c); + m->ptr = NULL; + } +} + +static void clear_mem(struct client *c, struct mem *m) +{ + if (m->fd != -1) { + bool has_ref = false; + int fd; + struct mem *m2; + + fd = m->fd; + m->fd = -1; + + pw_array_for_each(m2, &c->mems) { + if (m2->fd == fd) { + has_ref = true; + break; + } + } + if (!has_ref) { + mem_unmap(c, m); + close(fd); + } + } +} + static void client_node_add_mem(void *object, uint32_t mem_id, uint32_t type, int memfd, uint32_t flags) { + struct client *c = object; + struct mem *m; + + m = find_mem(&c->mems, mem_id); + if (m) { + pw_log_debug(NAME" %p: update mem %u, fd %d, flags %d", c, + mem_id, memfd, flags); + clear_mem(c, m); + } else { + m = pw_array_add(&c->mems, sizeof(struct mem)); + pw_log_debug(NAME" %p: add mem %u, fd %d, flags %d", c, + mem_id, memfd, flags); + } + m->id = mem_id; + m->fd = memfd; + m->flags = flags; + m->ref = 0; + m->map = PW_MAP_RANGE_INIT; + m->ptr = NULL; +} + +static int +do_remove_sources(struct spa_loop *loop, + bool async, uint32_t seq, const void *data, size_t size, void *user_data) +{ + struct client *c = user_data; + + if (c->socket_source) { + pw_loop_destroy_source(c->context.core->data_loop, c->socket_source); + c->socket_source = NULL; + } + if (c->writefd != -1) { + close(c->writefd); + c->writefd = -1; + } + return 0; +} + +static void unhandle_socket(struct client *c) +{ + pw_loop_invoke(c->context.core->data_loop, + do_remove_sources, 1, NULL, 0, true, c); +} + +static void reuse_buffer(struct client *c, struct port *p, uint32_t id) +{ + struct buffer *b = &p->buffers[id]; + + if (SPA_FLAG_CHECK(b->flags, BUFFER_FLAG_OUT)) { + pw_log_trace(NAME" %p: recycle buffer %d", c, id); + spa_list_append(&p->queue, &b->link); + SPA_FLAG_UNSET(b->flags, BUFFER_FLAG_OUT); + } +} + +static inline void send_need_input(struct client *c) +{ + uint64_t cmd = 1; + pw_client_node_transport_add_message(c->trans, + &PW_CLIENT_NODE_MESSAGE_INIT(PW_CLIENT_NODE_MESSAGE_NEED_INPUT)); + write(c->writefd, &cmd, 8); +} + +static inline void send_have_output(struct client *c) +{ + uint64_t cmd = 1; + pw_client_node_transport_add_message(c->trans, + &PW_CLIENT_NODE_MESSAGE_INIT(PW_CLIENT_NODE_MESSAGE_HAVE_OUTPUT)); + write(c->writefd, &cmd, 8); +} + +static void handle_rtnode_message(struct client *c, struct pw_client_node_message *message) +{ + pw_log_trace("node message %d", PW_CLIENT_NODE_MESSAGE_TYPE(message)); + + switch (PW_CLIENT_NODE_MESSAGE_TYPE(message)) { + case PW_CLIENT_NODE_MESSAGE_PROCESS_INPUT: + { + if (c->process_callback) + c->process_callback(c->buffer_size, c->process_arg); + + send_have_output(c); + break; + } + case PW_CLIENT_NODE_MESSAGE_PROCESS_OUTPUT: + { + int i; + + for (i = 0; i < c->last_out_port; i++) { + struct port *p = &c->out_ports[i]; + struct spa_io_buffers *output; + + if (!p->valid) + continue; + + pw_log_trace("%d %d", i, p->id); + output = &c->trans->outputs[p->id]; + if (output->buffer_id == SPA_ID_INVALID) + continue; + + reuse_buffer(c, p, output->buffer_id); + output->buffer_id = SPA_ID_INVALID; + } + if (c->n_in_ports > 0) { + send_need_input(c); + } else { + if (c->process_callback) + c->process_callback(c->buffer_size, c->process_arg); + send_have_output(c); + } + break; + } + case PW_CLIENT_NODE_MESSAGE_PORT_REUSE_BUFFER: + { + struct pw_client_node_message_port_reuse_buffer *rb = + (struct pw_client_node_message_port_reuse_buffer *) message; + struct port *p; + uint32_t port_id = rb->body.port_id.value; + + p = &c->out_ports[port_id]; + + if (!p->valid) + return; + + reuse_buffer(c, p, rb->body.buffer_id.value); + break; + } + default: + pw_log_warn("unexpected node message %d", PW_CLIENT_NODE_MESSAGE_TYPE(message)); + break; + } +} + +static void +on_rtsocket_condition(void *data, int fd, enum spa_io mask) +{ + struct client *c = data; + + if (mask & (SPA_IO_ERR | SPA_IO_HUP)) { + pw_log_warn("got error"); + unhandle_socket(c); + return; + } + + if (mask & SPA_IO_IN) { + struct pw_client_node_message message; + uint64_t cmd; + + if (read(fd, &cmd, sizeof(uint64_t)) != sizeof(uint64_t)) + pw_log_warn("jack %p: read failed %m", c); + + while (pw_client_node_transport_next_message(c->trans, &message) == 1) { + struct pw_client_node_message *msg = alloca(SPA_POD_SIZE(&message)); + pw_client_node_transport_parse_message(c->trans, msg); + handle_rtnode_message(c, msg); + } + } +} + +static void clean_transport(struct client *c) +{ + struct mem *m; + + if (c->trans == NULL) + return; + + unhandle_socket(c); + + pw_array_for_each(m, &c->mems) + clear_mem(c, m); + pw_array_clear(&c->mems); + + pw_client_node_transport_destroy(c->trans); + c->trans = NULL; + close(c->writefd); } static void client_node_transport(void *object, @@ -241,13 +603,30 @@ static void client_node_transport(void *object, int writefd, struct pw_client_node_transport *transport) { -} + struct client *c = (struct client *) object; + struct pw_core *core = c->context.core; + clean_transport(c); + + c->node_id = node_id; + c->trans = transport; + + pw_log_info("client %p: create client transport %p with fds %d %d for node %u", + c, c->trans, readfd, writefd, node_id); + + c->writefd = writefd; + c->socket_source = pw_loop_add_io(core->data_loop, + readfd, + SPA_IO_ERR | SPA_IO_HUP, + true, on_rtsocket_condition, c); +} static void client_node_set_param(void *object, uint32_t seq, uint32_t id, uint32_t flags, const struct spa_pod *param) { + struct client *c = (struct client *) object; + pw_client_node_proxy_done(c->node_proxy, seq, -ENOTSUP); } static void client_node_event(void *object, const struct spa_event *event) @@ -256,6 +635,25 @@ static void client_node_event(void *object, const struct spa_event *event) static void client_node_command(void *object, uint32_t seq, const struct spa_command *command) { + struct client *c = (struct client *) object; + struct pw_type *t = c->context.t; + + if (SPA_COMMAND_TYPE(command) == t->command_node.Pause) { + pw_client_node_proxy_done(c->node_proxy, seq, 0); + + pw_loop_update_io(c->context.core->data_loop, + c->socket_source, SPA_IO_ERR | SPA_IO_HUP); + + } else if (SPA_COMMAND_TYPE(command) == t->command_node.Start) { + pw_client_node_proxy_done(c->node_proxy, seq, 0); + + pw_loop_update_io(c->context.core->data_loop, + c->socket_source, + SPA_IO_IN | SPA_IO_ERR | SPA_IO_HUP); + } else { + pw_log_warn("unhandled node command %d", SPA_COMMAND_TYPE(command)); + pw_client_node_proxy_done(c->node_proxy, seq, -ENOTSUP); + } } static void client_node_add_port(void *object, @@ -263,6 +661,8 @@ static void client_node_add_port(void *object, enum spa_direction direction, uint32_t port_id) { + struct client *c = (struct client *) object; + pw_client_node_proxy_done(c->node_proxy, seq, -ENOTSUP); } static void client_node_remove_port(void *object, @@ -270,6 +670,8 @@ static void client_node_remove_port(void *object, enum spa_direction direction, uint32_t port_id) { + struct client *c = (struct client *) object; + pw_client_node_proxy_done(c->node_proxy, seq, -ENOTSUP); } static void client_node_port_set_param(void *object, @@ -279,7 +681,83 @@ static void client_node_port_set_param(void *object, uint32_t id, uint32_t flags, const struct spa_pod *param) { + struct client *c = (struct client *) object; + struct pw_type *t = c->context.t; + struct spa_pod *params[4]; + uint8_t buffer[1024]; + struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); + + c->sample_rate = 44100; + c->buffer_size = 1024 / sizeof(float); + + params[0] = spa_pod_builder_object(&b, + t->param.idEnumFormat, t->spa_format, + "I", c->type.media_type.audio, + "I", c->type.media_subtype.raw, + ":", c->type.format_audio.format, "I", c->type.audio_format.F32, + ":", c->type.format_audio.channels, "i", 1, + ":", c->type.format_audio.rate, "iru", c->sample_rate, 2, 1, INT32_MAX); + + params[1] = spa_pod_builder_object(&b, + t->param.idFormat, t->spa_format, + "I", c->type.media_type.audio, + "I", c->type.media_subtype.raw, + ":", c->type.format_audio.format, "I", c->type.audio_format.F32, + ":", c->type.format_audio.channels, "i", 1, + ":", c->type.format_audio.rate, "i", c->sample_rate); + + params[2] = spa_pod_builder_object(&b, + t->param.idBuffers, t->param_buffers.Buffers, + ":", t->param_buffers.size, "isu", 1024, 3, 4, INT32_MAX, 4, + ":", t->param_buffers.stride, "i", 4, + ":", t->param_buffers.buffers, "iru", 1, 2, 1, 2, + ":", t->param_buffers.align, "i", 16); + + pw_thread_loop_lock(c->context.loop); + pw_client_node_proxy_port_update(c->node_proxy, + direction, + port_id, + PW_CLIENT_NODE_PORT_UPDATE_PARAMS, + 3, + (const struct spa_pod **) params, + NULL); + + pw_client_node_proxy_done(c->node_proxy, seq, 0); } + +static void clear_buffers(struct client *c, struct port *p) +{ + struct buffer *b; + int i; + + pw_log_debug(NAME" %p: port %p clear buffers", c, p); + + for (i = 0; i < p->n_buffers; i++) { + b = &p->buffers[i]; + + if (b->ptr != NULL) { + if (munmap(b->ptr, b->map.size) < 0) + pw_log_warn("failed to unmap: %m"); + } + for (i = 0; i < b->n_datas; i++) { + struct spa_data *d = &b->datas[i]; + if (d->fd != -1 && d->data) { + if (munmap(SPA_MEMBER(d->data, -d->mapoffset, void), + d->maxsize + d->mapoffset) < 0) + pw_log_warn("failed to unmap: %m"); + } + } + for (i = 0; i < b->n_mem; i++) { + if (--b->mem[i]->ref == 0) + clear_mem(c, b->mem[i]); + } + b->n_mem = 0; + b->ptr = NULL; + } + p->n_buffers = 0; + spa_list_init(&p->queue); +} + static void client_node_port_use_buffers(void *object, uint32_t seq, enum spa_direction direction, @@ -287,7 +765,115 @@ static void client_node_port_use_buffers(void *object, uint32_t n_buffers, struct pw_client_node_buffer *buffers) { + struct client *c = (struct client *) object; + struct port *p = GET_PORT(c, direction, port_id); + struct pw_type *t = c->context.t; + struct buffer *b; + uint32_t i, j, prot, res; + struct pw_core *core = c->context.core; + + if (!p->valid) { + res = -EINVAL; + goto done; + } + + pw_log_debug(NAME" %p: port %p use_buffers %d", c, p, n_buffers); + + prot = PROT_READ | (direction == SPA_DIRECTION_OUTPUT ? PROT_WRITE : 0); + + /* clear previous buffers */ + clear_buffers(c, p); + + for (i = 0; i < n_buffers; i++) { + off_t offset; + struct spa_buffer *buf; + + struct mem *m = find_mem(&c->mems, buffers[i].mem_id); + if (m == NULL) { + pw_log_warn(NAME" %p: unknown memory id %u", c, buffers[i].mem_id); + continue; + } + + buf = buffers[i].buffer; + + b = &p->buffers[buf->id]; + b->flags = 0; + b->id = buf->id; + b->n_mem = 0; + + pw_map_range_init(&b->map, buffers[i].offset, buffers[i].size, core->sc_pagesize); + + b->ptr = mmap(NULL, b->map.size, prot, MAP_SHARED, m->fd, b->map.offset); + if (b->ptr == MAP_FAILED) { + b->ptr = NULL; + pw_log_warn(NAME" %p: Failed to mmap memory %u %u: %m", c, + b->map.offset, b->map.size); + continue; + } + + + m->ref++; + b->mem[b->n_mem++] = m; + + pw_log_debug("add buffer %d %d %u %u", m->id, b->id, b->map.offset, b->map.size); + + offset = b->map.start; + for (j = 0; j < buf->n_metas; j++) { + struct spa_meta *m = &buf->metas[j]; + offset += m->size; + } + + b->n_datas = SPA_MIN(buf->n_datas, MAX_BUFFER_DATAS); + + for (j = 0; j < b->n_datas; j++) { + struct spa_data *d = &b->datas[j]; + + c->buffer_size = SPA_MAX(c->buffer_size, d->maxsize / sizeof(float)); + + memcpy(d, &buf->datas[j], sizeof(struct spa_data)); + d->chunk = + SPA_MEMBER(b->ptr, offset + sizeof(struct spa_chunk) * j, + struct spa_chunk); + + if (d->type == t->data.MemFd || d->type == t->data.DmaBuf) { + struct mem *bm = find_mem(&c->mems, SPA_PTR_TO_UINT32(d->data)); + + d->data = mmap(NULL, d->maxsize + d->mapoffset, prot, + MAP_SHARED, bm->fd, 0); + if (d->data == MAP_FAILED) { + pw_log_error(NAME" %p: failed to map buffer mem %m", c); + d->data = NULL; + res = -errno; + goto done; + } + d->data = SPA_MEMBER(d->data, d->mapoffset, void); + d->fd = bm->fd; + bm->ref++; + b->mem[b->n_mem++] = bm; + pw_log_debug(NAME" %p: data %d %u -> fd %d", c, j, bm->id, bm->fd); + } else if (d->type == t->data.MemPtr) { + d->data = SPA_MEMBER(b->ptr, + b->map.start + SPA_PTR_TO_INT(d->data), void); + d->fd = -1; + pw_log_debug(NAME" %p: data %d %u -> mem %p", c, j, b->id, d->data); + } else { + pw_log_warn("unknown buffer data type %d", d->type); + } + if (mlock(d->data, d->maxsize) < 0) + pw_log_warn(NAME" %p: Failed to mlock memory %p %u: %m", c, + d->data, d->maxsize); + } + SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT); + if (direction == SPA_DIRECTION_OUTPUT) + reuse_buffer(c, p, b->id); + } + p->n_buffers = n_buffers; + res = 0; + + done: + pw_client_node_proxy_done(c->node_proxy, seq, res); } + static void client_node_port_command(void *object, enum spa_direction direction, uint32_t port_id, @@ -304,10 +890,10 @@ static void client_node_port_set_io(void *object, uint32_t offset, uint32_t size) { + struct client *c = (struct client *) object; + pw_client_node_proxy_done(c->node_proxy, seq, -ENOTSUP); } - - static const struct pw_client_node_proxy_events client_node_events = { PW_VERSION_CLIENT_NODE_PROXY_EVENTS, .add_mem = client_node_add_mem, @@ -358,6 +944,10 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, else if (type == t->port) { const struct spa_dict_item *item; + if ((str = spa_dict_lookup(props, "port.dsp")) == NULL || + !pw_properties_parse_bool(str)) + return; + if ((str = spa_dict_lookup(props, "port.name")) == NULL) return; @@ -433,16 +1023,23 @@ jack_client_t * jack_client_open (const char *client_name, goto init_failed; strncpy(client->name, client_name, JACK_CLIENT_NAME_SIZE); - client->context.loop = pw_main_loop_new(NULL); - client->context.core = pw_core_new(pw_main_loop_get_loop(client->context.loop), NULL); + client->context.main = pw_main_loop_new(NULL); + client->context.loop = pw_thread_loop_new(pw_main_loop_get_loop(client->context.main), client_name); + client->context.core = pw_core_new(pw_thread_loop_get_loop(client->context.loop), NULL); client->context.t = pw_core_get_type(client->context.core); init_type(&client->type, client->context.t->map); + pw_array_init(&client->mems, 64); + pw_array_ensure_size(&client->mems, sizeof(struct mem) * 64); + pw_map_init(&client->context.globals, 64, 64); pw_array_init(&client->context.nodes, 64); pw_array_init(&client->context.ports, 64); pw_array_init(&client->context.links, 64); + pw_thread_loop_start(client->context.loop); + + pw_thread_loop_lock(client->context.loop); client->remote = pw_remote_new(client->context.core, pw_properties_new( "client.name", client_name, @@ -450,12 +1047,13 @@ jack_client_t * jack_client_open (const char *client_name, 0); pw_remote_add_listener(client->remote, &client->remote_listener, &remote_events, client); + pw_remote_connect(client->remote); while (busy) { const char *error = NULL; - pw_main_loop_run(client->context.loop); + pw_thread_loop_wait(client->context.loop); switch (pw_remote_get_state(client->remote, &error)) { case PW_REMOTE_STATE_ERROR: @@ -503,15 +1101,20 @@ jack_client_t * jack_client_open (const char *client_name, if (do_sync(client) < 0) goto init_failed; + pw_thread_loop_unlock(client->context.loop); + *status = 0; return (jack_client_t *)client; init_failed: *status = JackFailure | JackInitFailure; - return NULL; + goto exit; server_failed: *status = JackFailure | JackServerFailed; + goto exit; + exit: + pw_thread_loop_unlock(client->context.loop); return NULL; } @@ -532,8 +1135,11 @@ int jack_client_close (jack_client_t *client) pw_log_debug("client %p: close", client); + pw_thread_loop_stop(c->context.loop); + pw_core_destroy(c->context.core); - pw_main_loop_destroy(c->context.loop); + pw_thread_loop_destroy(c->context.loop); + pw_main_loop_destroy(c->context.main); free(c); return 0; @@ -576,19 +1182,39 @@ void jack_internal_client_close (const char *client_name) int jack_activate (jack_client_t *client) { struct client *c = (struct client *) client; + int res = 0; + pw_thread_loop_lock(c->context.loop); pw_client_node_proxy_done(c->node_proxy, 0, 0); pw_client_node_proxy_set_active(c->node_proxy, true); - if (do_sync(c) < 0) - return -1; + res = do_sync(c); - return 0; + pw_thread_loop_unlock(c->context.loop); + + if (res > 0) + c->active = true; + + return res; } int jack_deactivate (jack_client_t *client) { - return 0; + struct client *c = (struct client *) client; + int res = 0; + + pw_thread_loop_lock(c->context.loop); + pw_client_node_proxy_done(c->node_proxy, 0, 0); + pw_client_node_proxy_set_active(c->node_proxy, false); + + res = do_sync(c); + + pw_thread_loop_unlock(c->context.loop); + + if (res > 0) + c->active = false; + + return res; } int jack_get_client_pid (const char *name) @@ -622,6 +1248,18 @@ void jack_cycle_signal (jack_client_t* client, int status) int jack_set_process_thread(jack_client_t* client, JackThreadCallback thread_callback, void *arg) { + struct client *c = (struct client *) client; + + if (c->active) { + pw_log_error("jack %p: can't set callback on active client", c); + return -EIO; + } else if (c->process_callback) { + pw_log_error("jack %p: process callback was already set", c); + return -EIO; + } + + c->thread_callback = thread_callback; + c->thread_arg = arg; return 0; } @@ -646,6 +1284,19 @@ int jack_set_process_callback (jack_client_t *client, JackProcessCallback process_callback, void *arg) { + struct client *c = (struct client *) client; + + if (c->active) { + pw_log_error("jack %p: can't set callback on active client", c); + return -EIO; + } else if (c->thread_callback) { + pw_log_error("jack %p: thread callback was already set", c); + return -EIO; + } + + pw_log_debug("jack %p: %p %p", c, process_callback, arg); + c->process_callback = process_callback; + c->process_arg = arg; return 0; } @@ -730,12 +1381,14 @@ int jack_set_buffer_size (jack_client_t *client, jack_nframes_t nframes) jack_nframes_t jack_get_sample_rate (jack_client_t *client) { - return 0; + struct client *c = (struct client *) client; + return c->sample_rate; } jack_nframes_t jack_get_buffer_size (jack_client_t *client) { - return 0; + struct client *c = (struct client *) client; + return c->buffer_size; } int jack_engine_takeover_timebase (jack_client_t *client) @@ -765,6 +1418,7 @@ jack_port_t * jack_port_register (jack_client_t *client, struct spa_pod *params[4]; uint32_t port_id; struct port *p; + int res; pw_log_debug("client %p: port register \"%s\" \"%s\" %ld %ld", c, port_name, port_type, flags, buffer_size); @@ -776,13 +1430,11 @@ jack_port_t * jack_port_register (jack_client_t *client, else return NULL; - if ((port_id = alloc_port(c)) == SPA_ID_INVALID) + if ((p = alloc_port(c, direction)) == NULL) return NULL; - p = &c->ports[port_id]; - p->id = port_id; + port_id = p->id; snprintf(p->name, sizeof(p->name), "%s:%s", c->name, port_name); - p->direction = direction; p->type = port_type; if (strcmp(port_type, JACK_DEFAULT_AUDIO_TYPE) == 0) @@ -792,11 +1444,15 @@ jack_port_t * jack_port_register (jack_client_t *client, else return NULL; + spa_list_init(&p->queue); + p->n_buffers = 0; + port_info.flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS | SPA_PORT_INFO_FLAG_NO_REF; port_info.props = &dict; dict = SPA_DICT_INIT(items, 0); + items[dict.n_items++] = SPA_DICT_ITEM_INIT("port.dsp", "1"); items[dict.n_items++] = SPA_DICT_ITEM_INIT("port.name", port_name); items[dict.n_items++] = SPA_DICT_ITEM_INIT("port.type", port_type); @@ -810,11 +1466,12 @@ jack_port_t * jack_port_register (jack_client_t *client, params[1] = spa_pod_builder_object(&b, t->param.idBuffers, t->param_buffers.Buffers, - ":", t->param_buffers.size, "isu", 128, 3, 4, INT32_MAX, 4, + ":", t->param_buffers.size, "isu", 1024, 3, 4, INT32_MAX, 4, ":", t->param_buffers.stride, "i", 4, ":", t->param_buffers.buffers, "iru", 1, 2, 1, 2, ":", t->param_buffers.align, "i", 16); + pw_thread_loop_lock(c->context.loop); pw_client_node_proxy_port_update(c->node_proxy, direction, port_id, @@ -824,35 +1481,66 @@ jack_port_t * jack_port_register (jack_client_t *client, (const struct spa_pod **) params, &port_info); - if (do_sync(c) < 0) + res = do_sync(c); + + pw_thread_loop_unlock(c->context.loop); + + if (res < 0) return NULL; - return (jack_port_t *) &c->ports[port_id]; + return (jack_port_t *) p; } int jack_port_unregister (jack_client_t *client, jack_port_t *port) { struct client *c = (struct client *) client; struct port *p = (struct port *) port; + int res; pw_log_debug("client %p: port unregister %p", client, port); - p->valid = false; + pw_thread_loop_lock(c->context.loop); + free_port(c, p); pw_client_node_proxy_port_update(c->node_proxy, p->direction, p->id, 0, 0, NULL, NULL); - if (do_sync(c) < 0) - return -1; + res = do_sync(c); - return 0; + pw_thread_loop_unlock(c->context.loop); + + return res; } void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) { - return 0; + struct port *p = (struct port *) port; + struct client *c = p->client; + struct buffer *b; + struct spa_io_buffers *io; + + pw_log_trace("port %p: get buffer", p); + + if (p->direction == SPA_DIRECTION_INPUT) { + io = &c->trans->inputs[p->id]; + if (io->status != SPA_STATUS_HAVE_BUFFER) + return NULL; + + b = &p->buffers[io->buffer_id]; + io->status = SPA_STATUS_NEED_BUFFER; + } else { + b = dequeue_buffer(p); + if (b == NULL) { + pw_log_warn("port %p: out of buffers", p); + return NULL; + } + io = &c->trans->outputs[p->id]; + io->status = SPA_STATUS_HAVE_BUFFER; + io->buffer_id = b->id; + } + return b->datas[0].data; } jack_uuid_t jack_port_uuid (const jack_port_t *port) @@ -975,34 +1663,46 @@ int jack_connect (jack_client_t *client, struct client *c = (struct client *) client; struct port *src, *dst; struct pw_properties *props; + int res; pw_log_debug("client %p: connect %s %s", client, source_port, destination_port); + pw_thread_loop_lock(c->context.loop); + src = find_port(c, source_port); dst = find_port(c, destination_port); - if (src == NULL || dst == NULL) - return -1; + if (src == NULL || dst == NULL) { + res = -ENOENT; + goto exit; + } props = pw_properties_new(NULL, NULL); + if (props == NULL) { + res = -ENOMEM; + goto exit; + } + pw_properties_setf(props, PW_LINK_OUTPUT_NODE_ID, "%d", src->parent_id); pw_properties_setf(props, PW_LINK_OUTPUT_PORT_ID, "%d", src->id); pw_properties_setf(props, PW_LINK_INPUT_NODE_ID, "%d", dst->parent_id); pw_properties_setf(props, PW_LINK_INPUT_PORT_ID, "%d", dst->id); pw_core_proxy_create_object(c->core_proxy, - "link-factory", - c->context.t->link, - PW_VERSION_LINK, - &props->dict, - 0); + "link-factory", + c->context.t->link, + PW_VERSION_LINK, + &props->dict, + 0); pw_properties_free(props); - if (do_sync(c) < 0) - return -1; + res = do_sync(c); - return 0; + exit: + pw_thread_loop_unlock(c->context.loop); + + return res; } int jack_disconnect (jack_client_t *client, @@ -1077,6 +1777,8 @@ const char ** jack_get_ports (jack_client_t *client, int count = 0; struct port *p; + pw_thread_loop_lock(c->context.loop); + pw_array_for_each(p, &c->context.ports) { if (!p->valid) continue; @@ -1086,6 +1788,7 @@ const char ** jack_get_ports (jack_client_t *client, res[count++] = p->name; } res[count] = NULL; + pw_thread_loop_unlock(c->context.loop); return res; } From 7dc03336e5497aa98d13631059b2bbf16a707eec Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 15 Feb 2018 17:57:43 +0100 Subject: [PATCH 003/133] pipewire-jack: implement more jack api Implement more callbacks. Keep track of links. Improve object handling. Implement port connections. Implement disconnect. Implement port lookup --- src/pipewire-jack.c | 630 +++++++++++++++++++++++++++++++++----------- 1 file changed, 483 insertions(+), 147 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 8ead28a8e..8cea054fb 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -40,7 +40,11 @@ #define JACK_PORT_MAX 4096 #define JACK_PORT_TYPE_SIZE 32 #define PORT_NUM_FOR_CLIENT 1024 +#define CONNECTION_NUM_FOR_PORT 2048 +#define BUFFER_SIZE_MAX 8192 + +#define MAX_OBJECTS 8192 #define MAX_PORTS (PORT_NUM_FOR_CLIENT/2) #define MAX_BUFFERS 64 #define MAX_BUFFER_DATAS 4 @@ -70,16 +74,32 @@ static inline void init_type(struct type *type, struct spa_type_map *map) spa_type_audio_format_map(map, &type->audio_format); } -struct global { - bool valid; - uint32_t type; - void *data; -}; +#define OBJECT_CHUNK 8 -struct node { - bool valid; +struct object { + struct spa_list link; + + struct client *client; + + uint32_t type; uint32_t id; - char name[JACK_CLIENT_NAME_SIZE]; + uint32_t parent_id; + + union { + struct { + char name[JACK_CLIENT_NAME_SIZE]; + } node; + struct { + uint32_t src; + uint32_t dst; + } port_link; + struct { + unsigned long flags; + char name[REAL_JACK_PORT_NAME_SIZE]; + uint32_t type_id; + uint32_t port_id; + } port; + }; }; struct mem { @@ -112,14 +132,10 @@ struct port { struct client *client; - enum spa_direction direction; uint32_t id; - uint32_t parent_id; - unsigned long flags; - char name[REAL_JACK_PORT_NAME_SIZE]; + struct object *object; - const char *type; - uint32_t type_id; + enum spa_direction direction; struct buffer buffers[MAX_BUFFERS]; uint32_t n_buffers; @@ -127,12 +143,6 @@ struct port { struct spa_list queue; }; -struct link { - bool valid; - uint32_t id; - uint32_t src; - uint32_t dst; -}; struct context { struct pw_main_loop *main; @@ -141,14 +151,17 @@ struct context { struct pw_type *t; struct pw_map globals; - struct pw_array nodes; - struct pw_array ports; - struct pw_array links; + struct spa_list free_objects; + struct spa_list ports; + struct spa_list nodes; + struct spa_list links; }; -#define GET_IN_PORT(c,p) (&c->in_ports[p]) -#define GET_OUT_PORT(c,p) (&c->out_ports[p]) -#define GET_PORT(c,d,p) (d == SPA_DIRECTION_INPUT ? GET_IN_PORT(c,p) : GET_OUT_PORT(c,p)) +#define GET_DIRECTION(f) ((f) & JackPortIsInput ? SPA_DIRECTION_INPUT : SPA_DIRECTION_OUTPUT) + +#define GET_IN_PORT(c,p) (&c->in_ports[p]) +#define GET_OUT_PORT(c,p) (&c->out_ports[p]) +#define GET_PORT(c,d,p) (d == SPA_DIRECTION_INPUT ? GET_IN_PORT(c,p) : GET_OUT_PORT(c,p)) struct client { struct type type; @@ -180,8 +193,26 @@ struct client { JackThreadCallback thread_callback; void *thread_arg; + JackThreadInitCallback thread_init_callback; + void *thread_init_arg; + JackShutdownCallback shutdown_callback; + void *shutdown_arg; + JackInfoShutdownCallback info_shutdown_callback; + void *info_shutdown_arg; JackProcessCallback process_callback; void *process_arg; + JackFreewheelCallback freewheel_callback; + void *freewheel_arg; + JackBufferSizeCallback bufsize_callback; + void *bufsize_arg; + JackSampleRateCallback srate_callback; + void *srate_arg; + JackClientRegistrationCallback registration_callback; + void *registration_arg; + JackPortRegistrationCallback portregistration_callback; + void *portregistration_arg; + JackPortConnectCallback connect_callback; + void *connect_arg; uint32_t sample_rate; uint32_t buffer_size; @@ -194,12 +225,41 @@ struct client { uint32_t last_out_port; struct pw_array mems; + + float empty[BUFFER_SIZE_MAX + 8]; }; +static struct object * alloc_object(struct client *c) +{ + struct object *o; + int i; + + if (spa_list_is_empty(&c->context.free_objects)) { + o = calloc(OBJECT_CHUNK, sizeof(struct object)); + if (o == NULL) + return NULL; + for (i = 0; i < OBJECT_CHUNK; i++) + spa_list_append(&c->context.free_objects, &o[i].link); + } + + o = spa_list_first(&c->context.free_objects, struct object, link); + spa_list_remove(&o->link); + o->client = c; + + return o; +} + +static void free_object(struct client *c, struct object *o) +{ + spa_list_remove(&o->link); + spa_list_append(&c->context.free_objects, &o->link); +} + static struct port * alloc_port(struct client *c, enum spa_direction direction) { int i; struct port *p; + struct object *o; if (direction == SPA_DIRECTION_INPUT) { for (i = 0; i < c->n_in_ports; i++) { @@ -224,12 +284,19 @@ static struct port * alloc_port(struct client *c, enum spa_direction direction) c->n_out_ports++; c->last_out_port = SPA_MAX(c->last_out_port, i + 1); } + + o = alloc_object(c); + o->type = c->context.t->port; + o->id = SPA_ID_INVALID; + o->parent_id = c->node_id; + o->port.port_id = i; + spa_list_append(&c->context.ports, &o->link); + p->valid = true; p->client = c; p->direction = direction; p->id = i; - - pw_log_debug("port %p: dir %d, id %d", p, p->direction, p->id); + p->object = o; return p; } @@ -249,18 +316,17 @@ static void free_port(struct client *c, struct port *p) c->last_out_port--; } p->valid = false; + free_object(c, p->object); } -static struct port *find_port(struct client *c, const char *name) +static struct object *find_port(struct client *c, const char *name) { - struct port *p; + struct object *o; - pw_array_for_each(p, &c->context.ports) { - if (!p->valid) - continue; - pw_log_debug("\"%s\" <-> \"%s\"", p->name, name); - if (!strcmp(p->name, name)) - return p; + spa_list_for_each(o, &c->context.ports, link) { + pw_log_debug("%s", o->port.name); + if (!strcmp(o->port.name, name)) + return o; } return NULL; } @@ -306,6 +372,9 @@ static void on_state_changed(void *data, enum pw_remote_state old, struct client *client = data; switch (state) { + case PW_REMOTE_STATE_UNCONNECTED: + if (client->shutdown_callback) + client->shutdown_callback(client->shutdown_arg); case PW_REMOTE_STATE_ERROR: client->error = true; case PW_REMOTE_STATE_CONNECTED: @@ -611,7 +680,7 @@ static void client_node_transport(void *object, c->node_id = node_id; c->trans = transport; - pw_log_info("client %p: create client transport %p with fds %d %d for node %u", + pw_log_debug("client %p: create client transport %p with fds %d %d for node %u", c, c->trans, readfd, writefd, node_id); c->writefd = writefd; @@ -687,9 +756,6 @@ static void client_node_port_set_param(void *object, uint8_t buffer[1024]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); - c->sample_rate = 44100; - c->buffer_size = 1024 / sizeof(float); - params[0] = spa_pod_builder_object(&b, t->param.idEnumFormat, t->spa_format, "I", c->type.media_type.audio, @@ -713,7 +779,6 @@ static void client_node_port_set_param(void *object, ":", t->param_buffers.buffers, "iru", 1, 2, 1, 2, ":", t->param_buffers.align, "i", 16); - pw_thread_loop_lock(c->context.loop); pw_client_node_proxy_port_update(c->node_proxy, direction, port_id, @@ -777,7 +842,7 @@ static void client_node_port_use_buffers(void *object, goto done; } - pw_log_debug(NAME" %p: port %p use_buffers %d", c, p, n_buffers); + pw_log_debug(NAME" %p: port %p %d use_buffers %d", c, p, direction, n_buffers); prot = PROT_READ | (direction == SPA_DIRECTION_OUTPUT ? PROT_WRITE : 0); @@ -915,90 +980,145 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, { struct client *c = (struct client *) data; struct pw_type *t = c->context.t; - struct global *g, *gt; + struct object *o, *ot; const char *str; size_t size; - struct node *n; - struct port *p; if (props == NULL) return; - pw_log_debug("added: %u", id); - - g = pw_array_add(&c->context.nodes, sizeof(struct global)); - g->valid = true; - g->type = type; + pw_log_debug("added: %u %u %u", id, type, parent_id); if (type == t->node) { if ((str = spa_dict_lookup(props, "node.name")) == NULL) - return; + goto exit; - n = pw_array_add(&c->context.nodes, sizeof(struct node)); - n->valid = true; - n->id = id; - strncpy(n->name, str, sizeof(n->name)); + o = alloc_object(c); + spa_list_append(&c->context.nodes, &o->link); - g->data = n; + strncpy(o->node.name, str, sizeof(o->node.name)); } else if (type == t->port) { const struct spa_dict_item *item; + unsigned long flags = 0; + char full_name[1024]; if ((str = spa_dict_lookup(props, "port.dsp")) == NULL || !pw_properties_parse_bool(str)) - return; + goto exit; if ((str = spa_dict_lookup(props, "port.name")) == NULL) - return; - - if ((gt = pw_map_lookup(&c->context.globals, parent_id)) == NULL) - return; - - n = gt->data; - - p = pw_array_add(&c->context.ports, sizeof(struct port)); - p->valid = true; - p->id = id; - p->parent_id = parent_id; - snprintf(p->name, sizeof(p->name), "%s:%s", n->name, str); + goto exit; spa_dict_for_each(item, props) { if (!strcmp(item->key, "port.direction")) { if (!strcmp(item->value, "in")) - p->flags |= JackPortIsInput; + flags |= JackPortIsInput; else if (!strcmp(item->value, "out")) - p->flags |= JackPortIsOutput; + flags |= JackPortIsOutput; } else if (!strcmp(item->key, "port.physical")) { if (pw_properties_parse_bool(item->value)) - p->flags |= JackPortIsPhysical; + flags |= JackPortIsPhysical; } else if (!strcmp(item->key, "port.terminal")) { if (pw_properties_parse_bool(item->value)) - p->flags |= JackPortIsTerminal; + flags |= JackPortIsTerminal; } } - g->data = n; + snprintf(full_name, sizeof(full_name), "%s:%s", c->name, str); + o = find_port(c, full_name); + if (o != NULL) { + pw_log_debug("client %p: found our port %p", c, o); + } + else { + o = alloc_object(c); + if (o == NULL) + goto exit; + + spa_list_append(&c->context.ports, &o->link); + ot = pw_map_lookup(&c->context.globals, parent_id); + if (ot == NULL || ot->type != t->node) + goto exit_free; + + snprintf(o->port.name, sizeof(o->port.name), "%s:%s", ot->node.name, str); + o->port.port_id = SPA_ID_INVALID; + } + o->port.flags = flags; } else if (type == t->link) { + o = alloc_object(c); + spa_list_append(&c->context.links, &o->link); + + if ((str = spa_dict_lookup(props, "link.output")) == NULL) + goto exit_free; + o->port_link.src = pw_properties_parse_int(str); + + if ((str = spa_dict_lookup(props, "link.input")) == NULL) + goto exit_free; + o->port_link.dst = pw_properties_parse_int(str); + + pw_log_debug("add link %d %d", o->port_link.src, o->port_link.dst); } else - return; + goto exit; + + o->type = type; + o->id = id; + o->parent_id = parent_id; size = pw_map_get_size(&c->context.globals); while (id > size) - pw_map_insert_at(&c->context.globals, size++, NULL); - pw_map_insert_at(&c->context.globals, id, g); + pw_map_insert_at(&c->context.globals, size++, NULL); + pw_map_insert_at(&c->context.globals, id, o); + + if (type == t->node) { + if (c->registration_callback) + c->registration_callback(o->node.name, 1, c->registration_arg); + } + else if (type == t->port) { + if (c->portregistration_callback) + c->portregistration_callback(o->id, 1, c->portregistration_arg); + } + else if (type == t->link) { + if (c->connect_callback) + c->connect_callback(o->port_link.src, o->port_link.dst, 1, c->connect_arg); + } + + exit: + return; + exit_free: + free_object(c, o); + return; } static void registry_event_global_remove(void *object, uint32_t id) { struct client *c = (struct client *) object; + struct pw_type *t = c->context.t; + struct object *o; pw_log_debug("removed: %u", id); - pw_map_insert_at(&c->context.globals, id, NULL); + o = pw_map_lookup(&c->context.globals, id); + if (o == NULL) + return; + + if (o->type == t->node) { + if (c->registration_callback) + c->registration_callback(o->node.name, 0, c->registration_arg); + } + else if (o->type == t->port) { + if (c->portregistration_callback) + c->portregistration_callback(o->id, 0, c->portregistration_arg); + } + else if (o->type == t->link) { + if (c->connect_callback) + c->connect_callback(o->port_link.src, o->port_link.dst, 0, c->connect_arg); + } + pw_map_insert_at(&c->context.globals, id, NULL); + free_object(c, o); } static const struct pw_registry_proxy_events registry_events = { @@ -1028,14 +1148,18 @@ jack_client_t * jack_client_open (const char *client_name, client->context.core = pw_core_new(pw_thread_loop_get_loop(client->context.loop), NULL); client->context.t = pw_core_get_type(client->context.core); init_type(&client->type, client->context.t->map); + spa_list_init(&client->context.free_objects); + spa_list_init(&client->context.nodes); + spa_list_init(&client->context.ports); + spa_list_init(&client->context.links); pw_array_init(&client->mems, 64); pw_array_ensure_size(&client->mems, sizeof(struct mem) * 64); + client->sample_rate = 44100; + client->buffer_size = 1024 / sizeof(float); + pw_map_init(&client->context.globals, 64, 64); - pw_array_init(&client->context.nodes, 64); - pw_array_init(&client->context.ports, 64); - pw_array_init(&client->context.links, 64); pw_thread_loop_start(client->context.loop); @@ -1048,7 +1172,8 @@ jack_client_t * jack_client_open (const char *client_name, pw_remote_add_listener(client->remote, &client->remote_listener, &remote_events, client); - pw_remote_connect(client->remote); + if (pw_remote_connect(client->remote) < 0) + goto server_failed; while (busy) { const char *error = NULL; @@ -1103,15 +1228,18 @@ jack_client_t * jack_client_open (const char *client_name, pw_thread_loop_unlock(client->context.loop); - *status = 0; + if (status) + *status = 0; return (jack_client_t *)client; init_failed: - *status = JackFailure | JackInitFailure; + if (status) + *status = JackFailure | JackInitFailure; goto exit; server_failed: - *status = JackFailure | JackServerFailed; + if (status) + *status = JackFailure | JackServerFailed; goto exit; exit: pw_thread_loop_unlock(client->context.loop); @@ -1267,17 +1395,26 @@ int jack_set_thread_init_callback (jack_client_t *client, JackThreadInitCallback thread_init_callback, void *arg) { + struct client *c = (struct client *) client; + c->thread_init_callback = thread_init_callback; + c->thread_init_arg = arg; return 0; } void jack_on_shutdown (jack_client_t *client, JackShutdownCallback shutdown_callback, void *arg) { + struct client *c = (struct client *) client; + c->shutdown_callback = shutdown_callback; + c->shutdown_arg = arg; } void jack_on_info_shutdown (jack_client_t *client, JackInfoShutdownCallback shutdown_callback, void *arg) { + struct client *c = (struct client *) client; + c->info_shutdown_callback = shutdown_callback; + c->info_shutdown_arg = arg; } int jack_set_process_callback (jack_client_t *client, @@ -1304,6 +1441,9 @@ int jack_set_freewheel_callback (jack_client_t *client, JackFreewheelCallback freewheel_callback, void *arg) { + struct client *c = (struct client *) client; + c->freewheel_callback = freewheel_callback; + c->freewheel_arg = arg; return 0; } @@ -1311,6 +1451,9 @@ int jack_set_buffer_size_callback (jack_client_t *client, JackBufferSizeCallback bufsize_callback, void *arg) { + struct client *c = (struct client *) client; + c->bufsize_callback = bufsize_callback; + c->bufsize_arg = arg; return 0; } @@ -1318,6 +1461,9 @@ int jack_set_sample_rate_callback (jack_client_t *client, JackSampleRateCallback srate_callback, void *arg) { + struct client *c = (struct client *) client; + c->srate_callback = srate_callback; + c->srate_arg = arg; return 0; } @@ -1325,6 +1471,9 @@ int jack_set_client_registration_callback (jack_client_t *client, JackClientRegistrationCallback registration_callback, void *arg) { + struct client *c = (struct client *) client; + c->registration_callback = registration_callback; + c->registration_arg = arg; return 0; } @@ -1332,13 +1481,20 @@ int jack_set_port_registration_callback (jack_client_t *client, JackPortRegistrationCallback registration_callback, void *arg) { + struct client *c = (struct client *) client; + c->portregistration_callback = registration_callback; + c->portregistration_arg = arg; return 0; } + int jack_set_port_connect_callback (jack_client_t *client, JackPortConnectCallback connect_callback, void *arg) { + struct client *c = (struct client *) client; + c->connect_callback = connect_callback; + c->connect_arg = arg; return 0; } @@ -1412,11 +1568,12 @@ jack_port_t * jack_port_register (jack_client_t *client, struct spa_port_info port_info = { 0, }; struct spa_dict dict; struct spa_dict_item items[10]; + struct object *o; + jack_port_type_id_t type_id; uint8_t buffer[1024]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); struct pw_type *t = c->context.t; struct spa_pod *params[4]; - uint32_t port_id; struct port *p; int res; @@ -1430,19 +1587,20 @@ jack_port_t * jack_port_register (jack_client_t *client, else return NULL; + if (!strcmp(JACK_DEFAULT_AUDIO_TYPE, port_type)) + type_id = 0; + else if (!strcmp(JACK_DEFAULT_MIDI_TYPE, port_type)) + type_id = 1; + else + return NULL; + if ((p = alloc_port(c, direction)) == NULL) return NULL; - port_id = p->id; - snprintf(p->name, sizeof(p->name), "%s:%s", c->name, port_name); - p->type = port_type; - - if (strcmp(port_type, JACK_DEFAULT_AUDIO_TYPE) == 0) - p->type_id = 0; - else if (strcmp(port_type, JACK_DEFAULT_MIDI_TYPE) == 0) - p->type_id = 1; - else - return NULL; + o = p->object; + o->port.flags = flags; + snprintf(o->port.name, sizeof(o->port.name), "%s:%s", c->name, port_name); + o->port.type_id = type_id; spa_list_init(&p->queue); p->n_buffers = 0; @@ -1474,7 +1632,7 @@ jack_port_t * jack_port_register (jack_client_t *client, pw_thread_loop_lock(c->context.loop); pw_client_node_proxy_port_update(c->node_proxy, direction, - port_id, + p->id, PW_CLIENT_NODE_PORT_UPDATE_PARAMS | PW_CLIENT_NODE_PORT_UPDATE_INFO , 2, @@ -1488,18 +1646,26 @@ jack_port_t * jack_port_register (jack_client_t *client, if (res < 0) return NULL; - return (jack_port_t *) p; + return (jack_port_t *) o; } int jack_port_unregister (jack_client_t *client, jack_port_t *port) { - struct client *c = (struct client *) client; - struct port *p = (struct port *) port; + struct object *o = (struct object *) port; + struct client *c = o->client; + struct port *p; int res; + if (o->type != c->context.t->port || o->port.port_id == SPA_ID_INVALID) { + pw_log_error("client %p: invalid port %p", client, port); + return -EINVAL; + } pw_log_debug("client %p: port unregister %p", client, port); pw_thread_loop_lock(c->context.loop); + + p = GET_PORT(c, GET_DIRECTION(o->port.flags), o->port.port_id); + free_port(c, p); pw_client_node_proxy_port_update(c->node_proxy, @@ -1516,12 +1682,19 @@ int jack_port_unregister (jack_client_t *client, jack_port_t *port) void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) { - struct port *p = (struct port *) port; - struct client *c = p->client; + struct object *o = (struct object *) port; + struct client *c = o->client; + struct port *p; struct buffer *b; struct spa_io_buffers *io; - pw_log_trace("port %p: get buffer", p); + pw_log_trace("port %p: get buffer", port); + + if (o->type != c->context.t->port || o->port.port_id == SPA_ID_INVALID) { + pw_log_error("client %p: invalid port %p", c, port); + return NULL; + } + p = GET_PORT(c, GET_DIRECTION(o->port.flags), o->port.port_id); if (p->direction == SPA_DIRECTION_INPUT) { io = &c->trans->inputs[p->id]; @@ -1534,7 +1707,7 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) b = dequeue_buffer(p); if (b == NULL) { pw_log_warn("port %p: out of buffers", p); - return NULL; + return c->empty; } io = &c->trans->outputs[p->id]; io->status = SPA_STATUS_HAVE_BUFFER; @@ -1550,59 +1723,139 @@ jack_uuid_t jack_port_uuid (const jack_port_t *port) const char * jack_port_name (const jack_port_t *port) { - struct port *p = (struct port *) port; - return p->name; + struct object *o = (struct object *) port; + return o->port.name; } const char * jack_port_short_name (const jack_port_t *port) { - struct port *p = (struct port *) port; - return strchr(p->name, ':') + 1; + struct object *o = (struct object *) port; + return strchr(o->port.name, ':') + 1; } int jack_port_flags (const jack_port_t *port) { - struct port *p = (struct port *) port; - return p->flags; + struct object *o = (struct object *) port; + return o->port.flags; } const char * jack_port_type (const jack_port_t *port) { - struct port *p = (struct port *) port; - return p->type; + struct object *o = (struct object *) port; + switch(o->port.type_id) { + case 0: + return JACK_DEFAULT_AUDIO_TYPE; + case 1: + return JACK_DEFAULT_MIDI_TYPE; + default: + return NULL; + } } jack_port_type_id_t jack_port_type_id (const jack_port_t *port) { - struct port *p = (struct port *) port; - return p->type_id; + struct object *o = (struct object *) port; + return o->port.type_id; } int jack_port_is_mine (const jack_client_t *client, const jack_port_t *port) { - return 0; + struct object *o = (struct object *) port; + struct client *c = (struct client *) client; + return o->type == c->context.t->port && o->port.port_id != SPA_ID_INVALID; } int jack_port_connected (const jack_port_t *port) { - return 0; + struct object *o = (struct object *) port; + struct client *c = o->client; + struct object *l; + int res = 0; + + pw_thread_loop_lock(c->context.loop); + spa_list_for_each(l, &c->context.links, link) { + if (l->port_link.src == o->id || + l->port_link.dst == o->id) + res++; + } + pw_thread_loop_unlock(c->context.loop); + + return res; } int jack_port_connected_to (const jack_port_t *port, const char *port_name) { - return 0; + struct object *o = (struct object *) port; + struct client *c = o->client; + struct object *p, *l; + int res = 0; + + pw_thread_loop_lock(c->context.loop); + + p = find_port(c, port_name); + if (p == NULL) + goto exit; + + if (GET_DIRECTION(p->port.flags) == GET_DIRECTION(o->port.flags)) + goto exit; + + if (p->port.flags & JackPortIsOutput) { + l = p; + p = o; + o = l; + } + + spa_list_for_each(l, &c->context.links, link) { + if (l->port_link.src == o->id && + l->port_link.dst == p->id) { + res = 1; + break; + } + } + exit: + pw_thread_loop_unlock(c->context.loop); + + return res; } const char ** jack_port_get_connections (const jack_port_t *port) { - return NULL; + struct object *o = (struct object *) port; + struct client *c = o->client; + + return jack_port_get_all_connections((jack_client_t *)c, port); } const char ** jack_port_get_all_connections (const jack_client_t *client, const jack_port_t *port) { - return NULL; + struct client *c = (struct client *) client; + struct object *o = (struct object *) port; + struct object *p, *l; + const char **res = malloc(sizeof(char*) * CONNECTION_NUM_FOR_PORT); + int count = 0; + + pw_thread_loop_lock(c->context.loop); + + spa_list_for_each(l, &c->context.links, link) { + if (l->port_link.src == o->id) + p = pw_map_lookup(&c->context.globals, l->port_link.dst); + else if (l->port_link.dst == o->id) + p = pw_map_lookup(&c->context.globals, l->port_link.src); + else + continue; + + if (p == NULL) + continue; + + res[count++] = p->port.name; + } + pw_thread_loop_unlock(c->context.loop); + + res[count] = NULL; + + return res; } int jack_port_tie (jack_port_t *src, jack_port_t *dst) @@ -1661,8 +1914,11 @@ int jack_connect (jack_client_t *client, const char *destination_port) { struct client *c = (struct client *) client; - struct port *src, *dst; - struct pw_properties *props; + struct object *src, *dst; + struct pw_type *t = c->context.t; + struct spa_dict props; + struct spa_dict_item items[4]; + char val[4][16]; int res; pw_log_debug("client %p: connect %s %s", client, source_port, destination_port); @@ -1672,31 +1928,30 @@ int jack_connect (jack_client_t *client, src = find_port(c, source_port); dst = find_port(c, destination_port); - if (src == NULL || dst == NULL) { + if (src == NULL || dst == NULL || + !(src->port.flags & JackPortIsOutput) || + !(dst->port.flags & JackPortIsInput)) { res = -ENOENT; goto exit; } - props = pw_properties_new(NULL, NULL); - if (props == NULL) { - res = -ENOMEM; - goto exit; - } + snprintf(val[0], sizeof(val[0]), "%d", src->parent_id); + snprintf(val[1], sizeof(val[1]), "%d", src->id); + snprintf(val[2], sizeof(val[2]), "%d", dst->parent_id); + snprintf(val[3], sizeof(val[3]), "%d", dst->id); - pw_properties_setf(props, PW_LINK_OUTPUT_NODE_ID, "%d", src->parent_id); - pw_properties_setf(props, PW_LINK_OUTPUT_PORT_ID, "%d", src->id); - pw_properties_setf(props, PW_LINK_INPUT_NODE_ID, "%d", dst->parent_id); - pw_properties_setf(props, PW_LINK_INPUT_PORT_ID, "%d", dst->id); + props = SPA_DICT_INIT(items, 0); + items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_LINK_OUTPUT_NODE_ID, val[0]); + items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_LINK_OUTPUT_PORT_ID, val[1]); + items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_LINK_INPUT_NODE_ID, val[2]); + items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_LINK_INPUT_PORT_ID, val[3]); pw_core_proxy_create_object(c->core_proxy, "link-factory", - c->context.t->link, + t->link, PW_VERSION_LINK, - &props->dict, + &props, 0); - - pw_properties_free(props); - res = do_sync(c); exit: @@ -1709,14 +1964,71 @@ int jack_disconnect (jack_client_t *client, const char *source_port, const char *destination_port) { - pw_log_debug("client %p: connect %s %s", client, source_port, destination_port); + struct client *c = (struct client *) client; + struct object *src, *dst, *l, *found = NULL; + int res; - return 0; + pw_log_debug("client %p: disconnect %s %s", client, source_port, destination_port); + + pw_thread_loop_lock(c->context.loop); + + src = find_port(c, source_port); + dst = find_port(c, destination_port); + + pw_log_debug("client %p: %d %d", client, src->id, dst->id); + + if (src == NULL || dst == NULL || + !(src->port.flags & JackPortIsOutput) || + !(dst->port.flags & JackPortIsInput)) { + res = -ENOENT; + goto exit; + } + + spa_list_for_each(l, &c->context.links, link) { + pw_log_debug("%d %d", l->port_link.src, l->port_link.dst); + if (l->port_link.src == src->id && + l->port_link.dst == dst->id) { + found = l; + break; + } + } + if (found == NULL) { + res = -ENOENT; + goto exit; + } + + pw_core_proxy_destroy(c->core_proxy, l->id); + + res = do_sync(c); + + exit: + pw_thread_loop_unlock(c->context.loop); + + return res; } int jack_port_disconnect (jack_client_t *client, jack_port_t *port) { - return 0; + struct client *c = (struct client *) client; + struct object *o = (struct object *) port; + struct object *l; + int res; + + pw_log_debug("client %p: disconnect %p", client, port); + + pw_thread_loop_lock(c->context.loop); + + spa_list_for_each(l, &c->context.links, link) { + if (l->port_link.src == o->id || + l->port_link.dst == o->id) { + pw_core_proxy_destroy(c->core_proxy, l->id); + } + } + res = do_sync(c); + + pw_thread_loop_unlock(c->context.loop); + + return res; } int jack_port_name_size(void) @@ -1775,19 +2087,18 @@ const char ** jack_get_ports (jack_client_t *client, struct client *c = (struct client *) client; const char **res = malloc(sizeof(char*) * JACK_PORT_MAX); int count = 0; - struct port *p; + struct object *o; pw_thread_loop_lock(c->context.loop); - pw_array_for_each(p, &c->context.ports) { - if (!p->valid) - continue; - if (!SPA_FLAG_CHECK(p->flags, flags)) + spa_list_for_each(o, &c->context.ports, link) { + if (!SPA_FLAG_CHECK(o->port.flags, flags)) continue; - res[count++] = p->name; + res[count++] = o->port.name; } res[count] = NULL; + pw_thread_loop_unlock(c->context.loop); return res; @@ -1795,13 +2106,37 @@ const char ** jack_get_ports (jack_client_t *client, jack_port_t * jack_port_by_name (jack_client_t *client, const char *port_name) { - return NULL; + struct client *c = (struct client *) client; + struct object *res; + + pw_thread_loop_lock(c->context.loop); + + res = find_port(c, port_name); + + pw_thread_loop_unlock(c->context.loop); + + return (jack_port_t *)res; } jack_port_t * jack_port_by_id (jack_client_t *client, jack_port_id_t port_id) { - return NULL; + struct client *c = (struct client *) client; + struct object *res = NULL, *o; + + pw_thread_loop_lock(c->context.loop); + + o = pw_map_lookup(&c->context.globals, port_id); + + if (o == NULL || o->type != c->context.t->port) + goto exit; + + res = o; + + exit: + pw_thread_loop_unlock(c->context.loop); + + return (jack_port_t *)res; } jack_nframes_t jack_frames_since_cycle_start (const jack_client_t *client) @@ -1853,6 +2188,7 @@ void jack_set_info_function (void (*func)(const char *)) void jack_free(void* ptr) { + free(ptr); } static void reg(void) __attribute__ ((constructor)); From d73e77cd51628cf7c53b2ca9578a67f72f8dad3e Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 16 Feb 2018 12:08:16 +0100 Subject: [PATCH 004/133] pipewire-jack: add aliases Add port aliases Small cleanups --- src/pipewire-jack.c | 101 ++++++++++++++++++++++++++++++++------------ 1 file changed, 73 insertions(+), 28 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 8cea054fb..724f63094 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -96,6 +96,8 @@ struct object { struct { unsigned long flags; char name[REAL_JACK_PORT_NAME_SIZE]; + char alias1[REAL_JACK_PORT_NAME_SIZE]; + char alias2[REAL_JACK_PORT_NAME_SIZE]; uint32_t type_id; uint32_t port_id; } port; @@ -213,6 +215,8 @@ struct client { void *portregistration_arg; JackPortConnectCallback connect_callback; void *connect_arg; + JackGraphOrderCallback graph_callback; + void *graph_arg; uint32_t sample_rate; uint32_t buffer_size; @@ -324,13 +328,25 @@ static struct object *find_port(struct client *c, const char *name) struct object *o; spa_list_for_each(o, &c->context.ports, link) { - pw_log_debug("%s", o->port.name); if (!strcmp(o->port.name, name)) return o; } return NULL; } +static struct object *find_link(struct client *c, uint32_t src, uint32_t dst) +{ + struct object *l; + + spa_list_for_each(l, &c->context.links, link) { + if (l->port_link.src == src && + l->port_link.dst == dst) { + return l; + } + } + return NULL; +} + static struct buffer *dequeue_buffer(struct port *p) { struct buffer *b; @@ -987,8 +1003,6 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, if (props == NULL) return; - pw_log_debug("added: %u %u %u", id, type, parent_id); - if (type == t->node) { if ((str = spa_dict_lookup(props, "node.name")) == NULL) goto exit; @@ -997,6 +1011,7 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, spa_list_append(&c->context.nodes, &o->link); strncpy(o->node.name, str, sizeof(o->node.name)); + pw_log_debug("add node %d", id); } else if (type == t->port) { const struct spa_dict_item *item; @@ -1045,7 +1060,18 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, snprintf(o->port.name, sizeof(o->port.name), "%s:%s", ot->node.name, str); o->port.port_id = SPA_ID_INVALID; } + + if ((str = spa_dict_lookup(props, "port.alias1")) != NULL) + snprintf(o->port.alias1, sizeof(o->port.alias1), "%s", str); + else + o->port.alias1[0] = '\0'; + + if ((str = spa_dict_lookup(props, "port.alias2")) != NULL) + snprintf(o->port.alias2, sizeof(o->port.alias2), "%s", str); + else + o->port.alias2[0] = '\0'; o->port.flags = flags; + pw_log_debug("add port %d", id); } else if (type == t->link) { o = alloc_object(c); @@ -1117,6 +1143,7 @@ static void registry_event_global_remove(void *object, uint32_t id) if (c->connect_callback) c->connect_callback(o->port_link.src, o->port_link.dst, 0, c->connect_arg); } + pw_map_insert_at(&c->context.globals, id, NULL); free_object(c, o); } @@ -1509,6 +1536,9 @@ int jack_set_graph_order_callback (jack_client_t *client, JackGraphOrderCallback graph_callback, void *data) { + struct client *c = (struct client *) client; + c->graph_callback = graph_callback; + c->graph_arg = data; return 0; } @@ -1805,14 +1835,9 @@ int jack_port_connected_to (const jack_port_t *port, p = o; o = l; } + if (find_link(c, o->id, p->id)) + res = 1; - spa_list_for_each(l, &c->context.links, link) { - if (l->port_link.src == o->id && - l->port_link.dst == p->id) { - res = 1; - break; - } - } exit: pw_thread_loop_unlock(c->context.loop); @@ -1833,7 +1858,7 @@ const char ** jack_port_get_all_connections (const jack_client_t *client, struct client *c = (struct client *) client; struct object *o = (struct object *) port; struct object *p, *l; - const char **res = malloc(sizeof(char*) * CONNECTION_NUM_FOR_PORT); + const char **res = malloc(sizeof(char*) * (CONNECTION_NUM_FOR_PORT + 1)); int count = 0; pw_thread_loop_lock(c->context.loop); @@ -1850,10 +1875,16 @@ const char ** jack_port_get_all_connections (const jack_client_t *client, continue; res[count++] = p->port.name; + if (count == CONNECTION_NUM_FOR_PORT) + break; } pw_thread_loop_unlock(c->context.loop); - res[count] = NULL; + if (count == 0) { + free(res); + res = NULL; + } else + res[count] = NULL; return res; } @@ -1885,7 +1916,23 @@ int jack_port_unset_alias (jack_port_t *port, const char *alias) int jack_port_get_aliases (const jack_port_t *port, char* const aliases[2]) { - return 0; + struct object *o = (struct object *) port; + struct client *c = o->client; + int res = 0; + + pw_thread_loop_lock(c->context.loop); + + if (o->port.alias1[0] != '\0') { + snprintf(aliases[0], REAL_JACK_PORT_NAME_SIZE, "%s", o->port.alias1); + res++; + } + if (o->port.alias2[0] != '\0') { + snprintf(aliases[1], REAL_JACK_PORT_NAME_SIZE, "%s", o->port.alias2); + res++; + } + pw_thread_loop_unlock(c->context.loop); + + return res; } int jack_port_request_monitor (jack_port_t *port, int onoff) @@ -1931,7 +1978,7 @@ int jack_connect (jack_client_t *client, if (src == NULL || dst == NULL || !(src->port.flags & JackPortIsOutput) || !(dst->port.flags & JackPortIsInput)) { - res = -ENOENT; + res = -EINVAL; goto exit; } @@ -1965,7 +2012,7 @@ int jack_disconnect (jack_client_t *client, const char *destination_port) { struct client *c = (struct client *) client; - struct object *src, *dst, *l, *found = NULL; + struct object *src, *dst, *l; int res; pw_log_debug("client %p: disconnect %s %s", client, source_port, destination_port); @@ -1980,19 +2027,11 @@ int jack_disconnect (jack_client_t *client, if (src == NULL || dst == NULL || !(src->port.flags & JackPortIsOutput) || !(dst->port.flags & JackPortIsInput)) { - res = -ENOENT; + res = -EINVAL; goto exit; } - spa_list_for_each(l, &c->context.links, link) { - pw_log_debug("%d %d", l->port_link.src, l->port_link.dst); - if (l->port_link.src == src->id && - l->port_link.dst == dst->id) { - found = l; - break; - } - } - if (found == NULL) { + if ((l = find_link(c, src->id, dst->id)) == NULL) { res = -ENOENT; goto exit; } @@ -2085,7 +2124,7 @@ const char ** jack_get_ports (jack_client_t *client, unsigned long flags) { struct client *c = (struct client *) client; - const char **res = malloc(sizeof(char*) * JACK_PORT_MAX); + const char **res = malloc(sizeof(char*) * (JACK_PORT_MAX + 1)); int count = 0; struct object *o; @@ -2096,11 +2135,17 @@ const char ** jack_get_ports (jack_client_t *client, continue; res[count++] = o->port.name; + if (count == JACK_PORT_MAX) + break; } - res[count] = NULL; - pw_thread_loop_unlock(c->context.loop); + if (count == 0) { + free(res); + res = NULL; + } else + res[count] = NULL; + return res; } From 570d32de2b21e73be063ac41ba60bcb2672d62a1 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 16 Feb 2018 16:53:18 +0100 Subject: [PATCH 005/133] pipewire-jack: fix loop index --- src/pipewire-jack.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 724f63094..276ae4cd7 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -600,7 +600,6 @@ static void handle_rtnode_message(struct client *c, struct pw_client_node_messag if (!p->valid) continue; - pw_log_trace("%d %d", i, p->id); output = &c->trans->outputs[p->id]; if (output->buffer_id == SPA_ID_INVALID) continue; @@ -809,7 +808,7 @@ static void client_node_port_set_param(void *object, static void clear_buffers(struct client *c, struct port *p) { struct buffer *b; - int i; + int i, j; pw_log_debug(NAME" %p: port %p clear buffers", c, p); @@ -820,17 +819,18 @@ static void clear_buffers(struct client *c, struct port *p) if (munmap(b->ptr, b->map.size) < 0) pw_log_warn("failed to unmap: %m"); } - for (i = 0; i < b->n_datas; i++) { - struct spa_data *d = &b->datas[i]; + for (j = 0; j < b->n_datas; j++) { + struct spa_data *d = &b->datas[j]; if (d->fd != -1 && d->data) { if (munmap(SPA_MEMBER(d->data, -d->mapoffset, void), d->maxsize + d->mapoffset) < 0) pw_log_warn("failed to unmap: %m"); } + d->fd = -1; } - for (i = 0; i < b->n_mem; i++) { - if (--b->mem[i]->ref == 0) - clear_mem(c, b->mem[i]); + for (j = 0; j < b->n_mem; j++) { + if (--b->mem[j]->ref == 0) + clear_mem(c, b->mem[j]); } b->n_mem = 0; b->ptr = NULL; From b15da50c13148436fa758e2cfcadcf50ddb7a6bb Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 16 Feb 2018 17:10:25 +0100 Subject: [PATCH 006/133] pipewire-jack: return empty buffer when unnegotiated --- src/pipewire-jack.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 276ae4cd7..ca4c97f24 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -1725,17 +1725,18 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) return NULL; } p = GET_PORT(c, GET_DIRECTION(o->port.flags), o->port.port_id); + if (p->n_buffers == 0) + return c->empty; if (p->direction == SPA_DIRECTION_INPUT) { io = &c->trans->inputs[p->id]; if (io->status != SPA_STATUS_HAVE_BUFFER) - return NULL; + return c->empty; b = &p->buffers[io->buffer_id]; io->status = SPA_STATUS_NEED_BUFFER; } else { - b = dequeue_buffer(p); - if (b == NULL) { + if ((b = dequeue_buffer(p)) == NULL) { pw_log_warn("port %p: out of buffers", p); return c->empty; } From c0e5dbafc90342bc2cce4cbfe60a699292fac00f Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 16 Feb 2018 17:57:26 +0100 Subject: [PATCH 007/133] pipewire-jack: clear buffers --- src/pipewire-jack.c | 74 ++++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 35 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index ca4c97f24..84772f1eb 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -758,6 +758,40 @@ static void client_node_remove_port(void *object, pw_client_node_proxy_done(c->node_proxy, seq, -ENOTSUP); } +static void clear_buffers(struct client *c, struct port *p) +{ + struct buffer *b; + int i, j; + + pw_log_debug(NAME" %p: port %p clear buffers", c, p); + + for (i = 0; i < p->n_buffers; i++) { + b = &p->buffers[i]; + + if (b->ptr != NULL) { + if (munmap(b->ptr, b->map.size) < 0) + pw_log_warn("failed to unmap: %m"); + } + for (j = 0; j < b->n_datas; j++) { + struct spa_data *d = &b->datas[j]; + if (d->fd != -1 && d->data) { + if (munmap(SPA_MEMBER(d->data, -d->mapoffset, void), + d->maxsize + d->mapoffset) < 0) + pw_log_warn("failed to unmap: %m"); + } + d->fd = -1; + } + for (j = 0; j < b->n_mem; j++) { + if (--b->mem[j]->ref == 0) + clear_mem(c, b->mem[j]); + } + b->n_mem = 0; + b->ptr = NULL; + } + p->n_buffers = 0; + spa_list_init(&p->queue); +} + static void client_node_port_set_param(void *object, uint32_t seq, enum spa_direction direction, @@ -766,11 +800,15 @@ static void client_node_port_set_param(void *object, const struct spa_pod *param) { struct client *c = (struct client *) object; + struct port *p = GET_PORT(c, direction, port_id); struct pw_type *t = c->context.t; struct spa_pod *params[4]; uint8_t buffer[1024]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); + if (id == t->param.idFormat && param == NULL) + clear_buffers(c, p); + params[0] = spa_pod_builder_object(&b, t->param.idEnumFormat, t->spa_format, "I", c->type.media_type.audio, @@ -805,40 +843,6 @@ static void client_node_port_set_param(void *object, pw_client_node_proxy_done(c->node_proxy, seq, 0); } -static void clear_buffers(struct client *c, struct port *p) -{ - struct buffer *b; - int i, j; - - pw_log_debug(NAME" %p: port %p clear buffers", c, p); - - for (i = 0; i < p->n_buffers; i++) { - b = &p->buffers[i]; - - if (b->ptr != NULL) { - if (munmap(b->ptr, b->map.size) < 0) - pw_log_warn("failed to unmap: %m"); - } - for (j = 0; j < b->n_datas; j++) { - struct spa_data *d = &b->datas[j]; - if (d->fd != -1 && d->data) { - if (munmap(SPA_MEMBER(d->data, -d->mapoffset, void), - d->maxsize + d->mapoffset) < 0) - pw_log_warn("failed to unmap: %m"); - } - d->fd = -1; - } - for (j = 0; j < b->n_mem; j++) { - if (--b->mem[j]->ref == 0) - clear_mem(c, b->mem[j]); - } - b->n_mem = 0; - b->ptr = NULL; - } - p->n_buffers = 0; - spa_list_init(&p->queue); -} - static void client_node_port_use_buffers(void *object, uint32_t seq, enum spa_direction direction, @@ -1085,7 +1089,7 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, goto exit_free; o->port_link.dst = pw_properties_parse_int(str); - pw_log_debug("add link %d %d", o->port_link.src, o->port_link.dst); + pw_log_debug("add link %d %d->%d", o->id, o->port_link.src, o->port_link.dst); } else goto exit; From 5395a8dbf5a5135671c18fc1d9016caeb3511ad3 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 20 Feb 2018 13:06:07 +0100 Subject: [PATCH 008/133] pipewire-jack: small cleanups --- src/pipewire-jack.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 84772f1eb..9954faad3 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -46,7 +46,7 @@ #define MAX_OBJECTS 8192 #define MAX_PORTS (PORT_NUM_FOR_CLIENT/2) -#define MAX_BUFFERS 64 +#define MAX_BUFFERS 2 #define MAX_BUFFER_DATAS 4 #define MAX_BUFFER_MEMS 4 @@ -450,6 +450,7 @@ static struct mem *find_mem(struct pw_array *mems, uint32_t id) return NULL; } +#if 0 static void *mem_map(struct client *c, struct mem *m, uint32_t offset, uint32_t size) { if (m->ptr == NULL) { @@ -466,6 +467,7 @@ static void *mem_map(struct client *c, struct mem *m, uint32_t offset, uint32_t } return SPA_MEMBER(m->ptr, m->map.start, void); } +#endif static void mem_unmap(struct client *c, struct mem *m) { @@ -806,8 +808,10 @@ static void client_node_port_set_param(void *object, uint8_t buffer[1024]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); - if (id == t->param.idFormat && param == NULL) + if (id == t->param.idFormat && param == NULL) { + pw_log_debug(NAME" %p: port %p clear format", c, p); clear_buffers(c, p); + } params[0] = spa_pod_builder_object(&b, t->param.idEnumFormat, t->spa_format, @@ -815,7 +819,8 @@ static void client_node_port_set_param(void *object, "I", c->type.media_subtype.raw, ":", c->type.format_audio.format, "I", c->type.audio_format.F32, ":", c->type.format_audio.channels, "i", 1, - ":", c->type.format_audio.rate, "iru", c->sample_rate, 2, 1, INT32_MAX); + ":", c->type.format_audio.rate, "iru", c->sample_rate, + SPA_POD_PROP_MIN_MAX(1, INT32_MAX)); params[1] = spa_pod_builder_object(&b, t->param.idFormat, t->spa_format, @@ -827,9 +832,11 @@ static void client_node_port_set_param(void *object, params[2] = spa_pod_builder_object(&b, t->param.idBuffers, t->param_buffers.Buffers, - ":", t->param_buffers.size, "isu", 1024, 3, 4, INT32_MAX, 4, + ":", t->param_buffers.size, "isu", 1024, + SPA_POD_PROP_STEP(4, INT32_MAX, 4), ":", t->param_buffers.stride, "i", 4, - ":", t->param_buffers.buffers, "iru", 1, 2, 1, 2, + ":", t->param_buffers.buffers, "iru", 2, + SPA_POD_PROP_MIN_MAX(1, MAX_BUFFERS), ":", t->param_buffers.align, "i", 16); pw_client_node_proxy_port_update(c->node_proxy, @@ -1654,13 +1661,16 @@ jack_port_t * jack_port_register (jack_client_t *client, "I", c->type.media_subtype.raw, ":", c->type.format_audio.format, "I", c->type.audio_format.F32, ":", c->type.format_audio.channels, "i", 1, - ":", c->type.format_audio.rate, "iru", 44100, 2, 1, INT32_MAX); + ":", c->type.format_audio.rate, "iru", 44100, + SPA_POD_PROP_MIN_MAX(1, INT32_MAX)); params[1] = spa_pod_builder_object(&b, t->param.idBuffers, t->param_buffers.Buffers, - ":", t->param_buffers.size, "isu", 1024, 3, 4, INT32_MAX, 4, + ":", t->param_buffers.size, "isu", 1024, + SPA_POD_PROP_STEP(4, INT32_MAX, 4), ":", t->param_buffers.stride, "i", 4, - ":", t->param_buffers.buffers, "iru", 1, 2, 1, 2, + ":", t->param_buffers.buffers, "iru", 1, + SPA_POD_PROP_MIN_MAX(1, MAX_BUFFERS), ":", t->param_buffers.align, "i", 16); pw_thread_loop_lock(c->context.loop); From 895ec3865d2be9a7f61f8b433a9435ccfe12311f Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 20 Feb 2018 15:50:55 +0100 Subject: [PATCH 009/133] pipewire-jack: improvements Set the right format params based on the port type Set the right for type for the port based on the port.dsp property Don't remove globals from the id map, jack clients tend to want to use it after they are removed. --- src/pipewire-jack.c | 163 +++++++++++++++++++++++++++++--------------- 1 file changed, 108 insertions(+), 55 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 9954faad3..87fcd3c15 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -63,6 +63,7 @@ struct type { struct spa_type_media_subtype media_subtype; struct spa_type_format_audio format_audio; struct spa_type_audio_format audio_format; + struct spa_type_media_subtype_audio media_subtype_audio; }; static inline void init_type(struct type *type, struct spa_type_map *map) @@ -72,6 +73,7 @@ static inline void init_type(struct type *type, struct spa_type_map *map) 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); + spa_type_media_subtype_audio_map(map, &type->media_subtype_audio); } #define OBJECT_CHUNK 8 @@ -794,6 +796,78 @@ static void clear_buffers(struct client *c, struct port *p) spa_list_init(&p->queue); } +static int param_enum_format(struct client *c, struct port *p, + struct spa_pod **param, struct spa_pod_builder *b) +{ + struct pw_type *t = c->context.t; + + switch (p->object->port.type_id) { + case 0: + *param = spa_pod_builder_object(b, + t->param.idEnumFormat, t->spa_format, + "I", c->type.media_type.audio, + "I", c->type.media_subtype.raw, + ":", c->type.format_audio.format, "I", c->type.audio_format.F32, + ":", c->type.format_audio.channels, "i", 1, + ":", c->type.format_audio.rate, "iru", 44100, + SPA_POD_PROP_MIN_MAX(1, INT32_MAX)); + break; + case 1: + *param = spa_pod_builder_object(b, + t->param.idEnumFormat, t->spa_format, + "I", c->type.media_type.audio, + "I", c->type.media_subtype_audio.midi); + break; + default: + return -EINVAL; + } + return 1; +} + +static int param_format(struct client *c, struct port *p, + struct spa_pod **param, struct spa_pod_builder *b) +{ + struct pw_type *t = c->context.t; + + switch (p->object->port.type_id) { + case 0: + *param = spa_pod_builder_object(b, + t->param.idFormat, t->spa_format, + "I", c->type.media_type.audio, + "I", c->type.media_subtype.raw, + ":", c->type.format_audio.format, "I", c->type.audio_format.F32, + ":", c->type.format_audio.channels, "i", 1, + ":", c->type.format_audio.rate, "i", c->sample_rate); + break; + case 1: + *param = spa_pod_builder_object(b, + t->param.idFormat, t->spa_format, + "I", c->type.media_type.audio, + "I", c->type.media_subtype_audio.midi); + break; + default: + return -EINVAL; + } + return 1; +} + +static int param_buffers(struct client *c, struct port *p, + struct spa_pod **param, struct spa_pod_builder *b) +{ + struct pw_type *t = c->context.t; + + *param = spa_pod_builder_object(b, + t->param.idBuffers, t->param_buffers.Buffers, + ":", t->param_buffers.size, "isu", 1024, + SPA_POD_PROP_STEP(4, INT32_MAX, 4), + ":", t->param_buffers.stride, "i", 4, + ":", t->param_buffers.buffers, "iru", 1, + SPA_POD_PROP_MIN_MAX(1, MAX_BUFFERS), + ":", t->param_buffers.align, "i", 16); + + return 1; +} + static void client_node_port_set_param(void *object, uint32_t seq, enum spa_direction direction, @@ -805,7 +879,7 @@ static void client_node_port_set_param(void *object, struct port *p = GET_PORT(c, direction, port_id); struct pw_type *t = c->context.t; struct spa_pod *params[4]; - uint8_t buffer[1024]; + uint8_t buffer[4096]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); if (id == t->param.idFormat && param == NULL) { @@ -813,31 +887,9 @@ static void client_node_port_set_param(void *object, clear_buffers(c, p); } - params[0] = spa_pod_builder_object(&b, - t->param.idEnumFormat, t->spa_format, - "I", c->type.media_type.audio, - "I", c->type.media_subtype.raw, - ":", c->type.format_audio.format, "I", c->type.audio_format.F32, - ":", c->type.format_audio.channels, "i", 1, - ":", c->type.format_audio.rate, "iru", c->sample_rate, - SPA_POD_PROP_MIN_MAX(1, INT32_MAX)); - - params[1] = spa_pod_builder_object(&b, - t->param.idFormat, t->spa_format, - "I", c->type.media_type.audio, - "I", c->type.media_subtype.raw, - ":", c->type.format_audio.format, "I", c->type.audio_format.F32, - ":", c->type.format_audio.channels, "i", 1, - ":", c->type.format_audio.rate, "i", c->sample_rate); - - params[2] = spa_pod_builder_object(&b, - t->param.idBuffers, t->param_buffers.Buffers, - ":", t->param_buffers.size, "isu", 1024, - SPA_POD_PROP_STEP(4, INT32_MAX, 4), - ":", t->param_buffers.stride, "i", 4, - ":", t->param_buffers.buffers, "iru", 2, - SPA_POD_PROP_MIN_MAX(1, MAX_BUFFERS), - ":", t->param_buffers.align, "i", 16); + param_enum_format(c, p, ¶ms[0], &b); + param_format(c, p, ¶ms[1], &b); + param_buffers(c, p, ¶ms[2], &b); pw_client_node_proxy_port_update(c->node_proxy, direction, @@ -1001,6 +1053,16 @@ static const struct pw_client_node_proxy_events client_node_events = { .port_set_io = client_node_port_set_io, }; +static jack_port_type_id_t string_to_type(const char *port_type) +{ + if (!strcmp(JACK_DEFAULT_AUDIO_TYPE, port_type)) + return 0; + else if (!strcmp(JACK_DEFAULT_MIDI_TYPE, port_type)) + return 1; + else + return SPA_ID_INVALID; +} + 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) @@ -1027,10 +1089,13 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, else if (type == t->port) { const struct spa_dict_item *item; unsigned long flags = 0; + jack_port_type_id_t type_id; char full_name[1024]; - if ((str = spa_dict_lookup(props, "port.dsp")) == NULL || - !pw_properties_parse_bool(str)) + if ((str = spa_dict_lookup(props, "port.dsp")) == NULL) + goto exit; + + if ((type_id = string_to_type(str)) == SPA_ID_INVALID) goto exit; if ((str = spa_dict_lookup(props, "port.name")) == NULL) @@ -1081,7 +1146,10 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, snprintf(o->port.alias2, sizeof(o->port.alias2), "%s", str); else o->port.alias2[0] = '\0'; + o->port.flags = flags; + o->port.type_id = type_id; + pw_log_debug("add port %d", id); } else if (type == t->link) { @@ -1096,7 +1164,7 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, goto exit_free; o->port_link.dst = pw_properties_parse_int(str); - pw_log_debug("add link %d %d->%d", o->id, o->port_link.src, o->port_link.dst); + pw_log_debug("add link %d %d->%d", id, o->port_link.src, o->port_link.dst); } else goto exit; @@ -1155,7 +1223,11 @@ static void registry_event_global_remove(void *object, uint32_t id) c->connect_callback(o->port_link.src, o->port_link.dst, 0, c->connect_arg); } - pw_map_insert_at(&c->context.globals, id, NULL); + /* JACK clients expect the objects to hang around after + * they are unregistered. We keep them in the map but reuse the + * object when we can + * pw_map_insert_at(&c->context.globals, id, NULL); + **/ free_object(c, o); } @@ -1613,7 +1685,6 @@ jack_port_t * jack_port_register (jack_client_t *client, jack_port_type_id_t type_id; uint8_t buffer[1024]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); - struct pw_type *t = c->context.t; struct spa_pod *params[4]; struct port *p; int res; @@ -1628,11 +1699,7 @@ jack_port_t * jack_port_register (jack_client_t *client, else return NULL; - if (!strcmp(JACK_DEFAULT_AUDIO_TYPE, port_type)) - type_id = 0; - else if (!strcmp(JACK_DEFAULT_MIDI_TYPE, port_type)) - type_id = 1; - else + if ((type_id = string_to_type(port_type)) == SPA_ID_INVALID) return NULL; if ((p = alloc_port(c, direction)) == NULL) @@ -1651,29 +1718,14 @@ jack_port_t * jack_port_register (jack_client_t *client, port_info.props = &dict; dict = SPA_DICT_INIT(items, 0); - items[dict.n_items++] = SPA_DICT_ITEM_INIT("port.dsp", "1"); + items[dict.n_items++] = SPA_DICT_ITEM_INIT("port.dsp", port_type); items[dict.n_items++] = SPA_DICT_ITEM_INIT("port.name", port_name); - items[dict.n_items++] = SPA_DICT_ITEM_INIT("port.type", port_type); - params[0] = spa_pod_builder_object(&b, - t->param.idEnumFormat, t->spa_format, - "I", c->type.media_type.audio, - "I", c->type.media_subtype.raw, - ":", c->type.format_audio.format, "I", c->type.audio_format.F32, - ":", c->type.format_audio.channels, "i", 1, - ":", c->type.format_audio.rate, "iru", 44100, - SPA_POD_PROP_MIN_MAX(1, INT32_MAX)); - - params[1] = spa_pod_builder_object(&b, - t->param.idBuffers, t->param_buffers.Buffers, - ":", t->param_buffers.size, "isu", 1024, - SPA_POD_PROP_STEP(4, INT32_MAX, 4), - ":", t->param_buffers.stride, "i", 4, - ":", t->param_buffers.buffers, "iru", 1, - SPA_POD_PROP_MIN_MAX(1, MAX_BUFFERS), - ":", t->param_buffers.align, "i", 16); + param_enum_format(c, p, ¶ms[0], &b); + param_buffers(c, p, ¶ms[1], &b); pw_thread_loop_lock(c->context.loop); + pw_client_node_proxy_port_update(c->node_proxy, direction, p->id, @@ -2187,6 +2239,7 @@ jack_port_t * jack_port_by_id (jack_client_t *client, pw_thread_loop_lock(c->context.loop); o = pw_map_lookup(&c->context.globals, port_id); + pw_log_debug("client %p: port %d -> %p", c, port_id, o); if (o == NULL || o->type != c->context.t->port) goto exit; From a7954ea908b701f1e4e7a1da8b1f8e43f3c8eac6 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 26 Feb 2018 17:31:22 +0100 Subject: [PATCH 010/133] pipewire-jack: improve processing --- src/pipewire-jack.c | 221 +++++++++++++++++++++++++++++++++----------- 1 file changed, 168 insertions(+), 53 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 87fcd3c15..fe07267e4 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -47,7 +47,7 @@ #define MAX_OBJECTS 8192 #define MAX_PORTS (PORT_NUM_FOR_CLIENT/2) #define MAX_BUFFERS 2 -#define MAX_BUFFER_DATAS 4 +#define MAX_BUFFER_DATAS 4u #define MAX_BUFFER_MEMS 4 @@ -143,6 +143,7 @@ struct port { struct buffer buffers[MAX_BUFFERS]; uint32_t n_buffers; + struct spa_io_buffers *io; struct spa_list queue; }; @@ -233,6 +234,9 @@ struct client { struct pw_array mems; float empty[BUFFER_SIZE_MAX + 8]; + + bool started; + int status; }; static struct object * alloc_object(struct client *c) @@ -263,7 +267,7 @@ static void free_object(struct client *c, struct object *o) static struct port * alloc_port(struct client *c, enum spa_direction direction) { - int i; + uint32_t i; struct port *p; struct object *o; @@ -393,8 +397,10 @@ static void on_state_changed(void *data, enum pw_remote_state old, case PW_REMOTE_STATE_UNCONNECTED: if (client->shutdown_callback) client->shutdown_callback(client->shutdown_arg); + /* fallthrough*/ case PW_REMOTE_STATE_ERROR: client->error = true; + /* fallthrough*/ case PW_REMOTE_STATE_CONNECTED: pw_thread_loop_signal(client->context.loop, false); break; @@ -452,7 +458,6 @@ static struct mem *find_mem(struct pw_array *mems, uint32_t id) return NULL; } -#if 0 static void *mem_map(struct client *c, struct mem *m, uint32_t offset, uint32_t size) { if (m->ptr == NULL) { @@ -469,7 +474,6 @@ static void *mem_map(struct client *c, struct mem *m, uint32_t offset, uint32_t } return SPA_MEMBER(m->ptr, m->map.start, void); } -#endif static void mem_unmap(struct client *c, struct mem *m) { @@ -558,7 +562,7 @@ static void reuse_buffer(struct client *c, struct port *p, uint32_t id) struct buffer *b = &p->buffers[id]; if (SPA_FLAG_CHECK(b->flags, BUFFER_FLAG_OUT)) { - pw_log_trace(NAME" %p: recycle buffer %d", c, id); + pw_log_trace(NAME" %p: port %p: recycle buffer %d", c, p, id); spa_list_append(&p->queue, &b->link); SPA_FLAG_UNSET(b->flags, BUFFER_FLAG_OUT); } @@ -567,6 +571,7 @@ static void reuse_buffer(struct client *c, struct port *p, uint32_t id) static inline void send_need_input(struct client *c) { uint64_t cmd = 1; + pw_log_trace("send need input"); pw_client_node_transport_add_message(c->trans, &PW_CLIENT_NODE_MESSAGE_INIT(PW_CLIENT_NODE_MESSAGE_NEED_INPUT)); write(c->writefd, &cmd, 8); @@ -575,11 +580,63 @@ static inline void send_need_input(struct client *c) static inline void send_have_output(struct client *c) { uint64_t cmd = 1; + pw_log_trace("send have output"); pw_client_node_transport_add_message(c->trans, &PW_CLIENT_NODE_MESSAGE_INIT(PW_CLIENT_NODE_MESSAGE_HAVE_OUTPUT)); write(c->writefd, &cmd, 8); } +static int do_process_input(struct client *c) +{ + if (c->status == SPA_STATUS_HAVE_BUFFER) + return SPA_STATUS_HAVE_BUFFER; + + if (c->process_callback) + c->process_callback(c->buffer_size, c->process_arg); + + return c->status = SPA_STATUS_HAVE_BUFFER; +} + +static int do_process_output(struct client *c) +{ + uint32_t i; + + for (i = 0; i < c->last_out_port; i++) { + struct port *p = GET_OUT_PORT(c, i); + struct spa_io_buffers *io = p->io; + + pw_log_trace("port %p: %d %d %d", p, p->valid, io->status, io->buffer_id); + + if (!p->valid || io == NULL) + continue; + + if (io->status != SPA_STATUS_NEED_BUFFER) + continue; + + if (io->buffer_id < p->n_buffers) + reuse_buffer(c, p, io->buffer_id); + + io->status = SPA_STATUS_NEED_BUFFER; + io->buffer_id = SPA_ID_INVALID; + } + + c->status = SPA_STATUS_HAVE_BUFFER; + for (i = 0; i < c->last_in_port; i++) { + struct port *p = GET_IN_PORT(c, i); + struct spa_io_buffers *io = p->io; + + if (!p->valid || io == NULL) + continue; + + c->status = SPA_STATUS_NEED_BUFFER; + } + if (c->status == SPA_STATUS_HAVE_BUFFER) { + if (c->process_callback) + c->process_callback(c->buffer_size, c->process_arg); + } + return c->status; +} + static void handle_rtnode_message(struct client *c, struct pw_client_node_message *message) { pw_log_trace("node message %d", PW_CLIENT_NODE_MESSAGE_TYPE(message)); @@ -587,36 +644,29 @@ static void handle_rtnode_message(struct client *c, struct pw_client_node_messag switch (PW_CLIENT_NODE_MESSAGE_TYPE(message)) { case PW_CLIENT_NODE_MESSAGE_PROCESS_INPUT: { - if (c->process_callback) - c->process_callback(c->buffer_size, c->process_arg); - - send_have_output(c); + switch (do_process_input(c)) { + case SPA_STATUS_HAVE_BUFFER: + send_have_output(c); + break; + case SPA_STATUS_NEED_BUFFER: + send_need_input(c); + break; + default: + break; + } break; } case PW_CLIENT_NODE_MESSAGE_PROCESS_OUTPUT: { - int i; - - for (i = 0; i < c->last_out_port; i++) { - struct port *p = &c->out_ports[i]; - struct spa_io_buffers *output; - - if (!p->valid) - continue; - - output = &c->trans->outputs[p->id]; - if (output->buffer_id == SPA_ID_INVALID) - continue; - - reuse_buffer(c, p, output->buffer_id); - output->buffer_id = SPA_ID_INVALID; - } - if (c->n_in_ports > 0) { - send_need_input(c); - } else { - if (c->process_callback) - c->process_callback(c->buffer_size, c->process_arg); + switch (do_process_output(c)) { + case SPA_STATUS_HAVE_BUFFER: send_have_output(c); + break; + case SPA_STATUS_NEED_BUFFER: + send_need_input(c); + break; + default: + break; } break; } @@ -626,13 +676,12 @@ static void handle_rtnode_message(struct client *c, struct pw_client_node_messag (struct pw_client_node_message_port_reuse_buffer *) message; struct port *p; uint32_t port_id = rb->body.port_id.value; + uint32_t buffer_id = rb->body.buffer_id.value; - p = &c->out_ports[port_id]; + p = GET_OUT_PORT(c, port_id); + if (buffer_id < p->n_buffers) + reuse_buffer(c, p, buffer_id); - if (!p->valid) - return; - - reuse_buffer(c, p, rb->body.buffer_id.value); break; } default: @@ -693,6 +742,7 @@ static void client_node_transport(void *object, { struct client *c = (struct client *) object; struct pw_core *core = c->context.core; + uint32_t i; clean_transport(c); @@ -702,6 +752,15 @@ static void client_node_transport(void *object, pw_log_debug("client %p: create client transport %p with fds %d %d for node %u", c, c->trans, readfd, writefd, node_id); + for (i = 0; i < c->trans->area->max_input_ports; i++) { + struct port *p = GET_IN_PORT(c, i); + p->io = &c->trans->inputs[i]; + } + for (i = 0; i < c->trans->area->max_output_ports; i++) { + struct port *p = GET_OUT_PORT(c, i); + p->io = &c->trans->outputs[i]; + } + c->writefd = writefd; c->socket_source = pw_loop_add_io(core->data_loop, readfd, @@ -727,17 +786,33 @@ static void client_node_command(void *object, uint32_t seq, const struct spa_com struct pw_type *t = c->context.t; if (SPA_COMMAND_TYPE(command) == t->command_node.Pause) { - pw_client_node_proxy_done(c->node_proxy, seq, 0); + if (c->started) { + pw_loop_update_io(c->context.core->data_loop, + c->socket_source, SPA_IO_ERR | SPA_IO_HUP); - pw_loop_update_io(c->context.core->data_loop, - c->socket_source, SPA_IO_ERR | SPA_IO_HUP); + c->started = false; + } + pw_client_node_proxy_done(c->node_proxy, seq, 0); } else if (SPA_COMMAND_TYPE(command) == t->command_node.Start) { - pw_client_node_proxy_done(c->node_proxy, seq, 0); + if (!c->started) { + pw_loop_update_io(c->context.core->data_loop, + c->socket_source, + SPA_IO_IN | SPA_IO_ERR | SPA_IO_HUP); - pw_loop_update_io(c->context.core->data_loop, - c->socket_source, - SPA_IO_IN | SPA_IO_ERR | SPA_IO_HUP); + switch (do_process_output(c)) { + case SPA_STATUS_HAVE_BUFFER: + send_have_output(c); + break; + case SPA_STATUS_NEED_BUFFER: + send_need_input(c); + break; + default: + break; + } + c->started = true; + } + pw_client_node_proxy_done(c->node_proxy, seq, 0); } else { pw_log_warn("unhandled node command %d", SPA_COMMAND_TYPE(command)); pw_client_node_proxy_done(c->node_proxy, seq, -ENOTSUP); @@ -765,7 +840,7 @@ static void client_node_remove_port(void *object, static void clear_buffers(struct client *c, struct port *p) { struct buffer *b; - int i, j; + uint32_t i, j; pw_log_debug(NAME" %p: port %p clear buffers", c, p); @@ -1035,7 +1110,39 @@ static void client_node_port_set_io(void *object, uint32_t size) { struct client *c = (struct client *) object; - pw_client_node_proxy_done(c->node_proxy, seq, -ENOTSUP); + struct port *p = GET_PORT(c, direction, port_id); + struct pw_type *t = c->context.t; + struct mem *m; + void *ptr; + int res; + + if (mem_id == SPA_ID_INVALID) { + ptr = NULL; + size = 0; + } + else { + m = find_mem(&c->mems, mem_id); + if (m == NULL) { + pw_log_warn("unknown memory id %u", mem_id); + res = -EINVAL; + goto exit; + } + if ((ptr = mem_map(c, m, offset, size)) == NULL) { + res = -errno; + goto exit; + } + } + + + if (id == t->io.Buffers) { + p->io = ptr; + } + pw_log_debug("port %p: set io id %u %p %d %d", p, id, ptr, p->io->status, p->io->buffer_id); + + res = 0; + + exit: + pw_client_node_proxy_done(c->node_proxy, seq, res); } static const struct pw_client_node_proxy_events client_node_events = { @@ -1783,34 +1890,42 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) struct port *p; struct buffer *b; struct spa_io_buffers *io; - - pw_log_trace("port %p: get buffer", port); + int status; if (o->type != c->context.t->port || o->port.port_id == SPA_ID_INVALID) { pw_log_error("client %p: invalid port %p", c, port); return NULL; } p = GET_PORT(c, GET_DIRECTION(o->port.flags), o->port.port_id); + if (p->n_buffers == 0) return c->empty; + io = p->io; + status = io->status; + pw_log_trace("port %p: get buffer %d %d", p, status, io->buffer_id); + if (p->direction == SPA_DIRECTION_INPUT) { - io = &c->trans->inputs[p->id]; - if (io->status != SPA_STATUS_HAVE_BUFFER) + io->status = SPA_STATUS_NEED_BUFFER; + if (io->buffer_id >= p->n_buffers) return c->empty; b = &p->buffers[io->buffer_id]; - io->status = SPA_STATUS_NEED_BUFFER; } else { + io->status = SPA_STATUS_HAVE_BUFFER; if ((b = dequeue_buffer(p)) == NULL) { pw_log_warn("port %p: out of buffers", p); - return c->empty; + goto empty_out; } - io = &c->trans->outputs[p->id]; - io->status = SPA_STATUS_HAVE_BUFFER; - io->buffer_id = b->id; + reuse_buffer(c, p, b->id); } + io->buffer_id = b->id; + pw_log_trace("port %p: get buffer %d %d", p, io->status, io->buffer_id); return b->datas[0].data; + + empty_out: + io->buffer_id = SPA_ID_INVALID; + return c->empty; } jack_uuid_t jack_port_uuid (const jack_port_t *port) From c6c3b73ee31b9b5296f45d6ccd238e92c1785f13 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 21 May 2018 13:12:26 +0200 Subject: [PATCH 011/133] jack: many improvements Implement more jack api Implement mix ports Improve memory mapping Adapt to the pipewire work branch Implement dynamic buffersize and samplerate --- src/pipewire-jack.c | 973 ++++++++++++++++++++++++++++---------------- 1 file changed, 626 insertions(+), 347 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index fe07267e4..735b6a13e 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -26,6 +26,8 @@ #include #include +#include +#include #include #include @@ -39,23 +41,27 @@ #define JACK_PORT_NAME_SIZE 256 #define JACK_PORT_MAX 4096 #define JACK_PORT_TYPE_SIZE 32 -#define PORT_NUM_FOR_CLIENT 1024 #define CONNECTION_NUM_FOR_PORT 2048 #define BUFFER_SIZE_MAX 8192 #define MAX_OBJECTS 8192 -#define MAX_PORTS (PORT_NUM_FOR_CLIENT/2) +#define MAX_PORTS 1024 #define MAX_BUFFERS 2 #define MAX_BUFFER_DATAS 4u #define MAX_BUFFER_MEMS 4 +#define MAX_MIX 4096 +#define DEFAULT_SAMPLE_RATE 44100 +#define DEFAULT_BUFFER_SIZE 1024 +#define MAX_BUFFER_SIZE 2048 -#define REAL_JACK_PORT_NAME_SIZE JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE +#define REAL_JACK_PORT_NAME_SIZE (JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE) #define NAME "jack-client" struct client; +struct port; struct type { uint32_t client_node; @@ -76,6 +82,12 @@ static inline void init_type(struct type *type, struct spa_type_map *map) spa_type_media_subtype_audio_map(map, &type->media_subtype_audio); } +struct globals { + jack_thread_creator_t creator; +}; + +static struct globals globals; + #define OBJECT_CHUNK 8 struct object { @@ -89,7 +101,7 @@ struct object { union { struct { - char name[JACK_CLIENT_NAME_SIZE]; + char name[JACK_CLIENT_NAME_SIZE+1]; } node; struct { uint32_t src; @@ -97,9 +109,9 @@ struct object { } port_link; struct { unsigned long flags; - char name[REAL_JACK_PORT_NAME_SIZE]; - char alias1[REAL_JACK_PORT_NAME_SIZE]; - char alias2[REAL_JACK_PORT_NAME_SIZE]; + char name[REAL_JACK_PORT_NAME_SIZE+1]; + char alias1[REAL_JACK_PORT_NAME_SIZE+1]; + char alias2[REAL_JACK_PORT_NAME_SIZE+1]; uint32_t type_id; uint32_t port_id; } port; @@ -127,28 +139,37 @@ struct buffer { struct spa_data datas[MAX_BUFFER_DATAS]; uint32_t n_datas; - struct mem *mem[MAX_BUFFER_DATAS+1]; + uint32_t mem[MAX_BUFFER_DATAS+1]; uint32_t n_mem; }; +struct mix { + struct spa_list link; + struct spa_list port_link; + uint32_t id; + struct port *port; + + struct spa_io_buffers *io; + struct spa_io_control_range *ctrl; + + struct buffer buffers[MAX_BUFFERS]; + uint32_t n_buffers; + struct spa_list queue; +}; + struct port { bool valid; + struct spa_list link; struct client *client; + enum spa_direction direction; uint32_t id; struct object *object; - enum spa_direction direction; - - struct buffer buffers[MAX_BUFFERS]; - uint32_t n_buffers; - struct spa_io_buffers *io; - - struct spa_list queue; + struct spa_list mix; }; - struct context { struct pw_main_loop *main; struct pw_thread_loop *loop; @@ -164,14 +185,14 @@ struct context { #define GET_DIRECTION(f) ((f) & JackPortIsInput ? SPA_DIRECTION_INPUT : SPA_DIRECTION_OUTPUT) -#define GET_IN_PORT(c,p) (&c->in_ports[p]) -#define GET_OUT_PORT(c,p) (&c->out_ports[p]) +#define GET_IN_PORT(c,p) (&c->port_pool[SPA_DIRECTION_INPUT][p]) +#define GET_OUT_PORT(c,p) (&c->port_pool[SPA_DIRECTION_OUTPUT][p]) #define GET_PORT(c,d,p) (d == SPA_DIRECTION_INPUT ? GET_IN_PORT(c,p) : GET_OUT_PORT(c,p)) struct client { struct type type; - char name[JACK_CLIENT_NAME_SIZE]; + char name[JACK_CLIENT_NAME_SIZE+1]; struct context context; @@ -190,7 +211,6 @@ struct client { struct spa_hook proxy_listener; uint32_t node_id; - struct pw_client_node_transport *trans; int writefd; struct spa_source *socket_source; @@ -221,15 +241,17 @@ struct client { JackGraphOrderCallback graph_callback; void *graph_arg; + struct pw_client_node_position *position; uint32_t sample_rate; uint32_t buffer_size; - struct port in_ports[MAX_PORTS]; - uint32_t n_in_ports; - uint32_t last_in_port; - struct port out_ports[MAX_PORTS]; - uint32_t n_out_ports; - uint32_t last_out_port; + struct spa_list mix[2]; + struct mix mix_pool[MAX_MIX]; + struct spa_list free_mix; + + struct port port_pool[2][MAX_PORTS]; + struct spa_list ports[2]; + struct spa_list free_ports[2]; struct pw_array mems; @@ -239,6 +261,19 @@ struct client { int status; }; +static void init_port_pool(struct client *c, enum spa_direction direction) +{ + int i; + + spa_list_init(&c->ports[direction]); + spa_list_init(&c->free_ports[direction]); + for (i = 0; i < MAX_PORTS; i++) { + c->port_pool[direction][i].direction = direction; + c->port_pool[direction][i].id = i; + spa_list_append(&c->free_ports[direction], &c->port_pool[direction][i].link); + } +} + static struct object * alloc_object(struct client *c) { struct object *o; @@ -265,68 +300,80 @@ static void free_object(struct client *c, struct object *o) spa_list_append(&c->context.free_objects, &o->link); } +static struct mix *ensure_mix(struct client *c, struct port *port, uint32_t mix_id) +{ + struct mix *mix; + + spa_list_for_each(mix, &port->mix, port_link) { + if (mix->id == mix_id) + return mix; + } + if (spa_list_is_empty(&c->free_mix)) + return NULL; + + mix = spa_list_first(&c->free_mix, struct mix, link); + spa_list_remove(&mix->link); + + spa_list_append(&c->mix[port->direction], &mix->link); + spa_list_append(&port->mix, &mix->port_link); + + mix->id = mix_id; + mix->port = port; + mix->io = NULL; + mix->n_buffers = 0; + + return mix; +} + +static void free_mix(struct client *c, struct mix *mix) +{ + spa_list_remove(&mix->link); + spa_list_remove(&mix->port_link); + spa_list_append(&c->free_mix, &mix->link); +} + static struct port * alloc_port(struct client *c, enum spa_direction direction) { - uint32_t i; struct port *p; struct object *o; - if (direction == SPA_DIRECTION_INPUT) { - for (i = 0; i < c->n_in_ports; i++) { - if (!c->in_ports[i].valid) - break; - } - if (i >= 1024) - return NULL; + if (spa_list_is_empty(&c->free_ports[direction])) + return NULL; - p = GET_IN_PORT(c, i); - c->n_in_ports++; - c->last_in_port = SPA_MAX(c->last_in_port, i + 1); - } else { - for (i = 0; i < c->n_out_ports; i++) { - if (!c->out_ports[i].valid) - break; - } - if (i >= 1024) - return NULL; - - p = GET_OUT_PORT(c, i); - c->n_out_ports++; - c->last_out_port = SPA_MAX(c->last_out_port, i + 1); - } + p = spa_list_first(&c->free_ports[direction], struct port, link); + spa_list_remove(&p->link); o = alloc_object(c); o->type = c->context.t->port; o->id = SPA_ID_INVALID; o->parent_id = c->node_id; - o->port.port_id = i; + o->port.port_id = p->id; spa_list_append(&c->context.ports, &o->link); p->valid = true; p->client = c; - p->direction = direction; - p->id = i; p->object = o; + spa_list_init(&p->mix); + + spa_list_append(&c->ports[direction], &p->link); return p; } static void free_port(struct client *c, struct port *p) { + struct mix *m, *t; + if (!p->valid) return; - if (p->direction == SPA_DIRECTION_INPUT) { - c->n_in_ports--; - while (c->last_in_port > 0 && !c->in_ports[c->last_in_port - 1].valid) - c->last_in_port--; - } else { - c->n_out_ports--; - while (c->last_out_port > 0 && !c->out_ports[c->last_out_port - 1].valid) - c->last_out_port--; - } + spa_list_for_each_safe(m, t, &p->mix, port_link) + free_mix(c, m); + + spa_list_remove(&p->link); p->valid = false; free_object(c, p->object); + spa_list_append(&c->free_ports[p->direction], &p->link); } static struct object *find_port(struct client *c, const char *name) @@ -353,14 +400,14 @@ static struct object *find_link(struct client *c, uint32_t src, uint32_t dst) return NULL; } -static struct buffer *dequeue_buffer(struct port *p) +static struct buffer *dequeue_buffer(struct mix *mix) { struct buffer *b; - if (spa_list_is_empty(&p->queue)) + if (spa_list_is_empty(&mix->queue)) return NULL; - b = spa_list_first(&p->queue, struct buffer, link); + b = spa_list_first(&mix->queue, struct buffer, link); spa_list_remove(&b->link); SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT); @@ -393,14 +440,15 @@ static void on_state_changed(void *data, enum pw_remote_state old, { struct client *client = data; + pw_log_debug(NAME" %p: state %s", client, pw_remote_state_as_string(state)); switch (state) { + case PW_REMOTE_STATE_ERROR: + client->error = true; + /* fallthrough*/ case PW_REMOTE_STATE_UNCONNECTED: if (client->shutdown_callback) client->shutdown_callback(client->shutdown_arg); /* fallthrough*/ - case PW_REMOTE_STATE_ERROR: - client->error = true; - /* fallthrough*/ case PW_REMOTE_STATE_CONNECTED: pw_thread_loop_signal(client->context.loop, false); break; @@ -460,23 +508,27 @@ static struct mem *find_mem(struct pw_array *mems, uint32_t id) static void *mem_map(struct client *c, struct mem *m, uint32_t offset, uint32_t size) { - if (m->ptr == NULL) { - pw_map_range_init(&m->map, offset, size, c->context.core->sc_pagesize); + struct pw_map_range map; - m->ptr = mmap(NULL, m->map.size, PROT_READ|PROT_WRITE, - MAP_SHARED, m->fd, m->map.offset); + pw_map_range_init(&map, offset, size, c->context.core->sc_pagesize); + + if (m->ptr == NULL || m->map.offset != map.offset || m->map.size != map.size) { + m->ptr = mmap(m->ptr, map.size, PROT_READ|PROT_WRITE, + MAP_SHARED, m->fd, map.offset); if (m->ptr == MAP_FAILED) { pw_log_error(NAME" %p: Failed to mmap memory %d %p: %m", c, size, m); m->ptr = NULL; return NULL; } + m->map = map; } - return SPA_MEMBER(m->ptr, m->map.start, void); + return SPA_MEMBER(m->ptr, map.start, void); } static void mem_unmap(struct client *c, struct mem *m) { + pw_log_debug(NAME" %p: %d fd %d", c, m->id, m->fd); if (m->ptr != NULL) { if (munmap(m->ptr, m->map.size) < 0) pw_log_warn(NAME" %p: failed to unmap: %m", c); @@ -486,23 +538,23 @@ static void mem_unmap(struct client *c, struct mem *m) static void clear_mem(struct client *c, struct mem *m) { + pw_log_debug(NAME" %p: %d fd %d", c, m->id, m->fd); if (m->fd != -1) { bool has_ref = false; - int fd; struct mem *m2; - fd = m->fd; - m->fd = -1; - pw_array_for_each(m2, &c->mems) { - if (m2->fd == fd) { + if (m2 != m && m2->fd == m->fd) { has_ref = true; break; } } if (!has_ref) { mem_unmap(c, m); - close(fd); + pw_log_debug(NAME" %p: close %d fd %d", c, m->id, m->fd); + close(m->fd); + m->id = -1; + m->fd = -1; } } } @@ -518,14 +570,14 @@ static void client_node_add_mem(void *object, m = find_mem(&c->mems, mem_id); if (m) { - pw_log_debug(NAME" %p: update mem %u, fd %d, flags %d", c, - mem_id, memfd, flags); - clear_mem(c, m); - } else { - m = pw_array_add(&c->mems, sizeof(struct mem)); - pw_log_debug(NAME" %p: add mem %u, fd %d, flags %d", c, + pw_log_warn(NAME" %p: duplicate mem %u, fd %d, flags %d", c, mem_id, memfd, flags); + return; } + + m = pw_array_add(&c->mems, sizeof(struct mem)); + pw_log_debug(NAME" %p: add mem %u, fd %d, flags %d", c, mem_id, memfd, flags); + m->id = mem_id; m->fd = memfd; m->flags = flags; @@ -557,139 +609,19 @@ static void unhandle_socket(struct client *c) do_remove_sources, 1, NULL, 0, true, c); } -static void reuse_buffer(struct client *c, struct port *p, uint32_t id) +static void reuse_buffer(struct client *c, struct mix *mix, uint32_t id) { - struct buffer *b = &p->buffers[id]; + struct buffer *b; + + b = &mix->buffers[id]; if (SPA_FLAG_CHECK(b->flags, BUFFER_FLAG_OUT)) { - pw_log_trace(NAME" %p: port %p: recycle buffer %d", c, p, id); - spa_list_append(&p->queue, &b->link); + pw_log_trace(NAME" %p: port %p: recycle buffer %d", c, mix->port, id); + spa_list_append(&mix->queue, &b->link); SPA_FLAG_UNSET(b->flags, BUFFER_FLAG_OUT); } } -static inline void send_need_input(struct client *c) -{ - uint64_t cmd = 1; - pw_log_trace("send need input"); - pw_client_node_transport_add_message(c->trans, - &PW_CLIENT_NODE_MESSAGE_INIT(PW_CLIENT_NODE_MESSAGE_NEED_INPUT)); - write(c->writefd, &cmd, 8); -} - -static inline void send_have_output(struct client *c) -{ - uint64_t cmd = 1; - pw_log_trace("send have output"); - pw_client_node_transport_add_message(c->trans, - &PW_CLIENT_NODE_MESSAGE_INIT(PW_CLIENT_NODE_MESSAGE_HAVE_OUTPUT)); - write(c->writefd, &cmd, 8); -} - -static int do_process_input(struct client *c) -{ - if (c->status == SPA_STATUS_HAVE_BUFFER) - return SPA_STATUS_HAVE_BUFFER; - - if (c->process_callback) - c->process_callback(c->buffer_size, c->process_arg); - - return c->status = SPA_STATUS_HAVE_BUFFER; -} - -static int do_process_output(struct client *c) -{ - uint32_t i; - - for (i = 0; i < c->last_out_port; i++) { - struct port *p = GET_OUT_PORT(c, i); - struct spa_io_buffers *io = p->io; - - pw_log_trace("port %p: %d %d %d", p, p->valid, io->status, io->buffer_id); - - if (!p->valid || io == NULL) - continue; - - if (io->status != SPA_STATUS_NEED_BUFFER) - continue; - - if (io->buffer_id < p->n_buffers) - reuse_buffer(c, p, io->buffer_id); - - io->status = SPA_STATUS_NEED_BUFFER; - io->buffer_id = SPA_ID_INVALID; - } - - c->status = SPA_STATUS_HAVE_BUFFER; - for (i = 0; i < c->last_in_port; i++) { - struct port *p = GET_IN_PORT(c, i); - struct spa_io_buffers *io = p->io; - - if (!p->valid || io == NULL) - continue; - - c->status = SPA_STATUS_NEED_BUFFER; - } - if (c->status == SPA_STATUS_HAVE_BUFFER) { - if (c->process_callback) - c->process_callback(c->buffer_size, c->process_arg); - } - return c->status; -} - -static void handle_rtnode_message(struct client *c, struct pw_client_node_message *message) -{ - pw_log_trace("node message %d", PW_CLIENT_NODE_MESSAGE_TYPE(message)); - - switch (PW_CLIENT_NODE_MESSAGE_TYPE(message)) { - case PW_CLIENT_NODE_MESSAGE_PROCESS_INPUT: - { - switch (do_process_input(c)) { - case SPA_STATUS_HAVE_BUFFER: - send_have_output(c); - break; - case SPA_STATUS_NEED_BUFFER: - send_need_input(c); - break; - default: - break; - } - break; - } - case PW_CLIENT_NODE_MESSAGE_PROCESS_OUTPUT: - { - switch (do_process_output(c)) { - case SPA_STATUS_HAVE_BUFFER: - send_have_output(c); - break; - case SPA_STATUS_NEED_BUFFER: - send_need_input(c); - break; - default: - break; - } - break; - } - case PW_CLIENT_NODE_MESSAGE_PORT_REUSE_BUFFER: - { - struct pw_client_node_message_port_reuse_buffer *rb = - (struct pw_client_node_message_port_reuse_buffer *) message; - struct port *p; - uint32_t port_id = rb->body.port_id.value; - uint32_t buffer_id = rb->body.buffer_id.value; - - p = GET_OUT_PORT(c, port_id); - if (buffer_id < p->n_buffers) - reuse_buffer(c, p, buffer_id); - - break; - } - default: - pw_log_warn("unexpected node message %d", PW_CLIENT_NODE_MESSAGE_TYPE(message)); - break; - } -} - static void on_rtsocket_condition(void *data, int fd, enum spa_io mask) { @@ -702,17 +634,34 @@ on_rtsocket_condition(void *data, int fd, enum spa_io mask) } if (mask & SPA_IO_IN) { - struct pw_client_node_message message; uint64_t cmd; + uint32_t buffer_size, sample_rate; if (read(fd, &cmd, sizeof(uint64_t)) != sizeof(uint64_t)) pw_log_warn("jack %p: read failed %m", c); - while (pw_client_node_transport_next_message(c->trans, &message) == 1) { - struct pw_client_node_message *msg = alloca(SPA_POD_SIZE(&message)); - pw_client_node_transport_parse_message(c->trans, msg); - handle_rtnode_message(c, msg); + buffer_size = c->position->duration / sizeof(float); + if (buffer_size != c->buffer_size) { + pw_log_info("jack %p: buffersize %d", c, buffer_size); + c->buffer_size = buffer_size; + if (c->bufsize_callback) + c->bufsize_callback(c->buffer_size, c->bufsize_arg); } + + sample_rate = c->position->rate.denom; + if (sample_rate != c->sample_rate) { + pw_log_info("jack %p: sample_rate %d", c, sample_rate); + c->sample_rate = sample_rate; + if (c->srate_callback) + c->srate_callback(c->sample_rate, c->srate_arg); + } + + pw_log_trace("do process %d %d", c->buffer_size, c->sample_rate); + if (c->process_callback) + c->process_callback(c->buffer_size, c->process_arg); + + cmd = 1; + write(c->writefd, &cmd, 8); } } @@ -720,7 +669,7 @@ static void clean_transport(struct client *c) { struct mem *m; - if (c->trans == NULL) + if (c->node_id == SPA_ID_INVALID) return; unhandle_socket(c); @@ -729,37 +678,25 @@ static void clean_transport(struct client *c) clear_mem(c, m); pw_array_clear(&c->mems); - pw_client_node_transport_destroy(c->trans); - c->trans = NULL; close(c->writefd); + + c->node_id = SPA_ID_INVALID; } static void client_node_transport(void *object, uint32_t node_id, int readfd, - int writefd, - struct pw_client_node_transport *transport) + int writefd) { struct client *c = (struct client *) object; struct pw_core *core = c->context.core; - uint32_t i; clean_transport(c); c->node_id = node_id; - c->trans = transport; - pw_log_debug("client %p: create client transport %p with fds %d %d for node %u", - c, c->trans, readfd, writefd, node_id); - - for (i = 0; i < c->trans->area->max_input_ports; i++) { - struct port *p = GET_IN_PORT(c, i); - p->io = &c->trans->inputs[i]; - } - for (i = 0; i < c->trans->area->max_output_ports; i++) { - struct port *p = GET_OUT_PORT(c, i); - p->io = &c->trans->outputs[i]; - } + pw_log_debug("client %p: create client transport with fds %d %d for node %u", + c, readfd, writefd, node_id); c->writefd = writefd; c->socket_source = pw_loop_add_io(core->data_loop, @@ -785,6 +722,7 @@ static void client_node_command(void *object, uint32_t seq, const struct spa_com struct client *c = (struct client *) object; struct pw_type *t = c->context.t; + pw_log_debug("got command %d", SPA_COMMAND_TYPE(command)); if (SPA_COMMAND_TYPE(command) == t->command_node.Pause) { if (c->started) { pw_loop_update_io(c->context.core->data_loop, @@ -799,17 +737,6 @@ static void client_node_command(void *object, uint32_t seq, const struct spa_com pw_loop_update_io(c->context.core->data_loop, c->socket_source, SPA_IO_IN | SPA_IO_ERR | SPA_IO_HUP); - - switch (do_process_output(c)) { - case SPA_STATUS_HAVE_BUFFER: - send_have_output(c); - break; - case SPA_STATUS_NEED_BUFFER: - send_need_input(c); - break; - default: - break; - } c->started = true; } pw_client_node_proxy_done(c->node_proxy, seq, 0); @@ -837,15 +764,16 @@ static void client_node_remove_port(void *object, pw_client_node_proxy_done(c->node_proxy, seq, -ENOTSUP); } -static void clear_buffers(struct client *c, struct port *p) +static void clear_buffers(struct client *c, struct mix *mix) { + struct port *port = mix->port; struct buffer *b; uint32_t i, j; - pw_log_debug(NAME" %p: port %p clear buffers", c, p); + pw_log_debug(NAME" %p: port %p clear buffers", c, port); - for (i = 0; i < p->n_buffers; i++) { - b = &p->buffers[i]; + for (i = 0; i < mix->n_buffers; i++) { + b = &mix->buffers[i]; if (b->ptr != NULL) { if (munmap(b->ptr, b->map.size) < 0) @@ -861,14 +789,19 @@ static void clear_buffers(struct client *c, struct port *p) d->fd = -1; } for (j = 0; j < b->n_mem; j++) { - if (--b->mem[j]->ref == 0) - clear_mem(c, b->mem[j]); + struct mem *m; + + if ((m = find_mem(&c->mems, b->mem[i])) == NULL) + continue; + + if (--m->ref == 0) + clear_mem(c, m); } b->n_mem = 0; b->ptr = NULL; } - p->n_buffers = 0; - spa_list_init(&p->queue); + mix->n_buffers = 0; + spa_list_init(&mix->queue); } static int param_enum_format(struct client *c, struct port *p, @@ -883,8 +816,9 @@ static int param_enum_format(struct client *c, struct port *p, "I", c->type.media_type.audio, "I", c->type.media_subtype.raw, ":", c->type.format_audio.format, "I", c->type.audio_format.F32, + ":", c->type.format_audio.layout, "i", SPA_AUDIO_LAYOUT_NON_INTERLEAVED, ":", c->type.format_audio.channels, "i", 1, - ":", c->type.format_audio.rate, "iru", 44100, + ":", c->type.format_audio.rate, "iru", DEFAULT_SAMPLE_RATE, SPA_POD_PROP_MIN_MAX(1, INT32_MAX)); break; case 1: @@ -911,8 +845,10 @@ static int param_format(struct client *c, struct port *p, "I", c->type.media_type.audio, "I", c->type.media_subtype.raw, ":", c->type.format_audio.format, "I", c->type.audio_format.F32, + ":", c->type.format_audio.layout, "i", SPA_AUDIO_LAYOUT_NON_INTERLEAVED, ":", c->type.format_audio.channels, "i", 1, - ":", c->type.format_audio.rate, "i", c->sample_rate); + ":", c->type.format_audio.rate, "iru", 44100, + SPA_POD_PROP_MIN_MAX(1, INT32_MAX)); break; case 1: *param = spa_pod_builder_object(b, @@ -933,7 +869,7 @@ static int param_buffers(struct client *c, struct port *p, *param = spa_pod_builder_object(b, t->param.idBuffers, t->param_buffers.Buffers, - ":", t->param_buffers.size, "isu", 1024, + ":", t->param_buffers.size, "isu", MAX_BUFFER_SIZE * sizeof(float), SPA_POD_PROP_STEP(4, INT32_MAX, 4), ":", t->param_buffers.stride, "i", 4, ":", t->param_buffers.buffers, "iru", 1, @@ -958,8 +894,12 @@ static void client_node_port_set_param(void *object, struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); if (id == t->param.idFormat && param == NULL) { + struct mix *mix; + pw_log_debug(NAME" %p: port %p clear format", c, p); - clear_buffers(c, p); + + spa_list_for_each(mix, &p->mix, port_link) + clear_buffers(c, mix); } param_enum_format(c, p, ¶ms[0], &b); @@ -981,6 +921,7 @@ static void client_node_port_use_buffers(void *object, uint32_t seq, enum spa_direction direction, uint32_t port_id, + uint32_t mix_id, uint32_t n_buffers, struct pw_client_node_buffer *buffers) { @@ -990,32 +931,39 @@ static void client_node_port_use_buffers(void *object, struct buffer *b; uint32_t i, j, prot, res; struct pw_core *core = c->context.core; + struct mix *mix; if (!p->valid) { res = -EINVAL; goto done; } - pw_log_debug(NAME" %p: port %p %d use_buffers %d", c, p, direction, n_buffers); + if ((mix = ensure_mix(c, p, mix_id)) == NULL) { + res = -ENOMEM; + goto done; + } + + pw_log_debug(NAME" %p: port %p %d %d.%d use_buffers %d", c, p, direction, + port_id, mix_id, n_buffers); prot = PROT_READ | (direction == SPA_DIRECTION_OUTPUT ? PROT_WRITE : 0); /* clear previous buffers */ - clear_buffers(c, p); + clear_buffers(c, mix); for (i = 0; i < n_buffers; i++) { off_t offset; struct spa_buffer *buf; + struct mem *m; - struct mem *m = find_mem(&c->mems, buffers[i].mem_id); - if (m == NULL) { + if ((m = find_mem(&c->mems, buffers[i].mem_id)) == NULL) { pw_log_warn(NAME" %p: unknown memory id %u", c, buffers[i].mem_id); continue; } buf = buffers[i].buffer; - b = &p->buffers[buf->id]; + b = &mix->buffers[buf->id]; b->flags = 0; b->id = buf->id; b->n_mem = 0; @@ -1032,7 +980,7 @@ static void client_node_port_use_buffers(void *object, m->ref++; - b->mem[b->n_mem++] = m; + b->mem[b->n_mem++] = m->id; pw_log_debug("add buffer %d %d %u %u", m->id, b->id, b->map.offset, b->map.size); @@ -1047,8 +995,6 @@ static void client_node_port_use_buffers(void *object, for (j = 0; j < b->n_datas; j++) { struct spa_data *d = &b->datas[j]; - c->buffer_size = SPA_MAX(c->buffer_size, d->maxsize / sizeof(float)); - memcpy(d, &buf->datas[j], sizeof(struct spa_data)); d->chunk = SPA_MEMBER(b->ptr, offset + sizeof(struct spa_chunk) * j, @@ -1068,7 +1014,7 @@ static void client_node_port_use_buffers(void *object, d->data = SPA_MEMBER(d->data, d->mapoffset, void); d->fd = bm->fd; bm->ref++; - b->mem[b->n_mem++] = bm; + b->mem[b->n_mem++] = bm->id; pw_log_debug(NAME" %p: data %d %u -> fd %d", c, j, bm->id, bm->fd); } else if (d->type == t->data.MemPtr) { d->data = SPA_MEMBER(b->ptr, @@ -1084,9 +1030,10 @@ static void client_node_port_use_buffers(void *object, } SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT); if (direction == SPA_DIRECTION_OUTPUT) - reuse_buffer(c, p, b->id); + reuse_buffer(c, mix, b->id); } - p->n_buffers = n_buffers; + pw_log_debug("have %d buffers", n_buffers); + mix->n_buffers = n_buffers; res = 0; done: @@ -1104,6 +1051,7 @@ static void client_node_port_set_io(void *object, uint32_t seq, enum spa_direction direction, uint32_t port_id, + uint32_t mix_id, uint32_t id, uint32_t mem_id, uint32_t offset, @@ -1113,8 +1061,9 @@ static void client_node_port_set_io(void *object, struct port *p = GET_PORT(c, direction, port_id); struct pw_type *t = c->context.t; struct mem *m; + struct mix *mix; void *ptr; - int res; + int res = 0; if (mem_id == SPA_ID_INVALID) { ptr = NULL; @@ -1133,18 +1082,51 @@ static void client_node_port_set_io(void *object, } } + if ((mix = ensure_mix(c, p, mix_id)) == NULL) { + res = -ENOMEM; + goto exit; + } if (id == t->io.Buffers) { - p->io = ptr; + mix->io = ptr; } - pw_log_debug("port %p: set io id %u %p %d %d", p, id, ptr, p->io->status, p->io->buffer_id); - - res = 0; + else if (id == t->io.ControlRange) { + mix->ctrl = ptr; + } + pw_log_debug("port %p: set io id %u %u %u %u %p", p, id, mem_id, offset, size, ptr); exit: pw_client_node_proxy_done(c->node_proxy, seq, res); } +static void client_node_set_position(void *object, + uint32_t mem_id, + uint32_t offset, + uint32_t size) +{ + struct client *c = (struct client *) object; + struct mem *m; + void *ptr; + + if (mem_id == SPA_ID_INVALID) { + ptr = NULL; + size = 0; + } + else { + m = find_mem(&c->mems, mem_id); + if (m == NULL) { + pw_log_warn("unknown memory id %u", mem_id); + return; + } + if ((ptr = mem_map(c, m, offset, size)) == NULL) { + return; + } + } + pw_log_debug("client %p: set position %p", c, ptr); + c->position = ptr; +} + + static const struct pw_client_node_proxy_events client_node_events = { PW_VERSION_CLIENT_NODE_PROXY_EVENTS, .add_mem = client_node_add_mem, @@ -1158,6 +1140,7 @@ static const struct pw_client_node_proxy_events client_node_events = { .port_use_buffers = client_node_port_use_buffers, .port_command = client_node_port_command, .port_set_io = client_node_port_set_io, + .set_position = client_node_set_position, }; static jack_port_type_id_t string_to_type(const char *port_type) @@ -1166,10 +1149,26 @@ static jack_port_type_id_t string_to_type(const char *port_type) return 0; else if (!strcmp(JACK_DEFAULT_MIDI_TYPE, port_type)) return 1; + else if (!strcmp("other", port_type)) + return 2; else return SPA_ID_INVALID; } +static const char* type_to_string(jack_port_type_id_t type_id) +{ + switch(type_id) { + case 0: + return JACK_DEFAULT_AUDIO_TYPE; + case 1: + return JACK_DEFAULT_MIDI_TYPE; + case 2: + return "other"; + default: + return 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) @@ -1190,7 +1189,7 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, o = alloc_object(c); spa_list_append(&c->context.nodes, &o->link); - strncpy(o->node.name, str, sizeof(o->node.name)); + snprintf(o->node.name, sizeof(o->node.name), "%s/%d", str, id); pw_log_debug("add node %d", id); } else if (type == t->port) { @@ -1199,11 +1198,12 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, jack_port_type_id_t type_id; char full_name[1024]; - if ((str = spa_dict_lookup(props, "port.dsp")) == NULL) - goto exit; - - if ((type_id = string_to_type(str)) == SPA_ID_INVALID) - goto exit; + if ((str = spa_dict_lookup(props, "port.dsp")) == NULL) { + type_id = 2; + } + else + if ((type_id = string_to_type(str)) == SPA_ID_INVALID) + goto exit; if ((str = spa_dict_lookup(props, "port.name")) == NULL) goto exit; @@ -1225,12 +1225,14 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, } } - snprintf(full_name, sizeof(full_name), "%s:%s", c->name, str); - o = find_port(c, full_name); - if (o != NULL) { - pw_log_debug("client %p: found our port %p", c, o); + o = NULL; + if (parent_id == c->node_id) { + snprintf(full_name, sizeof(full_name), "%s:%s", c->name, str); + o = find_port(c, full_name); + if (o != NULL) + pw_log_debug("client %p: %s found our port %p", c, full_name, o); } - else { + if (o == NULL) { o = alloc_object(c); if (o == NULL) goto exit; @@ -1257,7 +1259,7 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, o->port.flags = flags; o->port.type_id = type_id; - pw_log_debug("add port %d", id); + pw_log_debug("add port %d %s", id, o->port.name); } else if (type == t->link) { o = alloc_object(c); @@ -1352,6 +1354,7 @@ jack_client_t * jack_client_open (const char *client_name, bool busy = true; struct spa_dict props; struct spa_dict_item items[2]; + int i; pw_log_debug("client open %s %d", client_name, options); @@ -1359,6 +1362,7 @@ jack_client_t * jack_client_open (const char *client_name, if (client == NULL) goto init_failed; + client->node_id = SPA_ID_INVALID; strncpy(client->name, client_name, JACK_CLIENT_NAME_SIZE); client->context.main = pw_main_loop_new(NULL); client->context.loop = pw_thread_loop_new(pw_main_loop_get_loop(client->context.main), client_name); @@ -1373,8 +1377,17 @@ jack_client_t * jack_client_open (const char *client_name, pw_array_init(&client->mems, 64); pw_array_ensure_size(&client->mems, sizeof(struct mem) * 64); - client->sample_rate = 44100; - client->buffer_size = 1024 / sizeof(float); + client->buffer_size = -1; + client->sample_rate = -1; + + spa_list_init(&client->mix[SPA_DIRECTION_INPUT]); + spa_list_init(&client->mix[SPA_DIRECTION_OUTPUT]); + spa_list_init(&client->free_mix); + for (i = 0; i < MAX_MIX; i++) + spa_list_append(&client->free_mix, &client->mix_pool[i].link); + + init_port_pool(client, SPA_DIRECTION_INPUT); + init_port_pool(client, SPA_DIRECTION_OUTPUT); pw_map_init(&client->context.globals, 64, 64); @@ -1440,6 +1453,8 @@ jack_client_t * jack_client_open (const char *client_name, PW_CLIENT_NODE_UPDATE_MAX_OUTPUTS, 0, 0, 0, NULL); + pw_client_node_proxy_done(client->node_proxy, 0, 0); + if (do_sync(client) < 0) goto init_failed; @@ -1504,12 +1519,14 @@ char * jack_get_client_name (jack_client_t *client) char *jack_get_uuid_for_client_name (jack_client_t *client, const char *client_name) { + pw_log_warn("not implemented"); return NULL; } char *jack_get_client_name_by_uuid (jack_client_t *client, const char *client_uuid ) { + pw_log_warn("not implemented"); return NULL; } @@ -1517,11 +1534,13 @@ int jack_internal_client_new (const char *client_name, const char *load_name, const char *load_init) { - return 0; + pw_log_warn("not implemented"); + return -ENOTSUP; } void jack_internal_client_close (const char *client_name) { + pw_log_warn("not implemented"); } int jack_activate (jack_client_t *client) @@ -1564,31 +1583,37 @@ int jack_deactivate (jack_client_t *client) int jack_get_client_pid (const char *name) { - return 0; + pw_log_warn("not implemented"); + return -ENOTSUP; } jack_native_thread_t jack_client_thread_id (jack_client_t *client) { - return 0; + pw_log_warn("not implemented"); + return -ENOTSUP; } int jack_is_realtime (jack_client_t *client) { - return 0; + pw_log_warn("not implemented"); + return -ENOTSUP; } jack_nframes_t jack_thread_wait (jack_client_t *client, int status) { + pw_log_warn("not implemented"); return 0; } jack_nframes_t jack_cycle_wait (jack_client_t* client) { + pw_log_warn("not implemented"); return 0; } void jack_cycle_signal (jack_client_t* client, int status) { + pw_log_warn("not implemented"); } int jack_set_process_thread(jack_client_t* client, JackThreadCallback thread_callback, void *arg) @@ -1735,24 +1760,28 @@ int jack_set_graph_order_callback (jack_client_t *client, int jack_set_xrun_callback (jack_client_t *client, JackXRunCallback xrun_callback, void *arg) { - return 0; + pw_log_warn("not implemented"); + return -ENOTSUP; } int jack_set_latency_callback (jack_client_t *client, JackLatencyCallback latency_callback, void *data) { - return 0; + pw_log_warn("not implemented"); + return -ENOTSUP; } int jack_set_freewheel(jack_client_t* client, int onoff) { - return 0; + pw_log_warn("not implemented"); + return -ENOTSUP; } int jack_set_buffer_size (jack_client_t *client, jack_nframes_t nframes) { - return 0; + pw_log_warn("not implemented"); + return -ENOTSUP; } jack_nframes_t jack_get_sample_rate (jack_client_t *client) @@ -1769,11 +1798,13 @@ jack_nframes_t jack_get_buffer_size (jack_client_t *client) int jack_engine_takeover_timebase (jack_client_t *client) { - return 0; + pw_log_warn("not implemented"); + return -ENOTSUP; } float jack_cpu_load (jack_client_t *client) { + pw_log_warn("not implemented"); return 0.0; } @@ -1817,8 +1848,7 @@ jack_port_t * jack_port_register (jack_client_t *client, snprintf(o->port.name, sizeof(o->port.name), "%s:%s", c->name, port_name); o->port.type_id = type_id; - spa_list_init(&p->queue); - p->n_buffers = 0; + spa_list_init(&p->mix); port_info.flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS | SPA_PORT_INFO_FLAG_NO_REF; @@ -1883,6 +1913,13 @@ int jack_port_unregister (jack_client_t *client, jack_port_t *port) return res; } +static void add_f32(float *out, float *in, int n_samples) +{ + int i; + for (i = 0; i < n_samples; i++) + out[i] += in[i]; +} + void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) { struct object *o = (struct object *) port; @@ -1890,7 +1927,9 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) struct port *p; struct buffer *b; struct spa_io_buffers *io; - int status; + struct mix *mix; + void *ptr = c->empty; + int layer = 0; if (o->type != c->context.t->port || o->port.port_id == SPA_ID_INVALID) { pw_log_error("client %p: invalid port %p", c, port); @@ -1898,38 +1937,54 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) } p = GET_PORT(c, GET_DIRECTION(o->port.flags), o->port.port_id); - if (p->n_buffers == 0) - return c->empty; - - io = p->io; - status = io->status; - pw_log_trace("port %p: get buffer %d %d", p, status, io->buffer_id); - if (p->direction == SPA_DIRECTION_INPUT) { - io->status = SPA_STATUS_NEED_BUFFER; - if (io->buffer_id >= p->n_buffers) - return c->empty; + spa_list_for_each(mix, &p->mix, port_link) { + pw_log_trace("port %p: mix %d.%d get buffer", p, p->id, mix->id); + io = mix->io; + if (io == NULL || io->buffer_id >= mix->n_buffers) + continue; - b = &p->buffers[io->buffer_id]; - } else { - io->status = SPA_STATUS_HAVE_BUFFER; - if ((b = dequeue_buffer(p)) == NULL) { - pw_log_warn("port %p: out of buffers", p); - goto empty_out; + io->status = SPA_STATUS_NEED_BUFFER; + b = &mix->buffers[io->buffer_id]; + if (layer++ == 0) + ptr = b->datas[0].data; + else + add_f32(ptr, b->datas[0].data, frames); } - reuse_buffer(c, p, b->id); - } - io->buffer_id = b->id; - pw_log_trace("port %p: get buffer %d %d", p, io->status, io->buffer_id); - return b->datas[0].data; + } else { + spa_list_for_each(mix, &p->mix, port_link) { + pw_log_trace("port %p: mix %d.%d get buffer", p, p->id, mix->id); + io = mix->io; - empty_out: - io->buffer_id = SPA_ID_INVALID; - return c->empty; + if (mix->n_buffers == 0 || io == NULL) + continue; + + io->status = SPA_STATUS_HAVE_BUFFER; + if ((b = dequeue_buffer(mix)) == NULL) { + pw_log_warn("port %p: out of buffers", p); + io->buffer_id = SPA_ID_INVALID; + goto done; + } + reuse_buffer(c, mix, b->id); + io->buffer_id = b->id; + ptr = b->datas[0].data; + + b->datas[0].chunk->offset = 0; + b->datas[0].chunk->size = frames * sizeof(float); + b->datas[0].chunk->stride = sizeof(float); + + break; + } + } + + done: + pw_log_trace("port %p: buffer %p", p, ptr); + return ptr; } jack_uuid_t jack_port_uuid (const jack_port_t *port) { + pw_log_warn("not implemented"); return 0; } @@ -1954,14 +2009,7 @@ int jack_port_flags (const jack_port_t *port) const char * jack_port_type (const jack_port_t *port) { struct object *o = (struct object *) port; - switch(o->port.type_id) { - case 0: - return JACK_DEFAULT_AUDIO_TYPE; - case 1: - return JACK_DEFAULT_MIDI_TYPE; - default: - return NULL; - } + return type_to_string(o->port.type_id); } jack_port_type_id_t jack_port_type_id (const jack_port_t *port) @@ -2073,27 +2121,32 @@ const char ** jack_port_get_all_connections (const jack_client_t *client, int jack_port_tie (jack_port_t *src, jack_port_t *dst) { - return 0; + pw_log_warn("not implemented"); + return -ENOTSUP; } int jack_port_untie (jack_port_t *port) { - return 0; + pw_log_warn("not implemented"); + return -ENOTSUP; } int jack_port_set_name (jack_port_t *port, const char *port_name) { - return 0; + pw_log_warn("not implemented"); + return -ENOTSUP; } int jack_port_set_alias (jack_port_t *port, const char *alias) { - return 0; + pw_log_warn("not implemented"); + return -ENOTSUP; } int jack_port_unset_alias (jack_port_t *port, const char *alias) { - return 0; + pw_log_warn("not implemented"); + return -ENOTSUP; } int jack_port_get_aliases (const jack_port_t *port, char* const aliases[2]) @@ -2105,11 +2158,11 @@ int jack_port_get_aliases (const jack_port_t *port, char* const aliases[2]) pw_thread_loop_lock(c->context.loop); if (o->port.alias1[0] != '\0') { - snprintf(aliases[0], REAL_JACK_PORT_NAME_SIZE, "%s", o->port.alias1); + snprintf(aliases[0], REAL_JACK_PORT_NAME_SIZE+1, "%s", o->port.alias1); res++; } if (o->port.alias2[0] != '\0') { - snprintf(aliases[1], REAL_JACK_PORT_NAME_SIZE, "%s", o->port.alias2); + snprintf(aliases[1], REAL_JACK_PORT_NAME_SIZE+1, "%s", o->port.alias2); res++; } pw_thread_loop_unlock(c->context.loop); @@ -2119,23 +2172,27 @@ int jack_port_get_aliases (const jack_port_t *port, char* const aliases[2]) int jack_port_request_monitor (jack_port_t *port, int onoff) { - return 0; + pw_log_warn("not implemented"); + return -ENOTSUP; } int jack_port_request_monitor_by_name (jack_client_t *client, const char *port_name, int onoff) { - return 0; + pw_log_warn("not implemented"); + return -ENOTSUP; } int jack_port_ensure_monitor (jack_port_t *port, int onoff) { - return 0; + pw_log_warn("not implemented"); + return -ENOTSUP; } int jack_port_monitoring_input (jack_port_t *port) { - return 0; + pw_log_warn("not implemented"); + return -ENOTSUP; } int jack_connect (jack_client_t *client, @@ -2254,50 +2311,63 @@ int jack_port_disconnect (jack_client_t *client, jack_port_t *port) int jack_port_name_size(void) { - return REAL_JACK_PORT_NAME_SIZE; + return REAL_JACK_PORT_NAME_SIZE+1; } int jack_port_type_size(void) { - return JACK_PORT_TYPE_SIZE; + return JACK_PORT_TYPE_SIZE+1; } size_t jack_port_type_get_buffer_size (jack_client_t *client, const char *port_type) { - return 0; + struct client *c = (struct client *) client; + if (!strcmp(JACK_DEFAULT_AUDIO_TYPE, port_type)) + return c->buffer_size; + else if (!strcmp(JACK_DEFAULT_MIDI_TYPE, port_type)) + return BUFFER_SIZE_MAX; + else + return 0; } void jack_port_set_latency (jack_port_t *port, jack_nframes_t frames) { + pw_log_warn("not implemented"); } void jack_port_get_latency_range (jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range) { + pw_log_warn("not implemented"); } void jack_port_set_latency_range (jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range) { + pw_log_warn("not implemented"); } int jack_recompute_total_latencies (jack_client_t *client) { - return 0; + pw_log_warn("not implemented"); + return -ENOTSUP; } jack_nframes_t jack_port_get_latency (jack_port_t *port) { + pw_log_warn("not implemented"); return 0; } jack_nframes_t jack_port_get_total_latency (jack_client_t *client, jack_port_t *port) { + pw_log_warn("not implemented"); return 0; } int jack_recompute_total_latency (jack_client_t *client, jack_port_t* port) { - return 0; + pw_log_warn("not implemented"); + return -ENOTSUP; } const char ** jack_get_ports (jack_client_t *client, @@ -2309,13 +2379,32 @@ const char ** jack_get_ports (jack_client_t *client, const char **res = malloc(sizeof(char*) * (JACK_PORT_MAX + 1)); int count = 0; struct object *o; + const char *str; + uint32_t id; + + if ((str = getenv("PIPEWIRE_NODE")) != NULL) + id = pw_properties_parse_int(str); + else + id = SPA_ID_INVALID; pw_thread_loop_lock(c->context.loop); + pw_log_debug("ports %d %s %s %08lx", id, port_name_pattern, type_name_pattern, flags); + spa_list_for_each(o, &c->context.ports, link) { + pw_log_debug("check port %s", o->port.name); + if (o->port.type_id == 2) + continue; if (!SPA_FLAG_CHECK(o->port.flags, flags)) continue; + if (id != SPA_ID_INVALID && o->parent_id != id) + continue; + if (type_name_pattern && strcmp(type_to_string(o->port.type_id), type_name_pattern)) + continue; + if (port_name_pattern && strcmp(o->port.name, port_name_pattern)) + continue; + pw_log_debug("add port %d %s", count, o->port.name); res[count++] = o->port.name; if (count == JACK_PORT_MAX) break; @@ -2369,16 +2458,19 @@ jack_port_t * jack_port_by_id (jack_client_t *client, jack_nframes_t jack_frames_since_cycle_start (const jack_client_t *client) { + pw_log_warn("not implemented"); return 0; } jack_nframes_t jack_frame_time (const jack_client_t *client) { + pw_log_warn("not implemented"); return 0; } jack_nframes_t jack_last_frame_time (const jack_client_t *client) { + pw_log_warn("not implemented"); return 0; } @@ -2388,30 +2480,36 @@ int jack_get_cycle_times(const jack_client_t *client, jack_time_t *next_usecs, float *period_usecs) { - return 0; + pw_log_warn("not implemented"); + return -ENOTSUP; } jack_time_t jack_frames_to_time(const jack_client_t *client, jack_nframes_t frames) { + pw_log_warn("not implemented"); return 0; } jack_nframes_t jack_time_to_frames(const jack_client_t *client, jack_time_t usecs) { + pw_log_warn("not implemented"); return 0; } jack_time_t jack_get_time() { + pw_log_warn("not implemented"); return 0; } void jack_set_error_function (void (*func)(const char *)) { + pw_log_warn("not implemented"); } void jack_set_info_function (void (*func)(const char *)) { + pw_log_warn("not implemented"); } void jack_free(void* ptr) @@ -2419,6 +2517,187 @@ void jack_free(void* ptr) free(ptr); } +int jack_release_timebase (jack_client_t *client) +{ + pw_log_warn("not implemented"); + return -ENOTSUP; +} + +int jack_set_sync_callback (jack_client_t *client, + JackSyncCallback sync_callback, + void *arg) +{ + pw_log_warn("not implemented"); + return -ENOTSUP; +} + +int jack_set_sync_timeout (jack_client_t *client, + jack_time_t timeout) +{ + pw_log_warn("not implemented"); + return -ENOTSUP; +} + +int jack_set_timebase_callback (jack_client_t *client, + int conditional, + JackTimebaseCallback timebase_callback, + void *arg) +{ + pw_log_warn("not implemented"); + return -ENOTSUP; +} + +int jack_transport_locate (jack_client_t *client, + jack_nframes_t frame) +{ + pw_log_warn("not implemented"); + return -ENOTSUP; +} + +jack_transport_state_t jack_transport_query (const jack_client_t *client, + jack_position_t *pos) +{ + pw_log_warn("not implemented"); + return JackTransportRolling; +} + +jack_nframes_t jack_get_current_transport_frame (const jack_client_t *client) +{ + pw_log_warn("not implemented"); + return 0; +} + +int jack_transport_reposition (jack_client_t *client, + const jack_position_t *pos) +{ + pw_log_warn("not implemented"); + return -ENOTSUP; +} + +void jack_transport_start (jack_client_t *client) +{ + pw_log_warn("not implemented"); +} + +void jack_transport_stop (jack_client_t *client) +{ + pw_log_warn("not implemented"); +} + +void jack_get_transport_info (jack_client_t *client, + jack_transport_info_t *tinfo) +{ + static jack_transport_info_t dummy; + memcpy(tinfo, &dummy, sizeof(jack_transport_info_t)); + pw_log_warn("not implemented"); +} + +void jack_set_transport_info (jack_client_t *client, + jack_transport_info_t *tinfo) +{ + pw_log_warn("not implemented"); +} + +int jack_set_session_callback (jack_client_t *client, + JackSessionCallback session_callback, + void *arg) +{ + pw_log_warn("not implemented"); + return -ENOTSUP; +} + +int jack_session_reply (jack_client_t *client, + jack_session_event_t *event) +{ + pw_log_warn("not implemented"); + return -ENOTSUP; +} + + +void jack_session_event_free (jack_session_event_t *event) +{ + pw_log_warn("not implemented"); +} + +char *jack_client_get_uuid (jack_client_t *client) +{ + pw_log_warn("not implemented"); + return ""; +} + +int jack_client_real_time_priority (jack_client_t * client) +{ + pw_log_warn("not implemented"); + return -ENOTSUP; +} + +int jack_client_max_real_time_priority (jack_client_t *client) +{ + pw_log_warn("not implemented"); + return -ENOTSUP; +} + +int jack_acquire_real_time_scheduling (jack_native_thread_t thread, int priority) +{ + pw_log_warn("not implemented"); + return -ENOTSUP; +} + +/** + * Create a thread for JACK or one of its clients. The thread is + * created executing @a start_routine with @a arg as its sole + * argument. + * + * @param client the JACK client for whom the thread is being created. May be + * NULL if the client is being created within the JACK server. + * @param thread place to return POSIX thread ID. + * @param priority thread priority, if realtime. + * @param realtime true for the thread to use realtime scheduling. On + * some systems that may require special privileges. + * @param start_routine function the thread calls when it starts. + * @param arg parameter passed to the @a start_routine. + * + * @returns 0, if successful; otherwise some error number. + */ +int jack_client_create_thread (jack_client_t* client, + jack_native_thread_t *thread, + int priority, + int realtime, /* boolean */ + void *(*start_routine)(void*), + void *arg) +{ + if (globals.creator == NULL) + globals.creator = pthread_create; + + return globals.creator(thread, NULL, start_routine, arg); +} + +int jack_drop_real_time_scheduling (jack_native_thread_t thread) +{ + pw_log_warn("not implemented"); + return -ENOTSUP; +} + +int jack_client_stop_thread(jack_client_t* client, jack_native_thread_t thread) +{ + pw_log_warn("not implemented"); + return -ENOTSUP; +} + +int jack_client_kill_thread(jack_client_t* client, jack_native_thread_t thread) +{ + pw_log_warn("not implemented"); + return -ENOTSUP; +} + +void jack_set_thread_creator (jack_thread_creator_t creator) +{ + if (creator == NULL) + globals.creator = pthread_create; + else + globals.creator = creator; +} + static void reg(void) __attribute__ ((constructor)); static void reg(void) { From 9902886ba5a7e94211afbca08c2de934c252f05d Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 24 May 2018 16:11:54 +0200 Subject: [PATCH 012/133] jack: support port matching with regex --- src/pipewire-jack.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 735b6a13e..b0c0616fa 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -2381,28 +2382,41 @@ const char ** jack_get_ports (jack_client_t *client, struct object *o; const char *str; uint32_t id; + regex_t port_regex, type_regex; if ((str = getenv("PIPEWIRE_NODE")) != NULL) id = pw_properties_parse_int(str); else id = SPA_ID_INVALID; + if (port_name_pattern && port_name_pattern[0]) + regcomp(&port_regex, port_name_pattern, REG_EXTENDED | REG_NOSUB); + if (type_name_pattern && type_name_pattern[0]) + regcomp(&type_regex, type_name_pattern, REG_EXTENDED | REG_NOSUB); + pw_thread_loop_lock(c->context.loop); pw_log_debug("ports %d %s %s %08lx", id, port_name_pattern, type_name_pattern, flags); spa_list_for_each(o, &c->context.ports, link) { - pw_log_debug("check port %s", o->port.name); + pw_log_debug("check port %s %d %lu", + o->port.name, o->port.type_id, o->port.flags); if (o->port.type_id == 2) continue; if (!SPA_FLAG_CHECK(o->port.flags, flags)) continue; if (id != SPA_ID_INVALID && o->parent_id != id) continue; - if (type_name_pattern && strcmp(type_to_string(o->port.type_id), type_name_pattern)) - continue; - if (port_name_pattern && strcmp(o->port.name, port_name_pattern)) - continue; + + if (port_name_pattern && port_name_pattern[0]) { + if (regexec(&port_regex, o->port.name, 0, NULL, 0) == REG_NOMATCH) + continue; + } + if (type_name_pattern && type_name_pattern[0]) { + if (regexec(&type_regex, type_to_string(o->port.type_id), + 0, NULL, 0) == REG_NOMATCH) + continue; + } pw_log_debug("add port %d %s", count, o->port.name); res[count++] = o->port.name; @@ -2417,6 +2431,11 @@ const char ** jack_get_ports (jack_client_t *client, } else res[count] = NULL; + if (port_name_pattern && port_name_pattern[0]) + regfree(&port_regex); + if (type_name_pattern && type_name_pattern[0]) + regfree(&type_regex); + return res; } From 796c91440e19ad401d8d39c4a05875045d9921d4 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 24 May 2018 16:12:59 +0200 Subject: [PATCH 013/133] jack: support midi buffers --- src/pipewire-jack.c | 41 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index b0c0616fa..b3aa63e41 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -128,6 +128,16 @@ struct mem { void *ptr; }; +struct midi_buffer { +#define MIDI_BUFFER_MAGIC 0x900df00d + uint32_t magic; + int32_t buffer_size; + uint32_t nframes; + int32_t write_pos; + uint32_t event_count; + uint32_t lost_events; +}; + struct buffer { struct spa_list link; #define BUFFER_FLAG_OUT (1<<0) @@ -918,6 +928,19 @@ static void client_node_port_set_param(void *object, pw_client_node_proxy_done(c->node_proxy, seq, 0); } +static void init_buffer(struct port *p, struct buffer *b) +{ + if (p->object->port.type_id == 1) { + struct midi_buffer *mb = b->datas[0].data; + mb->magic = MIDI_BUFFER_MAGIC; + mb->buffer_size = b->datas[0].maxsize; + mb->nframes = b->datas[0].maxsize / sizeof(float); + mb->write_pos = 0; + mb->event_count = 0; + mb->lost_events = 0; + } +} + static void client_node_port_use_buffers(void *object, uint32_t seq, enum spa_direction direction, @@ -1016,12 +1039,14 @@ static void client_node_port_use_buffers(void *object, d->fd = bm->fd; bm->ref++; b->mem[b->n_mem++] = bm->id; - pw_log_debug(NAME" %p: data %d %u -> fd %d", c, j, bm->id, bm->fd); + pw_log_debug(NAME" %p: data %d %u -> fd %d %d", + c, j, bm->id, bm->fd, d->maxsize); } else if (d->type == t->data.MemPtr) { d->data = SPA_MEMBER(b->ptr, b->map.start + SPA_PTR_TO_INT(d->data), void); d->fd = -1; - pw_log_debug(NAME" %p: data %d %u -> mem %p", c, j, b->id, d->data); + pw_log_debug(NAME" %p: data %d %u -> mem %p %d", + c, j, b->id, d->data, d->maxsize); } else { pw_log_warn("unknown buffer data type %d", d->type); } @@ -1029,9 +1054,13 @@ static void client_node_port_use_buffers(void *object, pw_log_warn(NAME" %p: Failed to mlock memory %p %u: %m", c, d->data, d->maxsize); } + SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT); - if (direction == SPA_DIRECTION_OUTPUT) + if (direction == SPA_DIRECTION_OUTPUT) { + init_buffer(p, b); reuse_buffer(c, mix, b->id); + } + } pw_log_debug("have %d buffers", n_buffers); mix->n_buffers = n_buffers; @@ -1940,7 +1969,8 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) if (p->direction == SPA_DIRECTION_INPUT) { spa_list_for_each(mix, &p->mix, port_link) { - pw_log_trace("port %p: mix %d.%d get buffer", p, p->id, mix->id); + pw_log_trace("port %p: mix %d.%d get buffer %d", + p, p->id, mix->id, frames); io = mix->io; if (io == NULL || io->buffer_id >= mix->n_buffers) continue; @@ -1954,7 +1984,8 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) } } else { spa_list_for_each(mix, &p->mix, port_link) { - pw_log_trace("port %p: mix %d.%d get buffer", p, p->id, mix->id); + pw_log_trace("port %p: mix %d.%d get buffer %d", + p, p->id, mix->id, frames); io = mix->io; if (mix->n_buffers == 0 || io == NULL) From 5b05b21865e3ccb5110d5c5c66ae948bd034632c Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 24 May 2018 16:13:23 +0200 Subject: [PATCH 014/133] jack: implement more transport info --- src/pipewire-jack.c | 51 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index b3aa63e41..b5c5eb2bf 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -251,6 +251,10 @@ struct client { void *connect_arg; JackGraphOrderCallback graph_callback; void *graph_arg; + JackSyncCallback sync_callback; + void *sync_arg; + JackTimebaseCallback timebase_callback; + void *timebase_arg; struct pw_client_node_position *position; uint32_t sample_rate; @@ -270,6 +274,8 @@ struct client { bool started; int status; + + jack_position_t jack_position; }; static void init_port_pool(struct client *c, enum spa_direction direction) @@ -667,10 +673,27 @@ on_rtsocket_condition(void *data, int fd, enum spa_io mask) c->srate_callback(c->sample_rate, c->srate_arg); } + c->jack_position.usecs = c->position->nsec/1000; + c->jack_position.frame_rate = c->sample_rate; + c->jack_position.frame = c->position->position / sizeof(float); + + if (c->sync_callback) { + c->sync_callback(JackTransportRolling, + &c->jack_position, c->sync_arg); + } + pw_log_trace("do process %d %d", c->buffer_size, c->sample_rate); if (c->process_callback) c->process_callback(c->buffer_size, c->process_arg); + if (c->timebase_callback) { + c->timebase_callback(JackTransportRolling, + buffer_size, + &c->jack_position, + false, + c->timebase_arg); + } + cmd = 1; write(c->writefd, &cmd, 8); } @@ -1817,12 +1840,16 @@ int jack_set_buffer_size (jack_client_t *client, jack_nframes_t nframes) jack_nframes_t jack_get_sample_rate (jack_client_t *client) { struct client *c = (struct client *) client; + if (c->sample_rate == -1) + return DEFAULT_SAMPLE_RATE; return c->sample_rate; } jack_nframes_t jack_get_buffer_size (jack_client_t *client) { struct client *c = (struct client *) client; + if (c->buffer_size == -1) + return DEFAULT_BUFFER_SIZE; return c->buffer_size; } @@ -2514,14 +2541,14 @@ jack_nframes_t jack_frames_since_cycle_start (const jack_client_t *client) jack_nframes_t jack_frame_time (const jack_client_t *client) { - pw_log_warn("not implemented"); - return 0; + struct client *c = (struct client *) client; + return c->jack_position.frame; } jack_nframes_t jack_last_frame_time (const jack_client_t *client) { - pw_log_warn("not implemented"); - return 0; + struct client *c = (struct client *) client; + return c->jack_position.frame; } int jack_get_cycle_times(const jack_client_t *client, @@ -2577,8 +2604,10 @@ int jack_set_sync_callback (jack_client_t *client, JackSyncCallback sync_callback, void *arg) { - pw_log_warn("not implemented"); - return -ENOTSUP; + struct client *c = (struct client *) client; + c->sync_callback = sync_callback; + c->sync_arg = arg; + return 0; } int jack_set_sync_timeout (jack_client_t *client, @@ -2593,8 +2622,10 @@ int jack_set_timebase_callback (jack_client_t *client, JackTimebaseCallback timebase_callback, void *arg) { - pw_log_warn("not implemented"); - return -ENOTSUP; + struct client *c = (struct client *) client; + c->timebase_callback = timebase_callback; + c->timebase_arg = arg; + return 0; } int jack_transport_locate (jack_client_t *client, @@ -2607,7 +2638,9 @@ int jack_transport_locate (jack_client_t *client, jack_transport_state_t jack_transport_query (const jack_client_t *client, jack_position_t *pos) { - pw_log_warn("not implemented"); + struct client *c = (struct client *) client; + if (pos != NULL) + memcpy(pos, &c->jack_position, sizeof(jack_position_t)); return JackTransportRolling; } From b4d2e47c345f1480e91dc3d9c814e50ab9c67e00 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 25 May 2018 12:48:23 +0200 Subject: [PATCH 015/133] jack: manage io areas better --- src/pipewire-jack.c | 65 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 10 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index b5c5eb2bf..ebb28a9ed 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -52,6 +52,7 @@ #define MAX_BUFFER_DATAS 4u #define MAX_BUFFER_MEMS 4 #define MAX_MIX 4096 +#define MAX_IO 32 #define DEFAULT_SAMPLE_RATE 44100 #define DEFAULT_BUFFER_SIZE 1024 @@ -154,14 +155,20 @@ struct buffer { uint32_t n_mem; }; +struct io { + uint32_t id; + uint32_t memid; +}; + struct mix { struct spa_list link; struct spa_list port_link; uint32_t id; struct port *port; + struct io ios[MAX_IO]; + struct spa_io_buffers *io; - struct spa_io_control_range *ctrl; struct buffer buffers[MAX_BUFFERS]; uint32_t n_buffers; @@ -320,6 +327,7 @@ static void free_object(struct client *c, struct object *o) static struct mix *ensure_mix(struct client *c, struct port *port, uint32_t mix_id) { struct mix *mix; + int i; spa_list_for_each(mix, &port->mix, port_link) { if (mix->id == mix_id) @@ -338,6 +346,8 @@ static struct mix *ensure_mix(struct client *c, struct port *port, uint32_t mix_ mix->port = port; mix->io = NULL; mix->n_buffers = 0; + for (i = 0; i < MAX_IO; i++) + mix->ios[i].id = SPA_ID_INVALID; return mix; } @@ -1100,6 +1110,41 @@ static void client_node_port_command(void *object, { } +static void clear_io(struct client *c, struct io *io) +{ + struct mem *m; + m = find_mem(&c->mems, io->memid); + if (--m->ref == 0) + clear_mem(c, m); + io->id = SPA_ID_INVALID; +} + +static struct io *update_io(struct client *c, struct mix *mix, + uint32_t id, uint32_t memid) +{ + int i; + struct io *io, *f = NULL; + + for (i = 0; i < MAX_IO; i++) { + io = &mix->ios[i]; + if (io->id == SPA_ID_INVALID) + f = io; + else if (io->id == id) { + if (io->memid != memid) + clear_io(c, io); + f = io; + break; + } + } + if (f == NULL) + return NULL; + + io = f; + io->id = id; + io->memid = memid; + return io; +} + static void client_node_port_set_io(void *object, uint32_t seq, enum spa_direction direction, @@ -1118,6 +1163,11 @@ static void client_node_port_set_io(void *object, void *ptr; int res = 0; + if ((mix = ensure_mix(c, p, mix_id)) == NULL) { + res = -ENOMEM; + goto exit; + } + if (mem_id == SPA_ID_INVALID) { ptr = NULL; size = 0; @@ -1133,19 +1183,14 @@ static void client_node_port_set_io(void *object, res = -errno; goto exit; } + m->ref++; } - if ((mix = ensure_mix(c, p, mix_id)) == NULL) { - res = -ENOMEM; - goto exit; - } + update_io(c, mix, id, mem_id); - if (id == t->io.Buffers) { + if (id == t->io.Buffers) mix->io = ptr; - } - else if (id == t->io.ControlRange) { - mix->ctrl = ptr; - } + pw_log_debug("port %p: set io id %u %u %u %u %p", p, id, mem_id, offset, size, ptr); exit: From 261452d4000a38c9b88d0f72f0b7cc7eb10edd09 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 7 Jun 2018 11:03:00 +0200 Subject: [PATCH 016/133] jack: fix for position and properties --- src/pipewire-jack.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index ebb28a9ed..995da71bd 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -264,6 +264,7 @@ struct client { void *timebase_arg; struct pw_client_node_position *position; + struct pw_driver_quantum *quantum; uint32_t sample_rate; uint32_t buffer_size; @@ -667,7 +668,7 @@ on_rtsocket_condition(void *data, int fd, enum spa_io mask) if (read(fd, &cmd, sizeof(uint64_t)) != sizeof(uint64_t)) pw_log_warn("jack %p: read failed %m", c); - buffer_size = c->position->duration / sizeof(float); + buffer_size = c->quantum->size / sizeof(float); if (buffer_size != c->buffer_size) { pw_log_info("jack %p: buffersize %d", c, buffer_size); c->buffer_size = buffer_size; @@ -675,7 +676,7 @@ on_rtsocket_condition(void *data, int fd, enum spa_io mask) c->bufsize_callback(c->buffer_size, c->bufsize_arg); } - sample_rate = c->position->rate.denom; + sample_rate = c->quantum->rate.denom; if (sample_rate != c->sample_rate) { pw_log_info("jack %p: sample_rate %d", c, sample_rate); c->sample_rate = sample_rate; @@ -683,9 +684,9 @@ on_rtsocket_condition(void *data, int fd, enum spa_io mask) c->srate_callback(c->sample_rate, c->srate_arg); } - c->jack_position.usecs = c->position->nsec/1000; + c->jack_position.usecs = c->quantum->nsec/1000; c->jack_position.frame_rate = c->sample_rate; - c->jack_position.frame = c->position->position / sizeof(float); + c->jack_position.frame = c->quantum->position / sizeof(float); if (c->sync_callback) { c->sync_callback(JackTransportRolling, @@ -1222,6 +1223,7 @@ static void client_node_set_position(void *object, } pw_log_debug("client %p: set position %p", c, ptr); c->position = ptr; + c->quantum = SPA_MEMBER(ptr, sizeof(struct pw_client_node_position), void); } @@ -1495,6 +1497,7 @@ jack_client_t * jack_client_open (const char *client_name, client->remote = pw_remote_new(client->context.core, pw_properties_new( "client.name", client_name, + "client.api", "jack", NULL), 0); @@ -1549,7 +1552,7 @@ jack_client_t * jack_client_open (const char *client_name, pw_client_node_proxy_update(client->node_proxy, PW_CLIENT_NODE_UPDATE_MAX_INPUTS | PW_CLIENT_NODE_UPDATE_MAX_OUTPUTS, - 0, 0, 0, NULL); + 0, 0, 0, NULL, NULL); pw_client_node_proxy_done(client->node_proxy, 0, 0); From 653a28a21e841484ad2b1f9b832205e9c2ffae0b Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 3 Jul 2018 21:58:34 +0200 Subject: [PATCH 017/133] jack: quantum is expressed in rate now --- src/pipewire-jack.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 995da71bd..bcb8a94e5 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -668,7 +668,7 @@ on_rtsocket_condition(void *data, int fd, enum spa_io mask) if (read(fd, &cmd, sizeof(uint64_t)) != sizeof(uint64_t)) pw_log_warn("jack %p: read failed %m", c); - buffer_size = c->quantum->size / sizeof(float); + buffer_size = c->quantum->size; if (buffer_size != c->buffer_size) { pw_log_info("jack %p: buffersize %d", c, buffer_size); c->buffer_size = buffer_size; @@ -686,7 +686,7 @@ on_rtsocket_condition(void *data, int fd, enum spa_io mask) c->jack_position.usecs = c->quantum->nsec/1000; c->jack_position.frame_rate = c->sample_rate; - c->jack_position.frame = c->quantum->position / sizeof(float); + c->jack_position.frame = c->quantum->position; if (c->sync_callback) { c->sync_callback(JackTransportRolling, From 3b8b67bc99bcc5b181ed2d42f5d5f2dca2922d27 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 4 Jul 2018 18:55:28 +0200 Subject: [PATCH 018/133] jack: update properties --- src/pipewire-jack.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index bcb8a94e5..3d708d3fa 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -1453,7 +1453,7 @@ jack_client_t * jack_client_open (const char *client_name, struct client *client; bool busy = true; struct spa_dict props; - struct spa_dict_item items[2]; + struct spa_dict_item items[4]; int i; pw_log_debug("client open %s %d", client_name, options); @@ -1534,6 +1534,9 @@ jack_client_t * jack_client_open (const char *client_name, props = SPA_DICT_INIT(items, 0); items[props.n_items++] = SPA_DICT_ITEM_INIT("node.name", client_name); + items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_NODE_PROP_MEDIA, "Audio"); + items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_NODE_PROP_CATEGORY, "Duplex"); + items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_NODE_PROP_ROLE, "DSP"); client->node_proxy = pw_core_proxy_create_object(client->core_proxy, "client-node", From 4f054f9551c26d8fcf038a66cd6647f33e97e170 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Sat, 7 Jul 2018 10:47:49 +0200 Subject: [PATCH 019/133] jack: fix refcount of memory --- src/pipewire-jack.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 3d708d3fa..0c5b4a027 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -534,6 +534,17 @@ static struct mem *find_mem(struct pw_array *mems, uint32_t id) return NULL; } +static struct mem *find_mem_ptr(struct pw_array *mems, void *ptr) +{ + struct mem *m; + pw_array_for_each(m, mems) { + if (m->ptr == ptr) + return m; + } + return NULL; +} + + static void *mem_map(struct client *c, struct mem *m, uint32_t offset, uint32_t size) { struct pw_map_range map; @@ -1220,8 +1231,14 @@ static void client_node_set_position(void *object, if ((ptr = mem_map(c, m, offset, size)) == NULL) { return; } + m->ref++; } pw_log_debug("client %p: set position %p", c, ptr); + if (ptr == NULL && c->position) { + m = find_mem_ptr(&c->mems, c->position); + if (m && --m->ref == 0) + clear_mem(c, m); + } c->position = ptr; c->quantum = SPA_MEMBER(ptr, sizeof(struct pw_client_node_position), void); } From 16178eb5de076e820ba6fb5492194726239c5c31 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 12 Jul 2018 15:32:13 +0200 Subject: [PATCH 020/133] jack: use set_io --- src/pipewire-jack.c | 90 +++++++++++++++++++++++++-------------------- 1 file changed, 50 insertions(+), 40 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 0c5b4a027..fc5d3e2a5 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -67,6 +67,7 @@ struct port; struct type { uint32_t client_node; + uint32_t client_node_position; struct spa_type_media_type media_type; struct spa_type_media_subtype media_subtype; struct spa_type_format_audio format_audio; @@ -77,10 +78,11 @@ struct type { static inline void init_type(struct type *type, struct spa_type_map *map) { type->client_node = spa_type_map_get_id(map, PW_TYPE_INTERFACE__ClientNode); - 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); + type->client_node_position = spa_type_map_get_id(map, PW_TYPE_CLIENT_NODE_IO__Position); + 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); spa_type_media_subtype_audio_map(map, &type->media_subtype_audio); } @@ -769,6 +771,49 @@ static void client_node_set_param(void *object, uint32_t seq, pw_client_node_proxy_done(c->node_proxy, seq, -ENOTSUP); } +static void client_node_set_io(void *object, + uint32_t id, + uint32_t mem_id, + uint32_t offset, + uint32_t size) +{ + struct client *c = (struct client *) object; + struct pw_type *t = c->context.t; + struct mem *m; + void *ptr; + + if (mem_id == SPA_ID_INVALID) { + ptr = NULL; + size = 0; + } + else { + m = find_mem(&c->mems, mem_id); + if (m == NULL) { + pw_log_warn("unknown memory id %u", mem_id); + return; + } + if ((ptr = mem_map(c, m, offset, size)) == NULL) { + return; + } + m->ref++; + } + pw_log_debug("client %p: set io %s %p", c, + spa_type_map_get_type(t->map, id), ptr); + + if (id == c->type.client_node_position) { + if (ptr == NULL && c->position) { + m = find_mem_ptr(&c->mems, c->position); + if (m && --m->ref == 0) + clear_mem(c, m); + } + c->position = ptr; + if (ptr) + c->quantum = SPA_MEMBER(ptr, sizeof(struct pw_client_node_position), void); + else + c->quantum = NULL; + } +} + static void client_node_event(void *object, const struct spa_event *event) { } @@ -1209,46 +1254,12 @@ static void client_node_port_set_io(void *object, pw_client_node_proxy_done(c->node_proxy, seq, res); } -static void client_node_set_position(void *object, - uint32_t mem_id, - uint32_t offset, - uint32_t size) -{ - struct client *c = (struct client *) object; - struct mem *m; - void *ptr; - - if (mem_id == SPA_ID_INVALID) { - ptr = NULL; - size = 0; - } - else { - m = find_mem(&c->mems, mem_id); - if (m == NULL) { - pw_log_warn("unknown memory id %u", mem_id); - return; - } - if ((ptr = mem_map(c, m, offset, size)) == NULL) { - return; - } - m->ref++; - } - pw_log_debug("client %p: set position %p", c, ptr); - if (ptr == NULL && c->position) { - m = find_mem_ptr(&c->mems, c->position); - if (m && --m->ref == 0) - clear_mem(c, m); - } - c->position = ptr; - c->quantum = SPA_MEMBER(ptr, sizeof(struct pw_client_node_position), void); -} - - static const struct pw_client_node_proxy_events client_node_events = { PW_VERSION_CLIENT_NODE_PROXY_EVENTS, .add_mem = client_node_add_mem, .transport = client_node_transport, .set_param = client_node_set_param, + .set_io = client_node_set_io, .event = client_node_event, .command = client_node_command, .add_port = client_node_add_port, @@ -1257,7 +1268,6 @@ static const struct pw_client_node_proxy_events client_node_events = { .port_use_buffers = client_node_port_use_buffers, .port_command = client_node_port_command, .port_set_io = client_node_port_set_io, - .set_position = client_node_set_position, }; static jack_port_type_id_t string_to_type(const char *port_type) From 63cc6e920f83068966d9f9d5d082ba12b003bb64 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 17 Jul 2018 17:39:15 +0200 Subject: [PATCH 021/133] jack: update for rate of the graph --- src/pipewire-jack.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index fc5d3e2a5..d3f65c428 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -689,7 +689,11 @@ on_rtsocket_condition(void *data, int fd, enum spa_io mask) c->bufsize_callback(c->buffer_size, c->bufsize_arg); } - sample_rate = c->quantum->rate.denom; + if (c->quantum->rate.denom != 0) + sample_rate = c->quantum->rate.num / c->quantum->rate.denom; + else + sample_rate = c->sample_rate; + if (sample_rate != c->sample_rate) { pw_log_info("jack %p: sample_rate %d", c, sample_rate); c->sample_rate = sample_rate; @@ -697,9 +701,12 @@ on_rtsocket_condition(void *data, int fd, enum spa_io mask) c->srate_callback(c->sample_rate, c->srate_arg); } + c->jack_position.unique_1++; c->jack_position.usecs = c->quantum->nsec/1000; c->jack_position.frame_rate = c->sample_rate; c->jack_position.frame = c->quantum->position; + c->jack_position.valid = 0; + c->jack_position.unique_2 = c->jack_position.unique_1; if (c->sync_callback) { c->sync_callback(JackTransportRolling, From ac11392fad4fd0121aff7189b18d9c0c84dd4745 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 17 Jul 2018 17:40:07 +0200 Subject: [PATCH 022/133] jack: implement tee --- src/pipewire-jack.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index d3f65c428..7f961bc44 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -2095,6 +2095,7 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) add_f32(ptr, b->datas[0].data, frames); } } else { + b = NULL; spa_list_for_each(mix, &p->mix, port_link) { pw_log_trace("port %p: mix %d.%d get buffer %d", p, p->id, mix->id, frames); @@ -2103,22 +2104,28 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) if (mix->n_buffers == 0 || io == NULL) continue; - io->status = SPA_STATUS_HAVE_BUFFER; if ((b = dequeue_buffer(mix)) == NULL) { pw_log_warn("port %p: out of buffers", p); io->buffer_id = SPA_ID_INVALID; goto done; } reuse_buffer(c, mix, b->id); - io->buffer_id = b->id; ptr = b->datas[0].data; b->datas[0].chunk->offset = 0; b->datas[0].chunk->size = frames * sizeof(float); b->datas[0].chunk->stride = sizeof(float); + io->status = SPA_STATUS_HAVE_BUFFER; + io->buffer_id = b->id; break; } + spa_list_for_each(mix, &p->mix, port_link) { + struct spa_io_buffers *mio = mix->io; + if (mio == NULL) + continue; + *mio = *io; + } } done: From d608819a33d642c18b482995fcf77412504ce007 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 31 Jul 2018 21:42:28 +0200 Subject: [PATCH 023/133] jack: fix mixing Don't write to the input buffer when mixing but use a temp buffer that we keep for each port. Return the right format when negotiated. --- src/pipewire-jack.c | 89 +++++++++++++++++++++++++++++++++------------ 1 file changed, 66 insertions(+), 23 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 7f961bc44..6b8112ad5 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -54,7 +54,7 @@ #define MAX_MIX 4096 #define MAX_IO 32 -#define DEFAULT_SAMPLE_RATE 44100 +#define DEFAULT_SAMPLE_RATE 48000 #define DEFAULT_BUFFER_SIZE 1024 #define MAX_BUFFER_SIZE 2048 @@ -188,6 +188,11 @@ struct port { struct object *object; struct spa_list mix; + + bool have_format; + uint32_t rate; + + float empty[BUFFER_SIZE_MAX + 8]; }; struct context { @@ -280,8 +285,6 @@ struct client { struct pw_array mems; - float empty[BUFFER_SIZE_MAX + 8]; - bool started; int status; @@ -713,7 +716,9 @@ on_rtsocket_condition(void *data, int fd, enum spa_io mask) &c->jack_position, c->sync_arg); } - pw_log_trace("do process %d %d", c->buffer_size, c->sample_rate); + pw_log_trace("do process %d %d %d %"PRIi64, c->buffer_size, c->sample_rate, + c->jack_position.frame, c->quantum->delay); + if (c->process_callback) c->process_callback(c->buffer_size, c->process_arg); @@ -925,9 +930,9 @@ static int param_enum_format(struct client *c, struct port *p, "I", c->type.media_subtype.raw, ":", c->type.format_audio.format, "I", c->type.audio_format.F32, ":", c->type.format_audio.layout, "i", SPA_AUDIO_LAYOUT_NON_INTERLEAVED, - ":", c->type.format_audio.channels, "i", 1, ":", c->type.format_audio.rate, "iru", DEFAULT_SAMPLE_RATE, - SPA_POD_PROP_MIN_MAX(1, INT32_MAX)); + SPA_POD_PROP_MIN_MAX(1, INT32_MAX), + ":", c->type.format_audio.channels, "i", 1); break; case 1: *param = spa_pod_builder_object(b, @@ -948,15 +953,17 @@ static int param_format(struct client *c, struct port *p, switch (p->object->port.type_id) { case 0: + *param = spa_pod_builder_object(b, t->param.idFormat, t->spa_format, "I", c->type.media_type.audio, "I", c->type.media_subtype.raw, ":", c->type.format_audio.format, "I", c->type.audio_format.F32, ":", c->type.format_audio.layout, "i", SPA_AUDIO_LAYOUT_NON_INTERLEAVED, - ":", c->type.format_audio.channels, "i", 1, - ":", c->type.format_audio.rate, "iru", 44100, - SPA_POD_PROP_MIN_MAX(1, INT32_MAX)); + ":", c->type.format_audio.rate, p->have_format ? "iru" : "ir", + p->have_format ? p->rate : DEFAULT_SAMPLE_RATE, + SPA_POD_PROP_MIN_MAX(1, INT32_MAX), + ":", c->type.format_audio.channels, "i", 1); break; case 1: *param = spa_pod_builder_object(b, @@ -987,6 +994,43 @@ static int param_buffers(struct client *c, struct port *p, return 1; } +static int port_set_format(struct client *c, struct port *p, + uint32_t flags, const struct spa_pod *param) +{ + if (param == NULL) { + struct mix *mix; + + pw_log_debug(NAME" %p: port %p clear format", c, p); + + spa_list_for_each(mix, &p->mix, port_link) + clear_buffers(c, mix); + p->have_format = false; + } + else { + struct spa_audio_info info = { 0 }; + + spa_pod_object_parse(param, + "I", &info.media_type, + "I", &info.media_subtype); + + if (info.media_type != c->type.media_type.audio) + return -EINVAL; + + if (info.media_subtype == c->type.media_subtype.raw) { + if (spa_format_audio_raw_parse(param, &info.info.raw, + &c->type.format_audio) < 0) + return -EINVAL; + + p->rate = info.info.raw.rate; + } + if (info.media_subtype != c->type.media_subtype_audio.midi) + return -EINVAL; + + p->have_format = true; + } + return 0; +} + static void client_node_port_set_param(void *object, uint32_t seq, enum spa_direction direction, @@ -1001,13 +1045,8 @@ static void client_node_port_set_param(void *object, uint8_t buffer[4096]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); - if (id == t->param.idFormat && param == NULL) { - struct mix *mix; - - pw_log_debug(NAME" %p: port %p clear format", c, p); - - spa_list_for_each(mix, &p->mix, port_link) - clear_buffers(c, mix); + if (id == t->param.idFormat) { + port_set_format(c, p, flags, param); } param_enum_format(c, p, ¶ms[0], &b); @@ -1178,7 +1217,7 @@ static void clear_io(struct client *c, struct io *io) { struct mem *m; m = find_mem(&c->mems, io->memid); - if (--m->ref == 0) + if (m && --m->ref == 0) clear_mem(c, m); io->id = SPA_ID_INVALID; } @@ -1487,7 +1526,7 @@ jack_client_t * jack_client_open (const char *client_name, struct client *client; bool busy = true; struct spa_dict props; - struct spa_dict_item items[4]; + struct spa_dict_item items[5]; int i; pw_log_debug("client open %s %d", client_name, options); @@ -1571,6 +1610,7 @@ jack_client_t * jack_client_open (const char *client_name, items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_NODE_PROP_MEDIA, "Audio"); items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_NODE_PROP_CATEGORY, "Duplex"); items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_NODE_PROP_ROLE, "DSP"); + items[props.n_items++] = SPA_DICT_ITEM_INIT("node.latency", "128/48000"); client->node_proxy = pw_core_proxy_create_object(client->core_proxy, "client-node", @@ -2055,11 +2095,11 @@ int jack_port_unregister (jack_client_t *client, jack_port_t *port) return res; } -static void add_f32(float *out, float *in, int n_samples) +static void add_f32(float *out, float *in1, float *in2, int n_samples) { int i; for (i = 0; i < n_samples; i++) - out[i] += in[i]; + out[i] = in1[i] + in2[i]; } void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) @@ -2070,14 +2110,15 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) struct buffer *b; struct spa_io_buffers *io; struct mix *mix; - void *ptr = c->empty; int layer = 0; + void *ptr; if (o->type != c->context.t->port || o->port.port_id == SPA_ID_INVALID) { pw_log_error("client %p: invalid port %p", c, port); return NULL; } p = GET_PORT(c, GET_DIRECTION(o->port.flags), o->port.port_id); + ptr = p->empty; if (p->direction == SPA_DIRECTION_INPUT) { spa_list_for_each(mix, &p->mix, port_link) { @@ -2091,8 +2132,10 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) b = &mix->buffers[io->buffer_id]; if (layer++ == 0) ptr = b->datas[0].data; - else - add_f32(ptr, b->datas[0].data, frames); + else { + add_f32(p->empty, ptr, b->datas[0].data, frames); + ptr = p->empty; + } } } else { b = NULL; From 9255315c6adae09decd42bc9ea24478f638ddb20 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 1 Aug 2018 11:56:04 +0200 Subject: [PATCH 024/133] jack: zero empty buffer when needed --- src/pipewire-jack.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 6b8112ad5..440e54dde 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -192,6 +192,7 @@ struct port { bool have_format; uint32_t rate; + bool zeroed; float empty[BUFFER_SIZE_MAX + 8]; }; @@ -2111,14 +2112,13 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) struct spa_io_buffers *io; struct mix *mix; int layer = 0; - void *ptr; + void *ptr = NULL; if (o->type != c->context.t->port || o->port.port_id == SPA_ID_INVALID) { pw_log_error("client %p: invalid port %p", c, port); return NULL; } p = GET_PORT(c, GET_DIRECTION(o->port.flags), o->port.port_id); - ptr = p->empty; if (p->direction == SPA_DIRECTION_INPUT) { spa_list_for_each(mix, &p->mix, port_link) { @@ -2135,6 +2135,7 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) else { add_f32(p->empty, ptr, b->datas[0].data, frames); ptr = p->empty; + p->zeroed = false; } } } else { @@ -2172,6 +2173,13 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) } done: + if (ptr == NULL) { + ptr = p->empty; + if (!p->zeroed) { + memset(ptr, 0, sizeof(p->empty)); + p->zeroed = true; + } + } pw_log_trace("port %p: buffer %p", p, ptr); return ptr; } From 1826d00f82a843f96068709d71f955c22b040eec Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 15 Aug 2018 21:26:05 +0200 Subject: [PATCH 025/133] jack: improve init buffers --- src/pipewire-jack.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 440e54dde..7a6d49452 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -385,6 +385,7 @@ static struct port * alloc_port(struct client *c, enum spa_direction direction) spa_list_append(&c->context.ports, &o->link); p->valid = true; + p->zeroed = false; p->client = c; p->object = o; spa_list_init(&p->mix); @@ -1065,17 +1066,19 @@ static void client_node_port_set_param(void *object, pw_client_node_proxy_done(c->node_proxy, seq, 0); } -static void init_buffer(struct port *p, struct buffer *b) +static void init_buffer(struct port *p, void *data, size_t maxsize) { if (p->object->port.type_id == 1) { - struct midi_buffer *mb = b->datas[0].data; + struct midi_buffer *mb = data; mb->magic = MIDI_BUFFER_MAGIC; - mb->buffer_size = b->datas[0].maxsize; - mb->nframes = b->datas[0].maxsize / sizeof(float); + mb->buffer_size = maxsize; + mb->nframes = maxsize / sizeof(float); mb->write_pos = 0; mb->event_count = 0; mb->lost_events = 0; } + else + memset(data, 0, maxsize); } static void client_node_port_use_buffers(void *object, @@ -1194,7 +1197,7 @@ static void client_node_port_use_buffers(void *object, SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT); if (direction == SPA_DIRECTION_OUTPUT) { - init_buffer(p, b); + init_buffer(p, b->datas[0].data, b->datas[0].maxsize); reuse_buffer(c, mix, b->id); } @@ -2176,7 +2179,7 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) if (ptr == NULL) { ptr = p->empty; if (!p->zeroed) { - memset(ptr, 0, sizeof(p->empty)); + init_buffer(p, ptr, sizeof(p->empty)); p->zeroed = true; } } From 2a0c5c5a8f4f79bde0066c10848c76c21da71b39 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 15 Aug 2018 21:44:32 +0200 Subject: [PATCH 026/133] jack: fix for timing rate update --- src/pipewire-jack.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 7a6d49452..bf4fa39fa 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -694,8 +694,8 @@ on_rtsocket_condition(void *data, int fd, enum spa_io mask) c->bufsize_callback(c->buffer_size, c->bufsize_arg); } - if (c->quantum->rate.denom != 0) - sample_rate = c->quantum->rate.num / c->quantum->rate.denom; + if (c->quantum->rate.num != 0 && c->quantum->rate.denom != 0) + sample_rate = c->quantum->rate.denom / c->quantum->rate.num; else sample_rate = c->sample_rate; From e341b2792def0d1fbd97c22151bb3aa2ff1bf3bf Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 24 Aug 2018 11:01:04 +0200 Subject: [PATCH 027/133] types: update --- src/meson.build | 3 + src/pipewire-jack.c | 173 ++++++++++++++++++-------------------------- 2 files changed, 72 insertions(+), 104 deletions(-) diff --git a/src/meson.build b/src/meson.build index e297c9efd..7d256e126 100644 --- a/src/meson.build +++ b/src/meson.build @@ -7,6 +7,9 @@ pipewire_jack_c_args = [ '-DPIC', ] +#optional dependencies +jack_dep = dependency('jack', version : '>= 1.9.10', required : false) + pipewire_jack = shared_library('jack-pipewire', pipewire_jack_sources, c_args : pipewire_jack_c_args, diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index bf4fa39fa..5c8d03fb4 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -30,8 +30,8 @@ #include #include -#include #include +#include #include #include @@ -65,27 +65,6 @@ struct client; struct port; -struct type { - uint32_t client_node; - uint32_t client_node_position; - 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; - struct spa_type_media_subtype_audio media_subtype_audio; -}; - -static inline void init_type(struct type *type, struct spa_type_map *map) -{ - type->client_node = spa_type_map_get_id(map, PW_TYPE_INTERFACE__ClientNode); - type->client_node_position = spa_type_map_get_id(map, PW_TYPE_CLIENT_NODE_IO__Position); - 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); - spa_type_media_subtype_audio_map(map, &type->media_subtype_audio); -} - struct globals { jack_thread_creator_t creator; }; @@ -200,7 +179,6 @@ struct context { struct pw_main_loop *main; struct pw_thread_loop *loop; struct pw_core *core; - struct pw_type *t; struct pw_map globals; struct spa_list free_objects; @@ -216,8 +194,6 @@ struct context { #define GET_PORT(c,d,p) (d == SPA_DIRECTION_INPUT ? GET_IN_PORT(c,p) : GET_OUT_PORT(c,p)) struct client { - struct type type; - char name[JACK_CLIENT_NAME_SIZE+1]; struct context context; @@ -378,7 +354,7 @@ static struct port * alloc_port(struct client *c, enum spa_direction direction) spa_list_remove(&p->link); o = alloc_object(c); - o->type = c->context.t->port; + o->type = PW_ID_INTERFACE_Port; o->id = SPA_ID_INVALID; o->parent_id = c->node_id; o->port.port_id = p->id; @@ -792,7 +768,6 @@ static void client_node_set_io(void *object, uint32_t size) { struct client *c = (struct client *) object; - struct pw_type *t = c->context.t; struct mem *m; void *ptr; @@ -812,9 +787,9 @@ static void client_node_set_io(void *object, m->ref++; } pw_log_debug("client %p: set io %s %p", c, - spa_type_map_get_type(t->map, id), ptr); + spa_debug_type_find_name(spa_debug_types, id), ptr); - if (id == c->type.client_node_position) { + if (id == PW_ID_IO_ClientNodePosition) { if (ptr == NULL && c->position) { m = find_mem_ptr(&c->mems, c->position); if (m && --m->ref == 0) @@ -835,10 +810,10 @@ static void client_node_event(void *object, const struct spa_event *event) static void client_node_command(void *object, uint32_t seq, const struct spa_command *command) { struct client *c = (struct client *) object; - struct pw_type *t = c->context.t; pw_log_debug("got command %d", SPA_COMMAND_TYPE(command)); - if (SPA_COMMAND_TYPE(command) == t->command_node.Pause) { + switch (SPA_NODE_COMMAND_ID(command)) { + case SPA_NODE_COMMAND_Pause: if (c->started) { pw_loop_update_io(c->context.core->data_loop, c->socket_source, SPA_IO_ERR | SPA_IO_HUP); @@ -846,8 +821,9 @@ static void client_node_command(void *object, uint32_t seq, const struct spa_com c->started = false; } pw_client_node_proxy_done(c->node_proxy, seq, 0); + break; - } else if (SPA_COMMAND_TYPE(command) == t->command_node.Start) { + case SPA_NODE_COMMAND_Start: if (!c->started) { pw_loop_update_io(c->context.core->data_loop, c->socket_source, @@ -855,7 +831,8 @@ static void client_node_command(void *object, uint32_t seq, const struct spa_com c->started = true; } pw_client_node_proxy_done(c->node_proxy, seq, 0); - } else { + break; + default: pw_log_warn("unhandled node command %d", SPA_COMMAND_TYPE(command)); pw_client_node_proxy_done(c->node_proxy, seq, -ENOTSUP); } @@ -922,25 +899,23 @@ static void clear_buffers(struct client *c, struct mix *mix) static int param_enum_format(struct client *c, struct port *p, struct spa_pod **param, struct spa_pod_builder *b) { - struct pw_type *t = c->context.t; - switch (p->object->port.type_id) { case 0: *param = spa_pod_builder_object(b, - t->param.idEnumFormat, t->spa_format, - "I", c->type.media_type.audio, - "I", c->type.media_subtype.raw, - ":", c->type.format_audio.format, "I", c->type.audio_format.F32, - ":", c->type.format_audio.layout, "i", SPA_AUDIO_LAYOUT_NON_INTERLEAVED, - ":", c->type.format_audio.rate, "iru", DEFAULT_SAMPLE_RATE, + SPA_ID_PARAM_EnumFormat, SPA_ID_OBJECT_Format, + "I", SPA_MEDIA_TYPE_audio, + "I", SPA_MEDIA_SUBTYPE_raw, + ":", SPA_FORMAT_AUDIO_format, "I", SPA_AUDIO_FORMAT_F32, + ":", SPA_FORMAT_AUDIO_layout, "i", SPA_AUDIO_LAYOUT_NON_INTERLEAVED, + ":", SPA_FORMAT_AUDIO_rate, "iru", DEFAULT_SAMPLE_RATE, SPA_POD_PROP_MIN_MAX(1, INT32_MAX), - ":", c->type.format_audio.channels, "i", 1); + ":", SPA_FORMAT_AUDIO_channels, "i", 1); break; case 1: *param = spa_pod_builder_object(b, - t->param.idEnumFormat, t->spa_format, - "I", c->type.media_type.audio, - "I", c->type.media_subtype_audio.midi); + SPA_ID_PARAM_EnumFormat, SPA_ID_OBJECT_Format, + "I", SPA_MEDIA_TYPE_stream, + "I", SPA_MEDIA_SUBTYPE_midi); break; default: return -EINVAL; @@ -951,27 +926,25 @@ static int param_enum_format(struct client *c, struct port *p, static int param_format(struct client *c, struct port *p, struct spa_pod **param, struct spa_pod_builder *b) { - struct pw_type *t = c->context.t; - switch (p->object->port.type_id) { case 0: *param = spa_pod_builder_object(b, - t->param.idFormat, t->spa_format, - "I", c->type.media_type.audio, - "I", c->type.media_subtype.raw, - ":", c->type.format_audio.format, "I", c->type.audio_format.F32, - ":", c->type.format_audio.layout, "i", SPA_AUDIO_LAYOUT_NON_INTERLEAVED, - ":", c->type.format_audio.rate, p->have_format ? "iru" : "ir", + SPA_ID_PARAM_Format, SPA_ID_OBJECT_Format, + "I", SPA_MEDIA_TYPE_audio, + "I", SPA_MEDIA_SUBTYPE_raw, + ":", SPA_FORMAT_AUDIO_format, "I", SPA_AUDIO_FORMAT_F32, + ":", SPA_FORMAT_AUDIO_layout, "i", SPA_AUDIO_LAYOUT_NON_INTERLEAVED, + ":", SPA_FORMAT_AUDIO_rate, p->have_format ? "iru" : "ir", p->have_format ? p->rate : DEFAULT_SAMPLE_RATE, SPA_POD_PROP_MIN_MAX(1, INT32_MAX), - ":", c->type.format_audio.channels, "i", 1); + ":", SPA_FORMAT_AUDIO_channels, "i", 1); break; case 1: *param = spa_pod_builder_object(b, - t->param.idFormat, t->spa_format, - "I", c->type.media_type.audio, - "I", c->type.media_subtype_audio.midi); + SPA_ID_PARAM_Format, SPA_ID_OBJECT_Format, + "I", SPA_MEDIA_TYPE_stream, + "I", SPA_MEDIA_SUBTYPE_midi); break; default: return -EINVAL; @@ -982,17 +955,15 @@ static int param_format(struct client *c, struct port *p, static int param_buffers(struct client *c, struct port *p, struct spa_pod **param, struct spa_pod_builder *b) { - struct pw_type *t = c->context.t; - *param = spa_pod_builder_object(b, - t->param.idBuffers, t->param_buffers.Buffers, - ":", t->param_buffers.size, "isu", MAX_BUFFER_SIZE * sizeof(float), - SPA_POD_PROP_STEP(4, INT32_MAX, 4), - ":", t->param_buffers.stride, "i", 4, - ":", t->param_buffers.buffers, "iru", 1, + SPA_ID_PARAM_Buffers, SPA_ID_OBJECT_ParamBuffers, + ":", SPA_PARAM_BUFFERS_buffers, "iru", 1, SPA_POD_PROP_MIN_MAX(1, MAX_BUFFERS), - ":", t->param_buffers.align, "i", 16); - + ":", SPA_PARAM_BUFFERS_blocks, "i", 1, + ":", SPA_PARAM_BUFFERS_size, "isu", MAX_BUFFER_SIZE * sizeof(float), + SPA_POD_PROP_STEP(4, INT32_MAX, 4), + ":", SPA_PARAM_BUFFERS_stride, "i", 4, + ":", SPA_PARAM_BUFFERS_align, "i", 16); return 1; } @@ -1015,19 +986,22 @@ static int port_set_format(struct client *c, struct port *p, "I", &info.media_type, "I", &info.media_subtype); - if (info.media_type != c->type.media_type.audio) - return -EINVAL; + switch (info.media_type) { + case SPA_MEDIA_TYPE_audio: + if (info.media_subtype != SPA_MEDIA_SUBTYPE_raw) + return -EINVAL; - if (info.media_subtype == c->type.media_subtype.raw) { - if (spa_format_audio_raw_parse(param, &info.info.raw, - &c->type.format_audio) < 0) + if (spa_format_audio_raw_parse(param, &info.info.raw) < 0) return -EINVAL; p->rate = info.info.raw.rate; - } - if (info.media_subtype != c->type.media_subtype_audio.midi) - return -EINVAL; + break; + case SPA_MEDIA_TYPE_stream: + break; + default: + return -EINVAL; + } p->have_format = true; } return 0; @@ -1042,12 +1016,11 @@ static void client_node_port_set_param(void *object, { struct client *c = (struct client *) object; struct port *p = GET_PORT(c, direction, port_id); - struct pw_type *t = c->context.t; struct spa_pod *params[4]; uint8_t buffer[4096]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); - if (id == t->param.idFormat) { + if (id == SPA_ID_PARAM_Format) { port_set_format(c, p, flags, param); } @@ -1091,7 +1064,6 @@ static void client_node_port_use_buffers(void *object, { struct client *c = (struct client *) object; struct port *p = GET_PORT(c, direction, port_id); - struct pw_type *t = c->context.t; struct buffer *b; uint32_t i, j, prot, res; struct pw_core *core = c->context.core; @@ -1164,7 +1136,7 @@ static void client_node_port_use_buffers(void *object, SPA_MEMBER(b->ptr, offset + sizeof(struct spa_chunk) * j, struct spa_chunk); - if (d->type == t->data.MemFd || d->type == t->data.DmaBuf) { + if (d->type == SPA_DATA_MemFd || d->type == SPA_DATA_DmaBuf) { struct mem *bm = find_mem(&c->mems, SPA_PTR_TO_UINT32(d->data)); d->data = mmap(NULL, d->maxsize + d->mapoffset, prot, @@ -1181,7 +1153,7 @@ static void client_node_port_use_buffers(void *object, b->mem[b->n_mem++] = bm->id; pw_log_debug(NAME" %p: data %d %u -> fd %d %d", c, j, bm->id, bm->fd, d->maxsize); - } else if (d->type == t->data.MemPtr) { + } else if (d->type == SPA_DATA_MemPtr) { d->data = SPA_MEMBER(b->ptr, b->map.start + SPA_PTR_TO_INT(d->data), void); d->fd = -1; @@ -1264,7 +1236,6 @@ static void client_node_port_set_io(void *object, { struct client *c = (struct client *) object; struct port *p = GET_PORT(c, direction, port_id); - struct pw_type *t = c->context.t; struct mem *m; struct mix *mix; void *ptr; @@ -1295,7 +1266,7 @@ static void client_node_port_set_io(void *object, update_io(c, mix, id, mem_id); - if (id == t->io.Buffers) + if (id == SPA_ID_IO_Buffers) mix->io = ptr; pw_log_debug("port %p: set io id %u %u %u %u %p", p, id, mem_id, offset, size, ptr); @@ -1351,7 +1322,6 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, const struct spa_dict *props) { struct client *c = (struct client *) data; - struct pw_type *t = c->context.t; struct object *o, *ot; const char *str; size_t size; @@ -1359,7 +1329,7 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, if (props == NULL) return; - if (type == t->node) { + if (type == PW_ID_INTERFACE_Node) { if ((str = spa_dict_lookup(props, "node.name")) == NULL) goto exit; @@ -1369,7 +1339,7 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, snprintf(o->node.name, sizeof(o->node.name), "%s/%d", str, id); pw_log_debug("add node %d", id); } - else if (type == t->port) { + else if (type == PW_ID_INTERFACE_Port) { const struct spa_dict_item *item; unsigned long flags = 0; jack_port_type_id_t type_id; @@ -1416,7 +1386,7 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, spa_list_append(&c->context.ports, &o->link); ot = pw_map_lookup(&c->context.globals, parent_id); - if (ot == NULL || ot->type != t->node) + if (ot == NULL || ot->type != PW_ID_INTERFACE_Node) goto exit_free; snprintf(o->port.name, sizeof(o->port.name), "%s:%s", ot->node.name, str); @@ -1438,7 +1408,7 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, pw_log_debug("add port %d %s", id, o->port.name); } - else if (type == t->link) { + else if (type == PW_ID_INTERFACE_Link) { o = alloc_object(c); spa_list_append(&c->context.links, &o->link); @@ -1464,15 +1434,15 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, pw_map_insert_at(&c->context.globals, size++, NULL); pw_map_insert_at(&c->context.globals, id, o); - if (type == t->node) { + if (type == PW_ID_INTERFACE_Node) { if (c->registration_callback) c->registration_callback(o->node.name, 1, c->registration_arg); } - else if (type == t->port) { + else if (type == PW_ID_INTERFACE_Port) { if (c->portregistration_callback) c->portregistration_callback(o->id, 1, c->portregistration_arg); } - else if (type == t->link) { + else if (type == PW_ID_INTERFACE_Link) { if (c->connect_callback) c->connect_callback(o->port_link.src, o->port_link.dst, 1, c->connect_arg); } @@ -1487,7 +1457,6 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, static void registry_event_global_remove(void *object, uint32_t id) { struct client *c = (struct client *) object; - struct pw_type *t = c->context.t; struct object *o; pw_log_debug("removed: %u", id); @@ -1496,15 +1465,15 @@ static void registry_event_global_remove(void *object, uint32_t id) if (o == NULL) return; - if (o->type == t->node) { + if (o->type == PW_ID_INTERFACE_Node) { if (c->registration_callback) c->registration_callback(o->node.name, 0, c->registration_arg); } - else if (o->type == t->port) { + else if (o->type == PW_ID_INTERFACE_Port) { if (c->portregistration_callback) c->portregistration_callback(o->id, 0, c->portregistration_arg); } - else if (o->type == t->link) { + else if (o->type == PW_ID_INTERFACE_Link) { if (c->connect_callback) c->connect_callback(o->port_link.src, o->port_link.dst, 0, c->connect_arg); } @@ -1544,8 +1513,6 @@ jack_client_t * jack_client_open (const char *client_name, client->context.main = pw_main_loop_new(NULL); client->context.loop = pw_thread_loop_new(pw_main_loop_get_loop(client->context.main), client_name); client->context.core = pw_core_new(pw_thread_loop_get_loop(client->context.loop), NULL); - client->context.t = pw_core_get_type(client->context.core); - init_type(&client->type, client->context.t->map); spa_list_init(&client->context.free_objects); spa_list_init(&client->context.nodes); spa_list_init(&client->context.ports); @@ -1602,7 +1569,7 @@ jack_client_t * jack_client_open (const char *client_name, } client->core_proxy = pw_remote_get_core_proxy(client->remote); client->registry_proxy = pw_core_proxy_get_registry(client->core_proxy, - client->context.t->registry, + PW_ID_INTERFACE_Registry, PW_VERSION_REGISTRY, 0); pw_registry_proxy_add_listener(client->registry_proxy, &client->registry_listener, @@ -1618,7 +1585,7 @@ jack_client_t * jack_client_open (const char *client_name, client->node_proxy = pw_core_proxy_create_object(client->core_proxy, "client-node", - client->type.client_node, + PW_ID_INTERFACE_ClientNode, PW_VERSION_CLIENT_NODE, &props, 0); @@ -2075,7 +2042,7 @@ int jack_port_unregister (jack_client_t *client, jack_port_t *port) struct port *p; int res; - if (o->type != c->context.t->port || o->port.port_id == SPA_ID_INVALID) { + if (o->type != PW_ID_INTERFACE_Port || o->port.port_id == SPA_ID_INVALID) { pw_log_error("client %p: invalid port %p", client, port); return -EINVAL; } @@ -2117,7 +2084,7 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) int layer = 0; void *ptr = NULL; - if (o->type != c->context.t->port || o->port.port_id == SPA_ID_INVALID) { + if (o->type != PW_ID_INTERFACE_Port || o->port.port_id == SPA_ID_INVALID) { pw_log_error("client %p: invalid port %p", c, port); return NULL; } @@ -2226,8 +2193,7 @@ jack_port_type_id_t jack_port_type_id (const jack_port_t *port) int jack_port_is_mine (const jack_client_t *client, const jack_port_t *port) { struct object *o = (struct object *) port; - struct client *c = (struct client *) client; - return o->type == c->context.t->port && o->port.port_id != SPA_ID_INVALID; + return o->type == PW_ID_INTERFACE_Port && o->port.port_id != SPA_ID_INVALID; } int jack_port_connected (const jack_port_t *port) @@ -2406,7 +2372,6 @@ int jack_connect (jack_client_t *client, { struct client *c = (struct client *) client; struct object *src, *dst; - struct pw_type *t = c->context.t; struct spa_dict props; struct spa_dict_item items[4]; char val[4][16]; @@ -2439,7 +2404,7 @@ int jack_connect (jack_client_t *client, pw_core_proxy_create_object(c->core_proxy, "link-factory", - t->link, + PW_ID_INTERFACE_Link, PW_VERSION_LINK, &props, 0); @@ -2668,7 +2633,7 @@ jack_port_t * jack_port_by_id (jack_client_t *client, o = pw_map_lookup(&c->context.globals, port_id); pw_log_debug("client %p: port %d -> %p", c, port_id, o); - if (o == NULL || o->type != c->context.t->port) + if (o == NULL || o->type != PW_ID_INTERFACE_Port) goto exit; res = o; From 7e3df7dc60d50ca336a3dc8dc474965d534d31e0 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 27 Aug 2018 09:06:17 +0200 Subject: [PATCH 028/133] jack: update for API changes --- src/pipewire-jack.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 5c8d03fb4..65de05362 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -789,7 +789,7 @@ static void client_node_set_io(void *object, pw_log_debug("client %p: set io %s %p", c, spa_debug_type_find_name(spa_debug_types, id), ptr); - if (id == PW_ID_IO_ClientNodePosition) { + if (id == PW_IO_ClientNodePosition) { if (ptr == NULL && c->position) { m = find_mem_ptr(&c->mems, c->position); if (m && --m->ref == 0) @@ -902,18 +902,18 @@ static int param_enum_format(struct client *c, struct port *p, switch (p->object->port.type_id) { case 0: *param = spa_pod_builder_object(b, - SPA_ID_PARAM_EnumFormat, SPA_ID_OBJECT_Format, + SPA_PARAM_EnumFormat, SPA_ID_OBJECT_Format, "I", SPA_MEDIA_TYPE_audio, "I", SPA_MEDIA_SUBTYPE_raw, ":", SPA_FORMAT_AUDIO_format, "I", SPA_AUDIO_FORMAT_F32, - ":", SPA_FORMAT_AUDIO_layout, "i", SPA_AUDIO_LAYOUT_NON_INTERLEAVED, + ":", SPA_FORMAT_AUDIO_layout, "I", SPA_AUDIO_LAYOUT_NON_INTERLEAVED, ":", SPA_FORMAT_AUDIO_rate, "iru", DEFAULT_SAMPLE_RATE, SPA_POD_PROP_MIN_MAX(1, INT32_MAX), ":", SPA_FORMAT_AUDIO_channels, "i", 1); break; case 1: *param = spa_pod_builder_object(b, - SPA_ID_PARAM_EnumFormat, SPA_ID_OBJECT_Format, + SPA_PARAM_EnumFormat, SPA_ID_OBJECT_Format, "I", SPA_MEDIA_TYPE_stream, "I", SPA_MEDIA_SUBTYPE_midi); break; @@ -930,11 +930,11 @@ static int param_format(struct client *c, struct port *p, case 0: *param = spa_pod_builder_object(b, - SPA_ID_PARAM_Format, SPA_ID_OBJECT_Format, + SPA_PARAM_Format, SPA_ID_OBJECT_Format, "I", SPA_MEDIA_TYPE_audio, "I", SPA_MEDIA_SUBTYPE_raw, ":", SPA_FORMAT_AUDIO_format, "I", SPA_AUDIO_FORMAT_F32, - ":", SPA_FORMAT_AUDIO_layout, "i", SPA_AUDIO_LAYOUT_NON_INTERLEAVED, + ":", SPA_FORMAT_AUDIO_layout, "I", SPA_AUDIO_LAYOUT_NON_INTERLEAVED, ":", SPA_FORMAT_AUDIO_rate, p->have_format ? "iru" : "ir", p->have_format ? p->rate : DEFAULT_SAMPLE_RATE, SPA_POD_PROP_MIN_MAX(1, INT32_MAX), @@ -942,7 +942,7 @@ static int param_format(struct client *c, struct port *p, break; case 1: *param = spa_pod_builder_object(b, - SPA_ID_PARAM_Format, SPA_ID_OBJECT_Format, + SPA_PARAM_Format, SPA_ID_OBJECT_Format, "I", SPA_MEDIA_TYPE_stream, "I", SPA_MEDIA_SUBTYPE_midi); break; @@ -956,7 +956,7 @@ static int param_buffers(struct client *c, struct port *p, struct spa_pod **param, struct spa_pod_builder *b) { *param = spa_pod_builder_object(b, - SPA_ID_PARAM_Buffers, SPA_ID_OBJECT_ParamBuffers, + SPA_PARAM_Buffers, SPA_ID_OBJECT_ParamBuffers, ":", SPA_PARAM_BUFFERS_buffers, "iru", 1, SPA_POD_PROP_MIN_MAX(1, MAX_BUFFERS), ":", SPA_PARAM_BUFFERS_blocks, "i", 1, @@ -1020,7 +1020,7 @@ static void client_node_port_set_param(void *object, uint8_t buffer[4096]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); - if (id == SPA_ID_PARAM_Format) { + if (id == SPA_PARAM_Format) { port_set_format(c, p, flags, param); } @@ -1266,7 +1266,7 @@ static void client_node_port_set_io(void *object, update_io(c, mix, id, mem_id); - if (id == SPA_ID_IO_Buffers) + if (id == SPA_IO_Buffers) mix->io = ptr; pw_log_debug("port %p: set io id %u %u %u %u %p", p, id, mem_id, offset, size, ptr); From 19336672a979a0b917a969a9e58e8c3066c10cf9 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 29 Aug 2018 09:54:21 +0200 Subject: [PATCH 029/133] jack: update for type changes --- src/pipewire-jack.c | 46 ++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 65de05362..84d1d865a 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -354,7 +354,7 @@ static struct port * alloc_port(struct client *c, enum spa_direction direction) spa_list_remove(&p->link); o = alloc_object(c); - o->type = PW_ID_INTERFACE_Port; + o->type = PW_TYPE_INTERFACE_Port; o->id = SPA_ID_INVALID; o->parent_id = c->node_id; o->port.port_id = p->id; @@ -902,7 +902,7 @@ static int param_enum_format(struct client *c, struct port *p, switch (p->object->port.type_id) { case 0: *param = spa_pod_builder_object(b, - SPA_PARAM_EnumFormat, SPA_ID_OBJECT_Format, + SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, "I", SPA_MEDIA_TYPE_audio, "I", SPA_MEDIA_SUBTYPE_raw, ":", SPA_FORMAT_AUDIO_format, "I", SPA_AUDIO_FORMAT_F32, @@ -913,7 +913,7 @@ static int param_enum_format(struct client *c, struct port *p, break; case 1: *param = spa_pod_builder_object(b, - SPA_PARAM_EnumFormat, SPA_ID_OBJECT_Format, + SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, "I", SPA_MEDIA_TYPE_stream, "I", SPA_MEDIA_SUBTYPE_midi); break; @@ -930,7 +930,7 @@ static int param_format(struct client *c, struct port *p, case 0: *param = spa_pod_builder_object(b, - SPA_PARAM_Format, SPA_ID_OBJECT_Format, + SPA_TYPE_OBJECT_Format, SPA_PARAM_Format, "I", SPA_MEDIA_TYPE_audio, "I", SPA_MEDIA_SUBTYPE_raw, ":", SPA_FORMAT_AUDIO_format, "I", SPA_AUDIO_FORMAT_F32, @@ -942,7 +942,7 @@ static int param_format(struct client *c, struct port *p, break; case 1: *param = spa_pod_builder_object(b, - SPA_PARAM_Format, SPA_ID_OBJECT_Format, + SPA_TYPE_OBJECT_Format, SPA_PARAM_Format, "I", SPA_MEDIA_TYPE_stream, "I", SPA_MEDIA_SUBTYPE_midi); break; @@ -956,7 +956,7 @@ static int param_buffers(struct client *c, struct port *p, struct spa_pod **param, struct spa_pod_builder *b) { *param = spa_pod_builder_object(b, - SPA_PARAM_Buffers, SPA_ID_OBJECT_ParamBuffers, + SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, ":", SPA_PARAM_BUFFERS_buffers, "iru", 1, SPA_POD_PROP_MIN_MAX(1, MAX_BUFFERS), ":", SPA_PARAM_BUFFERS_blocks, "i", 1, @@ -1329,7 +1329,7 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, if (props == NULL) return; - if (type == PW_ID_INTERFACE_Node) { + if (type == PW_TYPE_INTERFACE_Node) { if ((str = spa_dict_lookup(props, "node.name")) == NULL) goto exit; @@ -1339,7 +1339,7 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, snprintf(o->node.name, sizeof(o->node.name), "%s/%d", str, id); pw_log_debug("add node %d", id); } - else if (type == PW_ID_INTERFACE_Port) { + else if (type == PW_TYPE_INTERFACE_Port) { const struct spa_dict_item *item; unsigned long flags = 0; jack_port_type_id_t type_id; @@ -1386,7 +1386,7 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, spa_list_append(&c->context.ports, &o->link); ot = pw_map_lookup(&c->context.globals, parent_id); - if (ot == NULL || ot->type != PW_ID_INTERFACE_Node) + if (ot == NULL || ot->type != PW_TYPE_INTERFACE_Node) goto exit_free; snprintf(o->port.name, sizeof(o->port.name), "%s:%s", ot->node.name, str); @@ -1408,7 +1408,7 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, pw_log_debug("add port %d %s", id, o->port.name); } - else if (type == PW_ID_INTERFACE_Link) { + else if (type == PW_TYPE_INTERFACE_Link) { o = alloc_object(c); spa_list_append(&c->context.links, &o->link); @@ -1434,15 +1434,15 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, pw_map_insert_at(&c->context.globals, size++, NULL); pw_map_insert_at(&c->context.globals, id, o); - if (type == PW_ID_INTERFACE_Node) { + if (type == PW_TYPE_INTERFACE_Node) { if (c->registration_callback) c->registration_callback(o->node.name, 1, c->registration_arg); } - else if (type == PW_ID_INTERFACE_Port) { + else if (type == PW_TYPE_INTERFACE_Port) { if (c->portregistration_callback) c->portregistration_callback(o->id, 1, c->portregistration_arg); } - else if (type == PW_ID_INTERFACE_Link) { + else if (type == PW_TYPE_INTERFACE_Link) { if (c->connect_callback) c->connect_callback(o->port_link.src, o->port_link.dst, 1, c->connect_arg); } @@ -1465,15 +1465,15 @@ static void registry_event_global_remove(void *object, uint32_t id) if (o == NULL) return; - if (o->type == PW_ID_INTERFACE_Node) { + if (o->type == PW_TYPE_INTERFACE_Node) { if (c->registration_callback) c->registration_callback(o->node.name, 0, c->registration_arg); } - else if (o->type == PW_ID_INTERFACE_Port) { + else if (o->type == PW_TYPE_INTERFACE_Port) { if (c->portregistration_callback) c->portregistration_callback(o->id, 0, c->portregistration_arg); } - else if (o->type == PW_ID_INTERFACE_Link) { + else if (o->type == PW_TYPE_INTERFACE_Link) { if (c->connect_callback) c->connect_callback(o->port_link.src, o->port_link.dst, 0, c->connect_arg); } @@ -1569,7 +1569,7 @@ jack_client_t * jack_client_open (const char *client_name, } client->core_proxy = pw_remote_get_core_proxy(client->remote); client->registry_proxy = pw_core_proxy_get_registry(client->core_proxy, - PW_ID_INTERFACE_Registry, + PW_TYPE_INTERFACE_Registry, PW_VERSION_REGISTRY, 0); pw_registry_proxy_add_listener(client->registry_proxy, &client->registry_listener, @@ -1585,7 +1585,7 @@ jack_client_t * jack_client_open (const char *client_name, client->node_proxy = pw_core_proxy_create_object(client->core_proxy, "client-node", - PW_ID_INTERFACE_ClientNode, + PW_TYPE_INTERFACE_ClientNode, PW_VERSION_CLIENT_NODE, &props, 0); @@ -2042,7 +2042,7 @@ int jack_port_unregister (jack_client_t *client, jack_port_t *port) struct port *p; int res; - if (o->type != PW_ID_INTERFACE_Port || o->port.port_id == SPA_ID_INVALID) { + if (o->type != PW_TYPE_INTERFACE_Port || o->port.port_id == SPA_ID_INVALID) { pw_log_error("client %p: invalid port %p", client, port); return -EINVAL; } @@ -2084,7 +2084,7 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) int layer = 0; void *ptr = NULL; - if (o->type != PW_ID_INTERFACE_Port || o->port.port_id == SPA_ID_INVALID) { + if (o->type != PW_TYPE_INTERFACE_Port || o->port.port_id == SPA_ID_INVALID) { pw_log_error("client %p: invalid port %p", c, port); return NULL; } @@ -2193,7 +2193,7 @@ jack_port_type_id_t jack_port_type_id (const jack_port_t *port) int jack_port_is_mine (const jack_client_t *client, const jack_port_t *port) { struct object *o = (struct object *) port; - return o->type == PW_ID_INTERFACE_Port && o->port.port_id != SPA_ID_INVALID; + return o->type == PW_TYPE_INTERFACE_Port && o->port.port_id != SPA_ID_INVALID; } int jack_port_connected (const jack_port_t *port) @@ -2404,7 +2404,7 @@ int jack_connect (jack_client_t *client, pw_core_proxy_create_object(c->core_proxy, "link-factory", - PW_ID_INTERFACE_Link, + PW_TYPE_INTERFACE_Link, PW_VERSION_LINK, &props, 0); @@ -2633,7 +2633,7 @@ jack_port_t * jack_port_by_id (jack_client_t *client, o = pw_map_lookup(&c->context.globals, port_id); pw_log_debug("client %p: port %d -> %p", c, port_id, o); - if (o == NULL || o->type != PW_ID_INTERFACE_Port) + if (o == NULL || o->type != PW_TYPE_INTERFACE_Port) goto exit; res = o; From fd974526540ff567fb2334ec59dc52ce8072074d Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Sat, 1 Sep 2018 14:12:24 +0200 Subject: [PATCH 030/133] update for format change --- src/pipewire-jack.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 84d1d865a..cc682b1f1 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -787,7 +787,7 @@ static void client_node_set_io(void *object, m->ref++; } pw_log_debug("client %p: set io %s %p", c, - spa_debug_type_find_name(spa_debug_types, id), ptr); + spa_debug_type_find_name(spa_type_io, id), ptr); if (id == PW_IO_ClientNodePosition) { if (ptr == NULL && c->position) { @@ -903,8 +903,8 @@ static int param_enum_format(struct client *c, struct port *p, case 0: *param = spa_pod_builder_object(b, SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, - "I", SPA_MEDIA_TYPE_audio, - "I", SPA_MEDIA_SUBTYPE_raw, + ":", SPA_FORMAT_mediaType, "I", SPA_MEDIA_TYPE_audio, + ":", SPA_FORMAT_mediaSubtype, "I", SPA_MEDIA_SUBTYPE_raw, ":", SPA_FORMAT_AUDIO_format, "I", SPA_AUDIO_FORMAT_F32, ":", SPA_FORMAT_AUDIO_layout, "I", SPA_AUDIO_LAYOUT_NON_INTERLEAVED, ":", SPA_FORMAT_AUDIO_rate, "iru", DEFAULT_SAMPLE_RATE, @@ -914,8 +914,8 @@ static int param_enum_format(struct client *c, struct port *p, case 1: *param = spa_pod_builder_object(b, SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, - "I", SPA_MEDIA_TYPE_stream, - "I", SPA_MEDIA_SUBTYPE_midi); + ":", SPA_FORMAT_mediaType, "I", SPA_MEDIA_TYPE_stream, + ":", SPA_FORMAT_mediaSubtype, "I", SPA_MEDIA_SUBTYPE_midi); break; default: return -EINVAL; @@ -931,8 +931,8 @@ static int param_format(struct client *c, struct port *p, *param = spa_pod_builder_object(b, SPA_TYPE_OBJECT_Format, SPA_PARAM_Format, - "I", SPA_MEDIA_TYPE_audio, - "I", SPA_MEDIA_SUBTYPE_raw, + ":", SPA_FORMAT_mediaType, "I", SPA_MEDIA_TYPE_audio, + ":", SPA_FORMAT_mediaSubtype, "I", SPA_MEDIA_SUBTYPE_raw, ":", SPA_FORMAT_AUDIO_format, "I", SPA_AUDIO_FORMAT_F32, ":", SPA_FORMAT_AUDIO_layout, "I", SPA_AUDIO_LAYOUT_NON_INTERLEAVED, ":", SPA_FORMAT_AUDIO_rate, p->have_format ? "iru" : "ir", @@ -943,8 +943,8 @@ static int param_format(struct client *c, struct port *p, case 1: *param = spa_pod_builder_object(b, SPA_TYPE_OBJECT_Format, SPA_PARAM_Format, - "I", SPA_MEDIA_TYPE_stream, - "I", SPA_MEDIA_SUBTYPE_midi); + ":", SPA_FORMAT_mediaType, "I", SPA_MEDIA_TYPE_stream, + ":", SPA_FORMAT_mediaSubtype, "I", SPA_MEDIA_SUBTYPE_midi); break; default: return -EINVAL; From 0673a1a4f8adeb14851cefafb0c807484ca7d9ab Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 5 Sep 2018 16:47:46 +0200 Subject: [PATCH 031/133] jack: API changes --- src/pipewire-jack.c | 54 +++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index cc682b1f1..802539bcc 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -903,19 +903,20 @@ static int param_enum_format(struct client *c, struct port *p, case 0: *param = spa_pod_builder_object(b, SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, - ":", SPA_FORMAT_mediaType, "I", SPA_MEDIA_TYPE_audio, - ":", SPA_FORMAT_mediaSubtype, "I", SPA_MEDIA_SUBTYPE_raw, - ":", SPA_FORMAT_AUDIO_format, "I", SPA_AUDIO_FORMAT_F32, - ":", SPA_FORMAT_AUDIO_layout, "I", SPA_AUDIO_LAYOUT_NON_INTERLEAVED, - ":", SPA_FORMAT_AUDIO_rate, "iru", DEFAULT_SAMPLE_RATE, - SPA_POD_PROP_MIN_MAX(1, INT32_MAX), - ":", SPA_FORMAT_AUDIO_channels, "i", 1); + SPA_FORMAT_mediaType, &SPA_POD_Id(SPA_MEDIA_TYPE_audio), + SPA_FORMAT_mediaSubtype, &SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), + SPA_FORMAT_AUDIO_format, &SPA_POD_Id(SPA_AUDIO_FORMAT_F32), + SPA_FORMAT_AUDIO_layout, &SPA_POD_Id(SPA_AUDIO_LAYOUT_NON_INTERLEAVED), + SPA_FORMAT_AUDIO_rate, &SPA_POD_CHOICE_RANGE_Int(DEFAULT_SAMPLE_RATE, 1, INT32_MAX), + SPA_FORMAT_AUDIO_channels, &SPA_POD_Int(1), + 0); break; case 1: *param = spa_pod_builder_object(b, SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, - ":", SPA_FORMAT_mediaType, "I", SPA_MEDIA_TYPE_stream, - ":", SPA_FORMAT_mediaSubtype, "I", SPA_MEDIA_SUBTYPE_midi); + SPA_FORMAT_mediaType, &SPA_POD_Id(SPA_MEDIA_TYPE_stream), + SPA_FORMAT_mediaSubtype, &SPA_POD_Id(SPA_MEDIA_SUBTYPE_midi), + 0); break; default: return -EINVAL; @@ -931,20 +932,22 @@ static int param_format(struct client *c, struct port *p, *param = spa_pod_builder_object(b, SPA_TYPE_OBJECT_Format, SPA_PARAM_Format, - ":", SPA_FORMAT_mediaType, "I", SPA_MEDIA_TYPE_audio, - ":", SPA_FORMAT_mediaSubtype, "I", SPA_MEDIA_SUBTYPE_raw, - ":", SPA_FORMAT_AUDIO_format, "I", SPA_AUDIO_FORMAT_F32, - ":", SPA_FORMAT_AUDIO_layout, "I", SPA_AUDIO_LAYOUT_NON_INTERLEAVED, - ":", SPA_FORMAT_AUDIO_rate, p->have_format ? "iru" : "ir", - p->have_format ? p->rate : DEFAULT_SAMPLE_RATE, - SPA_POD_PROP_MIN_MAX(1, INT32_MAX), - ":", SPA_FORMAT_AUDIO_channels, "i", 1); + SPA_FORMAT_mediaType, &SPA_POD_Id(SPA_MEDIA_TYPE_audio), + SPA_FORMAT_mediaSubtype, &SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), + SPA_FORMAT_AUDIO_format, &SPA_POD_Id(SPA_AUDIO_FORMAT_F32), + SPA_FORMAT_AUDIO_layout, &SPA_POD_Id(SPA_AUDIO_LAYOUT_NON_INTERLEAVED), + SPA_FORMAT_AUDIO_rate, p->have_format ? + (void*)&SPA_POD_Int(p->rate) : + (void*)&SPA_POD_CHOICE_RANGE_Int(DEFAULT_SAMPLE_RATE, 1, INT32_MAX), + SPA_FORMAT_AUDIO_channels, &SPA_POD_Int(1), + 0); break; case 1: *param = spa_pod_builder_object(b, SPA_TYPE_OBJECT_Format, SPA_PARAM_Format, - ":", SPA_FORMAT_mediaType, "I", SPA_MEDIA_TYPE_stream, - ":", SPA_FORMAT_mediaSubtype, "I", SPA_MEDIA_SUBTYPE_midi); + SPA_FORMAT_mediaType, &SPA_POD_Id(SPA_MEDIA_TYPE_stream), + SPA_FORMAT_mediaSubtype, &SPA_POD_Id(SPA_MEDIA_SUBTYPE_midi), + 0); break; default: return -EINVAL; @@ -957,13 +960,12 @@ static int param_buffers(struct client *c, struct port *p, { *param = spa_pod_builder_object(b, SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, - ":", SPA_PARAM_BUFFERS_buffers, "iru", 1, - SPA_POD_PROP_MIN_MAX(1, MAX_BUFFERS), - ":", SPA_PARAM_BUFFERS_blocks, "i", 1, - ":", SPA_PARAM_BUFFERS_size, "isu", MAX_BUFFER_SIZE * sizeof(float), - SPA_POD_PROP_STEP(4, INT32_MAX, 4), - ":", SPA_PARAM_BUFFERS_stride, "i", 4, - ":", SPA_PARAM_BUFFERS_align, "i", 16); + SPA_PARAM_BUFFERS_buffers, &SPA_POD_CHOICE_RANGE_Int(1, 1, MAX_BUFFERS), + SPA_PARAM_BUFFERS_blocks, &SPA_POD_Int(1), + SPA_PARAM_BUFFERS_size, &SPA_POD_CHOICE_STEP_Int(MAX_BUFFER_SIZE * sizeof(float), 4, INT32_MAX, 4), + SPA_PARAM_BUFFERS_stride, &SPA_POD_Int(4), + SPA_PARAM_BUFFERS_align, &SPA_POD_Int(16), + 0); return 1; } From 049e10b2c0d03f193530e928dfd6977b2d8b4ea4 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 17 Sep 2018 09:50:19 +0200 Subject: [PATCH 032/133] jack: update for format changes --- src/pipewire-jack.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 802539bcc..e7ff44a6d 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -905,8 +905,7 @@ static int param_enum_format(struct client *c, struct port *p, SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, SPA_FORMAT_mediaType, &SPA_POD_Id(SPA_MEDIA_TYPE_audio), SPA_FORMAT_mediaSubtype, &SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), - SPA_FORMAT_AUDIO_format, &SPA_POD_Id(SPA_AUDIO_FORMAT_F32), - SPA_FORMAT_AUDIO_layout, &SPA_POD_Id(SPA_AUDIO_LAYOUT_NON_INTERLEAVED), + SPA_FORMAT_AUDIO_format, &SPA_POD_Id(SPA_AUDIO_FORMAT_F32P), SPA_FORMAT_AUDIO_rate, &SPA_POD_CHOICE_RANGE_Int(DEFAULT_SAMPLE_RATE, 1, INT32_MAX), SPA_FORMAT_AUDIO_channels, &SPA_POD_Int(1), 0); @@ -934,12 +933,12 @@ static int param_format(struct client *c, struct port *p, SPA_TYPE_OBJECT_Format, SPA_PARAM_Format, SPA_FORMAT_mediaType, &SPA_POD_Id(SPA_MEDIA_TYPE_audio), SPA_FORMAT_mediaSubtype, &SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), - SPA_FORMAT_AUDIO_format, &SPA_POD_Id(SPA_AUDIO_FORMAT_F32), - SPA_FORMAT_AUDIO_layout, &SPA_POD_Id(SPA_AUDIO_LAYOUT_NON_INTERLEAVED), + SPA_FORMAT_AUDIO_format, &SPA_POD_Id(SPA_AUDIO_FORMAT_F32P), SPA_FORMAT_AUDIO_rate, p->have_format ? (void*)&SPA_POD_Int(p->rate) : (void*)&SPA_POD_CHOICE_RANGE_Int(DEFAULT_SAMPLE_RATE, 1, INT32_MAX), SPA_FORMAT_AUDIO_channels, &SPA_POD_Int(1), + SPA_FORMAT_AUDIO_position, &SPA_POD_Array(uint32_t, SPA_TYPE_Id, 1, SPA_AUDIO_CHANNEL_MONO), 0); break; case 1: From 7e0b889a11fabb426ab081d5aa3485d5e4dc1074 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 21 Sep 2018 16:47:44 +0200 Subject: [PATCH 033/133] jack: make connections linger Use PIPEWIRE_LATENCY to control the default latency --- src/pipewire-jack.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index e7ff44a6d..54014dcdc 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -57,6 +57,7 @@ #define DEFAULT_SAMPLE_RATE 48000 #define DEFAULT_BUFFER_SIZE 1024 #define MAX_BUFFER_SIZE 2048 +#define DEFAULT_LATENCY SPA_STRINGIFY(DEFAULT_BUFFER_SIZE/DEFAULT_SAMPLE_RATE) #define REAL_JACK_PORT_NAME_SIZE (JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE) @@ -1501,6 +1502,7 @@ jack_client_t * jack_client_open (const char *client_name, bool busy = true; struct spa_dict props; struct spa_dict_item items[5]; + const char *str; int i; pw_log_debug("client open %s %d", client_name, options); @@ -1582,7 +1584,9 @@ jack_client_t * jack_client_open (const char *client_name, items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_NODE_PROP_MEDIA, "Audio"); items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_NODE_PROP_CATEGORY, "Duplex"); items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_NODE_PROP_ROLE, "DSP"); - items[props.n_items++] = SPA_DICT_ITEM_INIT("node.latency", "128/48000"); + if ((str = getenv("PIPEWIRE_LATENCY")) == NULL) + str = DEFAULT_LATENCY; + items[props.n_items++] = SPA_DICT_ITEM_INIT("node.latency", str); client->node_proxy = pw_core_proxy_create_object(client->core_proxy, "client-node", @@ -1634,7 +1638,7 @@ jack_client_t * jack_client_new (const char *client_name) jack_status_t status; if (getenv("JACK_START_SERVER") == NULL) - options |= JackNoStartServer; + options |= JackNoStartServer; return jack_client_open(client_name, options, &status, NULL); } @@ -2374,7 +2378,7 @@ int jack_connect (jack_client_t *client, struct client *c = (struct client *) client; struct object *src, *dst; struct spa_dict props; - struct spa_dict_item items[4]; + struct spa_dict_item items[5]; char val[4][16]; int res; @@ -2402,6 +2406,7 @@ int jack_connect (jack_client_t *client, items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_LINK_OUTPUT_PORT_ID, val[1]); items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_LINK_INPUT_NODE_ID, val[2]); items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_LINK_INPUT_PORT_ID, val[3]); + items[props.n_items++] = SPA_DICT_ITEM_INIT("object.linger", "1"); pw_core_proxy_create_object(c->core_proxy, "link-factory", @@ -2446,7 +2451,7 @@ int jack_disconnect (jack_client_t *client, goto exit; } - pw_core_proxy_destroy(c->core_proxy, l->id); + pw_registry_proxy_destroy(c->registry_proxy, l->id); res = do_sync(c); @@ -2470,7 +2475,7 @@ int jack_port_disconnect (jack_client_t *client, jack_port_t *port) spa_list_for_each(l, &c->context.links, link) { if (l->port_link.src == o->id || l->port_link.dst == o->id) { - pw_core_proxy_destroy(c->core_proxy, l->id); + pw_registry_proxy_destroy(c->registry_proxy, l->id); } } res = do_sync(c); From ea3310bc6dd565cc9070ff57bf2608a1cf705306 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 3 Oct 2018 20:13:57 +0200 Subject: [PATCH 034/133] jack: implement midi to control conversion --- src/pipewire-jack.c | 244 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 236 insertions(+), 8 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 54014dcdc..2c66838f9 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -29,9 +29,11 @@ #include #include #include +#include #include #include +#include #include #include @@ -121,6 +123,17 @@ struct midi_buffer { uint32_t lost_events; }; +#define MIDI_INLINE_MAX 4 + +struct midi_event { + uint16_t time; + uint16_t size; + union { + uint32_t byte_offset; + uint8_t inline_data[MIDI_INLINE_MAX]; + }; +}; + struct buffer { struct spa_list link; #define BUFFER_FLAG_OUT (1<<0) @@ -151,6 +164,10 @@ struct mix { struct io ios[MAX_IO]; struct spa_io_buffers *io; + struct spa_io_sequence *notify; + size_t notify_size; + struct spa_io_sequence *control; + size_t control_size; struct buffer buffers[MAX_BUFFERS]; uint32_t n_buffers; @@ -645,6 +662,41 @@ static void reuse_buffer(struct client *c, struct mix *mix, uint32_t id) } } + +static void convert_midi(void *midi, void *buffer, size_t size) +{ + struct spa_pod_builder b = { 0, }; + uint32_t i, count; + + count = jack_midi_get_event_count(midi); + + spa_pod_builder_init(&b, buffer, size); + spa_pod_builder_push_sequence(&b, 0); + + for (i = 0; i < count; i++) { + jack_midi_event_t ev; + jack_midi_event_get(&ev, midi, i); + spa_pod_builder_control_header(&b, ev.time, SPA_CONTROL_Midi); + spa_pod_builder_bytes(&b, ev.buffer, ev.size); + } + spa_pod_builder_pop(&b); +} + +static void process_tee(struct client *c) +{ + struct port *p; + + spa_list_for_each(p, &c->ports[SPA_DIRECTION_OUTPUT], link) { + struct mix *mix; + spa_list_for_each(mix, &p->mix, port_link) { + if (mix->notify == NULL) + continue; + convert_midi(p->empty, mix->notify, mix->notify_size); + break; + } + } +} + static void on_rtsocket_condition(void *data, int fd, enum spa_io mask) { @@ -695,7 +747,8 @@ on_rtsocket_condition(void *data, int fd, enum spa_io mask) &c->jack_position, c->sync_arg); } - pw_log_trace("do process %d %d %d %"PRIi64, c->buffer_size, c->sample_rate, + pw_log_trace("do process %"PRIu64" %d %d %d %"PRIi64, c->quantum->nsec, + c->buffer_size, c->sample_rate, c->jack_position.frame, c->quantum->delay); if (c->process_callback) @@ -708,6 +761,7 @@ on_rtsocket_condition(void *data, int fd, enum spa_io mask) false, c->timebase_arg); } + process_tee(c); cmd = 1; write(c->writefd, &cmd, 8); @@ -969,6 +1023,36 @@ static int param_buffers(struct client *c, struct port *p, return 1; } +static int param_io(struct client *c, struct port *p, + struct spa_pod **param, struct spa_pod_builder *b) +{ + switch (p->object->port.type_id) { + case 0: + *param = spa_pod_builder_object(b, + SPA_TYPE_OBJECT_ParamIO, SPA_PARAM_IO, + SPA_PARAM_IO_id, &SPA_POD_Id(SPA_IO_Buffers), + SPA_PARAM_IO_size, &SPA_POD_Int(sizeof(struct spa_io_buffers)), + 0); + break; + case 1: + if (p->direction == SPA_DIRECTION_OUTPUT) { + *param = spa_pod_builder_object(b, + SPA_TYPE_OBJECT_ParamIO, SPA_PARAM_IO, + SPA_PARAM_IO_id, &SPA_POD_Id(SPA_IO_Notify), + SPA_PARAM_IO_size, &SPA_POD_Int(BUFFER_SIZE_MAX), + 0); + } else { + *param = spa_pod_builder_object(b, + SPA_TYPE_OBJECT_ParamIO, SPA_PARAM_IO, + SPA_PARAM_IO_id, &SPA_POD_Id(SPA_IO_Control), + SPA_PARAM_IO_size, &SPA_POD_Int(sizeof(struct spa_io_sequence)), + 0); + } + break; + } + return 1; +} + static int port_set_format(struct client *c, struct port *p, uint32_t flags, const struct spa_pod *param) { @@ -1268,8 +1352,21 @@ static void client_node_port_set_io(void *object, update_io(c, mix, id, mem_id); - if (id == SPA_IO_Buffers) + switch (id) { + case SPA_IO_Buffers: mix->io = ptr; + break; + case SPA_IO_Notify: + mix->notify = ptr; + mix->notify_size = size; + break; + case SPA_IO_Control: + mix->control = ptr; + mix->control_size = size; + break; + default: + break; + } pw_log_debug("port %p: set io id %u %u %u %u %p", p, id, mem_id, offset, size, ptr); @@ -1372,6 +1469,10 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, if (pw_properties_parse_bool(item->value)) flags |= JackPortIsTerminal; } + else if (!strcmp(item->key, "port.control")) { + if (pw_properties_parse_bool(item->value)) + type_id = 1; + } } o = NULL; @@ -1982,6 +2083,7 @@ jack_port_t * jack_port_register (jack_client_t *client, uint8_t buffer[1024]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); struct spa_pod *params[4]; + uint32_t n_params = 0; struct port *p; int res; @@ -2008,16 +2110,20 @@ jack_port_t * jack_port_register (jack_client_t *client, spa_list_init(&p->mix); - port_info.flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS | - SPA_PORT_INFO_FLAG_NO_REF; - port_info.props = &dict; dict = SPA_DICT_INIT(items, 0); items[dict.n_items++] = SPA_DICT_ITEM_INIT("port.dsp", port_type); items[dict.n_items++] = SPA_DICT_ITEM_INIT("port.name", port_name); - param_enum_format(c, p, ¶ms[0], &b); - param_buffers(c, p, ¶ms[1], &b); + if (type_id == 1) + port_info.flags = SPA_PORT_INFO_FLAG_NO_REF; + else + port_info.flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS | + SPA_PORT_INFO_FLAG_NO_REF; + + param_enum_format(c, p, ¶ms[n_params++], &b); + param_buffers(c, p, ¶ms[n_params++], &b); + param_io(c, p, ¶ms[n_params++], &b); pw_thread_loop_lock(c->context.loop); @@ -2026,7 +2132,7 @@ jack_port_t * jack_port_register (jack_client_t *client, p->id, PW_CLIENT_NODE_PORT_UPDATE_PARAMS | PW_CLIENT_NODE_PORT_UPDATE_INFO , - 2, + n_params, (const struct spa_pod **) params, &port_info); @@ -2898,6 +3004,128 @@ void jack_set_thread_creator (jack_thread_creator_t creator) globals.creator = creator; } +static inline uint8_t * midi_event_data (void* port_buffer, + const struct midi_event* event) +{ + if (event->size <= MIDI_INLINE_MAX) + return (uint8_t *)event->inline_data; + else + return SPA_MEMBER(port_buffer, event->byte_offset, uint8_t); +} + +uint32_t jack_midi_get_event_count(void* port_buffer) +{ + struct midi_buffer *mb = port_buffer; + return mb->event_count; +} + +int jack_midi_event_get(jack_midi_event_t *event, + void *port_buffer, + uint32_t event_index) +{ + struct midi_buffer *mb = port_buffer; + struct midi_event *ev = SPA_MEMBER(mb, sizeof(*mb), struct midi_event); + ev += event_index; + event->time = ev->time; + event->size = ev->size; + event->buffer = midi_event_data (port_buffer, ev); + return 0; +} + +void jack_midi_clear_buffer(void *port_buffer) +{ + struct midi_buffer *mb = port_buffer; + mb->event_count = 0; + mb->write_pos = 0; + mb->lost_events = 0; +} + +void jack_midi_reset_buffer(void *port_buffer) +{ + jack_midi_clear_buffer(port_buffer); +} + +size_t jack_midi_max_event_size(void* port_buffer) +{ + struct midi_buffer *mb = port_buffer; + size_t buffer_size = mb->buffer_size; + + /* (event_count + 1) below accounts for jack_midi_port_internal_event_t + * which would be needed to store the next event */ + size_t used_size = sizeof(struct midi_buffer) + + mb->write_pos + + ((mb->event_count + 1) + * sizeof(struct midi_event)); + + if (used_size > buffer_size) { + return 0; + } else if ((buffer_size - used_size) < MIDI_INLINE_MAX) { + return MIDI_INLINE_MAX; + } else { + return buffer_size - used_size; + } +} + +jack_midi_data_t* jack_midi_event_reserve(void *port_buffer, + jack_nframes_t time, + size_t data_size) +{ + struct midi_buffer *mb = port_buffer; + struct midi_event *events = SPA_MEMBER(mb, sizeof(*mb), struct midi_event); + size_t buffer_size = mb->buffer_size; + + if (time < 0 || time >= mb->nframes) + goto failed; + + if (mb->event_count > 0 && time < events[mb->event_count - 1].time) + goto failed; + + /* Check if data_size is >0 and there is enough space in the buffer for the event. */ + if (data_size <= 0) { + goto failed; // return NULL? + } else if (jack_midi_max_event_size (port_buffer) < data_size) { + goto failed; + } else { + struct midi_event *ev = &events[mb->event_count]; + uint8_t *res; + + ev->time = time; + ev->size = data_size; + if (data_size <= MIDI_INLINE_MAX) { + res = ev->inline_data; + } else { + mb->write_pos += data_size; + ev->byte_offset = buffer_size - 1 - mb->write_pos; + res = SPA_MEMBER(mb, ev->byte_offset, uint8_t); + } + mb->event_count += 1; + return res; + } +failed: + mb->lost_events++; + return NULL; +} + +int jack_midi_event_write(void *port_buffer, + jack_nframes_t time, + const jack_midi_data_t *data, + size_t data_size) +{ + jack_midi_data_t *retbuf = jack_midi_event_reserve (port_buffer, time, data_size); + if (retbuf) { + memcpy (retbuf, data, data_size); + return 0; + } else { + return ENOBUFS; + } +} + +uint32_t jack_midi_get_lost_event_count(void *port_buffer) +{ + struct midi_buffer *mb = port_buffer; + return mb->lost_events; +} + static void reg(void) __attribute__ ((constructor)); static void reg(void) { From 40cbc278734d5e1eaf9b87c99d72695ecffef138 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 10 Oct 2018 18:00:23 +0200 Subject: [PATCH 035/133] jack: add ringbuffer Add soversion and rename to libjack.so.0 --- src/meson.build | 4 +- src/ringbuffer.c | 283 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 286 insertions(+), 1 deletion(-) create mode 100644 src/ringbuffer.c diff --git a/src/meson.build b/src/meson.build index 7d256e126..ff27f6f73 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,5 +1,6 @@ pipewire_jack_sources = [ 'pipewire-jack.c', + 'ringbuffer.c', ] pipewire_jack_c_args = [ @@ -10,8 +11,9 @@ pipewire_jack_c_args = [ #optional dependencies jack_dep = dependency('jack', version : '>= 1.9.10', required : false) -pipewire_jack = shared_library('jack-pipewire', +pipewire_jack = shared_library('jack', pipewire_jack_sources, + soversion : 0, c_args : pipewire_jack_c_args, include_directories : [configinc], dependencies : [pipewire_dep, jack_dep], diff --git a/src/ringbuffer.c b/src/ringbuffer.c new file mode 100644 index 000000000..3ec9e28b1 --- /dev/null +++ b/src/ringbuffer.c @@ -0,0 +1,283 @@ +/* PipeWire + * Copyright (C) 2018 Wim Taymans + * + * 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 +#include +#include +#include + +#include + +jack_ringbuffer_t *jack_ringbuffer_create(size_t sz) +{ + int power_of_two; + jack_ringbuffer_t *rb; + + rb = calloc(1, sizeof(jack_ringbuffer_t)); + if (rb == NULL) + return NULL; + + for (power_of_two = 1; 1 << power_of_two < sz; power_of_two++); + + rb->size = 1 << power_of_two; + rb->size_mask = rb->size - 1; + if ((rb->buf = calloc(1, rb->size)) == NULL) { + free (rb); + return NULL; + } + rb->mlocked = 0; + + return rb; +} + +void jack_ringbuffer_free(jack_ringbuffer_t *rb) +{ +#ifdef USE_MLOCK + if (rb->mlocked) + munlock (rb->buf, rb->size); +#endif /* USE_MLOCK */ + free (rb->buf); + free (rb); +} + +void jack_ringbuffer_get_read_vector(const jack_ringbuffer_t *rb, + jack_ringbuffer_data_t *vec) +{ + size_t free_cnt; + size_t cnt2; + size_t w, r; + + w = rb->write_ptr; + r = rb->read_ptr; + + if (w > r) + free_cnt = w - r; + else + free_cnt = (w - r + rb->size) & rb->size_mask; + + cnt2 = r + free_cnt; + + if (cnt2 > rb->size) { + vec[0].buf = &(rb->buf[r]); + vec[0].len = rb->size - r; + vec[1].buf = rb->buf; + vec[1].len = cnt2 & rb->size_mask; + } else { + vec[0].buf = &(rb->buf[r]); + vec[0].len = free_cnt; + vec[1].len = 0; + } +} + +void jack_ringbuffer_get_write_vector(const jack_ringbuffer_t *rb, + jack_ringbuffer_data_t *vec) +{ + size_t free_cnt; + size_t cnt2; + size_t w, r; + + w = rb->write_ptr; + r = rb->read_ptr; + + if (w > r) + free_cnt = ((r - w + rb->size) & rb->size_mask) - 1; + else if (w < r) + free_cnt = (r - w) - 1; + else + free_cnt = rb->size - 1; + + cnt2 = w + free_cnt; + + if (cnt2 > rb->size) { + vec[0].buf = &(rb->buf[w]); + vec[0].len = rb->size - w; + vec[1].buf = rb->buf; + vec[1].len = cnt2 & rb->size_mask; + } else { + vec[0].buf = &(rb->buf[w]); + vec[0].len = free_cnt; + vec[1].len = 0; + } +} + +size_t jack_ringbuffer_read(jack_ringbuffer_t *rb, char *dest, size_t cnt) +{ + size_t free_cnt; + size_t cnt2; + size_t to_read; + size_t n1, n2; + + if ((free_cnt = jack_ringbuffer_read_space (rb)) == 0) + return 0; + + to_read = cnt > free_cnt ? free_cnt : cnt; + + cnt2 = rb->read_ptr + to_read; + + if (cnt2 > rb->size) { + n1 = rb->size - rb->read_ptr; + n2 = cnt2 & rb->size_mask; + } else { + n1 = to_read; + n2 = 0; + } + + memcpy (dest, &(rb->buf[rb->read_ptr]), n1); + rb->read_ptr = (rb->read_ptr + n1) & rb->size_mask; + if (n2) { + memcpy (dest + n1, &(rb->buf[rb->read_ptr]), n2); + rb->read_ptr = (rb->read_ptr + n2) & rb->size_mask; + } + return to_read; +} + +size_t jack_ringbuffer_peek(jack_ringbuffer_t *rb, char *dest, size_t cnt) +{ + size_t free_cnt; + size_t cnt2; + size_t to_read; + size_t n1, n2; + size_t tmp_read_ptr; + + tmp_read_ptr = rb->read_ptr; + + if ((free_cnt = jack_ringbuffer_read_space (rb)) == 0) + return 0; + + to_read = cnt > free_cnt ? free_cnt : cnt; + + cnt2 = tmp_read_ptr + to_read; + + if (cnt2 > rb->size) { + n1 = rb->size - tmp_read_ptr; + n2 = cnt2 & rb->size_mask; + } else { + n1 = to_read; + n2 = 0; + } + + memcpy (dest, &(rb->buf[tmp_read_ptr]), n1); + tmp_read_ptr = (tmp_read_ptr + n1) & rb->size_mask; + + if (n2) + memcpy (dest + n1, &(rb->buf[tmp_read_ptr]), n2); + + return to_read; +} + +void jack_ringbuffer_read_advance(jack_ringbuffer_t *rb, size_t cnt) +{ + size_t tmp = (rb->read_ptr + cnt) & rb->size_mask; + rb->read_ptr = tmp; +} + +size_t jack_ringbuffer_read_space(const jack_ringbuffer_t *rb) +{ + size_t w, r; + + w = rb->write_ptr; + r = rb->read_ptr; + + if (w > r) + return w - r; + else + return (w - r + rb->size) & rb->size_mask; +} + +int jack_ringbuffer_mlock(jack_ringbuffer_t *rb) +{ +#ifdef USE_MLOCK + if (mlock (rb->buf, rb->size)) + return -1; +#endif /* USE_MLOCK */ + rb->mlocked = 1; + return 0; +} + +void jack_ringbuffer_reset(jack_ringbuffer_t *rb) +{ + rb->read_ptr = 0; + rb->write_ptr = 0; + memset(rb->buf, 0, rb->size); +} + +void jack_ringbuffer_reset_size (jack_ringbuffer_t * rb, size_t sz) +{ + rb->size = sz; + rb->size_mask = rb->size - 1; + rb->read_ptr = 0; + rb->write_ptr = 0; +} + +size_t jack_ringbuffer_write(jack_ringbuffer_t *rb, const char *src, + size_t cnt) +{ + size_t free_cnt; + size_t cnt2; + size_t to_write; + size_t n1, n2; + + if ((free_cnt = jack_ringbuffer_write_space (rb)) == 0) + return 0; + + to_write = cnt > free_cnt ? free_cnt : cnt; + + cnt2 = rb->write_ptr + to_write; + + if (cnt2 > rb->size) { + n1 = rb->size - rb->write_ptr; + n2 = cnt2 & rb->size_mask; + } else { + n1 = to_write; + n2 = 0; + } + + memcpy (&(rb->buf[rb->write_ptr]), src, n1); + rb->write_ptr = (rb->write_ptr + n1) & rb->size_mask; + if (n2) { + memcpy (&(rb->buf[rb->write_ptr]), src + n1, n2); + rb->write_ptr = (rb->write_ptr + n2) & rb->size_mask; + } + return to_write; +} + +void jack_ringbuffer_write_advance(jack_ringbuffer_t *rb, size_t cnt) +{ + size_t tmp = (rb->write_ptr + cnt) & rb->size_mask; + rb->write_ptr = tmp; +} + +size_t jack_ringbuffer_write_space(const jack_ringbuffer_t *rb) +{ + size_t w, r; + + w = rb->write_ptr; + r = rb->read_ptr; + + if (w > r) + return ((r - w + rb->size) & rb->size_mask) - 1; + else if (w < r) + return (r - w) - 1; + else + return rb->size - 1; +} From 88c824208aab123e135f32e2787f2cca8d1be79d Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 10 Oct 2018 18:50:11 +0200 Subject: [PATCH 036/133] jack: add more functions --- src/meson.build | 3 ++ src/metadata.c | 105 +++++++++++++++++++++++++++++++++++++++++++++++ src/statistics.c | 47 +++++++++++++++++++++ src/uuid.c | 81 ++++++++++++++++++++++++++++++++++++ 4 files changed, 236 insertions(+) create mode 100644 src/metadata.c create mode 100644 src/statistics.c create mode 100644 src/uuid.c diff --git a/src/meson.build b/src/meson.build index ff27f6f73..728cd57a9 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,6 +1,9 @@ pipewire_jack_sources = [ 'pipewire-jack.c', + 'metadata.c', 'ringbuffer.c', + 'statistics.c', + 'uuid.c', ] pipewire_jack_c_args = [ diff --git a/src/metadata.c b/src/metadata.c new file mode 100644 index 000000000..472ea1448 --- /dev/null +++ b/src/metadata.c @@ -0,0 +1,105 @@ +/* PipeWire + * Copyright (C) 2018 Wim Taymans + * + * 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 +#include +#include + +#include + +#include + +int +jack_set_property(jack_client_t*client, + jack_uuid_t subject, + const char* key, + const char* value, + const char* type) +{ + pw_log_warn("not implemented"); + return -1; +} + +int +jack_get_property(jack_uuid_t subject, + const char* key, + char** value, + char** type) +{ + pw_log_warn("not implemented"); + return -1; +} + +void +jack_free_description (jack_description_t* desc, int free_description_itself) +{ + pw_log_warn("not implemented"); +} + +int +jack_get_properties (jack_uuid_t subject, + jack_description_t* desc) +{ + pw_log_warn("not implemented"); + return -1; +} + +int +jack_get_all_properties (jack_description_t** descs) +{ + pw_log_warn("not implemented"); + return -1; +} + +int jack_remove_property (jack_client_t* client, jack_uuid_t subject, const char* key) +{ + pw_log_warn("not implemented"); + return -1; +} + +int jack_remove_properties (jack_client_t* client, jack_uuid_t subject) +{ + pw_log_warn("not implemented"); + return -1; +} + +int jack_remove_all_properties (jack_client_t* client) +{ + pw_log_warn("not implemented"); + return -1; +} + +int jack_set_property_change_callback (jack_client_t* client, + JackPropertyChangeCallback callback, + void* arg) +{ + pw_log_warn("not implemented"); + return -1; +} + +const char* JACK_METADATA_PRETTY_NAME = "http://jackaudio.org/metadata/pretty-name"; +const char* JACK_METADATA_HARDWARE = "http://jackaudio.org/metadata/hardware"; +const char* JACK_METADATA_CONNECTED = "http://jackaudio.org/metadata/connected"; +const char* JACK_METADATA_PORT_GROUP = "http://jackaudio.org/metadata/port-group"; +const char* JACK_METADATA_ICON_SMALL = "http://jackaudio.org/metadata/icon-small"; +const char* JACK_METADATA_ICON_LARGE = "http://jackaudio.org/metadata/icon-large"; diff --git a/src/statistics.c b/src/statistics.c new file mode 100644 index 000000000..06e3c089a --- /dev/null +++ b/src/statistics.c @@ -0,0 +1,47 @@ +/* PipeWire + * Copyright (C) 2018 Wim Taymans + * + * 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 +#include +#include + +#include + +#include + +float jack_get_max_delayed_usecs (jack_client_t *client) +{ + pw_log_warn("not implemented"); + return 0.0f; +} + +float jack_get_xrun_delayed_usecs (jack_client_t *client) +{ + pw_log_warn("not implemented"); + return 0.0f; +} + +void jack_reset_max_delayed_usecs (jack_client_t *client) +{ + pw_log_warn("not implemented"); +} diff --git a/src/uuid.c b/src/uuid.c new file mode 100644 index 000000000..893980d90 --- /dev/null +++ b/src/uuid.c @@ -0,0 +1,81 @@ +/* PipeWire + * Copyright (C) 2018 Wim Taymans + * + * 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 +#include +#include + +#include + +#include + +jack_uuid_t jack_client_uuid_generate () +{ + pw_log_warn("not implemented"); + return 0; +} + +jack_uuid_t jack_port_uuid_generate (uint32_t port_id) +{ + pw_log_warn("not implemented"); + return 0; +} + +uint32_t jack_uuid_to_index (jack_uuid_t id) +{ + pw_log_warn("not implemented"); + return 0; +} + +int jack_uuid_compare (jack_uuid_t id1, jack_uuid_t id2) +{ + pw_log_warn("not implemented"); + return 0; +} + +void jack_uuid_copy (jack_uuid_t* dst, jack_uuid_t src) +{ + pw_log_warn("not implemented"); +} + +void jack_uuid_clear (jack_uuid_t*id) +{ + pw_log_warn("not implemented"); +} + +int jack_uuid_parse (const char *buf, jack_uuid_t*id) +{ + pw_log_warn("not implemented"); + return 0; +} + +void jack_uuid_unparse (jack_uuid_t id, char buf[JACK_UUID_STRING_SIZE]) +{ + pw_log_warn("not implemented"); +} + +int jack_uuid_empty (jack_uuid_t id) +{ + pw_log_warn("not implemented"); + return 0; +} From 98992d88d336549c471e5a2e7d2e5f1090447e5b Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 23 Oct 2018 16:50:59 +0200 Subject: [PATCH 037/133] jack: add jack_port_rename --- src/pipewire-jack.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 2c66838f9..a31fa3d52 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -2016,7 +2016,7 @@ int jack_set_xrun_callback (jack_client_t *client, JackXRunCallback xrun_callback, void *arg) { pw_log_warn("not implemented"); - return -ENOTSUP; + return 0; } int jack_set_latency_callback (jack_client_t *client, @@ -2419,6 +2419,12 @@ int jack_port_set_name (jack_port_t *port, const char *port_name) return -ENOTSUP; } +int jack_port_rename (jack_client_t* client, jack_port_t *port, const char *port_name) +{ + pw_log_warn("not implemented"); + return -ENOTSUP; +} + int jack_port_set_alias (jack_port_t *port, const char *alias) { pw_log_warn("not implemented"); From 1ffee101fbaec3ae07a883a33b05f812078bac20 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 24 Oct 2018 10:34:49 +0200 Subject: [PATCH 038/133] jack: use spa_io_position --- src/pipewire-jack.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index a31fa3d52..132fc517d 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -265,8 +265,7 @@ struct client { JackTimebaseCallback timebase_callback; void *timebase_arg; - struct pw_client_node_position *position; - struct pw_driver_quantum *quantum; + struct spa_io_position *position; uint32_t sample_rate; uint32_t buffer_size; @@ -715,7 +714,7 @@ on_rtsocket_condition(void *data, int fd, enum spa_io mask) if (read(fd, &cmd, sizeof(uint64_t)) != sizeof(uint64_t)) pw_log_warn("jack %p: read failed %m", c); - buffer_size = c->quantum->size; + buffer_size = c->position->size; if (buffer_size != c->buffer_size) { pw_log_info("jack %p: buffersize %d", c, buffer_size); c->buffer_size = buffer_size; @@ -723,8 +722,8 @@ on_rtsocket_condition(void *data, int fd, enum spa_io mask) c->bufsize_callback(c->buffer_size, c->bufsize_arg); } - if (c->quantum->rate.num != 0 && c->quantum->rate.denom != 0) - sample_rate = c->quantum->rate.denom / c->quantum->rate.num; + if (c->position->clock.rate.num != 0 && c->position->clock.rate.denom != 0) + sample_rate = c->position->clock.rate.denom / c->position->clock.rate.num; else sample_rate = c->sample_rate; @@ -736,9 +735,9 @@ on_rtsocket_condition(void *data, int fd, enum spa_io mask) } c->jack_position.unique_1++; - c->jack_position.usecs = c->quantum->nsec/1000; + c->jack_position.usecs = c->position->clock.nsec/1000; c->jack_position.frame_rate = c->sample_rate; - c->jack_position.frame = c->quantum->position; + c->jack_position.frame = c->position->clock.position; c->jack_position.valid = 0; c->jack_position.unique_2 = c->jack_position.unique_1; @@ -747,9 +746,9 @@ on_rtsocket_condition(void *data, int fd, enum spa_io mask) &c->jack_position, c->sync_arg); } - pw_log_trace("do process %"PRIu64" %d %d %d %"PRIi64, c->quantum->nsec, + pw_log_trace("do process %"PRIu64" %d %d %d %"PRIi64, c->position->clock.nsec, c->buffer_size, c->sample_rate, - c->jack_position.frame, c->quantum->delay); + c->jack_position.frame, c->position->clock.delay); if (c->process_callback) c->process_callback(c->buffer_size, c->process_arg); @@ -844,17 +843,13 @@ static void client_node_set_io(void *object, pw_log_debug("client %p: set io %s %p", c, spa_debug_type_find_name(spa_type_io, id), ptr); - if (id == PW_IO_ClientNodePosition) { + if (id == SPA_IO_Position) { if (ptr == NULL && c->position) { m = find_mem_ptr(&c->mems, c->position); if (m && --m->ref == 0) clear_mem(c, m); } c->position = ptr; - if (ptr) - c->quantum = SPA_MEMBER(ptr, sizeof(struct pw_client_node_position), void); - else - c->quantum = NULL; } } From 73888460cb78487d16a1c5fad0d811e7570ff52e Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 29 Oct 2018 14:29:59 +0000 Subject: [PATCH 039/133] jack: convert sequence to midi events --- src/pipewire-jack.c | 94 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 75 insertions(+), 19 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 132fc517d..e7b5d3b26 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -662,7 +662,7 @@ static void reuse_buffer(struct client *c, struct mix *mix, uint32_t id) } -static void convert_midi(void *midi, void *buffer, size_t size) +static void convert_from_midi(void *midi, void *buffer, size_t size) { struct spa_pod_builder b = { 0, }; uint32_t i, count; @@ -681,6 +681,24 @@ static void convert_midi(void *midi, void *buffer, size_t size) spa_pod_builder_pop(&b); } +static void convert_to_midi(struct spa_pod_sequence *seq, void *midi) +{ + struct spa_pod_control *c; + + jack_midi_reset_buffer(midi); + + SPA_POD_SEQUENCE_FOREACH(seq, c) { + switch(c->type) { + case SPA_CONTROL_Midi: + jack_midi_event_write(midi, + c->offset, + SPA_POD_BODY(&c->value), + SPA_POD_BODY_SIZE(&c->value)); + break; + } + } +} + static void process_tee(struct client *c) { struct port *p; @@ -690,7 +708,7 @@ static void process_tee(struct client *c) spa_list_for_each(mix, &p->mix, port_link) { if (mix->notify == NULL) continue; - convert_midi(p->empty, mix->notify, mix->notify_size); + convert_from_midi(p->empty, mix->notify, mix->notify_size); break; } } @@ -2179,6 +2197,54 @@ static void add_f32(float *out, float *in1, float *in2, int n_samples) out[i] = in1[i] + in2[i]; } +static void *mix_audio(struct port *p, jack_nframes_t frames) +{ + struct mix *mix; + struct buffer *b; + struct spa_io_buffers *io; + int layer = 0; + void *ptr = NULL; + + spa_list_for_each(mix, &p->mix, port_link) { + pw_log_trace("port %p: mix %d.%d get buffer %d", + p, p->id, mix->id, frames); + io = mix->io; + if (io == NULL || io->buffer_id >= mix->n_buffers) + continue; + + io->status = SPA_STATUS_NEED_BUFFER; + b = &mix->buffers[io->buffer_id]; + if (layer++ == 0) + ptr = b->datas[0].data; + else { + add_f32(p->empty, ptr, b->datas[0].data, frames); + ptr = p->empty; + p->zeroed = false; + } + } + return ptr; +} + +static void *mix_midi(struct port *p, jack_nframes_t frames) +{ + struct mix *mix; + struct spa_io_sequence *io; + void *ptr = NULL; + + spa_list_for_each(mix, &p->mix, port_link) { + pw_log_trace("port %p: mix %d.%d get buffer %d", + p, p->id, mix->id, frames); + io = mix->control; + if (io == NULL) + continue; + + convert_to_midi(&io->sequence, p->empty); + ptr = p->empty; + break; + } + return ptr; +} + void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) { struct object *o = (struct object *) port; @@ -2187,7 +2253,6 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) struct buffer *b; struct spa_io_buffers *io; struct mix *mix; - int layer = 0; void *ptr = NULL; if (o->type != PW_TYPE_INTERFACE_Port || o->port.port_id == SPA_ID_INVALID) { @@ -2197,22 +2262,13 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) p = GET_PORT(c, GET_DIRECTION(o->port.flags), o->port.port_id); if (p->direction == SPA_DIRECTION_INPUT) { - spa_list_for_each(mix, &p->mix, port_link) { - pw_log_trace("port %p: mix %d.%d get buffer %d", - p, p->id, mix->id, frames); - io = mix->io; - if (io == NULL || io->buffer_id >= mix->n_buffers) - continue; - - io->status = SPA_STATUS_NEED_BUFFER; - b = &mix->buffers[io->buffer_id]; - if (layer++ == 0) - ptr = b->datas[0].data; - else { - add_f32(p->empty, ptr, b->datas[0].data, frames); - ptr = p->empty; - p->zeroed = false; - } + switch (p->object->port.type_id) { + case 0: + ptr = mix_audio(p, frames); + break; + case 1: + ptr = mix_midi(p, frames); + break; } } else { b = NULL; From 2dd6307050e9a82b0658e68b37d4963c43cae7ed Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 7 Nov 2018 09:58:20 +0100 Subject: [PATCH 040/133] jack: add some timing functions --- src/pipewire-jack.c | 46 +++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index e7b5d3b26..4954e9f2b 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -1857,8 +1858,7 @@ int jack_get_client_pid (const char *name) jack_native_thread_t jack_client_thread_id (jack_client_t *client) { - pw_log_warn("not implemented"); - return -ENOTSUP; + return pthread_self(); } int jack_is_realtime (jack_client_t *client) @@ -2815,14 +2815,22 @@ jack_port_t * jack_port_by_id (jack_client_t *client, jack_nframes_t jack_frames_since_cycle_start (const jack_client_t *client) { - pw_log_warn("not implemented"); - return 0; + struct client *c = (struct client *) client; + struct timespec ts; + jack_nframes_t res; + uint64_t diff; + + clock_gettime(CLOCK_MONOTONIC, &ts); + diff = SPA_TIMESPEC_TO_NSEC(&ts) - c->position->clock.nsec; + res = (jack_nframes_t) floor(((float)c->sample_rate * diff) / 1000000000.0f); + return res; } jack_nframes_t jack_frame_time (const jack_client_t *client) { - struct client *c = (struct client *) client; - return c->jack_position.frame; + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return jack_time_to_frames(client, SPA_TIMESPEC_TO_USEC(&ts)); } jack_nframes_t jack_last_frame_time (const jack_client_t *client) @@ -2837,26 +2845,36 @@ int jack_get_cycle_times(const jack_client_t *client, jack_time_t *next_usecs, float *period_usecs) { - pw_log_warn("not implemented"); - return -ENOTSUP; + struct client *c = (struct client *) client; + + *current_frames = c->jack_position.frame; + *current_usecs = c->jack_position.usecs; + *period_usecs = (float)c->buffer_size / c->sample_rate; + *next_usecs = c->jack_position.usecs + (*period_usecs * 1000000.0f); + pw_log_trace("client %p: %d %ld %ld %f", c, *current_frames, + *current_usecs, *next_usecs, *period_usecs); + return 0; } jack_time_t jack_frames_to_time(const jack_client_t *client, jack_nframes_t frames) { - pw_log_warn("not implemented"); - return 0; + struct client *c = (struct client *) client; + int32_t df = frames - c->jack_position.frame; + return c->jack_position.usecs + (int64_t)rint((double) df / c->sample_rate); } jack_nframes_t jack_time_to_frames(const jack_client_t *client, jack_time_t usecs) { - pw_log_warn("not implemented"); - return 0; + struct client *c = (struct client *) client; + int64_t du = usecs - c->jack_position.usecs; + return c->jack_position.frame + (int32_t)rint((double)du * c->sample_rate); } jack_time_t jack_get_time() { - pw_log_warn("not implemented"); - return 0; + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return SPA_TIMESPEC_TO_USEC(&ts); } void jack_set_error_function (void (*func)(const char *)) From 33edb4ccd12be1a42f370226ffbfa637ddb834ce Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 19 Nov 2018 10:02:53 +0100 Subject: [PATCH 041/133] jack: more accurate get_cycle_times --- src/pipewire-jack.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 4954e9f2b..39ce104dc 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -267,6 +267,7 @@ struct client { void *timebase_arg; struct spa_io_position *position; + double rate_diff; uint32_t sample_rate; uint32_t buffer_size; @@ -727,11 +728,13 @@ on_rtsocket_condition(void *data, int fd, enum spa_io mask) } if (mask & SPA_IO_IN) { - uint64_t cmd; + uint64_t cmd, usec; uint32_t buffer_size, sample_rate; if (read(fd, &cmd, sizeof(uint64_t)) != sizeof(uint64_t)) pw_log_warn("jack %p: read failed %m", c); + if (cmd > 1) + pw_log_warn("jack %p: missed %"PRIu64" wakeups", c, cmd - 1); buffer_size = c->position->size; if (buffer_size != c->buffer_size) { @@ -753,8 +756,11 @@ on_rtsocket_condition(void *data, int fd, enum spa_io mask) c->srate_callback(c->sample_rate, c->srate_arg); } + c->rate_diff = c->position->clock.rate_diff; + usec = c->position->clock.nsec / SPA_NSEC_PER_USEC; + c->jack_position.unique_1++; - c->jack_position.usecs = c->position->clock.nsec/1000; + c->jack_position.usecs = usec; c->jack_position.frame_rate = c->sample_rate; c->jack_position.frame = c->position->clock.position; c->jack_position.valid = 0; @@ -765,9 +771,9 @@ on_rtsocket_condition(void *data, int fd, enum spa_io mask) &c->jack_position, c->sync_arg); } - pw_log_trace("do process %"PRIu64" %d %d %d %"PRIi64, c->position->clock.nsec, + pw_log_trace("do process %"PRIu64" %d %d %d %"PRIi64" %f", c->position->clock.nsec, c->buffer_size, c->sample_rate, - c->jack_position.frame, c->position->clock.delay); + c->jack_position.frame, c->position->clock.delay, c->rate_diff); if (c->process_callback) c->process_callback(c->buffer_size, c->process_arg); @@ -2849,7 +2855,7 @@ int jack_get_cycle_times(const jack_client_t *client, *current_frames = c->jack_position.frame; *current_usecs = c->jack_position.usecs; - *period_usecs = (float)c->buffer_size / c->sample_rate; + *period_usecs = c->rate_diff * c->buffer_size / c->sample_rate; *next_usecs = c->jack_position.usecs + (*period_usecs * 1000000.0f); pw_log_trace("client %p: %d %ld %ld %f", c, *current_frames, *current_usecs, *next_usecs, *period_usecs); From 4cb36762b0549b041ee17e66bbe6de947c47960d Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 11 Dec 2018 16:36:36 +0100 Subject: [PATCH 042/133] jack: add PIPEWIRE_NOJACK to disable jack connections --- src/pipewire-jack.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 39ce104dc..6c47a3fb2 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -1626,6 +1626,9 @@ jack_client_t * jack_client_open (const char *client_name, const char *str; int i; + if (getenv("PIPEWIRE_NOJACK") != NULL) + goto disabled; + pw_log_debug("client open %s %d", client_name, options); client = calloc(1, sizeof(struct client)); @@ -1751,6 +1754,10 @@ jack_client_t * jack_client_open (const char *client_name, exit: pw_thread_loop_unlock(client->context.loop); return NULL; + disabled: + if (status) + *status = JackFailure | JackServerFailed; + return NULL; } jack_client_t * jack_client_new (const char *client_name) From a4593a0d96757cb2362f9c02abdcd55f7206967d Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 7 Jan 2019 15:06:15 +0100 Subject: [PATCH 043/133] jack: fix signed and unsigned compares --- src/pipewire-jack.c | 8 ++++---- src/ringbuffer.c | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 6c47a3fb2..3a7347974 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -1648,8 +1648,8 @@ jack_client_t * jack_client_open (const char *client_name, pw_array_init(&client->mems, 64); pw_array_ensure_size(&client->mems, sizeof(struct mem) * 64); - client->buffer_size = -1; - client->sample_rate = -1; + client->buffer_size = (uint32_t)-1; + client->sample_rate = (uint32_t)-1; spa_list_init(&client->mix[SPA_DIRECTION_INPUT]); spa_list_init(&client->mix[SPA_DIRECTION_OUTPUT]); @@ -2068,7 +2068,7 @@ int jack_set_buffer_size (jack_client_t *client, jack_nframes_t nframes) jack_nframes_t jack_get_sample_rate (jack_client_t *client) { struct client *c = (struct client *) client; - if (c->sample_rate == -1) + if (c->sample_rate == (uint32_t)-1) return DEFAULT_SAMPLE_RATE; return c->sample_rate; } @@ -2076,7 +2076,7 @@ jack_nframes_t jack_get_sample_rate (jack_client_t *client) jack_nframes_t jack_get_buffer_size (jack_client_t *client) { struct client *c = (struct client *) client; - if (c->buffer_size == -1) + if (c->buffer_size == (uint32_t)-1) return DEFAULT_BUFFER_SIZE; return c->buffer_size; } diff --git a/src/ringbuffer.c b/src/ringbuffer.c index 3ec9e28b1..5004534d7 100644 --- a/src/ringbuffer.c +++ b/src/ringbuffer.c @@ -30,14 +30,14 @@ jack_ringbuffer_t *jack_ringbuffer_create(size_t sz) { - int power_of_two; + size_t power_of_two; jack_ringbuffer_t *rb; rb = calloc(1, sizeof(jack_ringbuffer_t)); if (rb == NULL) return NULL; - for (power_of_two = 1; 1 << power_of_two < sz; power_of_two++); + for (power_of_two = 1; 1u << power_of_two < sz; power_of_two++); rb->size = 1 << power_of_two; rb->size_mask = rb->size - 1; From 25b8a7919f021e5b6354febe37e320adc8b9a297 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 7 Jan 2019 17:53:13 +0100 Subject: [PATCH 044/133] jack: use internal buffer id --- src/pipewire-jack.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 3a7347974..173b8ab64 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -1205,9 +1205,9 @@ static void client_node_port_use_buffers(void *object, buf = buffers[i].buffer; - b = &mix->buffers[buf->id]; + b = &mix->buffers[i]; + b->id = i; b->flags = 0; - b->id = buf->id; b->n_mem = 0; pw_map_range_init(&b->map, buffers[i].offset, buffers[i].size, core->sc_pagesize); From a8fb217ebac920a8fd545dc182b3f292e4599002 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 8 Jan 2019 17:31:23 +0100 Subject: [PATCH 045/133] jack: add user_data to core --- src/pipewire-jack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 173b8ab64..8a1bc8db3 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -1639,7 +1639,7 @@ jack_client_t * jack_client_open (const char *client_name, strncpy(client->name, client_name, JACK_CLIENT_NAME_SIZE); client->context.main = pw_main_loop_new(NULL); client->context.loop = pw_thread_loop_new(pw_main_loop_get_loop(client->context.main), client_name); - client->context.core = pw_core_new(pw_thread_loop_get_loop(client->context.loop), NULL); + client->context.core = pw_core_new(pw_thread_loop_get_loop(client->context.loop), NULL, 0); spa_list_init(&client->context.free_objects); spa_list_init(&client->context.nodes); spa_list_init(&client->context.ports); From 5de91e837e0d395dfdb4559bded4946e3663b6cb Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 8 Jan 2019 17:36:22 +0100 Subject: [PATCH 046/133] add _GNU_SOURCE --- src/meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/src/meson.build b/src/meson.build index 728cd57a9..38c0a258f 100644 --- a/src/meson.build +++ b/src/meson.build @@ -8,6 +8,7 @@ pipewire_jack_sources = [ pipewire_jack_c_args = [ '-DHAVE_CONFIG_H', + '-D_GNU_SOURCE', '-DPIC', ] From 16874536363649535cfdda5227e9108ec158127a Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 10 Jan 2019 09:31:00 +0100 Subject: [PATCH 047/133] jack: use core_proxy to listen for done messages --- src/pipewire-jack.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 8a1bc8db3..06d8ababe 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -221,6 +221,7 @@ struct client { struct spa_hook remote_listener; struct pw_core_proxy *core_proxy; + struct spa_hook core_listener; uint32_t last_sync; bool error; @@ -489,10 +490,14 @@ static void on_state_changed(void *data, enum pw_remote_state old, static const struct pw_remote_events remote_events = { PW_VERSION_REMOTE_EVENTS, - .sync_reply = on_sync_reply, .state_changed = on_state_changed, }; +static const struct pw_core_proxy_events core_events = { + PW_VERSION_CORE_EVENTS, + .done = on_sync_reply, +}; + static int do_sync(struct client *client) { uint32_t seq = client->last_sync + 1; @@ -1695,6 +1700,9 @@ jack_client_t * jack_client_open (const char *client_name, } } client->core_proxy = pw_remote_get_core_proxy(client->remote); + pw_core_proxy_add_listener(client->core_proxy, + &client->core_listener, + &core_events, client); client->registry_proxy = pw_core_proxy_get_registry(client->core_proxy, PW_TYPE_INTERFACE_Registry, PW_VERSION_REGISTRY, 0); From c1da362a24351c8827d5b964d24ef5176e87ca95 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 16 Jan 2019 11:03:27 +0100 Subject: [PATCH 048/133] jack: update to new pod api --- src/pipewire-jack.c | 99 ++++++++++++++++++++++----------------------- 1 file changed, 49 insertions(+), 50 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 06d8ababe..c012ec99f 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -981,21 +981,19 @@ static int param_enum_format(struct client *c, struct port *p, { switch (p->object->port.type_id) { case 0: - *param = spa_pod_builder_object(b, + *param = spa_pod_builder_add_object(b, SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, - SPA_FORMAT_mediaType, &SPA_POD_Id(SPA_MEDIA_TYPE_audio), - SPA_FORMAT_mediaSubtype, &SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), - SPA_FORMAT_AUDIO_format, &SPA_POD_Id(SPA_AUDIO_FORMAT_F32P), - SPA_FORMAT_AUDIO_rate, &SPA_POD_CHOICE_RANGE_Int(DEFAULT_SAMPLE_RATE, 1, INT32_MAX), - SPA_FORMAT_AUDIO_channels, &SPA_POD_Int(1), - 0); + SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio), + SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), + SPA_FORMAT_AUDIO_format, SPA_POD_Id(SPA_AUDIO_FORMAT_F32P), + SPA_FORMAT_AUDIO_rate, SPA_POD_CHOICE_RANGE_Int(DEFAULT_SAMPLE_RATE, 1, INT32_MAX), + SPA_FORMAT_AUDIO_channels, SPA_POD_Int(1)); break; case 1: - *param = spa_pod_builder_object(b, + *param = spa_pod_builder_add_object(b, SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, - SPA_FORMAT_mediaType, &SPA_POD_Id(SPA_MEDIA_TYPE_stream), - SPA_FORMAT_mediaSubtype, &SPA_POD_Id(SPA_MEDIA_SUBTYPE_midi), - 0); + SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_stream), + SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_midi)); break; default: return -EINVAL; @@ -1006,27 +1004,33 @@ static int param_enum_format(struct client *c, struct port *p, static int param_format(struct client *c, struct port *p, struct spa_pod **param, struct spa_pod_builder *b) { + uint32_t channels[] = { SPA_AUDIO_CHANNEL_MONO }; switch (p->object->port.type_id) { case 0: - - *param = spa_pod_builder_object(b, + spa_pod_builder_add(b, + "{", SPA_TYPE_OBJECT_Format, SPA_PARAM_Format, - SPA_FORMAT_mediaType, &SPA_POD_Id(SPA_MEDIA_TYPE_audio), - SPA_FORMAT_mediaSubtype, &SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), - SPA_FORMAT_AUDIO_format, &SPA_POD_Id(SPA_AUDIO_FORMAT_F32P), - SPA_FORMAT_AUDIO_rate, p->have_format ? - (void*)&SPA_POD_Int(p->rate) : - (void*)&SPA_POD_CHOICE_RANGE_Int(DEFAULT_SAMPLE_RATE, 1, INT32_MAX), - SPA_FORMAT_AUDIO_channels, &SPA_POD_Int(1), - SPA_FORMAT_AUDIO_position, &SPA_POD_Array(uint32_t, SPA_TYPE_Id, 1, SPA_AUDIO_CHANNEL_MONO), - 0); + SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio), + SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), + SPA_FORMAT_AUDIO_format, SPA_POD_Id(SPA_AUDIO_FORMAT_F32P), NULL); + if (p->have_format) { + spa_pod_builder_add(b, + SPA_FORMAT_AUDIO_rate, SPA_POD_Int(p->rate), NULL); + } else { + spa_pod_builder_add(b, + SPA_FORMAT_AUDIO_rate, SPA_POD_CHOICE_RANGE_Int(DEFAULT_SAMPLE_RATE, + 1, INT32_MAX), NULL); + } + spa_pod_builder_add(b, + SPA_FORMAT_AUDIO_channels, SPA_POD_Int(1), + SPA_FORMAT_AUDIO_position, SPA_POD_Array(sizeof(uint32_t), SPA_TYPE_Id, 1, channels), NULL); + *param = spa_pod_builder_pop(b); break; case 1: - *param = spa_pod_builder_object(b, + *param = spa_pod_builder_add_object(b, SPA_TYPE_OBJECT_Format, SPA_PARAM_Format, - SPA_FORMAT_mediaType, &SPA_POD_Id(SPA_MEDIA_TYPE_stream), - SPA_FORMAT_mediaSubtype, &SPA_POD_Id(SPA_MEDIA_SUBTYPE_midi), - 0); + SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_stream), + SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_midi)); break; default: return -EINVAL; @@ -1037,14 +1041,13 @@ static int param_format(struct client *c, struct port *p, static int param_buffers(struct client *c, struct port *p, struct spa_pod **param, struct spa_pod_builder *b) { - *param = spa_pod_builder_object(b, + *param = spa_pod_builder_add_object(b, SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, - SPA_PARAM_BUFFERS_buffers, &SPA_POD_CHOICE_RANGE_Int(1, 1, MAX_BUFFERS), - SPA_PARAM_BUFFERS_blocks, &SPA_POD_Int(1), - SPA_PARAM_BUFFERS_size, &SPA_POD_CHOICE_STEP_Int(MAX_BUFFER_SIZE * sizeof(float), 4, INT32_MAX, 4), - SPA_PARAM_BUFFERS_stride, &SPA_POD_Int(4), - SPA_PARAM_BUFFERS_align, &SPA_POD_Int(16), - 0); + SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(1, 1, MAX_BUFFERS), + SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1), + SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_STEP_Int(MAX_BUFFER_SIZE * sizeof(float), 4, INT32_MAX, 4), + SPA_PARAM_BUFFERS_stride, SPA_POD_Int(4), + SPA_PARAM_BUFFERS_align, SPA_POD_Int(16)); return 1; } @@ -1053,25 +1056,22 @@ static int param_io(struct client *c, struct port *p, { switch (p->object->port.type_id) { case 0: - *param = spa_pod_builder_object(b, + *param = spa_pod_builder_add_object(b, SPA_TYPE_OBJECT_ParamIO, SPA_PARAM_IO, - SPA_PARAM_IO_id, &SPA_POD_Id(SPA_IO_Buffers), - SPA_PARAM_IO_size, &SPA_POD_Int(sizeof(struct spa_io_buffers)), - 0); + SPA_PARAM_IO_id, SPA_POD_Id(SPA_IO_Buffers), + SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_buffers))); break; case 1: if (p->direction == SPA_DIRECTION_OUTPUT) { - *param = spa_pod_builder_object(b, + *param = spa_pod_builder_add_object(b, SPA_TYPE_OBJECT_ParamIO, SPA_PARAM_IO, - SPA_PARAM_IO_id, &SPA_POD_Id(SPA_IO_Notify), - SPA_PARAM_IO_size, &SPA_POD_Int(BUFFER_SIZE_MAX), - 0); + SPA_PARAM_IO_id, SPA_POD_Id(SPA_IO_Notify), + SPA_PARAM_IO_size, SPA_POD_Int(BUFFER_SIZE_MAX)); } else { - *param = spa_pod_builder_object(b, + *param = spa_pod_builder_add_object(b, SPA_TYPE_OBJECT_ParamIO, SPA_PARAM_IO, - SPA_PARAM_IO_id, &SPA_POD_Id(SPA_IO_Control), - SPA_PARAM_IO_size, &SPA_POD_Int(sizeof(struct spa_io_sequence)), - 0); + SPA_PARAM_IO_id, SPA_POD_Id(SPA_IO_Control), + SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_sequence))); } break; } @@ -1093,9 +1093,7 @@ static int port_set_format(struct client *c, struct port *p, else { struct spa_audio_info info = { 0 }; - spa_pod_object_parse(param, - "I", &info.media_type, - "I", &info.media_subtype); + spa_format_parse(param, &info.media_type, &info.media_subtype); switch (info.media_type) { case SPA_MEDIA_TYPE_audio: @@ -1685,8 +1683,6 @@ jack_client_t * jack_client_open (const char *client_name, while (busy) { const char *error = NULL; - pw_thread_loop_wait(client->context.loop); - switch (pw_remote_get_state(client->remote, &error)) { case PW_REMOTE_STATE_ERROR: goto server_failed; @@ -1698,6 +1694,9 @@ jack_client_t * jack_client_open (const char *client_name, default: break; } + if (busy) + pw_thread_loop_wait(client->context.loop); + } client->core_proxy = pw_remote_get_core_proxy(client->remote); pw_core_proxy_add_listener(client->core_proxy, From 4bdaa083d13771e37d7d2c1e20953c7b617dfa2b Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 17 Jan 2019 17:06:47 +0100 Subject: [PATCH 049/133] jack: update for pod api --- src/pipewire-jack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index c012ec99f..dc9ff0bfe 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -682,7 +682,7 @@ static void convert_from_midi(void *midi, void *buffer, size_t size) for (i = 0; i < count; i++) { jack_midi_event_t ev; jack_midi_event_get(&ev, midi, i); - spa_pod_builder_control_header(&b, ev.time, SPA_CONTROL_Midi); + spa_pod_builder_control(&b, ev.time, SPA_CONTROL_Midi); spa_pod_builder_bytes(&b, ev.buffer, ev.size); } spa_pod_builder_pop(&b); From c404942e9d15bd3340c57121753fed8d38b247c6 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 22 Jan 2019 17:37:00 +0100 Subject: [PATCH 050/133] jack: update for api change --- src/pipewire-jack.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index dc9ff0bfe..511ffeefc 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -673,11 +673,12 @@ static void convert_from_midi(void *midi, void *buffer, size_t size) { struct spa_pod_builder b = { 0, }; uint32_t i, count; + struct spa_pod_frame f; count = jack_midi_get_event_count(midi); spa_pod_builder_init(&b, buffer, size); - spa_pod_builder_push_sequence(&b, 0); + spa_pod_builder_push_sequence(&b, &f, 0); for (i = 0; i < count; i++) { jack_midi_event_t ev; @@ -685,7 +686,7 @@ static void convert_from_midi(void *midi, void *buffer, size_t size) spa_pod_builder_control(&b, ev.time, SPA_CONTROL_Midi); spa_pod_builder_bytes(&b, ev.buffer, ev.size); } - spa_pod_builder_pop(&b); + spa_pod_builder_pop(&b, &f); } static void convert_to_midi(struct spa_pod_sequence *seq, void *midi) @@ -1005,11 +1006,11 @@ static int param_format(struct client *c, struct port *p, struct spa_pod **param, struct spa_pod_builder *b) { uint32_t channels[] = { SPA_AUDIO_CHANNEL_MONO }; + struct spa_pod_frame f; switch (p->object->port.type_id) { case 0: + spa_pod_builder_push_object(b, &f, SPA_TYPE_OBJECT_Format, SPA_PARAM_Format); spa_pod_builder_add(b, - "{", - SPA_TYPE_OBJECT_Format, SPA_PARAM_Format, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio), SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), SPA_FORMAT_AUDIO_format, SPA_POD_Id(SPA_AUDIO_FORMAT_F32P), NULL); @@ -1024,7 +1025,7 @@ static int param_format(struct client *c, struct port *p, spa_pod_builder_add(b, SPA_FORMAT_AUDIO_channels, SPA_POD_Int(1), SPA_FORMAT_AUDIO_position, SPA_POD_Array(sizeof(uint32_t), SPA_TYPE_Id, 1, channels), NULL); - *param = spa_pod_builder_pop(b); + *param = spa_pod_builder_pop(b, &f); break; case 1: *param = spa_pod_builder_add_object(b, From 9a57598b8379e4977469e3c8d9c96dbeeff18c0b Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 24 Jan 2019 18:25:44 +0100 Subject: [PATCH 051/133] jack: align temp samples and use optimized mix --- src/pipewire-jack.c | 52 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 511ffeefc..f87f25cec 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -191,7 +191,8 @@ struct port { uint32_t rate; bool zeroed; - float empty[BUFFER_SIZE_MAX + 8]; + float *emptyptr; + float empty[BUFFER_SIZE_MAX + 15]; }; struct context { @@ -297,6 +298,8 @@ static void init_port_pool(struct client *c, enum spa_direction direction) for (i = 0; i < MAX_PORTS; i++) { c->port_pool[direction][i].direction = direction; c->port_pool[direction][i].id = i; + c->port_pool[direction][i].emptyptr = + SPA_PTR_ALIGN(c->port_pool[direction][i].empty, 16, float); spa_list_append(&c->free_ports[direction], &c->port_pool[direction][i].link); } } @@ -716,7 +719,7 @@ static void process_tee(struct client *c) spa_list_for_each(mix, &p->mix, port_link) { if (mix->notify == NULL) continue; - convert_from_midi(p->empty, mix->notify, mix->notify_size); + convert_from_midi(p->emptyptr, mix->notify, mix->notify_size); break; } } @@ -2211,12 +2214,41 @@ int jack_port_unregister (jack_client_t *client, jack_port_t *port) return res; } -static void add_f32(float *out, float *in1, float *in2, int n_samples) +#if defined (__SSE__) +#include +static void mix_2(float *dst, float *src1, float *src2, int n_samples) +{ + int n, unrolled; + __m128 in[2]; + + if (SPA_IS_ALIGNED(src1, 16) && + SPA_IS_ALIGNED(src2, 16) && + SPA_IS_ALIGNED(dst, 16)) + unrolled = n_samples / 4; + else + unrolled = 0; + + for (n = 0; unrolled--; n += 4) { + in[0] = _mm_load_ps(&src1[n]), + in[1] = _mm_load_ps(&src2[n]), + in[0] = _mm_add_ps(in[0], in[1]); + _mm_store_ps(&dst[n], in[0]); + } + for (; n < n_samples; n++) { + in[0] = _mm_load_ss(&src1[n]), + in[1] = _mm_load_ss(&src2[n]), + in[0] = _mm_add_ss(in[0], in[1]); + _mm_store_ss(&dst[n], in[0]); + } +} +#else +static void mix_2(float *dst, float *src1, float *src2, int n_samples) { int i; for (i = 0; i < n_samples; i++) - out[i] = in1[i] + in2[i]; + dst[i] = src1[i] + src2[i]; } +#endif static void *mix_audio(struct port *p, jack_nframes_t frames) { @@ -2238,8 +2270,8 @@ static void *mix_audio(struct port *p, jack_nframes_t frames) if (layer++ == 0) ptr = b->datas[0].data; else { - add_f32(p->empty, ptr, b->datas[0].data, frames); - ptr = p->empty; + ptr = p->emptyptr; + mix_2(ptr, ptr, b->datas[0].data, frames); p->zeroed = false; } } @@ -2259,8 +2291,8 @@ static void *mix_midi(struct port *p, jack_nframes_t frames) if (io == NULL) continue; - convert_to_midi(&io->sequence, p->empty); - ptr = p->empty; + ptr = p->emptyptr; + convert_to_midi(&io->sequence, ptr); break; } return ptr; @@ -2327,9 +2359,9 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) done: if (ptr == NULL) { - ptr = p->empty; + ptr = p->emptyptr; if (!p->zeroed) { - init_buffer(p, ptr, sizeof(p->empty)); + init_buffer(p, p->empty, sizeof(p->empty)); p->zeroed = true; } } From 1cf3e01219d66f92ea655ddf5c2f4caa9b96bcf7 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 24 Jan 2019 18:27:40 +0100 Subject: [PATCH 052/133] jack: align metadata --- src/pipewire-jack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index f87f25cec..a5aa44d69 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -1236,7 +1236,7 @@ static void client_node_port_use_buffers(void *object, offset = b->map.start; for (j = 0; j < buf->n_metas; j++) { struct spa_meta *m = &buf->metas[j]; - offset += m->size; + offset += SPA_ROUND_UP_N(m->size, 8); } b->n_datas = SPA_MIN(buf->n_datas, MAX_BUFFER_DATAS); From 13d51dfe379c288309d1eb907ba3390de171b4f8 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 28 Jan 2019 17:10:37 +0100 Subject: [PATCH 053/133] jack: select sse only when cpu flags available --- src/pipewire-jack.c | 94 +++++++++++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 37 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index a5aa44d69..2c16f0462 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -77,6 +78,10 @@ static struct globals globals; #define OBJECT_CHUNK 8 +typedef void (*mix2_func) (float *dst, float *src1, float *src2, int n_samples); + +static mix2_func mix2; + struct object { struct spa_list link; @@ -448,6 +453,42 @@ static struct buffer *dequeue_buffer(struct mix *mix) return b; } +#if defined (__SSE__) +#include +static void mix2_sse(float *dst, float *src1, float *src2, int n_samples) +{ + int n, unrolled; + __m128 in[2]; + + if (SPA_IS_ALIGNED(src1, 16) && + SPA_IS_ALIGNED(src2, 16) && + SPA_IS_ALIGNED(dst, 16)) + unrolled = n_samples / 4; + else + unrolled = 0; + + for (n = 0; unrolled--; n += 4) { + in[0] = _mm_load_ps(&src1[n]), + in[1] = _mm_load_ps(&src2[n]), + in[0] = _mm_add_ps(in[0], in[1]); + _mm_store_ps(&dst[n], in[0]); + } + for (; n < n_samples; n++) { + in[0] = _mm_load_ss(&src1[n]), + in[1] = _mm_load_ss(&src2[n]), + in[0] = _mm_add_ss(in[0], in[1]); + _mm_store_ss(&dst[n], in[0]); + } +} +#endif + +static void mix2_c(float *dst, float *src1, float *src2, int n_samples) +{ + int i; + for (i = 0; i < n_samples; i++) + dst[i] = src1[i] + src2[i]; +} + void jack_get_version(int *major_ptr, int *minor_ptr, int *micro_ptr, int *proto_ptr) { *major_ptr = 0; @@ -1630,7 +1671,10 @@ jack_client_t * jack_client_open (const char *client_name, bool busy = true; struct spa_dict props; struct spa_dict_item items[5]; + const struct spa_support *support; + uint32_t n_support; const char *str; + struct spa_cpu *cpu_iface; int i; if (getenv("PIPEWIRE_NOJACK") != NULL) @@ -1652,6 +1696,18 @@ jack_client_t * jack_client_open (const char *client_name, spa_list_init(&client->context.ports); spa_list_init(&client->context.links); + support = pw_core_get_support(client->context.core, &n_support); + + mix2 = mix2_c; + cpu_iface = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_CPU); + if (cpu_iface) { + uint32_t flags = spa_cpu_get_flags(cpu_iface); +#if defined (__SSE__) + if (flags & SPA_CPU_FLAG_SSE) + mix2 = mix2_sse; +#endif + } + pw_array_init(&client->mems, 64); pw_array_ensure_size(&client->mems, sizeof(struct mem) * 64); @@ -2214,42 +2270,6 @@ int jack_port_unregister (jack_client_t *client, jack_port_t *port) return res; } -#if defined (__SSE__) -#include -static void mix_2(float *dst, float *src1, float *src2, int n_samples) -{ - int n, unrolled; - __m128 in[2]; - - if (SPA_IS_ALIGNED(src1, 16) && - SPA_IS_ALIGNED(src2, 16) && - SPA_IS_ALIGNED(dst, 16)) - unrolled = n_samples / 4; - else - unrolled = 0; - - for (n = 0; unrolled--; n += 4) { - in[0] = _mm_load_ps(&src1[n]), - in[1] = _mm_load_ps(&src2[n]), - in[0] = _mm_add_ps(in[0], in[1]); - _mm_store_ps(&dst[n], in[0]); - } - for (; n < n_samples; n++) { - in[0] = _mm_load_ss(&src1[n]), - in[1] = _mm_load_ss(&src2[n]), - in[0] = _mm_add_ss(in[0], in[1]); - _mm_store_ss(&dst[n], in[0]); - } -} -#else -static void mix_2(float *dst, float *src1, float *src2, int n_samples) -{ - int i; - for (i = 0; i < n_samples; i++) - dst[i] = src1[i] + src2[i]; -} -#endif - static void *mix_audio(struct port *p, jack_nframes_t frames) { struct mix *mix; @@ -2271,7 +2291,7 @@ static void *mix_audio(struct port *p, jack_nframes_t frames) ptr = b->datas[0].data; else { ptr = p->emptyptr; - mix_2(ptr, ptr, b->datas[0].data, frames); + mix2(ptr, ptr, b->datas[0].data, frames); p->zeroed = false; } } From 96460ab2d1260df22698e2a08509ee657b26d4dd Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 6 Feb 2019 13:23:15 +0100 Subject: [PATCH 054/133] jack: add SPA_EXPORT --- src/pipewire-jack.c | 123 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 2c16f0462..1cd891070 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -489,6 +489,7 @@ static void mix2_c(float *dst, float *src1, float *src2, int n_samples) dst[i] = src1[i] + src2[i]; } +SPA_EXPORT void jack_get_version(int *major_ptr, int *minor_ptr, int *micro_ptr, int *proto_ptr) { *major_ptr = 0; @@ -497,6 +498,7 @@ void jack_get_version(int *major_ptr, int *minor_ptr, int *micro_ptr, int *proto *proto_ptr = 0; } +SPA_EXPORT const char * jack_get_version_string(void) { @@ -1663,6 +1665,7 @@ static const struct pw_registry_proxy_events registry_events = { .global_remove = registry_event_global_remove, }; +SPA_EXPORT jack_client_t * jack_client_open (const char *client_name, jack_options_t options, jack_status_t *status, ...) @@ -1827,6 +1830,7 @@ jack_client_t * jack_client_open (const char *client_name, return NULL; } +SPA_EXPORT jack_client_t * jack_client_new (const char *client_name) { jack_options_t options = JackUseExactName; @@ -1838,6 +1842,7 @@ jack_client_t * jack_client_new (const char *client_name) return jack_client_open(client_name, options, &status, NULL); } +SPA_EXPORT int jack_client_close (jack_client_t *client) { struct client *c = (struct client *) client; @@ -1854,17 +1859,20 @@ int jack_client_close (jack_client_t *client) return 0; } +SPA_EXPORT int jack_client_name_size (void) { return JACK_CLIENT_NAME_SIZE; } +SPA_EXPORT char * jack_get_client_name (jack_client_t *client) { struct client *c = (struct client *) client; return c->name; } +SPA_EXPORT char *jack_get_uuid_for_client_name (jack_client_t *client, const char *client_name) { @@ -1872,6 +1880,7 @@ char *jack_get_uuid_for_client_name (jack_client_t *client, return NULL; } +SPA_EXPORT char *jack_get_client_name_by_uuid (jack_client_t *client, const char *client_uuid ) { @@ -1879,6 +1888,7 @@ char *jack_get_client_name_by_uuid (jack_client_t *client, return NULL; } +SPA_EXPORT int jack_internal_client_new (const char *client_name, const char *load_name, const char *load_init) @@ -1887,11 +1897,13 @@ int jack_internal_client_new (const char *client_name, return -ENOTSUP; } +SPA_EXPORT void jack_internal_client_close (const char *client_name) { pw_log_warn("not implemented"); } +SPA_EXPORT int jack_activate (jack_client_t *client) { struct client *c = (struct client *) client; @@ -1911,6 +1923,7 @@ int jack_activate (jack_client_t *client) return res; } +SPA_EXPORT int jack_deactivate (jack_client_t *client) { struct client *c = (struct client *) client; @@ -1930,40 +1943,47 @@ int jack_deactivate (jack_client_t *client) return res; } +SPA_EXPORT int jack_get_client_pid (const char *name) { pw_log_warn("not implemented"); return -ENOTSUP; } +SPA_EXPORT jack_native_thread_t jack_client_thread_id (jack_client_t *client) { return pthread_self(); } +SPA_EXPORT int jack_is_realtime (jack_client_t *client) { pw_log_warn("not implemented"); return -ENOTSUP; } +SPA_EXPORT jack_nframes_t jack_thread_wait (jack_client_t *client, int status) { pw_log_warn("not implemented"); return 0; } +SPA_EXPORT jack_nframes_t jack_cycle_wait (jack_client_t* client) { pw_log_warn("not implemented"); return 0; } +SPA_EXPORT void jack_cycle_signal (jack_client_t* client, int status) { pw_log_warn("not implemented"); } +SPA_EXPORT int jack_set_process_thread(jack_client_t* client, JackThreadCallback thread_callback, void *arg) { struct client *c = (struct client *) client; @@ -1981,6 +2001,7 @@ int jack_set_process_thread(jack_client_t* client, JackThreadCallback thread_cal return 0; } +SPA_EXPORT int jack_set_thread_init_callback (jack_client_t *client, JackThreadInitCallback thread_init_callback, void *arg) @@ -1991,6 +2012,7 @@ int jack_set_thread_init_callback (jack_client_t *client, return 0; } +SPA_EXPORT void jack_on_shutdown (jack_client_t *client, JackShutdownCallback shutdown_callback, void *arg) { @@ -1999,6 +2021,7 @@ void jack_on_shutdown (jack_client_t *client, c->shutdown_arg = arg; } +SPA_EXPORT void jack_on_info_shutdown (jack_client_t *client, JackInfoShutdownCallback shutdown_callback, void *arg) { @@ -2007,6 +2030,7 @@ void jack_on_info_shutdown (jack_client_t *client, c->info_shutdown_arg = arg; } +SPA_EXPORT int jack_set_process_callback (jack_client_t *client, JackProcessCallback process_callback, void *arg) @@ -2027,6 +2051,7 @@ int jack_set_process_callback (jack_client_t *client, return 0; } +SPA_EXPORT int jack_set_freewheel_callback (jack_client_t *client, JackFreewheelCallback freewheel_callback, void *arg) @@ -2037,6 +2062,7 @@ int jack_set_freewheel_callback (jack_client_t *client, return 0; } +SPA_EXPORT int jack_set_buffer_size_callback (jack_client_t *client, JackBufferSizeCallback bufsize_callback, void *arg) @@ -2047,6 +2073,7 @@ int jack_set_buffer_size_callback (jack_client_t *client, return 0; } +SPA_EXPORT int jack_set_sample_rate_callback (jack_client_t *client, JackSampleRateCallback srate_callback, void *arg) @@ -2057,6 +2084,7 @@ int jack_set_sample_rate_callback (jack_client_t *client, return 0; } +SPA_EXPORT int jack_set_client_registration_callback (jack_client_t *client, JackClientRegistrationCallback registration_callback, void *arg) @@ -2067,6 +2095,7 @@ int jack_set_client_registration_callback (jack_client_t *client, return 0; } +SPA_EXPORT int jack_set_port_registration_callback (jack_client_t *client, JackPortRegistrationCallback registration_callback, void *arg) @@ -2078,6 +2107,7 @@ int jack_set_port_registration_callback (jack_client_t *client, } +SPA_EXPORT int jack_set_port_connect_callback (jack_client_t *client, JackPortConnectCallback connect_callback, void *arg) @@ -2088,6 +2118,7 @@ int jack_set_port_connect_callback (jack_client_t *client, return 0; } +SPA_EXPORT int jack_set_port_rename_callback (jack_client_t *client, JackPortRenameCallback rename_callback, void *arg) @@ -2095,6 +2126,7 @@ int jack_set_port_rename_callback (jack_client_t *client, return 0; } +SPA_EXPORT int jack_set_graph_order_callback (jack_client_t *client, JackGraphOrderCallback graph_callback, void *data) @@ -2105,6 +2137,7 @@ int jack_set_graph_order_callback (jack_client_t *client, return 0; } +SPA_EXPORT int jack_set_xrun_callback (jack_client_t *client, JackXRunCallback xrun_callback, void *arg) { @@ -2112,6 +2145,7 @@ int jack_set_xrun_callback (jack_client_t *client, return 0; } +SPA_EXPORT int jack_set_latency_callback (jack_client_t *client, JackLatencyCallback latency_callback, void *data) @@ -2120,18 +2154,21 @@ int jack_set_latency_callback (jack_client_t *client, return -ENOTSUP; } +SPA_EXPORT int jack_set_freewheel(jack_client_t* client, int onoff) { pw_log_warn("not implemented"); return -ENOTSUP; } +SPA_EXPORT int jack_set_buffer_size (jack_client_t *client, jack_nframes_t nframes) { pw_log_warn("not implemented"); return -ENOTSUP; } +SPA_EXPORT jack_nframes_t jack_get_sample_rate (jack_client_t *client) { struct client *c = (struct client *) client; @@ -2140,6 +2177,7 @@ jack_nframes_t jack_get_sample_rate (jack_client_t *client) return c->sample_rate; } +SPA_EXPORT jack_nframes_t jack_get_buffer_size (jack_client_t *client) { struct client *c = (struct client *) client; @@ -2148,18 +2186,21 @@ jack_nframes_t jack_get_buffer_size (jack_client_t *client) return c->buffer_size; } +SPA_EXPORT int jack_engine_takeover_timebase (jack_client_t *client) { pw_log_warn("not implemented"); return -ENOTSUP; } +SPA_EXPORT float jack_cpu_load (jack_client_t *client) { pw_log_warn("not implemented"); return 0.0; } +SPA_EXPORT jack_port_t * jack_port_register (jack_client_t *client, const char *port_name, const char *port_type, @@ -2239,6 +2280,7 @@ jack_port_t * jack_port_register (jack_client_t *client, return (jack_port_t *) o; } +SPA_EXPORT int jack_port_unregister (jack_client_t *client, jack_port_t *port) { struct object *o = (struct object *) port; @@ -2318,6 +2360,7 @@ static void *mix_midi(struct port *p, jack_nframes_t frames) return ptr; } +SPA_EXPORT void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) { struct object *o = (struct object *) port; @@ -2389,48 +2432,56 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) return ptr; } +SPA_EXPORT jack_uuid_t jack_port_uuid (const jack_port_t *port) { pw_log_warn("not implemented"); return 0; } +SPA_EXPORT const char * jack_port_name (const jack_port_t *port) { struct object *o = (struct object *) port; return o->port.name; } +SPA_EXPORT const char * jack_port_short_name (const jack_port_t *port) { struct object *o = (struct object *) port; return strchr(o->port.name, ':') + 1; } +SPA_EXPORT int jack_port_flags (const jack_port_t *port) { struct object *o = (struct object *) port; return o->port.flags; } +SPA_EXPORT const char * jack_port_type (const jack_port_t *port) { struct object *o = (struct object *) port; return type_to_string(o->port.type_id); } +SPA_EXPORT jack_port_type_id_t jack_port_type_id (const jack_port_t *port) { struct object *o = (struct object *) port; return o->port.type_id; } +SPA_EXPORT int jack_port_is_mine (const jack_client_t *client, const jack_port_t *port) { struct object *o = (struct object *) port; return o->type == PW_TYPE_INTERFACE_Port && o->port.port_id != SPA_ID_INVALID; } +SPA_EXPORT int jack_port_connected (const jack_port_t *port) { struct object *o = (struct object *) port; @@ -2449,6 +2500,7 @@ int jack_port_connected (const jack_port_t *port) return res; } +SPA_EXPORT int jack_port_connected_to (const jack_port_t *port, const char *port_name) { @@ -2480,6 +2532,7 @@ int jack_port_connected_to (const jack_port_t *port, return res; } +SPA_EXPORT const char ** jack_port_get_connections (const jack_port_t *port) { struct object *o = (struct object *) port; @@ -2488,6 +2541,7 @@ const char ** jack_port_get_connections (const jack_port_t *port) return jack_port_get_all_connections((jack_client_t *)c, port); } +SPA_EXPORT const char ** jack_port_get_all_connections (const jack_client_t *client, const jack_port_t *port) { @@ -2525,42 +2579,49 @@ const char ** jack_port_get_all_connections (const jack_client_t *client, return res; } +SPA_EXPORT int jack_port_tie (jack_port_t *src, jack_port_t *dst) { pw_log_warn("not implemented"); return -ENOTSUP; } +SPA_EXPORT int jack_port_untie (jack_port_t *port) { pw_log_warn("not implemented"); return -ENOTSUP; } +SPA_EXPORT int jack_port_set_name (jack_port_t *port, const char *port_name) { pw_log_warn("not implemented"); return -ENOTSUP; } +SPA_EXPORT int jack_port_rename (jack_client_t* client, jack_port_t *port, const char *port_name) { pw_log_warn("not implemented"); return -ENOTSUP; } +SPA_EXPORT int jack_port_set_alias (jack_port_t *port, const char *alias) { pw_log_warn("not implemented"); return -ENOTSUP; } +SPA_EXPORT int jack_port_unset_alias (jack_port_t *port, const char *alias) { pw_log_warn("not implemented"); return -ENOTSUP; } +SPA_EXPORT int jack_port_get_aliases (const jack_port_t *port, char* const aliases[2]) { struct object *o = (struct object *) port; @@ -2582,12 +2643,14 @@ int jack_port_get_aliases (const jack_port_t *port, char* const aliases[2]) return res; } +SPA_EXPORT int jack_port_request_monitor (jack_port_t *port, int onoff) { pw_log_warn("not implemented"); return -ENOTSUP; } +SPA_EXPORT int jack_port_request_monitor_by_name (jack_client_t *client, const char *port_name, int onoff) { @@ -2595,18 +2658,21 @@ int jack_port_request_monitor_by_name (jack_client_t *client, return -ENOTSUP; } +SPA_EXPORT int jack_port_ensure_monitor (jack_port_t *port, int onoff) { pw_log_warn("not implemented"); return -ENOTSUP; } +SPA_EXPORT int jack_port_monitoring_input (jack_port_t *port) { pw_log_warn("not implemented"); return -ENOTSUP; } +SPA_EXPORT int jack_connect (jack_client_t *client, const char *source_port, const char *destination_port) @@ -2658,6 +2724,7 @@ int jack_connect (jack_client_t *client, return res; } +SPA_EXPORT int jack_disconnect (jack_client_t *client, const char *source_port, const char *destination_port) @@ -2697,6 +2764,7 @@ int jack_disconnect (jack_client_t *client, return res; } +SPA_EXPORT int jack_port_disconnect (jack_client_t *client, jack_port_t *port) { struct client *c = (struct client *) client; @@ -2721,16 +2789,19 @@ int jack_port_disconnect (jack_client_t *client, jack_port_t *port) return res; } +SPA_EXPORT int jack_port_name_size(void) { return REAL_JACK_PORT_NAME_SIZE+1; } +SPA_EXPORT int jack_port_type_size(void) { return JACK_PORT_TYPE_SIZE+1; } +SPA_EXPORT size_t jack_port_type_get_buffer_size (jack_client_t *client, const char *port_type) { struct client *c = (struct client *) client; @@ -2742,33 +2813,39 @@ size_t jack_port_type_get_buffer_size (jack_client_t *client, const char *port_t return 0; } +SPA_EXPORT void jack_port_set_latency (jack_port_t *port, jack_nframes_t frames) { pw_log_warn("not implemented"); } +SPA_EXPORT void jack_port_get_latency_range (jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range) { pw_log_warn("not implemented"); } +SPA_EXPORT void jack_port_set_latency_range (jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range) { pw_log_warn("not implemented"); } +SPA_EXPORT int jack_recompute_total_latencies (jack_client_t *client) { pw_log_warn("not implemented"); return -ENOTSUP; } +SPA_EXPORT jack_nframes_t jack_port_get_latency (jack_port_t *port) { pw_log_warn("not implemented"); return 0; } +SPA_EXPORT jack_nframes_t jack_port_get_total_latency (jack_client_t *client, jack_port_t *port) { @@ -2776,12 +2853,14 @@ jack_nframes_t jack_port_get_total_latency (jack_client_t *client, return 0; } +SPA_EXPORT int jack_recompute_total_latency (jack_client_t *client, jack_port_t* port) { pw_log_warn("not implemented"); return -ENOTSUP; } +SPA_EXPORT const char ** jack_get_ports (jack_client_t *client, const char *port_name_pattern, const char *type_name_pattern, @@ -2850,6 +2929,7 @@ const char ** jack_get_ports (jack_client_t *client, return res; } +SPA_EXPORT jack_port_t * jack_port_by_name (jack_client_t *client, const char *port_name) { struct client *c = (struct client *) client; @@ -2864,6 +2944,7 @@ jack_port_t * jack_port_by_name (jack_client_t *client, const char *port_name) return (jack_port_t *)res; } +SPA_EXPORT jack_port_t * jack_port_by_id (jack_client_t *client, jack_port_id_t port_id) { @@ -2886,6 +2967,7 @@ jack_port_t * jack_port_by_id (jack_client_t *client, return (jack_port_t *)res; } +SPA_EXPORT jack_nframes_t jack_frames_since_cycle_start (const jack_client_t *client) { struct client *c = (struct client *) client; @@ -2899,6 +2981,7 @@ jack_nframes_t jack_frames_since_cycle_start (const jack_client_t *client) return res; } +SPA_EXPORT jack_nframes_t jack_frame_time (const jack_client_t *client) { struct timespec ts; @@ -2906,12 +2989,14 @@ jack_nframes_t jack_frame_time (const jack_client_t *client) return jack_time_to_frames(client, SPA_TIMESPEC_TO_USEC(&ts)); } +SPA_EXPORT jack_nframes_t jack_last_frame_time (const jack_client_t *client) { struct client *c = (struct client *) client; return c->jack_position.frame; } +SPA_EXPORT int jack_get_cycle_times(const jack_client_t *client, jack_nframes_t *current_frames, jack_time_t *current_usecs, @@ -2929,6 +3014,7 @@ int jack_get_cycle_times(const jack_client_t *client, return 0; } +SPA_EXPORT jack_time_t jack_frames_to_time(const jack_client_t *client, jack_nframes_t frames) { struct client *c = (struct client *) client; @@ -2936,6 +3022,7 @@ jack_time_t jack_frames_to_time(const jack_client_t *client, jack_nframes_t fram return c->jack_position.usecs + (int64_t)rint((double) df / c->sample_rate); } +SPA_EXPORT jack_nframes_t jack_time_to_frames(const jack_client_t *client, jack_time_t usecs) { struct client *c = (struct client *) client; @@ -2943,6 +3030,7 @@ jack_nframes_t jack_time_to_frames(const jack_client_t *client, jack_time_t usec return c->jack_position.frame + (int32_t)rint((double)du * c->sample_rate); } +SPA_EXPORT jack_time_t jack_get_time() { struct timespec ts; @@ -2950,27 +3038,32 @@ jack_time_t jack_get_time() return SPA_TIMESPEC_TO_USEC(&ts); } +SPA_EXPORT void jack_set_error_function (void (*func)(const char *)) { pw_log_warn("not implemented"); } +SPA_EXPORT void jack_set_info_function (void (*func)(const char *)) { pw_log_warn("not implemented"); } +SPA_EXPORT void jack_free(void* ptr) { free(ptr); } +SPA_EXPORT int jack_release_timebase (jack_client_t *client) { pw_log_warn("not implemented"); return -ENOTSUP; } +SPA_EXPORT int jack_set_sync_callback (jack_client_t *client, JackSyncCallback sync_callback, void *arg) @@ -2981,6 +3074,7 @@ int jack_set_sync_callback (jack_client_t *client, return 0; } +SPA_EXPORT int jack_set_sync_timeout (jack_client_t *client, jack_time_t timeout) { @@ -2988,6 +3082,7 @@ int jack_set_sync_timeout (jack_client_t *client, return -ENOTSUP; } +SPA_EXPORT int jack_set_timebase_callback (jack_client_t *client, int conditional, JackTimebaseCallback timebase_callback, @@ -2999,6 +3094,7 @@ int jack_set_timebase_callback (jack_client_t *client, return 0; } +SPA_EXPORT int jack_transport_locate (jack_client_t *client, jack_nframes_t frame) { @@ -3006,6 +3102,7 @@ int jack_transport_locate (jack_client_t *client, return -ENOTSUP; } +SPA_EXPORT jack_transport_state_t jack_transport_query (const jack_client_t *client, jack_position_t *pos) { @@ -3015,12 +3112,14 @@ jack_transport_state_t jack_transport_query (const jack_client_t *client, return JackTransportRolling; } +SPA_EXPORT jack_nframes_t jack_get_current_transport_frame (const jack_client_t *client) { pw_log_warn("not implemented"); return 0; } +SPA_EXPORT int jack_transport_reposition (jack_client_t *client, const jack_position_t *pos) { @@ -3028,16 +3127,19 @@ int jack_transport_reposition (jack_client_t *client, return -ENOTSUP; } +SPA_EXPORT void jack_transport_start (jack_client_t *client) { pw_log_warn("not implemented"); } +SPA_EXPORT void jack_transport_stop (jack_client_t *client) { pw_log_warn("not implemented"); } +SPA_EXPORT void jack_get_transport_info (jack_client_t *client, jack_transport_info_t *tinfo) { @@ -3046,12 +3148,14 @@ void jack_get_transport_info (jack_client_t *client, pw_log_warn("not implemented"); } +SPA_EXPORT void jack_set_transport_info (jack_client_t *client, jack_transport_info_t *tinfo) { pw_log_warn("not implemented"); } +SPA_EXPORT int jack_set_session_callback (jack_client_t *client, JackSessionCallback session_callback, void *arg) @@ -3060,6 +3164,7 @@ int jack_set_session_callback (jack_client_t *client, return -ENOTSUP; } +SPA_EXPORT int jack_session_reply (jack_client_t *client, jack_session_event_t *event) { @@ -3068,29 +3173,34 @@ int jack_session_reply (jack_client_t *client, } +SPA_EXPORT void jack_session_event_free (jack_session_event_t *event) { pw_log_warn("not implemented"); } +SPA_EXPORT char *jack_client_get_uuid (jack_client_t *client) { pw_log_warn("not implemented"); return ""; } +SPA_EXPORT int jack_client_real_time_priority (jack_client_t * client) { pw_log_warn("not implemented"); return -ENOTSUP; } +SPA_EXPORT int jack_client_max_real_time_priority (jack_client_t *client) { pw_log_warn("not implemented"); return -ENOTSUP; } +SPA_EXPORT int jack_acquire_real_time_scheduling (jack_native_thread_t thread, int priority) { pw_log_warn("not implemented"); @@ -3113,6 +3223,7 @@ int jack_acquire_real_time_scheduling (jack_native_thread_t thread, int priority * * @returns 0, if successful; otherwise some error number. */ +SPA_EXPORT int jack_client_create_thread (jack_client_t* client, jack_native_thread_t *thread, int priority, @@ -3126,24 +3237,28 @@ int jack_client_create_thread (jack_client_t* client, return globals.creator(thread, NULL, start_routine, arg); } +SPA_EXPORT int jack_drop_real_time_scheduling (jack_native_thread_t thread) { pw_log_warn("not implemented"); return -ENOTSUP; } +SPA_EXPORT int jack_client_stop_thread(jack_client_t* client, jack_native_thread_t thread) { pw_log_warn("not implemented"); return -ENOTSUP; } +SPA_EXPORT int jack_client_kill_thread(jack_client_t* client, jack_native_thread_t thread) { pw_log_warn("not implemented"); return -ENOTSUP; } +SPA_EXPORT void jack_set_thread_creator (jack_thread_creator_t creator) { if (creator == NULL) @@ -3161,12 +3276,14 @@ static inline uint8_t * midi_event_data (void* port_buffer, return SPA_MEMBER(port_buffer, event->byte_offset, uint8_t); } +SPA_EXPORT uint32_t jack_midi_get_event_count(void* port_buffer) { struct midi_buffer *mb = port_buffer; return mb->event_count; } +SPA_EXPORT int jack_midi_event_get(jack_midi_event_t *event, void *port_buffer, uint32_t event_index) @@ -3180,6 +3297,7 @@ int jack_midi_event_get(jack_midi_event_t *event, return 0; } +SPA_EXPORT void jack_midi_clear_buffer(void *port_buffer) { struct midi_buffer *mb = port_buffer; @@ -3188,11 +3306,13 @@ void jack_midi_clear_buffer(void *port_buffer) mb->lost_events = 0; } +SPA_EXPORT void jack_midi_reset_buffer(void *port_buffer) { jack_midi_clear_buffer(port_buffer); } +SPA_EXPORT size_t jack_midi_max_event_size(void* port_buffer) { struct midi_buffer *mb = port_buffer; @@ -3214,6 +3334,7 @@ size_t jack_midi_max_event_size(void* port_buffer) } } +SPA_EXPORT jack_midi_data_t* jack_midi_event_reserve(void *port_buffer, jack_nframes_t time, size_t data_size) @@ -3254,6 +3375,7 @@ failed: return NULL; } +SPA_EXPORT int jack_midi_event_write(void *port_buffer, jack_nframes_t time, const jack_midi_data_t *data, @@ -3268,6 +3390,7 @@ int jack_midi_event_write(void *port_buffer, } } +SPA_EXPORT uint32_t jack_midi_get_lost_event_count(void *port_buffer) { struct midi_buffer *mb = port_buffer; From 25cc424d70b578df0fd305255f833e48b3e4e7af Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 7 Feb 2019 12:28:59 +0100 Subject: [PATCH 055/133] jack: handle NULL position --- src/pipewire-jack.c | 52 ++++++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 1cd891070..425f74adf 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -780,15 +780,35 @@ on_rtsocket_condition(void *data, int fd, enum spa_io mask) } if (mask & SPA_IO_IN) { - uint64_t cmd, usec; + uint64_t cmd, nsec, frame; + int64_t delay; uint32_t buffer_size, sample_rate; - if (read(fd, &cmd, sizeof(uint64_t)) != sizeof(uint64_t)) + if (read(fd, &cmd, sizeof(cmd)) != sizeof(cmd)) pw_log_warn("jack %p: read failed %m", c); if (cmd > 1) pw_log_warn("jack %p: missed %"PRIu64" wakeups", c, cmd - 1); - buffer_size = c->position->size; + if (c->position) { + buffer_size = c->position->size; + if (c->position->clock.rate.num != 0 && c->position->clock.rate.denom != 0) + sample_rate = c->position->clock.rate.denom / c->position->clock.rate.num; + else + sample_rate = c->sample_rate; + c->rate_diff = c->position->clock.rate_diff; + nsec = c->position->clock.nsec; + frame = c->position->clock.position; + delay = c->position->clock.delay; + } + else { + buffer_size = DEFAULT_BUFFER_SIZE; + sample_rate = DEFAULT_SAMPLE_RATE; + c->rate_diff = 1.0; + nsec = 0; + frame = c->jack_position.frame + buffer_size; + delay = 0; + } + if (buffer_size != c->buffer_size) { pw_log_info("jack %p: buffersize %d", c, buffer_size); c->buffer_size = buffer_size; @@ -796,11 +816,6 @@ on_rtsocket_condition(void *data, int fd, enum spa_io mask) c->bufsize_callback(c->buffer_size, c->bufsize_arg); } - if (c->position->clock.rate.num != 0 && c->position->clock.rate.denom != 0) - sample_rate = c->position->clock.rate.denom / c->position->clock.rate.num; - else - sample_rate = c->sample_rate; - if (sample_rate != c->sample_rate) { pw_log_info("jack %p: sample_rate %d", c, sample_rate); c->sample_rate = sample_rate; @@ -808,13 +823,10 @@ on_rtsocket_condition(void *data, int fd, enum spa_io mask) c->srate_callback(c->sample_rate, c->srate_arg); } - c->rate_diff = c->position->clock.rate_diff; - usec = c->position->clock.nsec / SPA_NSEC_PER_USEC; - c->jack_position.unique_1++; - c->jack_position.usecs = usec; - c->jack_position.frame_rate = c->sample_rate; - c->jack_position.frame = c->position->clock.position; + c->jack_position.usecs = nsec / SPA_NSEC_PER_USEC; + c->jack_position.frame_rate = sample_rate; + c->jack_position.frame = frame; c->jack_position.valid = 0; c->jack_position.unique_2 = c->jack_position.unique_1; @@ -823,9 +835,9 @@ on_rtsocket_condition(void *data, int fd, enum spa_io mask) &c->jack_position, c->sync_arg); } - pw_log_trace("do process %"PRIu64" %d %d %d %"PRIi64" %f", c->position->clock.nsec, + pw_log_trace("do process %"PRIu64" %d %d %d %"PRIi64" %f", nsec, c->buffer_size, c->sample_rate, - c->jack_position.frame, c->position->clock.delay, c->rate_diff); + c->jack_position.frame, delay, c->rate_diff); if (c->process_callback) c->process_callback(c->buffer_size, c->process_arg); @@ -840,7 +852,8 @@ on_rtsocket_condition(void *data, int fd, enum spa_io mask) process_tee(c); cmd = 1; - write(c->writefd, &cmd, 8); + if (write(c->writefd, &cmd, sizeof(cmd)) != sizeof(cmd)) + pw_log_warn("jack %p: write failed %m", c); } } @@ -1438,7 +1451,8 @@ static void client_node_port_set_io(void *object, break; } - pw_log_debug("port %p: set io id %u %u %u %u %p", p, id, mem_id, offset, size, ptr); + pw_log_debug("port %p: set io %s %u %u %u %p", p, + spa_debug_type_find_name(spa_type_io, id), mem_id, offset, size, ptr); exit: pw_client_node_proxy_done(c->node_proxy, seq, res); @@ -2976,7 +2990,7 @@ jack_nframes_t jack_frames_since_cycle_start (const jack_client_t *client) uint64_t diff; clock_gettime(CLOCK_MONOTONIC, &ts); - diff = SPA_TIMESPEC_TO_NSEC(&ts) - c->position->clock.nsec; + diff = SPA_TIMESPEC_TO_USEC(&ts) - c->jack_position.usecs; res = (jack_nframes_t) floor(((float)c->sample_rate * diff) / 1000000000.0f); return res; } From 766e528f40ab9108d5868b79c952104a355969bf Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 12 Feb 2019 17:40:46 +0100 Subject: [PATCH 056/133] jack: handle activation --- src/pipewire-jack.c | 133 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 119 insertions(+), 14 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 425f74adf..272b7cf4a 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -161,6 +161,13 @@ struct io { uint32_t memid; }; +struct link { + uint32_t node_id; + uint32_t memid; + struct pw_node_activation *activation; + int signalfd; +}; + struct mix { struct spa_list link; struct spa_list port_link; @@ -239,7 +246,6 @@ struct client { struct spa_hook proxy_listener; uint32_t node_id; - int writefd; struct spa_source *socket_source; bool active; @@ -286,7 +292,10 @@ struct client { struct spa_list ports[2]; struct spa_list free_ports[2]; - struct pw_array mems; + struct pw_array mems; + struct pw_array links; + + struct pw_node_activation *activation; bool started; int status; @@ -576,6 +585,17 @@ static const struct pw_proxy_events proxy_events = { .destroy = on_node_proxy_destroy, }; +static struct link *find_activation(struct pw_array *links, uint32_t node_id) +{ + struct link *l; + + pw_array_for_each(l, links) { + if (l->node_id == node_id) + return l; + } + return NULL; +} + static struct mem *find_mem(struct pw_array *mems, uint32_t id) { struct mem *m; @@ -688,10 +708,6 @@ do_remove_sources(struct spa_loop *loop, pw_loop_destroy_source(c->context.core->data_loop, c->socket_source); c->socket_source = NULL; } - if (c->writefd != -1) { - close(c->writefd); - c->writefd = -1; - } return 0; } @@ -772,6 +788,7 @@ static void on_rtsocket_condition(void *data, int fd, enum spa_io mask) { struct client *c = data; + struct timespec ts; if (mask & (SPA_IO_ERR | SPA_IO_HUP)) { pw_log_warn("got error"); @@ -783,6 +800,7 @@ on_rtsocket_condition(void *data, int fd, enum spa_io mask) uint64_t cmd, nsec, frame; int64_t delay; uint32_t buffer_size, sample_rate; + struct link *l; if (read(fd, &cmd, sizeof(cmd)) != sizeof(cmd)) pw_log_warn("jack %p: read failed %m", c); @@ -809,6 +827,11 @@ on_rtsocket_condition(void *data, int fd, enum spa_io mask) delay = 0; } + clock_gettime(CLOCK_MONOTONIC, &ts); + nsec = SPA_TIMESPEC_TO_NSEC(&ts); + c->activation->status = AWAKE; + c->activation->finish_time = nsec; + if (buffer_size != c->buffer_size) { pw_log_info("jack %p: buffersize %d", c, buffer_size); c->buffer_size = buffer_size; @@ -851,15 +874,37 @@ on_rtsocket_condition(void *data, int fd, enum spa_io mask) } process_tee(c); + clock_gettime(CLOCK_MONOTONIC, &ts); + nsec = SPA_TIMESPEC_TO_NSEC(&ts); + c->activation->status = FINISHED; + c->activation->finish_time = nsec; + cmd = 1; - if (write(c->writefd, &cmd, sizeof(cmd)) != sizeof(cmd)) - pw_log_warn("jack %p: write failed %m", c); + pw_array_for_each(l, &c->links) { + struct spa_graph_state *state; + + if (l->activation == NULL) + continue; + + state = &l->activation->state[0]; + + pw_log_trace("link %p %p %d/%d", l, state, state->pending, state->required); + if (spa_graph_state_dec(state, 1)) { + l->activation->status = TRIGGERED; + l->activation->signal_time = nsec; + + pw_log_trace("signal %p %p", l, state); + if (write(l->signalfd, &cmd, sizeof(cmd)) != sizeof(cmd)) + pw_log_warn("jack %p: write failed %m", c); + } + } } } static void clean_transport(struct client *c) { struct mem *m; + struct link *l; if (c->node_id == SPA_ID_INVALID) return; @@ -869,8 +914,9 @@ static void clean_transport(struct client *c) pw_array_for_each(m, &c->mems) clear_mem(c, m); pw_array_clear(&c->mems); - - close(c->writefd); + pw_array_for_each(l, &c->links) + close(l->signalfd); + pw_array_clear(&c->links); c->node_id = SPA_ID_INVALID; } @@ -890,8 +936,8 @@ static void client_node_transport(void *object, pw_log_debug("client %p: create client transport with fds %d %d for node %u", c, readfd, writefd, node_id); - c->writefd = writefd; - c->socket_source = pw_loop_add_io(core->data_loop, + close(writefd); + c->socket_source = pw_loop_add_io(core->data_loop, readfd, SPA_IO_ERR | SPA_IO_HUP, true, on_rtsocket_condition, c); @@ -1458,6 +1504,63 @@ static void client_node_port_set_io(void *object, pw_client_node_proxy_done(c->node_proxy, seq, res); } +static void client_node_set_activation(void *object, + uint32_t node_id, + int signalfd, + uint32_t mem_id, + uint32_t offset, + uint32_t size) +{ + struct client *c = (struct client *) object; + struct mem *m; + struct link *link; + void *ptr; + + if (mem_id == SPA_ID_INVALID) { + ptr = NULL; + size = 0; + } + else { + m = find_mem(&c->mems, mem_id); + if (m == NULL) { + pw_log_warn("unknown memory id %u", mem_id); + return; + } + if ((ptr = mem_map(c, m, offset, size)) == NULL) { + return; + } + m->ref++; + } + + pw_log_debug("node %p: set activation %u: %u %u %u %p", c, node_id, + mem_id, offset, size, ptr); + + if (c->node_id == node_id) { + pw_log_debug("node %p: our activation %u: %u %u %u %p", c, node_id, + mem_id, offset, size, ptr); + if (ptr) + c->activation = ptr; + return; + } + + if (ptr) { + link = pw_array_add(&c->links, sizeof(struct link)); + link->node_id = node_id; + link->memid = mem_id; + link->activation = ptr; + link->signalfd = signalfd; + } + else { + link = find_activation(&c->links, node_id); + if (link == NULL) + return; + + link->node_id = SPA_ID_INVALID; + link->activation = NULL; + close(link->signalfd); + } +} + static const struct pw_client_node_proxy_events client_node_events = { PW_VERSION_CLIENT_NODE_PROXY_EVENTS, .add_mem = client_node_add_mem, @@ -1472,6 +1575,7 @@ static const struct pw_client_node_proxy_events client_node_events = { .port_use_buffers = client_node_port_use_buffers, .port_command = client_node_port_command, .port_set_io = client_node_port_set_io, + .set_activation = client_node_set_activation, }; static jack_port_type_id_t string_to_type(const char *port_type) @@ -1725,8 +1829,9 @@ jack_client_t * jack_client_open (const char *client_name, #endif } - pw_array_init(&client->mems, 64); - pw_array_ensure_size(&client->mems, sizeof(struct mem) * 64); + pw_array_init(&client->mems, 64); + pw_array_ensure_size(&client->mems, sizeof(struct mem) * 64); + pw_array_init(&client->links, 64); client->buffer_size = (uint32_t)-1; client->sample_rate = (uint32_t)-1; From eef8bc593c69a5d53e91e7f03a25debff332887d Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 13 Feb 2019 12:46:14 +0100 Subject: [PATCH 057/133] jack: remote port_send_command --- src/pipewire-jack.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 272b7cf4a..19a96a7b3 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -1397,13 +1397,6 @@ static void client_node_port_use_buffers(void *object, pw_client_node_proxy_done(c->node_proxy, seq, res); } -static void client_node_port_command(void *object, - enum spa_direction direction, - uint32_t port_id, - const struct spa_command *command) -{ -} - static void clear_io(struct client *c, struct io *io) { struct mem *m; @@ -1573,7 +1566,6 @@ static const struct pw_client_node_proxy_events client_node_events = { .remove_port = client_node_remove_port, .port_set_param = client_node_port_set_param, .port_use_buffers = client_node_port_use_buffers, - .port_command = client_node_port_command, .port_set_io = client_node_port_set_io, .set_activation = client_node_set_activation, }; From 1108c5d83d2f105f6976a6edd79efa348b688d23 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 14 Feb 2019 17:39:16 +0100 Subject: [PATCH 058/133] jack: change port flags --- src/pipewire-jack.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 19a96a7b3..b2fc73c05 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -2361,10 +2361,10 @@ jack_port_t * jack_port_register (jack_client_t *client, items[dict.n_items++] = SPA_DICT_ITEM_INIT("port.name", port_name); if (type_id == 1) - port_info.flags = SPA_PORT_INFO_FLAG_NO_REF; + port_info.flags = SPA_PORT_FLAG_NO_REF; else - port_info.flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS | - SPA_PORT_INFO_FLAG_NO_REF; + port_info.flags = SPA_PORT_FLAG_CAN_USE_BUFFERS | + SPA_PORT_FLAG_NO_REF; param_enum_format(c, p, ¶ms[n_params++], &b); param_buffers(c, p, ¶ms[n_params++], &b); From af96643eaa5d3d9df727629e96a0b1f101a16664 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 18 Feb 2019 12:28:00 +0100 Subject: [PATCH 059/133] jack: fix for api changes --- src/pipewire-jack.c | 129 +++++++++++++++++++++++++------------------- 1 file changed, 73 insertions(+), 56 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index b2fc73c05..cd85c6472 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -514,11 +514,14 @@ jack_get_version_string(void) return "0.0.0.0"; } -static void on_sync_reply(void *data, uint32_t seq) +static int on_sync_reply(void *data, uint32_t id, uint32_t seq) { struct client *client = data; + if (id != 0) + return 0; client->last_sync = seq; pw_thread_loop_signal(client->context.loop, false); + return 0; } static void on_state_changed(void *data, enum pw_remote_state old, @@ -557,7 +560,7 @@ static int do_sync(struct client *client) { uint32_t seq = client->last_sync + 1; - pw_core_proxy_sync(client->core_proxy, seq); + pw_core_proxy_sync(client->core_proxy, 0, seq); while (true) { pw_thread_loop_wait(client->context.loop); @@ -671,7 +674,7 @@ static void clear_mem(struct client *c, struct mem *m) } } -static void client_node_add_mem(void *object, +static int client_node_add_mem(void *object, uint32_t mem_id, uint32_t type, int memfd, @@ -684,7 +687,7 @@ static void client_node_add_mem(void *object, if (m) { pw_log_warn(NAME" %p: duplicate mem %u, fd %d, flags %d", c, mem_id, memfd, flags); - return; + return -EINVAL; } m = pw_array_add(&c->mems, sizeof(struct mem)); @@ -696,6 +699,7 @@ static void client_node_add_mem(void *object, m->ref = 0; m->map = PW_MAP_RANGE_INIT; m->ptr = NULL; + return 0; } static int @@ -921,7 +925,7 @@ static void clean_transport(struct client *c) c->node_id = SPA_ID_INVALID; } -static void client_node_transport(void *object, +static int client_node_transport(void *object, uint32_t node_id, int readfd, int writefd) @@ -941,21 +945,23 @@ static void client_node_transport(void *object, readfd, SPA_IO_ERR | SPA_IO_HUP, true, on_rtsocket_condition, c); + return 0; } -static void client_node_set_param(void *object, uint32_t seq, - uint32_t id, uint32_t flags, - const struct spa_pod *param) +static int client_node_set_param(void *object, + uint32_t id, uint32_t flags, + const struct spa_pod *param) { struct client *c = (struct client *) object; - pw_client_node_proxy_done(c->node_proxy, seq, -ENOTSUP); + pw_proxy_error((struct pw_proxy*)c->node_proxy, -ENOTSUP, "not supported"); + return -ENOTSUP; } -static void client_node_set_io(void *object, - uint32_t id, - uint32_t mem_id, - uint32_t offset, - uint32_t size) +static int client_node_set_io(void *object, + uint32_t id, + uint32_t mem_id, + uint32_t offset, + uint32_t size) { struct client *c = (struct client *) object; struct mem *m; @@ -969,10 +975,10 @@ static void client_node_set_io(void *object, m = find_mem(&c->mems, mem_id); if (m == NULL) { pw_log_warn("unknown memory id %u", mem_id); - return; + return -EINVAL; } if ((ptr = mem_map(c, m, offset, size)) == NULL) { - return; + return -errno; } m->ref++; } @@ -987,13 +993,15 @@ static void client_node_set_io(void *object, } c->position = ptr; } + return 0; } -static void client_node_event(void *object, const struct spa_event *event) +static int client_node_event(void *object, const struct spa_event *event) { + return -ENOTSUP; } -static void client_node_command(void *object, uint32_t seq, const struct spa_command *command) +static int client_node_command(void *object, const struct spa_command *command) { struct client *c = (struct client *) object; @@ -1006,7 +1014,6 @@ static void client_node_command(void *object, uint32_t seq, const struct spa_com c->started = false; } - pw_client_node_proxy_done(c->node_proxy, seq, 0); break; case SPA_NODE_COMMAND_Start: @@ -1016,33 +1023,34 @@ static void client_node_command(void *object, uint32_t seq, const struct spa_com SPA_IO_IN | SPA_IO_ERR | SPA_IO_HUP); c->started = true; } - pw_client_node_proxy_done(c->node_proxy, seq, 0); break; default: pw_log_warn("unhandled node command %d", SPA_COMMAND_TYPE(command)); - pw_client_node_proxy_done(c->node_proxy, seq, -ENOTSUP); + pw_proxy_error((struct pw_proxy*)c->node_proxy, -ENOTSUP, + "unhandled command %d", SPA_COMMAND_TYPE(command)); } + return 0; } -static void client_node_add_port(void *object, - uint32_t seq, +static int client_node_add_port(void *object, enum spa_direction direction, uint32_t port_id) { struct client *c = (struct client *) object; - pw_client_node_proxy_done(c->node_proxy, seq, -ENOTSUP); + pw_proxy_error((struct pw_proxy*)c->node_proxy, -ENOTSUP, "add port not supported"); + return -ENOTSUP; } -static void client_node_remove_port(void *object, - uint32_t seq, +static int client_node_remove_port(void *object, enum spa_direction direction, uint32_t port_id) { struct client *c = (struct client *) object; - pw_client_node_proxy_done(c->node_proxy, seq, -ENOTSUP); + pw_proxy_error((struct pw_proxy*)c->node_proxy, -ENOTSUP, "remove port not supported"); + return -ENOTSUP; } -static void clear_buffers(struct client *c, struct mix *mix) +static int clear_buffers(struct client *c, struct mix *mix) { struct port *port = mix->port; struct buffer *b; @@ -1080,6 +1088,7 @@ static void clear_buffers(struct client *c, struct mix *mix) } mix->n_buffers = 0; spa_list_init(&mix->queue); + return 0; } static int param_enum_format(struct client *c, struct port *p, @@ -1222,8 +1231,7 @@ static int port_set_format(struct client *c, struct port *p, return 0; } -static void client_node_port_set_param(void *object, - uint32_t seq, +static int client_node_port_set_param(void *object, enum spa_direction direction, uint32_t port_id, uint32_t id, uint32_t flags, @@ -1243,15 +1251,13 @@ static void client_node_port_set_param(void *object, param_format(c, p, ¶ms[1], &b); param_buffers(c, p, ¶ms[2], &b); - pw_client_node_proxy_port_update(c->node_proxy, + return pw_client_node_proxy_port_update(c->node_proxy, direction, port_id, PW_CLIENT_NODE_PORT_UPDATE_PARAMS, 3, (const struct spa_pod **) params, NULL); - - pw_client_node_proxy_done(c->node_proxy, seq, 0); } static void init_buffer(struct port *p, void *data, size_t maxsize) @@ -1269,8 +1275,7 @@ static void init_buffer(struct port *p, void *data, size_t maxsize) memset(data, 0, maxsize); } -static void client_node_port_use_buffers(void *object, - uint32_t seq, +static int client_node_port_use_buffers(void *object, enum spa_direction direction, uint32_t port_id, uint32_t mix_id, @@ -1394,7 +1399,9 @@ static void client_node_port_use_buffers(void *object, res = 0; done: - pw_client_node_proxy_done(c->node_proxy, seq, res); + if (res < 0) + pw_proxy_error((struct pw_proxy*)c->node_proxy, res, spa_strerror(res)); + return res; } static void clear_io(struct client *c, struct io *io) @@ -1432,8 +1439,7 @@ static struct io *update_io(struct client *c, struct mix *mix, return io; } -static void client_node_port_set_io(void *object, - uint32_t seq, +static int client_node_port_set_io(void *object, enum spa_direction direction, uint32_t port_id, uint32_t mix_id, @@ -1494,10 +1500,12 @@ static void client_node_port_set_io(void *object, spa_debug_type_find_name(spa_type_io, id), mem_id, offset, size, ptr); exit: - pw_client_node_proxy_done(c->node_proxy, seq, res); + if (res < 0) + pw_proxy_error((struct pw_proxy*)c->node_proxy, res, spa_strerror(res)); + return res; } -static void client_node_set_activation(void *object, +static int client_node_set_activation(void *object, uint32_t node_id, int signalfd, uint32_t mem_id, @@ -1508,6 +1516,7 @@ static void client_node_set_activation(void *object, struct mem *m; struct link *link; void *ptr; + int res = 0; if (mem_id == SPA_ID_INVALID) { ptr = NULL; @@ -1517,10 +1526,12 @@ static void client_node_set_activation(void *object, m = find_mem(&c->mems, mem_id); if (m == NULL) { pw_log_warn("unknown memory id %u", mem_id); - return; + res = -EINVAL; + goto exit; } if ((ptr = mem_map(c, m, offset, size)) == NULL) { - return; + res = -errno; + goto exit; } m->ref++; } @@ -1533,7 +1544,7 @@ static void client_node_set_activation(void *object, mem_id, offset, size, ptr); if (ptr) c->activation = ptr; - return; + return 0; } if (ptr) { @@ -1545,13 +1556,19 @@ static void client_node_set_activation(void *object, } else { link = find_activation(&c->links, node_id); - if (link == NULL) - return; - + if (link == NULL) { + res = -EINVAL; + goto exit; + } link->node_id = SPA_ID_INVALID; link->activation = NULL; close(link->signalfd); } + + exit: + if (res < 0) + pw_proxy_error((struct pw_proxy*)c->node_proxy, res, spa_strerror(res)); + return res; } static const struct pw_client_node_proxy_events client_node_events = { @@ -1596,7 +1613,7 @@ static const char* type_to_string(jack_port_type_id_t type_id) } } -static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, +static int 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) { @@ -1606,7 +1623,7 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, size_t size; if (props == NULL) - return; + return 0; if (type == PW_TYPE_INTERFACE_Node) { if ((str = spa_dict_lookup(props, "node.name")) == NULL) @@ -1729,15 +1746,14 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, if (c->connect_callback) c->connect_callback(o->port_link.src, o->port_link.dst, 1, c->connect_arg); } - exit: - return; + return 0; exit_free: free_object(c, o); - return; + return 0; } -static void registry_event_global_remove(void *object, uint32_t id) +static int registry_event_global_remove(void *object, uint32_t id) { struct client *c = (struct client *) object; struct object *o; @@ -1746,7 +1762,7 @@ static void registry_event_global_remove(void *object, uint32_t id) o = pw_map_lookup(&c->context.globals, id); if (o == NULL) - return; + return 0; if (o->type == PW_TYPE_INTERFACE_Node) { if (c->registration_callback) @@ -1767,6 +1783,7 @@ static void registry_event_global_remove(void *object, uint32_t id) * pw_map_insert_at(&c->context.globals, id, NULL); **/ free_object(c, o); + return 0; } static const struct pw_registry_proxy_events registry_events = { @@ -1912,7 +1929,7 @@ jack_client_t * jack_client_open (const char *client_name, PW_CLIENT_NODE_UPDATE_MAX_OUTPUTS, 0, 0, 0, NULL, NULL); - pw_client_node_proxy_done(client->node_proxy, 0, 0); + pw_proxy_sync((struct pw_proxy*)client->node_proxy, 0); if (do_sync(client) < 0) goto init_failed; @@ -2021,7 +2038,7 @@ int jack_activate (jack_client_t *client) int res = 0; pw_thread_loop_lock(c->context.loop); - pw_client_node_proxy_done(c->node_proxy, 0, 0); + pw_proxy_sync((struct pw_proxy*)c->node_proxy, 0); pw_client_node_proxy_set_active(c->node_proxy, true); res = do_sync(c); @@ -2041,7 +2058,7 @@ int jack_deactivate (jack_client_t *client) int res = 0; pw_thread_loop_lock(c->context.loop); - pw_client_node_proxy_done(c->node_proxy, 0, 0); + pw_proxy_sync((struct pw_proxy*)c->node_proxy, 0); pw_client_node_proxy_set_active(c->node_proxy, false); res = do_sync(c); From 65f9c4652514675b5cc6769d17853aee9542c63f Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 20 Feb 2019 17:50:05 +0100 Subject: [PATCH 060/133] jack: fix for async changes --- src/pipewire-jack.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index cd85c6472..5a10d5105 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -558,9 +558,9 @@ static const struct pw_core_proxy_events core_events = { static int do_sync(struct client *client) { - uint32_t seq = client->last_sync + 1; + uint32_t seq; - pw_core_proxy_sync(client->core_proxy, 0, seq); + seq = pw_core_proxy_sync(client->core_proxy, 0); while (true) { pw_thread_loop_wait(client->context.loop); @@ -1034,7 +1034,7 @@ static int client_node_command(void *object, const struct spa_command *command) static int client_node_add_port(void *object, enum spa_direction direction, - uint32_t port_id) + uint32_t port_id, const struct spa_dict *props) { struct client *c = (struct client *) object; pw_proxy_error((struct pw_proxy*)c->node_proxy, -ENOTSUP, "add port not supported"); @@ -1929,7 +1929,7 @@ jack_client_t * jack_client_open (const char *client_name, PW_CLIENT_NODE_UPDATE_MAX_OUTPUTS, 0, 0, 0, NULL, NULL); - pw_proxy_sync((struct pw_proxy*)client->node_proxy, 0); + pw_proxy_sync((struct pw_proxy*)client->node_proxy); if (do_sync(client) < 0) goto init_failed; @@ -2038,7 +2038,6 @@ int jack_activate (jack_client_t *client) int res = 0; pw_thread_loop_lock(c->context.loop); - pw_proxy_sync((struct pw_proxy*)c->node_proxy, 0); pw_client_node_proxy_set_active(c->node_proxy, true); res = do_sync(c); @@ -2058,7 +2057,6 @@ int jack_deactivate (jack_client_t *client) int res = 0; pw_thread_loop_lock(c->context.loop); - pw_proxy_sync((struct pw_proxy*)c->node_proxy, 0); pw_client_node_proxy_set_active(c->node_proxy, false); res = do_sync(c); From 1887f17cc8e829c3bca74bc3d4e37c267654b377 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 25 Feb 2019 12:27:02 +0100 Subject: [PATCH 061/133] jack: update for async changes --- src/pipewire-jack.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 5a10d5105..c12cf3400 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -560,7 +560,7 @@ static int do_sync(struct client *client) { uint32_t seq; - seq = pw_core_proxy_sync(client->core_proxy, 0); + seq = pw_proxy_sync((struct pw_proxy*)client->core_proxy, client->last_sync); while (true) { pw_thread_loop_wait(client->context.loop); @@ -1929,8 +1929,6 @@ jack_client_t * jack_client_open (const char *client_name, PW_CLIENT_NODE_UPDATE_MAX_OUTPUTS, 0, 0, 0, NULL, NULL); - pw_proxy_sync((struct pw_proxy*)client->node_proxy); - if (do_sync(client) < 0) goto init_failed; From cc9fd857ad106dcd74327ccfedc334aadd79caaa Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 25 Feb 2019 17:15:41 +0100 Subject: [PATCH 062/133] jack: update for seq change --- src/pipewire-jack.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index c12cf3400..bc6856e32 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -235,7 +235,7 @@ struct client { struct pw_core_proxy *core_proxy; struct spa_hook core_listener; - uint32_t last_sync; + int last_sync; bool error; struct pw_registry_proxy *registry_proxy; @@ -514,7 +514,7 @@ jack_get_version_string(void) return "0.0.0.0"; } -static int on_sync_reply(void *data, uint32_t id, uint32_t seq) +static int on_sync_reply(void *data, uint32_t id, int seq) { struct client *client = data; if (id != 0) @@ -558,7 +558,7 @@ static const struct pw_core_proxy_events core_events = { static int do_sync(struct client *client) { - uint32_t seq; + int seq; seq = pw_proxy_sync((struct pw_proxy*)client->core_proxy, client->last_sync); From 91938991dc062085401f28ef8e847b9b2925b749 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 27 Feb 2019 16:37:10 +0100 Subject: [PATCH 063/133] jack: update for port params --- src/pipewire-jack.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index bc6856e32..72f5119e1 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -1250,12 +1250,13 @@ static int client_node_port_set_param(void *object, param_enum_format(c, p, ¶ms[0], &b); param_format(c, p, ¶ms[1], &b); param_buffers(c, p, ¶ms[2], &b); + param_io(c, p, ¶ms[3], &b); return pw_client_node_proxy_port_update(c->node_proxy, direction, port_id, PW_CLIENT_NODE_PORT_UPDATE_PARAMS, - 3, + 4, (const struct spa_pod **) params, NULL); } @@ -1706,7 +1707,7 @@ static int registry_event_global(void *data, uint32_t id, uint32_t parent_id, o->port.flags = flags; o->port.type_id = type_id; - pw_log_debug("add port %d %s", id, o->port.name); + pw_log_debug("add port %d %s %d", id, o->port.name, type_id); } else if (type == PW_TYPE_INTERFACE_Link) { o = alloc_object(c); @@ -2333,7 +2334,8 @@ jack_port_t * jack_port_register (jack_client_t *client, { struct client *c = (struct client *) client; enum spa_direction direction; - struct spa_port_info port_info = { 0, }; + struct spa_port_info port_info; + struct spa_param_info port_params[5]; struct spa_dict dict; struct spa_dict_item items[10]; struct object *o; @@ -2368,16 +2370,25 @@ jack_port_t * jack_port_register (jack_client_t *client, spa_list_init(&p->mix); - port_info.props = &dict; - dict = SPA_DICT_INIT(items, 0); - items[dict.n_items++] = SPA_DICT_ITEM_INIT("port.dsp", port_type); - items[dict.n_items++] = SPA_DICT_ITEM_INIT("port.name", port_name); - + port_info = SPA_PORT_INFO_INIT(); + port_info.change_mask |= SPA_PORT_CHANGE_MASK_FLAGS; if (type_id == 1) port_info.flags = SPA_PORT_FLAG_NO_REF; else port_info.flags = SPA_PORT_FLAG_CAN_USE_BUFFERS | SPA_PORT_FLAG_NO_REF; + port_info.change_mask |= SPA_PORT_CHANGE_MASK_PROPS; + dict = SPA_DICT_INIT(items, 0); + items[dict.n_items++] = SPA_DICT_ITEM_INIT("port.dsp", port_type); + items[dict.n_items++] = SPA_DICT_ITEM_INIT("port.name", port_name); + port_info.props = &dict; + port_info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; + port_params[0] = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ); + port_params[1] = SPA_PARAM_INFO(SPA_PARAM_Buffers, SPA_PARAM_INFO_READ); + port_params[2] = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ); + port_params[3] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); + port_info.params = port_params; + port_info.n_params = 4; param_enum_format(c, p, ¶ms[n_params++], &b); param_buffers(c, p, ¶ms[n_params++], &b); @@ -2389,7 +2400,7 @@ jack_port_t * jack_port_register (jack_client_t *client, direction, p->id, PW_CLIENT_NODE_PORT_UPDATE_PARAMS | - PW_CLIENT_NODE_PORT_UPDATE_INFO , + PW_CLIENT_NODE_PORT_UPDATE_INFO, n_params, (const struct spa_pod **) params, &port_info); From 6ca80e4ec7abb48ef19423b405c0add9f98f93a0 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 1 Mar 2019 14:02:59 +0100 Subject: [PATCH 064/133] jack: events are void --- src/pipewire-jack.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 72f5119e1..135a4fdf3 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -514,14 +514,13 @@ jack_get_version_string(void) return "0.0.0.0"; } -static int on_sync_reply(void *data, uint32_t id, int seq) +static void on_sync_reply(void *data, uint32_t id, int seq) { struct client *client = data; if (id != 0) - return 0; + return; client->last_sync = seq; pw_thread_loop_signal(client->context.loop, false); - return 0; } static void on_state_changed(void *data, enum pw_remote_state old, @@ -1614,7 +1613,7 @@ static const char* type_to_string(jack_port_type_id_t type_id) } } -static int registry_event_global(void *data, uint32_t id, uint32_t parent_id, +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) { @@ -1624,7 +1623,7 @@ static int registry_event_global(void *data, uint32_t id, uint32_t parent_id, size_t size; if (props == NULL) - return 0; + return; if (type == PW_TYPE_INTERFACE_Node) { if ((str = spa_dict_lookup(props, "node.name")) == NULL) @@ -1748,13 +1747,13 @@ static int registry_event_global(void *data, uint32_t id, uint32_t parent_id, c->connect_callback(o->port_link.src, o->port_link.dst, 1, c->connect_arg); } exit: - return 0; + return; exit_free: free_object(c, o); - return 0; + return; } -static int registry_event_global_remove(void *object, uint32_t id) +static void registry_event_global_remove(void *object, uint32_t id) { struct client *c = (struct client *) object; struct object *o; @@ -1763,7 +1762,7 @@ static int registry_event_global_remove(void *object, uint32_t id) o = pw_map_lookup(&c->context.globals, id); if (o == NULL) - return 0; + return; if (o->type == PW_TYPE_INTERFACE_Node) { if (c->registration_callback) @@ -1784,7 +1783,7 @@ static int registry_event_global_remove(void *object, uint32_t id) * pw_map_insert_at(&c->context.globals, id, NULL); **/ free_object(c, o); - return 0; + return; } static const struct pw_registry_proxy_events registry_events = { From 53693d1e328c2dde8fc167579f077e2e876c78d4 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 4 Mar 2019 12:30:17 +0100 Subject: [PATCH 065/133] jack_up date for client_node change --- src/pipewire-jack.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 135a4fdf3..f4f9629c1 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -1805,6 +1805,7 @@ jack_client_t * jack_client_open (const char *client_name, uint32_t n_support; const char *str; struct spa_cpu *cpu_iface; + struct spa_node_info ni; int i; if (getenv("PIPEWIRE_NOJACK") != NULL) @@ -1924,10 +1925,15 @@ jack_client_t * jack_client_open (const char *client_name, pw_proxy_add_listener((struct pw_proxy*)client->node_proxy, &client->proxy_listener, &proxy_events, client); + ni = SPA_NODE_INFO_INIT(); + ni.max_input_ports = MAX_PORTS; + ni.max_output_ports = MAX_PORTS; + ni.change_mask = SPA_NODE_CHANGE_MASK_FLAGS; + ni.flags = SPA_NODE_FLAG_RT; + pw_client_node_proxy_update(client->node_proxy, - PW_CLIENT_NODE_UPDATE_MAX_INPUTS | - PW_CLIENT_NODE_UPDATE_MAX_OUTPUTS, - 0, 0, 0, NULL, NULL); + PW_CLIENT_NODE_UPDATE_INFO, + 0, NULL, &ni); if (do_sync(client) < 0) goto init_failed; From 42b4529c4caac63d521ea343d2029f59b47929b6 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 6 Mar 2019 20:33:13 +0100 Subject: [PATCH 066/133] jack: update for scheduling changes --- src/pipewire-jack.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index f4f9629c1..602750d9b 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -884,7 +884,7 @@ on_rtsocket_condition(void *data, int fd, enum spa_io mask) cmd = 1; pw_array_for_each(l, &c->links) { - struct spa_graph_state *state; + struct pw_node_activation_state *state; if (l->activation == NULL) continue; @@ -892,7 +892,7 @@ on_rtsocket_condition(void *data, int fd, enum spa_io mask) state = &l->activation->state[0]; pw_log_trace("link %p %p %d/%d", l, state, state->pending, state->required); - if (spa_graph_state_dec(state, 1)) { + if (pw_node_activation_state_dec(state, 1)) { l->activation->status = TRIGGERED; l->activation->signal_time = nsec; From b50bd414ab497c461fcb2b25b545e59232dac7d1 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 8 Mar 2019 11:33:23 +0100 Subject: [PATCH 067/133] jack: fix awake time --- src/pipewire-jack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 602750d9b..e8cef2ffd 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -833,7 +833,7 @@ on_rtsocket_condition(void *data, int fd, enum spa_io mask) clock_gettime(CLOCK_MONOTONIC, &ts); nsec = SPA_TIMESPEC_TO_NSEC(&ts); c->activation->status = AWAKE; - c->activation->finish_time = nsec; + c->activation->awake_time = nsec; if (buffer_size != c->buffer_size) { pw_log_info("jack %p: buffersize %d", c, buffer_size); From 87bee58f49bbb7a0abab2c7391c1f9362af8d62a Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 11 Apr 2019 16:36:52 +0200 Subject: [PATCH 068/133] add SPA_EXPORT --- src/metadata.c | 46 ++++++++++++++++++++++++++++------------------ src/ringbuffer.c | 16 ++++++++++++++++ src/statistics.c | 3 +++ src/uuid.c | 9 +++++++++ 4 files changed, 56 insertions(+), 18 deletions(-) diff --git a/src/metadata.c b/src/metadata.c index 472ea1448..2dac2c3e8 100644 --- a/src/metadata.c +++ b/src/metadata.c @@ -29,66 +29,70 @@ #include -int -jack_set_property(jack_client_t*client, - jack_uuid_t subject, - const char* key, - const char* value, - const char* type) +SPA_EXPORT +int jack_set_property(jack_client_t*client, + jack_uuid_t subject, + const char* key, + const char* value, + const char* type) { pw_log_warn("not implemented"); return -1; } -int -jack_get_property(jack_uuid_t subject, - const char* key, - char** value, - char** type) +SPA_EXPORT +int jack_get_property(jack_uuid_t subject, + const char* key, + char** value, + char** type) { pw_log_warn("not implemented"); return -1; } -void -jack_free_description (jack_description_t* desc, int free_description_itself) +SPA_EXPORT +void jack_free_description (jack_description_t* desc, int free_description_itself) { pw_log_warn("not implemented"); } -int -jack_get_properties (jack_uuid_t subject, - jack_description_t* desc) +SPA_EXPORT +int jack_get_properties (jack_uuid_t subject, + jack_description_t* desc) { pw_log_warn("not implemented"); return -1; } -int -jack_get_all_properties (jack_description_t** descs) +SPA_EXPORT +int jack_get_all_properties (jack_description_t** descs) { pw_log_warn("not implemented"); return -1; } +SPA_EXPORT int jack_remove_property (jack_client_t* client, jack_uuid_t subject, const char* key) { pw_log_warn("not implemented"); return -1; } +SPA_EXPORT int jack_remove_properties (jack_client_t* client, jack_uuid_t subject) { pw_log_warn("not implemented"); return -1; } +SPA_EXPORT int jack_remove_all_properties (jack_client_t* client) { pw_log_warn("not implemented"); return -1; } +SPA_EXPORT int jack_set_property_change_callback (jack_client_t* client, JackPropertyChangeCallback callback, void* arg) @@ -97,9 +101,15 @@ int jack_set_property_change_callback (jack_client_t* client, return -1; } +SPA_EXPORT const char* JACK_METADATA_PRETTY_NAME = "http://jackaudio.org/metadata/pretty-name"; +SPA_EXPORT const char* JACK_METADATA_HARDWARE = "http://jackaudio.org/metadata/hardware"; +SPA_EXPORT const char* JACK_METADATA_CONNECTED = "http://jackaudio.org/metadata/connected"; +SPA_EXPORT const char* JACK_METADATA_PORT_GROUP = "http://jackaudio.org/metadata/port-group"; +SPA_EXPORT const char* JACK_METADATA_ICON_SMALL = "http://jackaudio.org/metadata/icon-small"; +SPA_EXPORT const char* JACK_METADATA_ICON_LARGE = "http://jackaudio.org/metadata/icon-large"; diff --git a/src/ringbuffer.c b/src/ringbuffer.c index 5004534d7..f76e38620 100644 --- a/src/ringbuffer.c +++ b/src/ringbuffer.c @@ -26,8 +26,11 @@ #include #include +#include + #include +SPA_EXPORT jack_ringbuffer_t *jack_ringbuffer_create(size_t sz) { size_t power_of_two; @@ -50,6 +53,7 @@ jack_ringbuffer_t *jack_ringbuffer_create(size_t sz) return rb; } +SPA_EXPORT void jack_ringbuffer_free(jack_ringbuffer_t *rb) { #ifdef USE_MLOCK @@ -60,6 +64,7 @@ void jack_ringbuffer_free(jack_ringbuffer_t *rb) free (rb); } +SPA_EXPORT void jack_ringbuffer_get_read_vector(const jack_ringbuffer_t *rb, jack_ringbuffer_data_t *vec) { @@ -89,6 +94,7 @@ void jack_ringbuffer_get_read_vector(const jack_ringbuffer_t *rb, } } +SPA_EXPORT void jack_ringbuffer_get_write_vector(const jack_ringbuffer_t *rb, jack_ringbuffer_data_t *vec) { @@ -120,6 +126,7 @@ void jack_ringbuffer_get_write_vector(const jack_ringbuffer_t *rb, } } +SPA_EXPORT size_t jack_ringbuffer_read(jack_ringbuffer_t *rb, char *dest, size_t cnt) { size_t free_cnt; @@ -151,6 +158,7 @@ size_t jack_ringbuffer_read(jack_ringbuffer_t *rb, char *dest, size_t cnt) return to_read; } +SPA_EXPORT size_t jack_ringbuffer_peek(jack_ringbuffer_t *rb, char *dest, size_t cnt) { size_t free_cnt; @@ -185,12 +193,14 @@ size_t jack_ringbuffer_peek(jack_ringbuffer_t *rb, char *dest, size_t cnt) return to_read; } +SPA_EXPORT void jack_ringbuffer_read_advance(jack_ringbuffer_t *rb, size_t cnt) { size_t tmp = (rb->read_ptr + cnt) & rb->size_mask; rb->read_ptr = tmp; } +SPA_EXPORT size_t jack_ringbuffer_read_space(const jack_ringbuffer_t *rb) { size_t w, r; @@ -204,6 +214,7 @@ size_t jack_ringbuffer_read_space(const jack_ringbuffer_t *rb) return (w - r + rb->size) & rb->size_mask; } +SPA_EXPORT int jack_ringbuffer_mlock(jack_ringbuffer_t *rb) { #ifdef USE_MLOCK @@ -214,6 +225,7 @@ int jack_ringbuffer_mlock(jack_ringbuffer_t *rb) return 0; } +SPA_EXPORT void jack_ringbuffer_reset(jack_ringbuffer_t *rb) { rb->read_ptr = 0; @@ -221,6 +233,7 @@ void jack_ringbuffer_reset(jack_ringbuffer_t *rb) memset(rb->buf, 0, rb->size); } +SPA_EXPORT void jack_ringbuffer_reset_size (jack_ringbuffer_t * rb, size_t sz) { rb->size = sz; @@ -229,6 +242,7 @@ void jack_ringbuffer_reset_size (jack_ringbuffer_t * rb, size_t sz) rb->write_ptr = 0; } +SPA_EXPORT size_t jack_ringbuffer_write(jack_ringbuffer_t *rb, const char *src, size_t cnt) { @@ -261,12 +275,14 @@ size_t jack_ringbuffer_write(jack_ringbuffer_t *rb, const char *src, return to_write; } +SPA_EXPORT void jack_ringbuffer_write_advance(jack_ringbuffer_t *rb, size_t cnt) { size_t tmp = (rb->write_ptr + cnt) & rb->size_mask; rb->write_ptr = tmp; } +SPA_EXPORT size_t jack_ringbuffer_write_space(const jack_ringbuffer_t *rb) { size_t w, r; diff --git a/src/statistics.c b/src/statistics.c index 06e3c089a..a99f7ee77 100644 --- a/src/statistics.c +++ b/src/statistics.c @@ -29,18 +29,21 @@ #include +SPA_EXPORT float jack_get_max_delayed_usecs (jack_client_t *client) { pw_log_warn("not implemented"); return 0.0f; } +SPA_EXPORT float jack_get_xrun_delayed_usecs (jack_client_t *client) { pw_log_warn("not implemented"); return 0.0f; } +SPA_EXPORT void jack_reset_max_delayed_usecs (jack_client_t *client) { pw_log_warn("not implemented"); diff --git a/src/uuid.c b/src/uuid.c index 893980d90..db2db86e7 100644 --- a/src/uuid.c +++ b/src/uuid.c @@ -29,51 +29,60 @@ #include +SPA_EXPORT jack_uuid_t jack_client_uuid_generate () { pw_log_warn("not implemented"); return 0; } +SPA_EXPORT jack_uuid_t jack_port_uuid_generate (uint32_t port_id) { pw_log_warn("not implemented"); return 0; } +SPA_EXPORT uint32_t jack_uuid_to_index (jack_uuid_t id) { pw_log_warn("not implemented"); return 0; } +SPA_EXPORT int jack_uuid_compare (jack_uuid_t id1, jack_uuid_t id2) { pw_log_warn("not implemented"); return 0; } +SPA_EXPORT void jack_uuid_copy (jack_uuid_t* dst, jack_uuid_t src) { pw_log_warn("not implemented"); } +SPA_EXPORT void jack_uuid_clear (jack_uuid_t*id) { pw_log_warn("not implemented"); } +SPA_EXPORT int jack_uuid_parse (const char *buf, jack_uuid_t*id) { pw_log_warn("not implemented"); return 0; } +SPA_EXPORT void jack_uuid_unparse (jack_uuid_t id, char buf[JACK_UUID_STRING_SIZE]) { pw_log_warn("not implemented"); } +SPA_EXPORT int jack_uuid_empty (jack_uuid_t id) { pw_log_warn("not implemented"); From 6cde4e76d338db78d3a66129be63949e141aa06b Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 23 Apr 2019 17:42:17 +0200 Subject: [PATCH 069/133] jack: use server timestamp when we can --- src/pipewire-jack.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index e8cef2ffd..8673fae6e 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -817,21 +817,20 @@ on_rtsocket_condition(void *data, int fd, enum spa_io mask) else sample_rate = c->sample_rate; c->rate_diff = c->position->clock.rate_diff; - nsec = c->position->clock.nsec; frame = c->position->clock.position; delay = c->position->clock.delay; + nsec = c->position->clock.nsec; } else { buffer_size = DEFAULT_BUFFER_SIZE; sample_rate = DEFAULT_SAMPLE_RATE; c->rate_diff = 1.0; - nsec = 0; frame = c->jack_position.frame + buffer_size; delay = 0; + clock_gettime(CLOCK_MONOTONIC, &ts); + nsec = SPA_TIMESPEC_TO_NSEC(&ts); } - clock_gettime(CLOCK_MONOTONIC, &ts); - nsec = SPA_TIMESPEC_TO_NSEC(&ts); c->activation->status = AWAKE; c->activation->awake_time = nsec; From 6ce9b5acf76566ad1c14d0ca258c7cbb0c9284c7 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 25 Apr 2019 13:03:19 +0200 Subject: [PATCH 070/133] jack: fix get_cycle_times --- src/pipewire-jack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 8673fae6e..62be45040 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -3146,7 +3146,7 @@ int jack_get_cycle_times(const jack_client_t *client, *current_frames = c->jack_position.frame; *current_usecs = c->jack_position.usecs; - *period_usecs = c->rate_diff * c->buffer_size / c->sample_rate; + *period_usecs = c->buffer_size / (c->sample_rate * c->rate_diff); *next_usecs = c->jack_position.usecs + (*period_usecs * 1000000.0f); pw_log_trace("client %p: %d %ld %ld %f", c, *current_frames, *current_usecs, *next_usecs, *period_usecs); From 640a50091ec972ecbb121b5f0928eb015435b0df Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 21 May 2019 15:36:25 +0200 Subject: [PATCH 071/133] jack: update for new API --- src/pipewire-jack.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 62be45040..5abc8871a 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -1894,7 +1894,6 @@ jack_client_t * jack_client_open (const char *client_name, &client->core_listener, &core_events, client); client->registry_proxy = pw_core_proxy_get_registry(client->core_proxy, - PW_TYPE_INTERFACE_Registry, PW_VERSION_REGISTRY, 0); pw_registry_proxy_add_listener(client->registry_proxy, &client->registry_listener, From 998263819ab94ca1188782b66dc26b9f518b1bc5 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 23 May 2019 12:28:31 +0200 Subject: [PATCH 072/133] update for api --- src/pipewire-jack.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 5abc8871a..7df0d2b1b 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -1894,7 +1894,7 @@ jack_client_t * jack_client_open (const char *client_name, &client->core_listener, &core_events, client); client->registry_proxy = pw_core_proxy_get_registry(client->core_proxy, - PW_VERSION_REGISTRY, 0); + PW_VERSION_REGISTRY_PROXY, 0); pw_registry_proxy_add_listener(client->registry_proxy, &client->registry_listener, ®istry_events, client); @@ -2851,7 +2851,7 @@ int jack_connect (jack_client_t *client, pw_core_proxy_create_object(c->core_proxy, "link-factory", PW_TYPE_INTERFACE_Link, - PW_VERSION_LINK, + PW_VERSION_LINK_PROXY, &props, 0); res = do_sync(c); From 8646c4976070a7c8cfbf710a294be45e15206b6c Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 24 May 2019 15:47:28 +0200 Subject: [PATCH 073/133] jack: update for keys --- src/pipewire-jack.c | 94 +++++++++++++++++++++++++-------------------- 1 file changed, 53 insertions(+), 41 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 7df0d2b1b..50646a4ff 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -1624,8 +1624,9 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, if (props == NULL) return; - if (type == PW_TYPE_INTERFACE_Node) { - if ((str = spa_dict_lookup(props, "node.name")) == NULL) + switch (type) { + case PW_TYPE_INTERFACE_Node: + if ((str = spa_dict_lookup(props, PW_KEY_NODE_NAME)) == NULL) goto exit; o = alloc_object(c); @@ -1633,39 +1634,41 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, snprintf(o->node.name, sizeof(o->node.name), "%s/%d", str, id); pw_log_debug("add node %d", id); - } - else if (type == PW_TYPE_INTERFACE_Port) { + break; + + case PW_TYPE_INTERFACE_Port: + { const struct spa_dict_item *item; unsigned long flags = 0; jack_port_type_id_t type_id; char full_name[1024]; - if ((str = spa_dict_lookup(props, "port.dsp")) == NULL) { + if ((str = spa_dict_lookup(props, PW_KEY_FORMAT_DSP)) == NULL) { type_id = 2; } else if ((type_id = string_to_type(str)) == SPA_ID_INVALID) goto exit; - if ((str = spa_dict_lookup(props, "port.name")) == NULL) + if ((str = spa_dict_lookup(props, PW_KEY_PORT_NAME)) == NULL) goto exit; spa_dict_for_each(item, props) { - if (!strcmp(item->key, "port.direction")) { + if (!strcmp(item->key, PW_KEY_PORT_DIRECTION)) { if (!strcmp(item->value, "in")) flags |= JackPortIsInput; else if (!strcmp(item->value, "out")) flags |= JackPortIsOutput; } - else if (!strcmp(item->key, "port.physical")) { + else if (!strcmp(item->key, PW_KEY_PORT_PHYSICAL)) { if (pw_properties_parse_bool(item->value)) flags |= JackPortIsPhysical; } - else if (!strcmp(item->key, "port.terminal")) { + else if (!strcmp(item->key, PW_KEY_PORT_TERMINAL)) { if (pw_properties_parse_bool(item->value)) flags |= JackPortIsTerminal; } - else if (!strcmp(item->key, "port.control")) { + else if (!strcmp(item->key, PW_KEY_PORT_CONTROL)) { if (pw_properties_parse_bool(item->value)) type_id = 1; } @@ -1692,12 +1695,12 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, o->port.port_id = SPA_ID_INVALID; } - if ((str = spa_dict_lookup(props, "port.alias1")) != NULL) + if ((str = spa_dict_lookup(props, PW_KEY_PORT_ALIAS1)) != NULL) snprintf(o->port.alias1, sizeof(o->port.alias1), "%s", str); else o->port.alias1[0] = '\0'; - if ((str = spa_dict_lookup(props, "port.alias2")) != NULL) + if ((str = spa_dict_lookup(props, PW_KEY_PORT_ALIAS2)) != NULL) snprintf(o->port.alias2, sizeof(o->port.alias2), "%s", str); else o->port.alias2[0] = '\0'; @@ -1706,23 +1709,26 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, o->port.type_id = type_id; pw_log_debug("add port %d %s %d", id, o->port.name, type_id); + break; } - else if (type == PW_TYPE_INTERFACE_Link) { + case PW_TYPE_INTERFACE_Link: o = alloc_object(c); spa_list_append(&c->context.links, &o->link); - if ((str = spa_dict_lookup(props, "link.output")) == NULL) + if ((str = spa_dict_lookup(props, PW_KEY_LINK_OUTPUT_PORT)) == NULL) goto exit_free; o->port_link.src = pw_properties_parse_int(str); - if ((str = spa_dict_lookup(props, "link.input")) == NULL) + if ((str = spa_dict_lookup(props, PW_KEY_LINK_INPUT_PORT)) == NULL) goto exit_free; o->port_link.dst = pw_properties_parse_int(str); pw_log_debug("add link %d %d->%d", id, o->port_link.src, o->port_link.dst); - } - else + break; + + default: goto exit; + } o->type = type; o->id = id; @@ -1733,17 +1739,21 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, pw_map_insert_at(&c->context.globals, size++, NULL); pw_map_insert_at(&c->context.globals, id, o); - if (type == PW_TYPE_INTERFACE_Node) { + switch (type) { + case PW_TYPE_INTERFACE_Node: if (c->registration_callback) c->registration_callback(o->node.name, 1, c->registration_arg); - } - else if (type == PW_TYPE_INTERFACE_Port) { + break; + + case PW_TYPE_INTERFACE_Port: if (c->portregistration_callback) c->portregistration_callback(o->id, 1, c->portregistration_arg); - } - else if (type == PW_TYPE_INTERFACE_Link) { + break; + + case PW_TYPE_INTERFACE_Link: if (c->connect_callback) c->connect_callback(o->port_link.src, o->port_link.dst, 1, c->connect_arg); + break; } exit: return; @@ -1763,17 +1773,19 @@ static void registry_event_global_remove(void *object, uint32_t id) if (o == NULL) return; - if (o->type == PW_TYPE_INTERFACE_Node) { + switch (o->type) { + case PW_TYPE_INTERFACE_Node: if (c->registration_callback) c->registration_callback(o->node.name, 0, c->registration_arg); - } - else if (o->type == PW_TYPE_INTERFACE_Port) { + break; + case PW_TYPE_INTERFACE_Port: if (c->portregistration_callback) c->portregistration_callback(o->id, 0, c->portregistration_arg); - } - else if (o->type == PW_TYPE_INTERFACE_Link) { + break; + case PW_TYPE_INTERFACE_Link: if (c->connect_callback) c->connect_callback(o->port_link.src, o->port_link.dst, 0, c->connect_arg); + break; } /* JACK clients expect the objects to hang around after @@ -1861,8 +1873,8 @@ jack_client_t * jack_client_open (const char *client_name, pw_thread_loop_lock(client->context.loop); client->remote = pw_remote_new(client->context.core, pw_properties_new( - "client.name", client_name, - "client.api", "jack", + PW_KEY_CLIENT_NAME, client_name, + PW_KEY_CLIENT_API, "jack", NULL), 0); @@ -1901,13 +1913,13 @@ jack_client_t * jack_client_open (const char *client_name, props = SPA_DICT_INIT(items, 0); - items[props.n_items++] = SPA_DICT_ITEM_INIT("node.name", client_name); - items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_NODE_PROP_MEDIA, "Audio"); - items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_NODE_PROP_CATEGORY, "Duplex"); - items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_NODE_PROP_ROLE, "DSP"); + items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_KEY_NODE_NAME, client_name); + items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_KEY_MEDIA_TYPE, "Audio"); + items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_KEY_MEDIA_CATEGORY, "Duplex"); + items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_KEY_MEDIA_ROLE, "DSP"); if ((str = getenv("PIPEWIRE_LATENCY")) == NULL) str = DEFAULT_LATENCY; - items[props.n_items++] = SPA_DICT_ITEM_INIT("node.latency", str); + items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_KEY_NODE_LATENCY, str); client->node_proxy = pw_core_proxy_create_object(client->core_proxy, "client-node", @@ -2382,8 +2394,8 @@ jack_port_t * jack_port_register (jack_client_t *client, SPA_PORT_FLAG_NO_REF; port_info.change_mask |= SPA_PORT_CHANGE_MASK_PROPS; dict = SPA_DICT_INIT(items, 0); - items[dict.n_items++] = SPA_DICT_ITEM_INIT("port.dsp", port_type); - items[dict.n_items++] = SPA_DICT_ITEM_INIT("port.name", port_name); + items[dict.n_items++] = SPA_DICT_ITEM_INIT(PW_KEY_FORMAT_DSP, port_type); + items[dict.n_items++] = SPA_DICT_ITEM_INIT(PW_KEY_PORT_NAME, port_name); port_info.props = &dict; port_info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; port_params[0] = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ); @@ -2842,11 +2854,11 @@ int jack_connect (jack_client_t *client, snprintf(val[3], sizeof(val[3]), "%d", dst->id); props = SPA_DICT_INIT(items, 0); - items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_LINK_OUTPUT_NODE_ID, val[0]); - items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_LINK_OUTPUT_PORT_ID, val[1]); - items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_LINK_INPUT_NODE_ID, val[2]); - items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_LINK_INPUT_PORT_ID, val[3]); - items[props.n_items++] = SPA_DICT_ITEM_INIT("object.linger", "1"); + items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_KEY_LINK_OUTPUT_NODE, val[0]); + items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_KEY_LINK_OUTPUT_PORT, val[1]); + items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_KEY_LINK_INPUT_NODE, val[2]); + items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_KEY_LINK_INPUT_PORT, val[3]); + items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_KEY_OBJECT_LINGER, "1"); pw_core_proxy_create_object(c->core_proxy, "link-factory", From 21e904d694bf203b7fc759075db94d55acb87138 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 6 Jun 2019 15:01:41 +0200 Subject: [PATCH 074/133] jack: update for API change --- src/pipewire-jack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 50646a4ff..a21f528f9 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -788,7 +788,7 @@ static void process_tee(struct client *c) } static void -on_rtsocket_condition(void *data, int fd, enum spa_io mask) +on_rtsocket_condition(void *data, int fd, uint32_t mask) { struct client *c = data; struct timespec ts; From 78109838a96ff94988df9eee1976f99ddcc938b2 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 7 Jun 2019 17:15:10 +0200 Subject: [PATCH 075/133] compiler fixes --- src/meson.build | 2 +- src/pipewire-jack.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/meson.build b/src/meson.build index 38c0a258f..f8e80625e 100644 --- a/src/meson.build +++ b/src/meson.build @@ -20,6 +20,6 @@ pipewire_jack = shared_library('jack', soversion : 0, c_args : pipewire_jack_c_args, include_directories : [configinc], - dependencies : [pipewire_dep, jack_dep], + dependencies : [pipewire_dep, jack_dep, mathlib], install : false, ) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index a21f528f9..01e795e10 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -1843,8 +1843,8 @@ jack_client_t * jack_client_open (const char *client_name, mix2 = mix2_c; cpu_iface = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_CPU); if (cpu_iface) { - uint32_t flags = spa_cpu_get_flags(cpu_iface); #if defined (__SSE__) + uint32_t flags = spa_cpu_get_flags(cpu_iface); if (flags & SPA_CPU_FLAG_SSE) mix2 = mix2_sse; #endif @@ -3159,7 +3159,7 @@ int jack_get_cycle_times(const jack_client_t *client, *current_usecs = c->jack_position.usecs; *period_usecs = c->buffer_size / (c->sample_rate * c->rate_diff); *next_usecs = c->jack_position.usecs + (*period_usecs * 1000000.0f); - pw_log_trace("client %p: %d %ld %ld %f", c, *current_frames, + pw_log_trace("client %p: %d %"PRIu64" %"PRIu64" %f", c, *current_frames, *current_usecs, *next_usecs, *period_usecs); return 0; } From 135b72ce70b5da10424f664edcb531bf530618b1 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 23 Jul 2019 17:46:06 +0200 Subject: [PATCH 076/133] jack: update for memory changes --- src/pipewire-jack.c | 413 ++++++++++++++++---------------------------- 1 file changed, 150 insertions(+), 263 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 01e795e10..9116cec4b 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -110,15 +110,6 @@ struct object { }; }; -struct mem { - uint32_t id; - int fd; - uint32_t flags; - uint32_t ref; - struct pw_map_range map; - void *ptr; -}; - struct midi_buffer { #define MIDI_BUFFER_MAGIC 0x900df00d uint32_t magic; @@ -146,24 +137,22 @@ struct buffer { #define BUFFER_FLAG_MAPPED (1<<1) uint32_t flags; uint32_t id; - void *ptr; - struct pw_map_range map; struct spa_data datas[MAX_BUFFER_DATAS]; uint32_t n_datas; - uint32_t mem[MAX_BUFFER_DATAS+1]; + struct pw_memmap *mem[MAX_BUFFER_DATAS+1]; uint32_t n_mem; }; struct io { uint32_t id; - uint32_t memid; + struct pw_memmap *mem; }; struct link { uint32_t node_id; - uint32_t memid; + struct pw_memmap *mem; struct pw_node_activation *activation; int signalfd; }; @@ -292,7 +281,7 @@ struct client { struct spa_list ports[2]; struct spa_list free_ports[2]; - struct pw_array mems; + struct io ios[MAX_IO]; struct pw_array links; struct pw_node_activation *activation; @@ -344,10 +333,52 @@ static void free_object(struct client *c, struct object *o) spa_list_append(&c->context.free_objects, &o->link); } +static void init_ios(struct io *ios) +{ + int i; + for (i = 0; i < MAX_IO; i++) + ios[i].id = SPA_ID_INVALID; +} + +static void clear_io(struct io *io) +{ + pw_log_debug("%p clear id:%u mem:%p", io, io->id, io->mem); + pw_memmap_free(io->mem); + io->mem = NULL; + io->id = SPA_ID_INVALID; +} + +static struct io *update_io(struct client *c, struct io *ios, + uint32_t id, struct pw_memmap *mem) +{ + int i; + struct io *io, *f = NULL; + + pw_log_debug("update id:%u mem:%p", id, mem); + + for (i = 0; i < MAX_IO; i++) { + io = &ios[i]; + if (io->id == SPA_ID_INVALID && f == NULL) + f = io; + else if (io->id == id) { + if (io->mem && io->mem != mem) + clear_io(io); + f = io; + break; + } + } + if (f == NULL) + return NULL; + + io = f; + io->id = id; + io->mem = mem; + return io; +} + static struct mix *ensure_mix(struct client *c, struct port *port, uint32_t mix_id) { struct mix *mix; - int i; spa_list_for_each(mix, &port->mix, port_link) { if (mix->id == mix_id) @@ -366,8 +397,7 @@ static struct mix *ensure_mix(struct client *c, struct port *port, uint32_t mix_ mix->port = port; mix->io = NULL; mix->n_buffers = 0; - for (i = 0; i < MAX_IO; i++) - mix->ios[i].id = SPA_ID_INVALID; + init_ios(mix->ios); return mix; } @@ -598,109 +628,6 @@ static struct link *find_activation(struct pw_array *links, uint32_t node_id) return NULL; } -static struct mem *find_mem(struct pw_array *mems, uint32_t id) -{ - struct mem *m; - - pw_array_for_each(m, mems) { - if (m->id == id) - return m; - } - return NULL; -} - -static struct mem *find_mem_ptr(struct pw_array *mems, void *ptr) -{ - struct mem *m; - pw_array_for_each(m, mems) { - if (m->ptr == ptr) - return m; - } - return NULL; -} - - -static void *mem_map(struct client *c, struct mem *m, uint32_t offset, uint32_t size) -{ - struct pw_map_range map; - - pw_map_range_init(&map, offset, size, c->context.core->sc_pagesize); - - if (m->ptr == NULL || m->map.offset != map.offset || m->map.size != map.size) { - m->ptr = mmap(m->ptr, map.size, PROT_READ|PROT_WRITE, - MAP_SHARED, m->fd, map.offset); - - if (m->ptr == MAP_FAILED) { - pw_log_error(NAME" %p: Failed to mmap memory %d %p: %m", c, size, m); - m->ptr = NULL; - return NULL; - } - m->map = map; - } - return SPA_MEMBER(m->ptr, map.start, void); -} - -static void mem_unmap(struct client *c, struct mem *m) -{ - pw_log_debug(NAME" %p: %d fd %d", c, m->id, m->fd); - if (m->ptr != NULL) { - if (munmap(m->ptr, m->map.size) < 0) - pw_log_warn(NAME" %p: failed to unmap: %m", c); - m->ptr = NULL; - } -} - -static void clear_mem(struct client *c, struct mem *m) -{ - pw_log_debug(NAME" %p: %d fd %d", c, m->id, m->fd); - if (m->fd != -1) { - bool has_ref = false; - struct mem *m2; - - pw_array_for_each(m2, &c->mems) { - if (m2 != m && m2->fd == m->fd) { - has_ref = true; - break; - } - } - if (!has_ref) { - mem_unmap(c, m); - pw_log_debug(NAME" %p: close %d fd %d", c, m->id, m->fd); - close(m->fd); - m->id = -1; - m->fd = -1; - } - } -} - -static int client_node_add_mem(void *object, - uint32_t mem_id, - uint32_t type, - int memfd, - uint32_t flags) -{ - struct client *c = object; - struct mem *m; - - m = find_mem(&c->mems, mem_id); - if (m) { - pw_log_warn(NAME" %p: duplicate mem %u, fd %d, flags %d", c, - mem_id, memfd, flags); - return -EINVAL; - } - - m = pw_array_add(&c->mems, sizeof(struct mem)); - pw_log_debug(NAME" %p: add mem %u, fd %d, flags %d", c, mem_id, memfd, flags); - - m->id = mem_id; - m->fd = memfd; - m->flags = flags; - m->ref = 0; - m->map = PW_MAP_RANGE_INIT; - m->ptr = NULL; - return 0; -} - static int do_remove_sources(struct spa_loop *loop, bool async, uint32_t seq, const void *data, size_t size, void *user_data) @@ -811,15 +738,17 @@ on_rtsocket_condition(void *data, int fd, uint32_t mask) pw_log_warn("jack %p: missed %"PRIu64" wakeups", c, cmd - 1); if (c->position) { - buffer_size = c->position->size; - if (c->position->clock.rate.num != 0 && c->position->clock.rate.denom != 0) - sample_rate = c->position->clock.rate.denom / c->position->clock.rate.num; + struct spa_io_position *pos = c->position; + + buffer_size = pos->size; + if (pos->clock.rate.num != 0 && pos->clock.rate.denom != 0) + sample_rate = pos->clock.rate.denom / pos->clock.rate.num; else sample_rate = c->sample_rate; - c->rate_diff = c->position->clock.rate_diff; - frame = c->position->clock.position; - delay = c->position->clock.delay; - nsec = c->position->clock.nsec; + c->rate_diff = pos->clock.rate_diff; + frame = pos->clock.position; + delay = pos->clock.delay; + nsec = pos->clock.nsec; } else { buffer_size = DEFAULT_BUFFER_SIZE; @@ -903,9 +832,16 @@ on_rtsocket_condition(void *data, int fd, uint32_t mask) } } +static void clear_link(struct client *c, struct link *link) +{ + link->node_id = SPA_ID_INVALID; + link->activation = NULL; + pw_memmap_free(link->mem); + close(link->signalfd); +} + static void clean_transport(struct client *c) { - struct mem *m; struct link *l; if (c->node_id == SPA_ID_INVALID) @@ -913,11 +849,9 @@ static void clean_transport(struct client *c) unhandle_socket(c); - pw_array_for_each(m, &c->mems) - clear_mem(c, m); - pw_array_clear(&c->mems); pw_array_for_each(l, &c->links) - close(l->signalfd); + if (l->node_id != SPA_ID_INVALID) + clear_link(c, l); pw_array_clear(&c->links); c->node_id = SPA_ID_INVALID; @@ -962,35 +896,35 @@ static int client_node_set_io(void *object, uint32_t size) { struct client *c = (struct client *) object; - struct mem *m; + struct pw_memmap *mm; void *ptr; if (mem_id == SPA_ID_INVALID) { - ptr = NULL; - size = 0; + size = 0; + mm = ptr = NULL; } else { - m = find_mem(&c->mems, mem_id); - if (m == NULL) { - pw_log_warn("unknown memory id %u", mem_id); - return -EINVAL; - } - if ((ptr = mem_map(c, m, offset, size)) == NULL) { + mm = pw_mempool_map_id(c->remote->pool, mem_id, + PROT_READ|PROT_WRITE, offset, size); + if (mm == NULL) { + pw_log_warn("can't map memory id %u", mem_id); return -errno; - } - m->ref++; + } + ptr = mm->ptr; } pw_log_debug("client %p: set io %s %p", c, spa_debug_type_find_name(spa_type_io, id), ptr); - if (id == SPA_IO_Position) { - if (ptr == NULL && c->position) { - m = find_mem_ptr(&c->mems, c->position); - if (m && --m->ref == 0) - clear_mem(c, m); - } + update_io(c, c->ios, id, mm); + + switch (id) { + case SPA_IO_Position: c->position = ptr; + break; + default: + break; } + return 0; } @@ -1059,10 +993,6 @@ static int clear_buffers(struct client *c, struct mix *mix) for (i = 0; i < mix->n_buffers; i++) { b = &mix->buffers[i]; - if (b->ptr != NULL) { - if (munmap(b->ptr, b->map.size) < 0) - pw_log_warn("failed to unmap: %m"); - } for (j = 0; j < b->n_datas; j++) { struct spa_data *d = &b->datas[j]; if (d->fd != -1 && d->data) { @@ -1072,17 +1002,10 @@ static int clear_buffers(struct client *c, struct mix *mix) } d->fd = -1; } - for (j = 0; j < b->n_mem; j++) { - struct mem *m; + for (j = 0; j < b->n_mem; j++) + pw_memmap_free(b->mem[j]); - if ((m = find_mem(&c->mems, b->mem[i])) == NULL) - continue; - - if (--m->ref == 0) - clear_mem(c, m); - } b->n_mem = 0; - b->ptr = NULL; } mix->n_buffers = 0; spa_list_init(&mix->queue); @@ -1284,8 +1207,7 @@ static int client_node_port_use_buffers(void *object, struct client *c = (struct client *) object; struct port *p = GET_PORT(c, direction, port_id); struct buffer *b; - uint32_t i, j, prot, res; - struct pw_core *core = c->context.core; + uint32_t i, j, flags, res; struct mix *mix; if (!p->valid) { @@ -1301,7 +1223,7 @@ static int client_node_port_use_buffers(void *object, pw_log_debug(NAME" %p: port %p %d %d.%d use_buffers %d", c, p, direction, port_id, mix_id, n_buffers); - prot = PROT_READ | (direction == SPA_DIRECTION_OUTPUT ? PROT_WRITE : 0); + flags = PW_MEMMAP_FLAG_READ | (direction == SPA_DIRECTION_OUTPUT ? PW_MEMMAP_FLAG_WRITE : 0); /* clear previous buffers */ clear_buffers(c, mix); @@ -1309,10 +1231,12 @@ static int client_node_port_use_buffers(void *object, for (i = 0; i < n_buffers; i++) { off_t offset; struct spa_buffer *buf; - struct mem *m; + struct pw_memmap *mm; - if ((m = find_mem(&c->mems, buffers[i].mem_id)) == NULL) { - pw_log_warn(NAME" %p: unknown memory id %u", c, buffers[i].mem_id); + mm = pw_mempool_map_id(c->remote->pool, buffers[i].mem_id, + flags, buffers[i].offset, buffers[i].size); + if (mm == NULL) { + pw_log_warn(NAME" %p: can't map memory id %u: %m", c, buffers[i].mem_id); continue; } @@ -1322,24 +1246,12 @@ static int client_node_port_use_buffers(void *object, b->id = i; b->flags = 0; b->n_mem = 0; + b->mem[b->n_mem++] = mm; - pw_map_range_init(&b->map, buffers[i].offset, buffers[i].size, core->sc_pagesize); + pw_log_debug("add buffer id:%u offset:%u size:%u map:%p ptr:%p", buffers[i].mem_id, + buffers[i].offset, buffers[i].size, mm, mm->ptr); - b->ptr = mmap(NULL, b->map.size, prot, MAP_SHARED, m->fd, b->map.offset); - if (b->ptr == MAP_FAILED) { - b->ptr = NULL; - pw_log_warn(NAME" %p: Failed to mmap memory %u %u: %m", c, - b->map.offset, b->map.size); - continue; - } - - - m->ref++; - b->mem[b->n_mem++] = m->id; - - pw_log_debug("add buffer %d %d %u %u", m->id, b->id, b->map.offset, b->map.size); - - offset = b->map.start; + offset = 0; for (j = 0; j < buf->n_metas; j++) { struct spa_meta *m = &buf->metas[j]; offset += SPA_ROUND_UP_N(m->size, 8); @@ -1352,29 +1264,41 @@ static int client_node_port_use_buffers(void *object, memcpy(d, &buf->datas[j], sizeof(struct spa_data)); d->chunk = - SPA_MEMBER(b->ptr, offset + sizeof(struct spa_chunk) * j, + SPA_MEMBER(mm->ptr, offset + sizeof(struct spa_chunk) * j, struct spa_chunk); - if (d->type == SPA_DATA_MemFd || d->type == SPA_DATA_DmaBuf) { - struct mem *bm = find_mem(&c->mems, SPA_PTR_TO_UINT32(d->data)); + if (d->type == SPA_DATA_MemId) { + uint32_t mem_id = SPA_PTR_TO_UINT32(d->data); + struct pw_memblock *bm; + struct pw_memmap *bmm; - d->data = mmap(NULL, d->maxsize + d->mapoffset, prot, - MAP_SHARED, bm->fd, 0); - if (d->data == MAP_FAILED) { + bm = pw_mempool_find_id(c->remote->pool, mem_id); + if (bm == NULL) { + pw_log_error("unknown buffer mem %u", mem_id); + res = -ENODEV; + goto done; + + } + + d->fd = bm->fd; + d->type = bm->type; + d->data = NULL; + + bmm = pw_memblock_map(bm, flags, d->mapoffset, d->maxsize); + if (bmm == NULL) { + res = -errno; pw_log_error(NAME" %p: failed to map buffer mem %m", c); d->data = NULL; - res = -errno; goto done; } - d->data = SPA_MEMBER(d->data, d->mapoffset, void); - d->fd = bm->fd; - bm->ref++; - b->mem[b->n_mem++] = bm->id; + b->mem[b->n_mem++] = bmm; + d->data = bmm->ptr; + pw_log_debug(NAME" %p: data %d %u -> fd %d %d", c, j, bm->id, bm->fd, d->maxsize); } else if (d->type == SPA_DATA_MemPtr) { - d->data = SPA_MEMBER(b->ptr, - b->map.start + SPA_PTR_TO_INT(d->data), void); + int offs = SPA_PTR_TO_INT(d->data); + d->data = SPA_MEMBER(mm->ptr, offs, void); d->fd = -1; pw_log_debug(NAME" %p: data %d %u -> mem %p %d", c, j, b->id, d->data, d->maxsize); @@ -1403,41 +1327,6 @@ static int client_node_port_use_buffers(void *object, return res; } -static void clear_io(struct client *c, struct io *io) -{ - struct mem *m; - m = find_mem(&c->mems, io->memid); - if (m && --m->ref == 0) - clear_mem(c, m); - io->id = SPA_ID_INVALID; -} - -static struct io *update_io(struct client *c, struct mix *mix, - uint32_t id, uint32_t memid) -{ - int i; - struct io *io, *f = NULL; - - for (i = 0; i < MAX_IO; i++) { - io = &mix->ios[i]; - if (io->id == SPA_ID_INVALID) - f = io; - else if (io->id == id) { - if (io->memid != memid) - clear_io(c, io); - f = io; - break; - } - } - if (f == NULL) - return NULL; - - io = f; - io->id = id; - io->memid = memid; - return io; -} - static int client_node_port_set_io(void *object, enum spa_direction direction, uint32_t port_id, @@ -1449,7 +1338,7 @@ static int client_node_port_set_io(void *object, { struct client *c = (struct client *) object; struct port *p = GET_PORT(c, direction, port_id); - struct mem *m; + struct pw_memmap *mm; struct mix *mix; void *ptr; int res = 0; @@ -1460,24 +1349,24 @@ static int client_node_port_set_io(void *object, } if (mem_id == SPA_ID_INVALID) { - ptr = NULL; + mm = ptr = NULL; size = 0; } else { - m = find_mem(&c->mems, mem_id); - if (m == NULL) { - pw_log_warn("unknown memory id %u", mem_id); + mm = pw_mempool_map_id(c->remote->pool, mem_id, + PROT_READ|PROT_WRITE, offset, size); + if (mm == NULL) { + pw_log_warn("can't map memory id %u", mem_id); res = -EINVAL; goto exit; } - if ((ptr = mem_map(c, m, offset, size)) == NULL) { - res = -errno; - goto exit; - } - m->ref++; + ptr = mm->ptr; } - update_io(c, mix, id, mem_id); + pw_log_debug("port %p: set io:%s id:%u ptr:%p", p, + spa_debug_type_find_name(spa_type_io, id), id, ptr); + + update_io(c, mix->ios, id, mm); switch (id) { case SPA_IO_Buffers: @@ -1495,9 +1384,6 @@ static int client_node_port_set_io(void *object, break; } - pw_log_debug("port %p: set io %s %u %u %u %p", p, - spa_debug_type_find_name(spa_type_io, id), mem_id, offset, size, ptr); - exit: if (res < 0) pw_proxy_error((struct pw_proxy*)c->node_proxy, res, spa_strerror(res)); @@ -1512,7 +1398,7 @@ static int client_node_set_activation(void *object, uint32_t size) { struct client *c = (struct client *) object; - struct mem *m; + struct pw_memmap *mm; struct link *link; void *ptr; int res = 0; @@ -1522,17 +1408,14 @@ static int client_node_set_activation(void *object, size = 0; } else { - m = find_mem(&c->mems, mem_id); - if (m == NULL) { - pw_log_warn("unknown memory id %u", mem_id); + mm = pw_mempool_map_id(c->remote->pool, mem_id, + PROT_READ|PROT_WRITE, offset, size); + if (mm == NULL) { + pw_log_warn("can't map memory id %u", mem_id); res = -EINVAL; goto exit; } - if ((ptr = mem_map(c, m, offset, size)) == NULL) { - res = -errno; - goto exit; - } - m->ref++; + ptr = mm->ptr; } pw_log_debug("node %p: set activation %u: %u %u %u %p", c, node_id, @@ -1543,13 +1426,19 @@ static int client_node_set_activation(void *object, mem_id, offset, size, ptr); if (ptr) c->activation = ptr; + close(signalfd); return 0; } if (ptr) { link = pw_array_add(&c->links, sizeof(struct link)); + if (link == NULL) { + res = -errno; + goto exit; + } + link->node_id = SPA_ID_INVALID; link->node_id = node_id; - link->memid = mem_id; + link->mem = mm; link->activation = ptr; link->signalfd = signalfd; } @@ -1559,9 +1448,7 @@ static int client_node_set_activation(void *object, res = -EINVAL; goto exit; } - link->node_id = SPA_ID_INVALID; - link->activation = NULL; - close(link->signalfd); + clear_link(c, link); } exit: @@ -1572,7 +1459,6 @@ static int client_node_set_activation(void *object, static const struct pw_client_node_proxy_events client_node_events = { PW_VERSION_CLIENT_NODE_PROXY_EVENTS, - .add_mem = client_node_add_mem, .transport = client_node_transport, .set_param = client_node_set_param, .set_io = client_node_set_io, @@ -1837,6 +1723,7 @@ jack_client_t * jack_client_open (const char *client_name, spa_list_init(&client->context.nodes); spa_list_init(&client->context.ports); spa_list_init(&client->context.links); + init_ios(client->ios); support = pw_core_get_support(client->context.core, &n_support); @@ -1850,8 +1737,6 @@ jack_client_t * jack_client_open (const char *client_name, #endif } - pw_array_init(&client->mems, 64); - pw_array_ensure_size(&client->mems, sizeof(struct mem) * 64); pw_array_init(&client->links, 64); client->buffer_size = (uint32_t)-1; @@ -1996,6 +1881,8 @@ int jack_client_close (jack_client_t *client) pw_core_destroy(c->context.core); pw_thread_loop_destroy(c->context.loop); pw_main_loop_destroy(c->context.main); + + pw_log_debug("client %p: free", client); free(c); return 0; From 3a9035a44cb1927ac1b20937d3d1e9c6e32ffe5c Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 25 Jul 2019 12:05:54 +0200 Subject: [PATCH 077/133] jack_update for memory tag --- src/pipewire-jack.c | 104 ++++++++++++-------------------------------- 1 file changed, 27 insertions(+), 77 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 9116cec4b..82dfee8e6 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -145,11 +145,6 @@ struct buffer { uint32_t n_mem; }; -struct io { - uint32_t id; - struct pw_memmap *mem; -}; - struct link { uint32_t node_id; struct pw_memmap *mem; @@ -163,8 +158,6 @@ struct mix { uint32_t id; struct port *port; - struct io ios[MAX_IO]; - struct spa_io_buffers *io; struct spa_io_sequence *notify; size_t notify_size; @@ -281,9 +274,9 @@ struct client { struct spa_list ports[2]; struct spa_list free_ports[2]; - struct io ios[MAX_IO]; struct pw_array links; + struct pw_memmap *mem; struct pw_node_activation *activation; bool started; @@ -333,49 +326,6 @@ static void free_object(struct client *c, struct object *o) spa_list_append(&c->context.free_objects, &o->link); } -static void init_ios(struct io *ios) -{ - int i; - for (i = 0; i < MAX_IO; i++) - ios[i].id = SPA_ID_INVALID; -} - -static void clear_io(struct io *io) -{ - pw_log_debug("%p clear id:%u mem:%p", io, io->id, io->mem); - pw_memmap_free(io->mem); - io->mem = NULL; - io->id = SPA_ID_INVALID; -} - -static struct io *update_io(struct client *c, struct io *ios, - uint32_t id, struct pw_memmap *mem) -{ - int i; - struct io *io, *f = NULL; - - pw_log_debug("update id:%u mem:%p", id, mem); - - for (i = 0; i < MAX_IO; i++) { - io = &ios[i]; - if (io->id == SPA_ID_INVALID && f == NULL) - f = io; - else if (io->id == id) { - if (io->mem && io->mem != mem) - clear_io(io); - f = io; - break; - } - } - if (f == NULL) - return NULL; - - io = f; - io->id = id; - io->mem = mem; - return io; -} - static struct mix *ensure_mix(struct client *c, struct port *port, uint32_t mix_id) { struct mix *mix; @@ -397,7 +347,6 @@ static struct mix *ensure_mix(struct client *c, struct port *port, uint32_t mix_ mix->port = port; mix->io = NULL; mix->n_buffers = 0; - init_ios(mix->ios); return mix; } @@ -859,8 +808,8 @@ static void clean_transport(struct client *c) static int client_node_transport(void *object, uint32_t node_id, - int readfd, - int writefd) + int readfd, int writefd, + uint32_t mem_id, uint32_t offset, uint32_t size) { struct client *c = (struct client *) object; struct pw_core *core = c->context.core; @@ -869,6 +818,14 @@ static int client_node_transport(void *object, c->node_id = node_id; + c->mem = pw_mempool_map_id(c->remote->pool, mem_id, + PW_MEMMAP_FLAG_READWRITE, offset, size, NULL); + if (c->mem == NULL) { + pw_log_debug("client %p: can't map activation: %m", c); + return -errno; + } + c->activation = c->mem->ptr; + pw_log_debug("client %p: create client transport with fds %d %d for node %u", c, readfd, writefd, node_id); @@ -898,14 +855,17 @@ static int client_node_set_io(void *object, struct client *c = (struct client *) object; struct pw_memmap *mm; void *ptr; + uint32_t tag[5] = { c->node_id, id, }; if (mem_id == SPA_ID_INVALID) { - size = 0; + if ((mm = pw_mempool_find_tag(c->remote->pool, tag)) != NULL) + pw_memmap_free(mm); mm = ptr = NULL; + size = 0; } else { mm = pw_mempool_map_id(c->remote->pool, mem_id, - PROT_READ|PROT_WRITE, offset, size); + PW_MEMMAP_FLAG_READWRITE, offset, size, tag); if (mm == NULL) { pw_log_warn("can't map memory id %u", mem_id); return -errno; @@ -915,8 +875,6 @@ static int client_node_set_io(void *object, pw_log_debug("client %p: set io %s %p", c, spa_debug_type_find_name(spa_type_io, id), ptr); - update_io(c, c->ios, id, mm); - switch (id) { case SPA_IO_Position: c->position = ptr; @@ -993,15 +951,6 @@ static int clear_buffers(struct client *c, struct mix *mix) for (i = 0; i < mix->n_buffers; i++) { b = &mix->buffers[i]; - for (j = 0; j < b->n_datas; j++) { - struct spa_data *d = &b->datas[j]; - if (d->fd != -1 && d->data) { - if (munmap(SPA_MEMBER(d->data, -d->mapoffset, void), - d->maxsize + d->mapoffset) < 0) - pw_log_warn("failed to unmap: %m"); - } - d->fd = -1; - } for (j = 0; j < b->n_mem; j++) pw_memmap_free(b->mem[j]); @@ -1234,7 +1183,7 @@ static int client_node_port_use_buffers(void *object, struct pw_memmap *mm; mm = pw_mempool_map_id(c->remote->pool, buffers[i].mem_id, - flags, buffers[i].offset, buffers[i].size); + flags, buffers[i].offset, buffers[i].size, NULL); if (mm == NULL) { pw_log_warn(NAME" %p: can't map memory id %u: %m", c, buffers[i].mem_id); continue; @@ -1284,7 +1233,7 @@ static int client_node_port_use_buffers(void *object, d->type = bm->type; d->data = NULL; - bmm = pw_memblock_map(bm, flags, d->mapoffset, d->maxsize); + bmm = pw_memblock_map(bm, flags, d->mapoffset, d->maxsize, NULL); if (bmm == NULL) { res = -errno; pw_log_error(NAME" %p: failed to map buffer mem %m", c); @@ -1340,6 +1289,7 @@ static int client_node_port_set_io(void *object, struct port *p = GET_PORT(c, direction, port_id); struct pw_memmap *mm; struct mix *mix; + uint32_t tag[5] = { c->node_id, direction, port_id, mix_id, id }; void *ptr; int res = 0; @@ -1349,12 +1299,15 @@ static int client_node_port_set_io(void *object, } if (mem_id == SPA_ID_INVALID) { + if ((mm = pw_mempool_find_tag(c->remote->pool, tag)) != NULL) + pw_memmap_free(mm); + mm = ptr = NULL; size = 0; } else { mm = pw_mempool_map_id(c->remote->pool, mem_id, - PROT_READ|PROT_WRITE, offset, size); + PW_MEMMAP_FLAG_READWRITE, offset, size, tag); if (mm == NULL) { pw_log_warn("can't map memory id %u", mem_id); res = -EINVAL; @@ -1366,8 +1319,6 @@ static int client_node_port_set_io(void *object, pw_log_debug("port %p: set io:%s id:%u ptr:%p", p, spa_debug_type_find_name(spa_type_io, id), id, ptr); - update_io(c, mix->ios, id, mm); - switch (id) { case SPA_IO_Buffers: mix->io = ptr; @@ -1404,12 +1355,12 @@ static int client_node_set_activation(void *object, int res = 0; if (mem_id == SPA_ID_INVALID) { - ptr = NULL; + mm = ptr = NULL; size = 0; } else { mm = pw_mempool_map_id(c->remote->pool, mem_id, - PROT_READ|PROT_WRITE, offset, size); + PW_MEMMAP_FLAG_READWRITE, offset, size, NULL); if (mm == NULL) { pw_log_warn("can't map memory id %u", mem_id); res = -EINVAL; @@ -1424,8 +1375,8 @@ static int client_node_set_activation(void *object, if (c->node_id == node_id) { pw_log_debug("node %p: our activation %u: %u %u %u %p", c, node_id, mem_id, offset, size, ptr); - if (ptr) - c->activation = ptr; + if (mm) + pw_memmap_free(mm); close(signalfd); return 0; } @@ -1723,7 +1674,6 @@ jack_client_t * jack_client_open (const char *client_name, spa_list_init(&client->context.nodes); spa_list_init(&client->context.ports); spa_list_init(&client->context.links); - init_ios(client->ios); support = pw_core_get_support(client->context.core, &n_support); From 8428103e925966c2d3c1c623f38452a8f6fdfeac Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 25 Jul 2019 13:16:52 +0200 Subject: [PATCH 078/133] jack: update for use_buffer flags --- src/pipewire-jack.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 82dfee8e6..1b6cfda79 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -1150,13 +1150,14 @@ static int client_node_port_use_buffers(void *object, enum spa_direction direction, uint32_t port_id, uint32_t mix_id, + uint32_t flags, uint32_t n_buffers, struct pw_client_node_buffer *buffers) { struct client *c = (struct client *) object; struct port *p = GET_PORT(c, direction, port_id); struct buffer *b; - uint32_t i, j, flags, res; + uint32_t i, j, fl, res; struct mix *mix; if (!p->valid) { @@ -1172,7 +1173,7 @@ static int client_node_port_use_buffers(void *object, pw_log_debug(NAME" %p: port %p %d %d.%d use_buffers %d", c, p, direction, port_id, mix_id, n_buffers); - flags = PW_MEMMAP_FLAG_READ | (direction == SPA_DIRECTION_OUTPUT ? PW_MEMMAP_FLAG_WRITE : 0); + fl = PW_MEMMAP_FLAG_READ | (direction == SPA_DIRECTION_OUTPUT ? PW_MEMMAP_FLAG_WRITE : 0); /* clear previous buffers */ clear_buffers(c, mix); @@ -1183,7 +1184,7 @@ static int client_node_port_use_buffers(void *object, struct pw_memmap *mm; mm = pw_mempool_map_id(c->remote->pool, buffers[i].mem_id, - flags, buffers[i].offset, buffers[i].size, NULL); + fl, buffers[i].offset, buffers[i].size, NULL); if (mm == NULL) { pw_log_warn(NAME" %p: can't map memory id %u: %m", c, buffers[i].mem_id); continue; @@ -1233,7 +1234,7 @@ static int client_node_port_use_buffers(void *object, d->type = bm->type; d->data = NULL; - bmm = pw_memblock_map(bm, flags, d->mapoffset, d->maxsize, NULL); + bmm = pw_memblock_map(bm, fl, d->mapoffset, d->maxsize, NULL); if (bmm == NULL) { res = -errno; pw_log_error(NAME" %p: failed to map buffer mem %m", c); @@ -2224,11 +2225,7 @@ jack_port_t * jack_port_register (jack_client_t *client, port_info = SPA_PORT_INFO_INIT(); port_info.change_mask |= SPA_PORT_CHANGE_MASK_FLAGS; - if (type_id == 1) - port_info.flags = SPA_PORT_FLAG_NO_REF; - else - port_info.flags = SPA_PORT_FLAG_CAN_USE_BUFFERS | - SPA_PORT_FLAG_NO_REF; + port_info.flags = SPA_PORT_FLAG_NO_REF; port_info.change_mask |= SPA_PORT_CHANGE_MASK_PROPS; dict = SPA_DICT_INIT(items, 0); items[dict.n_items++] = SPA_DICT_ITEM_INIT(PW_KEY_FORMAT_DSP, port_type); From d405ca97ece8efabac83ecb5294175dfd453d27c Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 1 Aug 2019 13:54:30 +0200 Subject: [PATCH 079/133] jack: fix for api change --- src/pipewire-jack.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 1b6cfda79..27fcf9cfc 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -858,7 +858,7 @@ static int client_node_set_io(void *object, uint32_t tag[5] = { c->node_id, id, }; if (mem_id == SPA_ID_INVALID) { - if ((mm = pw_mempool_find_tag(c->remote->pool, tag)) != NULL) + if ((mm = pw_mempool_find_tag(c->remote->pool, tag, sizeof(tag))) != NULL) pw_memmap_free(mm); mm = ptr = NULL; size = 0; @@ -1300,7 +1300,7 @@ static int client_node_port_set_io(void *object, } if (mem_id == SPA_ID_INVALID) { - if ((mm = pw_mempool_find_tag(c->remote->pool, tag)) != NULL) + if ((mm = pw_mempool_find_tag(c->remote->pool, tag, sizeof(tag))) != NULL) pw_memmap_free(mm); mm = ptr = NULL; From f84821f82145c78bfa0700feffefffca58565fb0 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 7 Aug 2019 12:58:09 +0200 Subject: [PATCH 080/133] jack: improve buffer usage Buffer on the output port are set on the mix once and used for all output mix ios. Find the mix with the buffers and use that on all output mix. --- src/pipewire-jack.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 27fcf9cfc..fb02c80df 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -1317,7 +1317,7 @@ static int client_node_port_set_io(void *object, ptr = mm->ptr; } - pw_log_debug("port %p: set io:%s id:%u ptr:%p", p, + pw_log_debug("port %p: mix:%d set io:%s id:%u ptr:%p", p, mix_id, spa_debug_type_find_name(spa_type_io, id), id, ptr); switch (id) { @@ -2350,8 +2350,6 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) struct object *o = (struct object *) port; struct client *c = o->client; struct port *p; - struct buffer *b; - struct spa_io_buffers *io; struct mix *mix; void *ptr = NULL; @@ -2371,18 +2369,22 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) break; } } else { - b = NULL; - spa_list_for_each(mix, &p->mix, port_link) { - pw_log_trace("port %p: mix %d.%d get buffer %d", - p, p->id, mix->id, frames); - io = mix->io; + struct spa_io_buffers io; - if (mix->n_buffers == 0 || io == NULL) + io.status = -EPIPE; + io.buffer_id = SPA_ID_INVALID; + + spa_list_for_each(mix, &p->mix, port_link) { + struct buffer *b; + + pw_log_trace("port %p: mix %d.%d get buffer %d io:%p n_buffers:%d", + p, p->id, mix->id, frames, mix->io, mix->n_buffers); + + if (mix->n_buffers == 0) continue; if ((b = dequeue_buffer(mix)) == NULL) { pw_log_warn("port %p: out of buffers", p); - io->buffer_id = SPA_ID_INVALID; goto done; } reuse_buffer(c, mix, b->id); @@ -2392,15 +2394,15 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) b->datas[0].chunk->size = frames * sizeof(float); b->datas[0].chunk->stride = sizeof(float); - io->status = SPA_STATUS_HAVE_BUFFER; - io->buffer_id = b->id; + io.status = SPA_STATUS_HAVE_BUFFER; + io.buffer_id = b->id; break; } spa_list_for_each(mix, &p->mix, port_link) { struct spa_io_buffers *mio = mix->io; if (mio == NULL) continue; - *mio = *io; + *mio = io; } } From 48f28f7b63658832f348b65511906eceb329b1ba Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 8 Aug 2019 11:02:40 +0200 Subject: [PATCH 081/133] jack: improve debug --- src/pipewire-jack.c | 237 +++++++++++++++++++++++--------------------- 1 file changed, 122 insertions(+), 115 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index fb02c80df..0fa37b631 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -670,7 +670,7 @@ on_rtsocket_condition(void *data, int fd, uint32_t mask) struct timespec ts; if (mask & (SPA_IO_ERR | SPA_IO_HUP)) { - pw_log_warn("got error"); + pw_log_warn(NAME" %p: got error", c); unhandle_socket(c); return; } @@ -682,9 +682,9 @@ on_rtsocket_condition(void *data, int fd, uint32_t mask) struct link *l; if (read(fd, &cmd, sizeof(cmd)) != sizeof(cmd)) - pw_log_warn("jack %p: read failed %m", c); + pw_log_warn(NAME" %p: read failed %m", c); if (cmd > 1) - pw_log_warn("jack %p: missed %"PRIu64" wakeups", c, cmd - 1); + pw_log_warn(NAME" %p: missed %"PRIu64" wakeups", c, cmd - 1); if (c->position) { struct spa_io_position *pos = c->position; @@ -713,14 +713,14 @@ on_rtsocket_condition(void *data, int fd, uint32_t mask) c->activation->awake_time = nsec; if (buffer_size != c->buffer_size) { - pw_log_info("jack %p: buffersize %d", c, buffer_size); + pw_log_info(NAME" %p: buffersize %d", c, buffer_size); c->buffer_size = buffer_size; if (c->bufsize_callback) c->bufsize_callback(c->buffer_size, c->bufsize_arg); } if (sample_rate != c->sample_rate) { - pw_log_info("jack %p: sample_rate %d", c, sample_rate); + pw_log_info(NAME" %p: sample_rate %d", c, sample_rate); c->sample_rate = sample_rate; if (c->srate_callback) c->srate_callback(c->sample_rate, c->srate_arg); @@ -738,8 +738,8 @@ on_rtsocket_condition(void *data, int fd, uint32_t mask) &c->jack_position, c->sync_arg); } - pw_log_trace("do process %"PRIu64" %d %d %d %"PRIi64" %f", nsec, - c->buffer_size, c->sample_rate, + pw_log_trace(NAME" %p: do process %"PRIu64" %d %d %d %"PRIi64" %f", c, + nsec, c->buffer_size, c->sample_rate, c->jack_position.frame, delay, c->rate_diff); if (c->process_callback) @@ -768,14 +768,17 @@ on_rtsocket_condition(void *data, int fd, uint32_t mask) state = &l->activation->state[0]; - pw_log_trace("link %p %p %d/%d", l, state, state->pending, state->required); + pw_log_trace(NAME" %p: link %p %p %d/%d", c, l, state, + state->pending, state->required); + if (pw_node_activation_state_dec(state, 1)) { l->activation->status = TRIGGERED; l->activation->signal_time = nsec; - pw_log_trace("signal %p %p", l, state); + pw_log_trace(NAME" %p: signal %p %p", c, l, state); + if (write(l->signalfd, &cmd, sizeof(cmd)) != sizeof(cmd)) - pw_log_warn("jack %p: write failed %m", c); + pw_log_warn(NAME" %p: write failed %m", c); } } } @@ -821,12 +824,12 @@ static int client_node_transport(void *object, c->mem = pw_mempool_map_id(c->remote->pool, mem_id, PW_MEMMAP_FLAG_READWRITE, offset, size, NULL); if (c->mem == NULL) { - pw_log_debug("client %p: can't map activation: %m", c); + pw_log_debug(NAME" %p: can't map activation: %m", c); return -errno; } c->activation = c->mem->ptr; - pw_log_debug("client %p: create client transport with fds %d %d for node %u", + pw_log_debug(NAME" %p: create client transport with fds %d %d for node %u", c, readfd, writefd, node_id); close(writefd); @@ -867,12 +870,12 @@ static int client_node_set_io(void *object, mm = pw_mempool_map_id(c->remote->pool, mem_id, PW_MEMMAP_FLAG_READWRITE, offset, size, tag); if (mm == NULL) { - pw_log_warn("can't map memory id %u", mem_id); + pw_log_warn(NAME" %p: can't map memory id %u", c, mem_id); return -errno; } ptr = mm->ptr; } - pw_log_debug("client %p: set io %s %p", c, + pw_log_debug(NAME" %p: set io %s %p", c, spa_debug_type_find_name(spa_type_io, id), ptr); switch (id) { @@ -895,7 +898,8 @@ static int client_node_command(void *object, const struct spa_command *command) { struct client *c = (struct client *) object; - pw_log_debug("got command %d", SPA_COMMAND_TYPE(command)); + pw_log_debug(NAME" %p: got command %d", c, SPA_COMMAND_TYPE(command)); + switch (SPA_NODE_COMMAND_ID(command)) { case SPA_NODE_COMMAND_Pause: if (c->started) { @@ -915,7 +919,7 @@ static int client_node_command(void *object, const struct spa_command *command) } break; default: - pw_log_warn("unhandled node command %d", SPA_COMMAND_TYPE(command)); + pw_log_warn(NAME" %p: unhandled node command %d", c, SPA_COMMAND_TYPE(command)); pw_proxy_error((struct pw_proxy*)c->node_proxy, -ENOTSUP, "unhandled command %d", SPA_COMMAND_TYPE(command)); } @@ -1198,8 +1202,9 @@ static int client_node_port_use_buffers(void *object, b->n_mem = 0; b->mem[b->n_mem++] = mm; - pw_log_debug("add buffer id:%u offset:%u size:%u map:%p ptr:%p", buffers[i].mem_id, - buffers[i].offset, buffers[i].size, mm, mm->ptr); + pw_log_debug(NAME" %p: add buffer id:%u offset:%u size:%u map:%p ptr:%p", + c, buffers[i].mem_id, buffers[i].offset, + buffers[i].size, mm, mm->ptr); offset = 0; for (j = 0; j < buf->n_metas; j++) { @@ -1224,7 +1229,7 @@ static int client_node_port_use_buffers(void *object, bm = pw_mempool_find_id(c->remote->pool, mem_id); if (bm == NULL) { - pw_log_error("unknown buffer mem %u", mem_id); + pw_log_error(NAME" %p: unknown buffer mem %u", c, mem_id); res = -ENODEV; goto done; @@ -1267,7 +1272,7 @@ static int client_node_port_use_buffers(void *object, } } - pw_log_debug("have %d buffers", n_buffers); + pw_log_debug(NAME" %p: have %d buffers", c, n_buffers); mix->n_buffers = n_buffers; res = 0; @@ -1310,14 +1315,14 @@ static int client_node_port_set_io(void *object, mm = pw_mempool_map_id(c->remote->pool, mem_id, PW_MEMMAP_FLAG_READWRITE, offset, size, tag); if (mm == NULL) { - pw_log_warn("can't map memory id %u", mem_id); + pw_log_warn(NAME" %p: can't map memory id %u", c, mem_id); res = -EINVAL; goto exit; } ptr = mm->ptr; } - pw_log_debug("port %p: mix:%d set io:%s id:%u ptr:%p", p, mix_id, + pw_log_debug(NAME" %p: port %p mix:%d set io:%s id:%u ptr:%p", c, p, mix_id, spa_debug_type_find_name(spa_type_io, id), id, ptr); switch (id) { @@ -1363,18 +1368,18 @@ static int client_node_set_activation(void *object, mm = pw_mempool_map_id(c->remote->pool, mem_id, PW_MEMMAP_FLAG_READWRITE, offset, size, NULL); if (mm == NULL) { - pw_log_warn("can't map memory id %u", mem_id); + pw_log_warn(NAME" %p: can't map memory id %u", c, mem_id); res = -EINVAL; goto exit; } ptr = mm->ptr; } - pw_log_debug("node %p: set activation %u: %u %u %u %p", c, node_id, + pw_log_debug(NAME" %p: set activation %u: %u %u %u %p", c, node_id, mem_id, offset, size, ptr); if (c->node_id == node_id) { - pw_log_debug("node %p: our activation %u: %u %u %u %p", c, node_id, + pw_log_debug(NAME" %p: our activation %u: %u %u %u %p", c, node_id, mem_id, offset, size, ptr); if (mm) pw_memmap_free(mm); @@ -1471,7 +1476,7 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, spa_list_append(&c->context.nodes, &o->link); snprintf(o->node.name, sizeof(o->node.name), "%s/%d", str, id); - pw_log_debug("add node %d", id); + pw_log_debug(NAME" %p: add node %d", c, id); break; case PW_TYPE_INTERFACE_Port: @@ -1517,7 +1522,7 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, snprintf(full_name, sizeof(full_name), "%s:%s", c->name, str); o = find_port(c, full_name); if (o != NULL) - pw_log_debug("client %p: %s found our port %p", c, full_name, o); + pw_log_debug(NAME" %p: %s found our port %p", c, full_name, o); } if (o == NULL) { o = alloc_object(c); @@ -1546,7 +1551,7 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, o->port.flags = flags; o->port.type_id = type_id; - pw_log_debug("add port %d %s %d", id, o->port.name, type_id); + pw_log_debug(NAME" %p: add port %d %s %d", c, id, o->port.name, type_id); break; } case PW_TYPE_INTERFACE_Link: @@ -1561,7 +1566,8 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, goto exit_free; o->port_link.dst = pw_properties_parse_int(str); - pw_log_debug("add link %d %d->%d", id, o->port_link.src, o->port_link.dst); + pw_log_debug(NAME" %p: add link %d %d->%d", c, id, + o->port_link.src, o->port_link.dst); break; default: @@ -1605,7 +1611,7 @@ static void registry_event_global_remove(void *object, uint32_t id) struct client *c = (struct client *) object; struct object *o; - pw_log_debug("removed: %u", id); + pw_log_debug(NAME" %p: removed: %u", c, id); o = pw_map_lookup(&c->context.globals, id); if (o == NULL) @@ -1660,12 +1666,12 @@ jack_client_t * jack_client_open (const char *client_name, if (getenv("PIPEWIRE_NOJACK") != NULL) goto disabled; - pw_log_debug("client open %s %d", client_name, options); - client = calloc(1, sizeof(struct client)); if (client == NULL) goto init_failed; + pw_log_debug(NAME" %p: open '%s' options:%d", client, client_name, options); + client->node_id = SPA_ID_INVALID; strncpy(client->name, client_name, JACK_CLIENT_NAME_SIZE); client->context.main = pw_main_loop_new(NULL); @@ -1825,7 +1831,7 @@ int jack_client_close (jack_client_t *client) { struct client *c = (struct client *) client; - pw_log_debug("client %p: close", client); + pw_log_debug(NAME" %p: close", client); pw_thread_loop_stop(c->context.loop); @@ -1833,7 +1839,7 @@ int jack_client_close (jack_client_t *client) pw_thread_loop_destroy(c->context.loop); pw_main_loop_destroy(c->context.main); - pw_log_debug("client %p: free", client); + pw_log_debug(NAME" %p: free", client); free(c); return 0; @@ -1856,7 +1862,7 @@ SPA_EXPORT char *jack_get_uuid_for_client_name (jack_client_t *client, const char *client_name) { - pw_log_warn("not implemented"); + pw_log_warn(NAME" %p: not implemented %s", client, client_name); return NULL; } @@ -1864,7 +1870,7 @@ SPA_EXPORT char *jack_get_client_name_by_uuid (jack_client_t *client, const char *client_uuid ) { - pw_log_warn("not implemented"); + pw_log_warn(NAME" %p: not implemented %s", client, client_uuid); return NULL; } @@ -1873,14 +1879,14 @@ int jack_internal_client_new (const char *client_name, const char *load_name, const char *load_init) { - pw_log_warn("not implemented"); + pw_log_warn("not implemented %s %s %s", client_name, load_name, load_init); return -ENOTSUP; } SPA_EXPORT void jack_internal_client_close (const char *client_name) { - pw_log_warn("not implemented"); + pw_log_warn("not implemented %s", client_name); } SPA_EXPORT @@ -1924,7 +1930,7 @@ int jack_deactivate (jack_client_t *client) SPA_EXPORT int jack_get_client_pid (const char *name) { - pw_log_warn("not implemented"); + pw_log_warn("not implemented %s", name); return -ENOTSUP; } @@ -1937,28 +1943,28 @@ jack_native_thread_t jack_client_thread_id (jack_client_t *client) SPA_EXPORT int jack_is_realtime (jack_client_t *client) { - pw_log_warn("not implemented"); + pw_log_warn(NAME" %p: not implemented", client); return -ENOTSUP; } SPA_EXPORT jack_nframes_t jack_thread_wait (jack_client_t *client, int status) { - pw_log_warn("not implemented"); + pw_log_warn(NAME" %p: not implemented %d", client, status); return 0; } SPA_EXPORT jack_nframes_t jack_cycle_wait (jack_client_t* client) { - pw_log_warn("not implemented"); + pw_log_warn(NAME" %p: not implemented", client); return 0; } SPA_EXPORT void jack_cycle_signal (jack_client_t* client, int status) { - pw_log_warn("not implemented"); + pw_log_warn(NAME" %p: not implemented %d", client, status); } SPA_EXPORT @@ -1967,10 +1973,10 @@ int jack_set_process_thread(jack_client_t* client, JackThreadCallback thread_cal struct client *c = (struct client *) client; if (c->active) { - pw_log_error("jack %p: can't set callback on active client", c); + pw_log_error(NAME" %p: can't set callback on active client", c); return -EIO; } else if (c->process_callback) { - pw_log_error("jack %p: process callback was already set", c); + pw_log_error(NAME" %p: process callback was already set", c); return -EIO; } @@ -2016,14 +2022,14 @@ int jack_set_process_callback (jack_client_t *client, struct client *c = (struct client *) client; if (c->active) { - pw_log_error("jack %p: can't set callback on active client", c); + pw_log_error(NAME" %p: can't set callback on active client", c); return -EIO; } else if (c->thread_callback) { - pw_log_error("jack %p: thread callback was already set", c); + pw_log_error(NAME" %p: thread callback was already set", c); return -EIO; } - pw_log_debug("jack %p: %p %p", c, process_callback, arg); + pw_log_debug(NAME" %p: %p %p", c, process_callback, arg); c->process_callback = process_callback; c->process_arg = arg; return 0; @@ -2119,7 +2125,7 @@ SPA_EXPORT int jack_set_xrun_callback (jack_client_t *client, JackXRunCallback xrun_callback, void *arg) { - pw_log_warn("not implemented"); + pw_log_warn(NAME" %p: not implemented", client); return 0; } @@ -2128,21 +2134,21 @@ int jack_set_latency_callback (jack_client_t *client, JackLatencyCallback latency_callback, void *data) { - pw_log_warn("not implemented"); + pw_log_warn(NAME" %p: not implemented", client); return -ENOTSUP; } SPA_EXPORT int jack_set_freewheel(jack_client_t* client, int onoff) { - pw_log_warn("not implemented"); + pw_log_warn(NAME" %p: not implemented %d", client, onoff); return -ENOTSUP; } SPA_EXPORT int jack_set_buffer_size (jack_client_t *client, jack_nframes_t nframes) { - pw_log_warn("not implemented"); + pw_log_warn(NAME" %p: not implemented %d", client, nframes); return -ENOTSUP; } @@ -2167,14 +2173,14 @@ jack_nframes_t jack_get_buffer_size (jack_client_t *client) SPA_EXPORT int jack_engine_takeover_timebase (jack_client_t *client) { - pw_log_warn("not implemented"); + pw_log_warn(NAME" %p: not implemented", client); return -ENOTSUP; } SPA_EXPORT float jack_cpu_load (jack_client_t *client) { - pw_log_warn("not implemented"); + pw_log_warn(NAME" %p: not implemented", client); return 0.0; } @@ -2200,7 +2206,7 @@ jack_port_t * jack_port_register (jack_client_t *client, struct port *p; int res; - pw_log_debug("client %p: port register \"%s\" \"%s\" %ld %ld", + pw_log_debug(NAME" %p: port register \"%s\" \"%s\" %ld %ld", c, port_name, port_type, flags, buffer_size); if (flags & JackPortIsInput) @@ -2273,10 +2279,10 @@ int jack_port_unregister (jack_client_t *client, jack_port_t *port) int res; if (o->type != PW_TYPE_INTERFACE_Port || o->port.port_id == SPA_ID_INVALID) { - pw_log_error("client %p: invalid port %p", client, port); + pw_log_error(NAME" %p: invalid port %p", client, port); return -EINVAL; } - pw_log_debug("client %p: port unregister %p", client, port); + pw_log_debug(NAME" %p: port unregister %p", client, port); pw_thread_loop_lock(c->context.loop); @@ -2296,7 +2302,7 @@ int jack_port_unregister (jack_client_t *client, jack_port_t *port) return res; } -static void *mix_audio(struct port *p, jack_nframes_t frames) +static void *mix_audio(struct client *c, struct port *p, jack_nframes_t frames) { struct mix *mix; struct buffer *b; @@ -2305,8 +2311,8 @@ static void *mix_audio(struct port *p, jack_nframes_t frames) void *ptr = NULL; spa_list_for_each(mix, &p->mix, port_link) { - pw_log_trace("port %p: mix %d.%d get buffer %d", - p, p->id, mix->id, frames); + pw_log_trace(NAME" %p: port %p mix %d.%d get buffer %d", + c, p, p->id, mix->id, frames); io = mix->io; if (io == NULL || io->buffer_id >= mix->n_buffers) continue; @@ -2324,15 +2330,15 @@ static void *mix_audio(struct port *p, jack_nframes_t frames) return ptr; } -static void *mix_midi(struct port *p, jack_nframes_t frames) +static void *mix_midi(struct client *c, struct port *p, jack_nframes_t frames) { struct mix *mix; struct spa_io_sequence *io; void *ptr = NULL; spa_list_for_each(mix, &p->mix, port_link) { - pw_log_trace("port %p: mix %d.%d get buffer %d", - p, p->id, mix->id, frames); + pw_log_trace(NAME" %p: port %p mix %d.%d get buffer %d", + c, p, p->id, mix->id, frames); io = mix->control; if (io == NULL) continue; @@ -2354,7 +2360,7 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) void *ptr = NULL; if (o->type != PW_TYPE_INTERFACE_Port || o->port.port_id == SPA_ID_INVALID) { - pw_log_error("client %p: invalid port %p", c, port); + pw_log_error(NAME" %p: invalid port %p", c, port); return NULL; } p = GET_PORT(c, GET_DIRECTION(o->port.flags), o->port.port_id); @@ -2362,10 +2368,10 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) if (p->direction == SPA_DIRECTION_INPUT) { switch (p->object->port.type_id) { case 0: - ptr = mix_audio(p, frames); + ptr = mix_audio(c, p, frames); break; case 1: - ptr = mix_midi(p, frames); + ptr = mix_midi(c, p, frames); break; } } else { @@ -2377,8 +2383,8 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) spa_list_for_each(mix, &p->mix, port_link) { struct buffer *b; - pw_log_trace("port %p: mix %d.%d get buffer %d io:%p n_buffers:%d", - p, p->id, mix->id, frames, mix->io, mix->n_buffers); + pw_log_trace(NAME" %p: port %p mix %d.%d get buffer %d io:%p n_buffers:%d", + c, p, p->id, mix->id, frames, mix->io, mix->n_buffers); if (mix->n_buffers == 0) continue; @@ -2414,14 +2420,14 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) p->zeroed = true; } } - pw_log_trace("port %p: buffer %p", p, ptr); + pw_log_trace(NAME" %p: port %p buffer %p", c, p, ptr); return ptr; } SPA_EXPORT jack_uuid_t jack_port_uuid (const jack_port_t *port) { - pw_log_warn("not implemented"); + pw_log_warn("not implemented %p", port); return 0; } @@ -2568,42 +2574,42 @@ const char ** jack_port_get_all_connections (const jack_client_t *client, SPA_EXPORT int jack_port_tie (jack_port_t *src, jack_port_t *dst) { - pw_log_warn("not implemented"); + pw_log_warn("not implemented %p %p", src, dst); return -ENOTSUP; } SPA_EXPORT int jack_port_untie (jack_port_t *port) { - pw_log_warn("not implemented"); + pw_log_warn("not implemented %p", port); return -ENOTSUP; } SPA_EXPORT int jack_port_set_name (jack_port_t *port, const char *port_name) { - pw_log_warn("not implemented"); + pw_log_warn("not implemented %p %s", port, port_name); return -ENOTSUP; } SPA_EXPORT int jack_port_rename (jack_client_t* client, jack_port_t *port, const char *port_name) { - pw_log_warn("not implemented"); + pw_log_warn(NAME" %p: not implemented %p %s", client, port, port_name); return -ENOTSUP; } SPA_EXPORT int jack_port_set_alias (jack_port_t *port, const char *alias) { - pw_log_warn("not implemented"); + pw_log_warn("not implemented %p %s", port, alias); return -ENOTSUP; } SPA_EXPORT int jack_port_unset_alias (jack_port_t *port, const char *alias) { - pw_log_warn("not implemented"); + pw_log_warn("not implemented %p %s", port, alias); return -ENOTSUP; } @@ -2632,7 +2638,7 @@ int jack_port_get_aliases (const jack_port_t *port, char* const aliases[2]) SPA_EXPORT int jack_port_request_monitor (jack_port_t *port, int onoff) { - pw_log_warn("not implemented"); + pw_log_warn("not implemented %p %d", port, onoff); return -ENOTSUP; } @@ -2640,21 +2646,21 @@ SPA_EXPORT int jack_port_request_monitor_by_name (jack_client_t *client, const char *port_name, int onoff) { - pw_log_warn("not implemented"); + pw_log_warn(NAME" %p: not implemented %s %d", client, port_name, onoff); return -ENOTSUP; } SPA_EXPORT int jack_port_ensure_monitor (jack_port_t *port, int onoff) { - pw_log_warn("not implemented"); + pw_log_warn("not implemented %p %d", port, onoff); return -ENOTSUP; } SPA_EXPORT int jack_port_monitoring_input (jack_port_t *port) { - pw_log_warn("not implemented"); + pw_log_warn("not implemented %p", port); return -ENOTSUP; } @@ -2670,7 +2676,7 @@ int jack_connect (jack_client_t *client, char val[4][16]; int res; - pw_log_debug("client %p: connect %s %s", client, source_port, destination_port); + pw_log_debug(NAME" %p: connect %s %s", client, source_port, destination_port); pw_thread_loop_lock(c->context.loop); @@ -2719,14 +2725,14 @@ int jack_disconnect (jack_client_t *client, struct object *src, *dst, *l; int res; - pw_log_debug("client %p: disconnect %s %s", client, source_port, destination_port); + pw_log_debug(NAME" %p: disconnect %s %s", client, source_port, destination_port); pw_thread_loop_lock(c->context.loop); src = find_port(c, source_port); dst = find_port(c, destination_port); - pw_log_debug("client %p: %d %d", client, src->id, dst->id); + pw_log_debug(NAME" %p: %d %d", client, src->id, dst->id); if (src == NULL || dst == NULL || !(src->port.flags & JackPortIsOutput) || @@ -2758,7 +2764,7 @@ int jack_port_disconnect (jack_client_t *client, jack_port_t *port) struct object *l; int res; - pw_log_debug("client %p: disconnect %p", client, port); + pw_log_debug(NAME" %p: disconnect %p", client, port); pw_thread_loop_lock(c->context.loop); @@ -2802,32 +2808,32 @@ size_t jack_port_type_get_buffer_size (jack_client_t *client, const char *port_t SPA_EXPORT void jack_port_set_latency (jack_port_t *port, jack_nframes_t frames) { - pw_log_warn("not implemented"); + pw_log_warn("not implemented %p %d", port, frames); } SPA_EXPORT void jack_port_get_latency_range (jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range) { - pw_log_warn("not implemented"); + pw_log_warn("not implemented %p %d", port, mode); } SPA_EXPORT void jack_port_set_latency_range (jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range) { - pw_log_warn("not implemented"); + pw_log_warn("not implemented %p %d", port, mode); } SPA_EXPORT int jack_recompute_total_latencies (jack_client_t *client) { - pw_log_warn("not implemented"); + pw_log_warn(NAME" %p: not implemented", client); return -ENOTSUP; } SPA_EXPORT jack_nframes_t jack_port_get_latency (jack_port_t *port) { - pw_log_warn("not implemented"); + pw_log_warn("not implemented %p", port); return 0; } @@ -2835,14 +2841,14 @@ SPA_EXPORT jack_nframes_t jack_port_get_total_latency (jack_client_t *client, jack_port_t *port) { - pw_log_warn("not implemented"); + pw_log_warn(NAME" %p: not implemented %p", client, port); return 0; } SPA_EXPORT int jack_recompute_total_latency (jack_client_t *client, jack_port_t* port) { - pw_log_warn("not implemented"); + pw_log_warn(NAME" %p: not implemented %p", client, port); return -ENOTSUP; } @@ -2872,11 +2878,12 @@ const char ** jack_get_ports (jack_client_t *client, pw_thread_loop_lock(c->context.loop); - pw_log_debug("ports %d %s %s %08lx", id, port_name_pattern, type_name_pattern, flags); + pw_log_debug(NAME" %p: ports id:%d name:%s type:%s flags:%08lx", c, id, + port_name_pattern, type_name_pattern, flags); spa_list_for_each(o, &c->context.ports, link) { - pw_log_debug("check port %s %d %lu", - o->port.name, o->port.type_id, o->port.flags); + pw_log_debug(NAME" %p: check port type:%d flags:%08lx name:%s", c, + o->port.type_id, o->port.flags, o->port.name); if (o->port.type_id == 2) continue; if (!SPA_FLAG_CHECK(o->port.flags, flags)) @@ -2894,7 +2901,7 @@ const char ** jack_get_ports (jack_client_t *client, continue; } - pw_log_debug("add port %d %s", count, o->port.name); + pw_log_debug(NAME" %p: port %s matches (%d)", c, o->port.name, count); res[count++] = o->port.name; if (count == JACK_PORT_MAX) break; @@ -2940,7 +2947,7 @@ jack_port_t * jack_port_by_id (jack_client_t *client, pw_thread_loop_lock(c->context.loop); o = pw_map_lookup(&c->context.globals, port_id); - pw_log_debug("client %p: port %d -> %p", c, port_id, o); + pw_log_debug(NAME" %p: port %d -> %p", c, port_id, o); if (o == NULL || o->type != PW_TYPE_INTERFACE_Port) goto exit; @@ -2995,7 +3002,7 @@ int jack_get_cycle_times(const jack_client_t *client, *current_usecs = c->jack_position.usecs; *period_usecs = c->buffer_size / (c->sample_rate * c->rate_diff); *next_usecs = c->jack_position.usecs + (*period_usecs * 1000000.0f); - pw_log_trace("client %p: %d %"PRIu64" %"PRIu64" %f", c, *current_frames, + pw_log_trace(NAME" %p: %d %"PRIu64" %"PRIu64" %f", c, *current_frames, *current_usecs, *next_usecs, *period_usecs); return 0; } @@ -3045,7 +3052,7 @@ void jack_free(void* ptr) SPA_EXPORT int jack_release_timebase (jack_client_t *client) { - pw_log_warn("not implemented"); + pw_log_warn(NAME" %p: not implemented", client); return -ENOTSUP; } @@ -3064,7 +3071,7 @@ SPA_EXPORT int jack_set_sync_timeout (jack_client_t *client, jack_time_t timeout) { - pw_log_warn("not implemented"); + pw_log_warn(NAME" %p: not implemented %lu", client, timeout); return -ENOTSUP; } @@ -3084,7 +3091,7 @@ SPA_EXPORT int jack_transport_locate (jack_client_t *client, jack_nframes_t frame) { - pw_log_warn("not implemented"); + pw_log_warn(NAME" %p: not implemented %d", client, frame); return -ENOTSUP; } @@ -3101,7 +3108,7 @@ jack_transport_state_t jack_transport_query (const jack_client_t *client, SPA_EXPORT jack_nframes_t jack_get_current_transport_frame (const jack_client_t *client) { - pw_log_warn("not implemented"); + pw_log_warn(NAME" %p: not implemented", client); return 0; } @@ -3109,20 +3116,20 @@ SPA_EXPORT int jack_transport_reposition (jack_client_t *client, const jack_position_t *pos) { - pw_log_warn("not implemented"); + pw_log_warn(NAME" %p: not implemented", client); return -ENOTSUP; } SPA_EXPORT void jack_transport_start (jack_client_t *client) { - pw_log_warn("not implemented"); + pw_log_warn(NAME" %p: not implemented", client); } SPA_EXPORT void jack_transport_stop (jack_client_t *client) { - pw_log_warn("not implemented"); + pw_log_warn(NAME" %p: not implemented", client); } SPA_EXPORT @@ -3131,14 +3138,14 @@ void jack_get_transport_info (jack_client_t *client, { static jack_transport_info_t dummy; memcpy(tinfo, &dummy, sizeof(jack_transport_info_t)); - pw_log_warn("not implemented"); + pw_log_warn(NAME" %p: not implemented", client); } SPA_EXPORT void jack_set_transport_info (jack_client_t *client, jack_transport_info_t *tinfo) { - pw_log_warn("not implemented"); + pw_log_warn(NAME" %p: not implemented", client); } SPA_EXPORT @@ -3146,7 +3153,7 @@ int jack_set_session_callback (jack_client_t *client, JackSessionCallback session_callback, void *arg) { - pw_log_warn("not implemented"); + pw_log_warn(NAME" %p: not implemented", client); return -ENOTSUP; } @@ -3154,7 +3161,7 @@ SPA_EXPORT int jack_session_reply (jack_client_t *client, jack_session_event_t *event) { - pw_log_warn("not implemented"); + pw_log_warn(NAME" %p: not implemented", client); return -ENOTSUP; } @@ -3168,28 +3175,28 @@ void jack_session_event_free (jack_session_event_t *event) SPA_EXPORT char *jack_client_get_uuid (jack_client_t *client) { - pw_log_warn("not implemented"); + pw_log_warn(NAME" %p: not implemented", client); return ""; } SPA_EXPORT int jack_client_real_time_priority (jack_client_t * client) { - pw_log_warn("not implemented"); + pw_log_warn(NAME" %p: not implemented", client); return -ENOTSUP; } SPA_EXPORT int jack_client_max_real_time_priority (jack_client_t *client) { - pw_log_warn("not implemented"); + pw_log_warn(NAME" %p: not implemented", client); return -ENOTSUP; } SPA_EXPORT int jack_acquire_real_time_scheduling (jack_native_thread_t thread, int priority) { - pw_log_warn("not implemented"); + pw_log_warn("not implemented %lu %d", thread, priority); return -ENOTSUP; } @@ -3226,21 +3233,21 @@ int jack_client_create_thread (jack_client_t* client, SPA_EXPORT int jack_drop_real_time_scheduling (jack_native_thread_t thread) { - pw_log_warn("not implemented"); + pw_log_warn("not implemented %lu", thread); return -ENOTSUP; } SPA_EXPORT int jack_client_stop_thread(jack_client_t* client, jack_native_thread_t thread) { - pw_log_warn("not implemented"); + pw_log_warn(NAME" %p: not implemented %lu", client, thread); return -ENOTSUP; } SPA_EXPORT int jack_client_kill_thread(jack_client_t* client, jack_native_thread_t thread) { - pw_log_warn("not implemented"); + pw_log_warn(NAME" %p: not implemented %lu", client, thread); return -ENOTSUP; } From 2d6f9950a5e036f908953727e61e3b3fb02eacf0 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 12 Aug 2019 12:28:54 +0200 Subject: [PATCH 082/133] jack: improve node name Try to use the short nick first and then the name/description --- src/pipewire-jack.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 0fa37b631..93c703814 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -1469,8 +1469,11 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, switch (type) { case PW_TYPE_INTERFACE_Node: - if ((str = spa_dict_lookup(props, PW_KEY_NODE_NAME)) == NULL) - goto exit; + if ((str = spa_dict_lookup(props, PW_KEY_NODE_NICK)) == NULL && + (str = spa_dict_lookup(props, PW_KEY_NODE_DESCRIPTION)) == NULL && + (str = spa_dict_lookup(props, PW_KEY_NODE_NAME)) == NULL) { + str = "node"; + } o = alloc_object(c); spa_list_append(&c->context.nodes, &o->link); From a560e8b92f550268e3e697cfe03bd517664b727d Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 16 Aug 2019 22:10:38 +0200 Subject: [PATCH 083/133] jack: use properties instead of parent_id --- src/pipewire-jack.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 93c703814..f94ea5c52 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -1455,7 +1455,7 @@ static const char* type_to_string(jack_port_type_id_t type_id) } } -static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, +static void registry_event_global(void *data, uint32_t id, uint32_t permissions, uint32_t type, uint32_t version, const struct spa_dict *props) { @@ -1463,6 +1463,7 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, struct object *o, *ot; const char *str; size_t size; + uint32_t parent_id = 0; if (props == NULL) return; @@ -1496,6 +1497,11 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, if ((type_id = string_to_type(str)) == SPA_ID_INVALID) goto exit; + if ((str = spa_dict_lookup(props, PW_KEY_NODE_ID)) == NULL) + goto exit; + + parent_id = atoi(str); + if ((str = spa_dict_lookup(props, PW_KEY_PORT_NAME)) == NULL) goto exit; From 44b7eb4630f20e34ff6a544de59d5c374e941252 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 21 Aug 2019 18:37:02 +0200 Subject: [PATCH 084/133] jack: implement some more Implement some parts of metadata and uuid --- src/metadata.c | 70 +++++++++++++++++++++++++++++++++++++++++---- src/pipewire-jack.c | 67 +++++++++++++++++++++++++++++++------------ src/uuid.c | 44 +++++++++++++++++----------- 3 files changed, 140 insertions(+), 41 deletions(-) diff --git a/src/metadata.c b/src/metadata.c index 2dac2c3e8..8ec3b212b 100644 --- a/src/metadata.c +++ b/src/metadata.c @@ -26,9 +26,29 @@ #include #include +#include #include + +static struct pw_properties * get_properties(void) +{ + static struct pw_properties *properties = NULL; + if (properties == NULL) { + properties = pw_properties_new(NULL, NULL); + } + return properties; +} + +static void make_key(char *dst, jack_uuid_t subject, const char *key, int keylen) +{ + int len; + jack_uuid_unparse (subject, dst); + len = strlen(dst); + dst[len] = '@'; + memcpy(&dst[len+1], key, keylen+1); +} + SPA_EXPORT int jack_set_property(jack_client_t*client, jack_uuid_t subject, @@ -36,8 +56,16 @@ int jack_set_property(jack_client_t*client, const char* value, const char* type) { - pw_log_warn("not implemented"); - return -1; + int keylen = strlen(key); + char *dst = alloca(JACK_UUID_STRING_SIZE + keylen); + struct pw_properties * props = get_properties(); + + make_key(dst, subject, key, keylen); + + pw_properties_setf(props, dst, "%s@%s", value, type); + pw_log_debug("set '%s' to '%s@%s'", dst, value, type); + + return 0; } SPA_EXPORT @@ -46,8 +74,30 @@ int jack_get_property(jack_uuid_t subject, char** value, char** type) { - pw_log_warn("not implemented"); - return -1; + int keylen = strlen(key); + char *dst = alloca(JACK_UUID_STRING_SIZE + keylen); + struct pw_properties * props = get_properties(); + const char *str, *at; + + make_key(dst, subject, key, keylen); + + if ((str = pw_properties_get(props, dst)) == NULL) { + pw_log_warn("no property '%s'", dst); + return -1; + } + + at = strrchr(str, '@'); + if (at == NULL) { + pw_log_warn("property '%s' invalid value '%s'", dst, str); + return -1; + } + + *value = strndup(str, at - str); + *type = strdup(at + 1); + + pw_log_debug("got '%s' with value:'%s' type:'%s'", dst, *value, *type); + + return 0; } SPA_EXPORT @@ -74,8 +124,16 @@ int jack_get_all_properties (jack_description_t** descs) SPA_EXPORT int jack_remove_property (jack_client_t* client, jack_uuid_t subject, const char* key) { - pw_log_warn("not implemented"); - return -1; + int keylen = strlen(key); + char *dst = alloca(JACK_UUID_STRING_SIZE + keylen); + struct pw_properties * props = get_properties(); + + make_key(dst, subject, key, keylen); + + pw_properties_set(props, dst, NULL); + pw_log_debug("removed %s", dst); + + return 0; } SPA_EXPORT diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index f94ea5c52..8b9312e5e 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -89,7 +90,6 @@ struct object { uint32_t type; uint32_t id; - uint32_t parent_id; union { struct { @@ -105,6 +105,7 @@ struct object { char alias1[REAL_JACK_PORT_NAME_SIZE+1]; char alias2[REAL_JACK_PORT_NAME_SIZE+1]; uint32_t type_id; + uint32_t node_id; uint32_t port_id; } port; }; @@ -372,7 +373,7 @@ static struct port * alloc_port(struct client *c, enum spa_direction direction) o = alloc_object(c); o->type = PW_TYPE_INTERFACE_Port; o->id = SPA_ID_INVALID; - o->parent_id = c->node_id; + o->port.node_id = c->node_id; o->port.port_id = p->id; spa_list_append(&c->context.ports, &o->link); @@ -738,9 +739,10 @@ on_rtsocket_condition(void *data, int fd, uint32_t mask) &c->jack_position, c->sync_arg); } - pw_log_trace(NAME" %p: do process %"PRIu64" %d %d %d %"PRIi64" %f", c, + pw_log_trace(NAME" %p: do process %"PRIu64" %d %d %d %"PRIi64" %f %p", c, nsec, c->buffer_size, c->sample_rate, - c->jack_position.frame, delay, c->rate_diff); + c->jack_position.frame, delay, c->rate_diff, + c->position); if (c->process_callback) c->process_callback(c->buffer_size, c->process_arg); @@ -1463,7 +1465,6 @@ static void registry_event_global(void *data, uint32_t id, struct object *o, *ot; const char *str; size_t size; - uint32_t parent_id = 0; if (props == NULL) return; @@ -1488,6 +1489,7 @@ static void registry_event_global(void *data, uint32_t id, const struct spa_dict_item *item; unsigned long flags = 0; jack_port_type_id_t type_id; + uint32_t node_id; char full_name[1024]; if ((str = spa_dict_lookup(props, PW_KEY_FORMAT_DSP)) == NULL) { @@ -1500,7 +1502,7 @@ static void registry_event_global(void *data, uint32_t id, if ((str = spa_dict_lookup(props, PW_KEY_NODE_ID)) == NULL) goto exit; - parent_id = atoi(str); + node_id = atoi(str); if ((str = spa_dict_lookup(props, PW_KEY_PORT_NAME)) == NULL) goto exit; @@ -1527,7 +1529,7 @@ static void registry_event_global(void *data, uint32_t id, } o = NULL; - if (parent_id == c->node_id) { + if (node_id == c->node_id) { snprintf(full_name, sizeof(full_name), "%s:%s", c->name, str); o = find_port(c, full_name); if (o != NULL) @@ -1539,7 +1541,7 @@ static void registry_event_global(void *data, uint32_t id, goto exit; spa_list_append(&c->context.ports, &o->link); - ot = pw_map_lookup(&c->context.globals, parent_id); + ot = pw_map_lookup(&c->context.globals, node_id); if (ot == NULL || ot->type != PW_TYPE_INTERFACE_Node) goto exit_free; @@ -1559,6 +1561,7 @@ static void registry_event_global(void *data, uint32_t id, o->port.flags = flags; o->port.type_id = type_id; + o->port.node_id = node_id; pw_log_debug(NAME" %p: add port %d %s %d", c, id, o->port.name, type_id); break; @@ -1585,7 +1588,6 @@ static void registry_event_global(void *data, uint32_t id, o->type = type; o->id = id; - o->parent_id = parent_id; size = pw_map_get_size(&c->context.globals); while (id > size) @@ -1867,11 +1869,24 @@ char * jack_get_client_name (jack_client_t *client) return c->name; } +static jack_uuid_t cuuid = 0x2; + SPA_EXPORT char *jack_get_uuid_for_client_name (jack_client_t *client, const char *client_name) { - pw_log_warn(NAME" %p: not implemented %s", client, client_name); + struct client *c = (struct client *) client; + struct object *o; + + spa_list_for_each(o, &c->context.nodes, link) { + if (strcmp(o->node.name, client_name) == 0) { + char *uuid; + asprintf(&uuid, "%" PRIu64, (cuuid << 32) | o->id); + pw_log_debug(NAME" %p: name %s -> %s", + client, client_name, uuid); + return uuid; + } + } return NULL; } @@ -1879,7 +1894,21 @@ SPA_EXPORT char *jack_get_client_name_by_uuid (jack_client_t *client, const char *client_uuid ) { - pw_log_warn(NAME" %p: not implemented %s", client, client_uuid); + struct client *c = (struct client *) client; + struct object *o; + jack_uuid_t uuid; + jack_uuid_t cuuid = 0x2; + + if (jack_uuid_parse(client_uuid, &uuid) < 0) + return NULL; + + spa_list_for_each(o, &c->context.nodes, link) { + if ((cuuid << 32 | o->id) == uuid) { + pw_log_debug(NAME" %p: uuid %s (%"PRIu64")-> %s", + client, client_uuid, uuid, o->node.name); + return strdup(o->node.name); + } + } return NULL; } @@ -2436,8 +2465,8 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) SPA_EXPORT jack_uuid_t jack_port_uuid (const jack_port_t *port) { - pw_log_warn("not implemented %p", port); - return 0; + struct object *o = (struct object *) port; + return jack_port_uuid_generate(o->id); } SPA_EXPORT @@ -2699,9 +2728,9 @@ int jack_connect (jack_client_t *client, goto exit; } - snprintf(val[0], sizeof(val[0]), "%d", src->parent_id); + snprintf(val[0], sizeof(val[0]), "%d", src->port.node_id); snprintf(val[1], sizeof(val[1]), "%d", src->id); - snprintf(val[2], sizeof(val[2]), "%d", dst->parent_id); + snprintf(val[2], sizeof(val[2]), "%d", dst->port.node_id); snprintf(val[3], sizeof(val[3]), "%d", dst->id); props = SPA_DICT_INIT(items, 0); @@ -2897,7 +2926,7 @@ const char ** jack_get_ports (jack_client_t *client, continue; if (!SPA_FLAG_CHECK(o->port.flags, flags)) continue; - if (id != SPA_ID_INVALID && o->parent_id != id) + if (id != SPA_ID_INVALID && o->port.node_id != id) continue; if (port_name_pattern && port_name_pattern[0]) { @@ -3184,8 +3213,10 @@ void jack_session_event_free (jack_session_event_t *event) SPA_EXPORT char *jack_client_get_uuid (jack_client_t *client) { - pw_log_warn(NAME" %p: not implemented", client); - return ""; + struct client *c = (struct client *) client; + char *uuid = NULL; + asprintf(&uuid, "%d", c->node_id); + return uuid; } SPA_EXPORT diff --git a/src/uuid.c b/src/uuid.c index db2db86e7..f49e52bc6 100644 --- a/src/uuid.c +++ b/src/uuid.c @@ -32,59 +32,69 @@ SPA_EXPORT jack_uuid_t jack_client_uuid_generate () { - pw_log_warn("not implemented"); - return 0; + static uint32_t uuid_cnt = 0; + jack_uuid_t uuid = 0x2; /* JackUUIDClient */; + uuid = (uuid << 32) | ++uuid_cnt; + return uuid; } SPA_EXPORT jack_uuid_t jack_port_uuid_generate (uint32_t port_id) { - pw_log_warn("not implemented"); - return 0; + jack_uuid_t uuid = 0x1; /* JackUUIDPort */ + uuid = (uuid << 32) | (port_id + 1); + return uuid; } SPA_EXPORT uint32_t jack_uuid_to_index (jack_uuid_t id) { - pw_log_warn("not implemented"); - return 0; + return (id & 0xffff) - 1; } SPA_EXPORT int jack_uuid_compare (jack_uuid_t id1, jack_uuid_t id2) { - pw_log_warn("not implemented"); - return 0; + if (id1 == id2) + return 0; + if (id1 < id2) + return -1; + return 1; } SPA_EXPORT void jack_uuid_copy (jack_uuid_t* dst, jack_uuid_t src) { - pw_log_warn("not implemented"); + *dst = src; } SPA_EXPORT -void jack_uuid_clear (jack_uuid_t*id) +void jack_uuid_clear (jack_uuid_t *id) { - pw_log_warn("not implemented"); + *id = 0; } SPA_EXPORT -int jack_uuid_parse (const char *buf, jack_uuid_t*id) +int jack_uuid_parse (const char *buf, jack_uuid_t *id) { - pw_log_warn("not implemented"); - return 0; + if (sscanf (buf, "%" PRIu64, id) == 1) { + if (*id < (0x1LL << 32)) { + /* has not type bits set - not legal */ + return -1; + } + return 0; + } + return -1; } SPA_EXPORT void jack_uuid_unparse (jack_uuid_t id, char buf[JACK_UUID_STRING_SIZE]) { - pw_log_warn("not implemented"); + snprintf (buf, JACK_UUID_STRING_SIZE, "%" PRIu64, id); } SPA_EXPORT int jack_uuid_empty (jack_uuid_t id) { - pw_log_warn("not implemented"); - return 0; + return id == 0; } From 2c25b8c21652bf74cf23f0a862e80e717da69d18 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 21 Aug 2019 20:36:29 +0200 Subject: [PATCH 085/133] jack: always unmap old io area --- src/pipewire-jack.c | 27 +++++++++++++-------------- src/uuid.c | 2 ++ 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 8b9312e5e..7ae1d137b 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -862,9 +862,10 @@ static int client_node_set_io(void *object, void *ptr; uint32_t tag[5] = { c->node_id, id, }; + if ((mm = pw_mempool_find_tag(c->remote->pool, tag, sizeof(tag))) != NULL) + pw_memmap_free(mm); + if (mem_id == SPA_ID_INVALID) { - if ((mm = pw_mempool_find_tag(c->remote->pool, tag, sizeof(tag))) != NULL) - pw_memmap_free(mm); mm = ptr = NULL; size = 0; } @@ -1306,9 +1307,10 @@ static int client_node_port_set_io(void *object, goto exit; } + if ((mm = pw_mempool_find_tag(c->remote->pool, tag, sizeof(tag))) != NULL) + pw_memmap_free(mm); + if (mem_id == SPA_ID_INVALID) { - if ((mm = pw_mempool_find_tag(c->remote->pool, tag, sizeof(tag))) != NULL) - pw_memmap_free(mm); mm = ptr = NULL; size = 0; @@ -1362,6 +1364,13 @@ static int client_node_set_activation(void *object, void *ptr; int res = 0; + if (c->node_id == node_id) { + pw_log_debug(NAME" %p: our activation %u: %u %u %u", c, node_id, + mem_id, offset, size); + close(signalfd); + return 0; + } + if (mem_id == SPA_ID_INVALID) { mm = ptr = NULL; size = 0; @@ -1380,22 +1389,12 @@ static int client_node_set_activation(void *object, pw_log_debug(NAME" %p: set activation %u: %u %u %u %p", c, node_id, mem_id, offset, size, ptr); - if (c->node_id == node_id) { - pw_log_debug(NAME" %p: our activation %u: %u %u %u %p", c, node_id, - mem_id, offset, size, ptr); - if (mm) - pw_memmap_free(mm); - close(signalfd); - return 0; - } - if (ptr) { link = pw_array_add(&c->links, sizeof(struct link)); if (link == NULL) { res = -errno; goto exit; } - link->node_id = SPA_ID_INVALID; link->node_id = node_id; link->mem = mm; link->activation = ptr; diff --git a/src/uuid.c b/src/uuid.c index f49e52bc6..8d7a7668c 100644 --- a/src/uuid.c +++ b/src/uuid.c @@ -35,6 +35,7 @@ jack_uuid_t jack_client_uuid_generate () static uint32_t uuid_cnt = 0; jack_uuid_t uuid = 0x2; /* JackUUIDClient */; uuid = (uuid << 32) | ++uuid_cnt; + pw_log_debug("uuid %"PRIu64, uuid); return uuid; } @@ -43,6 +44,7 @@ jack_uuid_t jack_port_uuid_generate (uint32_t port_id) { jack_uuid_t uuid = 0x1; /* JackUUIDPort */ uuid = (uuid << 32) | (port_id + 1); + pw_log_debug("uuid %d -> %"PRIu64, port_id, uuid); return uuid; } From 33cac9932c0febb6c0219b30795b4c1bb01a4160 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 22 Aug 2019 10:59:16 +0200 Subject: [PATCH 086/133] jack: report cpu load Find the driver activation and use it to get stats. --- src/pipewire-jack.c | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 7ae1d137b..0aeff5ed9 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -276,6 +276,8 @@ struct client { struct spa_list free_ports[2]; struct pw_array links; + uint32_t driver_id; + struct pw_node_activation *driver_activation; struct pw_memmap *mem; struct pw_node_activation *activation; @@ -284,6 +286,7 @@ struct client { int status; jack_position_t jack_position; + }; static void init_port_pool(struct client *c, enum spa_direction direction) @@ -851,6 +854,17 @@ static int client_node_set_param(void *object, return -ENOTSUP; } +static int update_driver_activation(struct client *c) +{ + struct link *link; + + pw_log_debug(NAME" %p: driver %d", c, c->driver_id); + + link = find_activation(&c->links, c->driver_id); + c->driver_activation = link ? link->activation : NULL; + return 0; +} + static int client_node_set_io(void *object, uint32_t id, uint32_t mem_id, @@ -884,6 +898,8 @@ static int client_node_set_io(void *object, switch (id) { case SPA_IO_Position: c->position = ptr; + c->driver_id = ptr ? c->position->clock.id : SPA_ID_INVALID; + update_driver_activation(c); break; default: break; @@ -1409,6 +1425,9 @@ static int client_node_set_activation(void *object, clear_link(c, link); } + if (c->driver_id == node_id) + update_driver_activation(c); + exit: if (res < 0) pw_proxy_error((struct pw_proxy*)c->node_proxy, res, spa_strerror(res)); @@ -2217,8 +2236,14 @@ int jack_engine_takeover_timebase (jack_client_t *client) SPA_EXPORT float jack_cpu_load (jack_client_t *client) { - pw_log_warn(NAME" %p: not implemented", client); - return 0.0; + struct client *c = (struct client *) client; + float res = 0.0f; + + if (c->driver_activation) + res = c->driver_activation->cpu_load[2]; + + pw_log_trace(NAME" %p: cpu load %f", client, res); + return res; } SPA_EXPORT From 84071d2cacc0773a21524211a20e6068989d3883 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 22 Aug 2019 13:25:01 +0200 Subject: [PATCH 087/133] jack: implement statistics --- src/meson.build | 1 - src/pipewire-jack.c | 2 ++ src/statistics.c | 34 +++++++++++++++++++--------------- 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/meson.build b/src/meson.build index f8e80625e..a2269e7b4 100644 --- a/src/meson.build +++ b/src/meson.build @@ -2,7 +2,6 @@ pipewire_jack_sources = [ 'pipewire-jack.c', 'metadata.c', 'ringbuffer.c', - 'statistics.c', 'uuid.c', ] diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 0aeff5ed9..bc549d5df 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -2246,6 +2246,8 @@ float jack_cpu_load (jack_client_t *client) return res; } +#include "statistics.c" + SPA_EXPORT jack_port_t * jack_port_register (jack_client_t *client, const char *port_name, diff --git a/src/statistics.c b/src/statistics.c index a99f7ee77..260b10b8e 100644 --- a/src/statistics.c +++ b/src/statistics.c @@ -17,34 +17,38 @@ * Boston, MA 02110-1301, USA. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include - #include -#include - SPA_EXPORT float jack_get_max_delayed_usecs (jack_client_t *client) { - pw_log_warn("not implemented"); - return 0.0f; + struct client *c = (struct client *) client; + float res = 0.0f; + + if (c->driver_activation) + res = (float)c->driver_activation->max_delay / SPA_USEC_PER_SEC; + + pw_log_trace(NAME" %p: max delay %f", client, res); + return res; } SPA_EXPORT float jack_get_xrun_delayed_usecs (jack_client_t *client) { - pw_log_warn("not implemented"); - return 0.0f; + struct client *c = (struct client *) client; + float res = 0.0f; + + if (c->driver_activation) + res = (float)c->driver_activation->xrun_delay / SPA_USEC_PER_SEC; + + pw_log_trace(NAME" %p: xrun delay %f", client, res); + return res; } SPA_EXPORT void jack_reset_max_delayed_usecs (jack_client_t *client) { - pw_log_warn("not implemented"); + struct client *c = (struct client *) client; + if (c->driver_activation) + c->driver_activation->max_delay = 0; } From 45f0e7c35bb551740a98510a157a14dcc11a9741 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 22 Aug 2019 13:56:39 +0200 Subject: [PATCH 088/133] jack: signal xrun callbacks Keep track of the number of xruns and signal the callback when it changes. CPU load must be expressed in percent. --- src/pipewire-jack.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index bc549d5df..6060bd38a 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -257,6 +257,8 @@ struct client { void *connect_arg; JackGraphOrderCallback graph_callback; void *graph_arg; + JackXRunCallback xrun_callback; + void *xrun_arg; JackSyncCallback sync_callback; void *sync_arg; JackTimebaseCallback timebase_callback; @@ -281,6 +283,7 @@ struct client { struct pw_memmap *mem; struct pw_node_activation *activation; + uint32_t xrun_count; bool started; int status; @@ -742,6 +745,14 @@ on_rtsocket_condition(void *data, int fd, uint32_t mask) &c->jack_position, c->sync_arg); } + if (c->driver_activation) { + struct pw_node_activation *a = c->driver_activation; + if (c->xrun_count != a->xrun_count && + c->xrun_count != 0 && c->xrun_callback) + c->xrun_callback(c->xrun_arg); + c->xrun_count = a->xrun_count; + } + pw_log_trace(NAME" %p: do process %"PRIu64" %d %d %d %"PRIi64" %f %p", c, nsec, c->buffer_size, c->sample_rate, c->jack_position.frame, delay, c->rate_diff, @@ -2181,7 +2192,9 @@ SPA_EXPORT int jack_set_xrun_callback (jack_client_t *client, JackXRunCallback xrun_callback, void *arg) { - pw_log_warn(NAME" %p: not implemented", client); + struct client *c = (struct client *) client; + c->xrun_callback = xrun_callback; + c->xrun_arg = arg; return 0; } @@ -2240,7 +2253,7 @@ float jack_cpu_load (jack_client_t *client) float res = 0.0f; if (c->driver_activation) - res = c->driver_activation->cpu_load[2]; + res = c->driver_activation->cpu_load[0] * 100.0f; pw_log_trace(NAME" %p: cpu load %f", client, res); return res; From 42f144e0978c2c785cea9320e7b5e08f161567fb Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 22 Aug 2019 14:30:44 +0200 Subject: [PATCH 089/133] jack: fix frame time --- src/pipewire-jack.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 6060bd38a..41f52dd7d 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -3042,13 +3042,11 @@ jack_nframes_t jack_frames_since_cycle_start (const jack_client_t *client) { struct client *c = (struct client *) client; struct timespec ts; - jack_nframes_t res; uint64_t diff; clock_gettime(CLOCK_MONOTONIC, &ts); diff = SPA_TIMESPEC_TO_USEC(&ts) - c->jack_position.usecs; - res = (jack_nframes_t) floor(((float)c->sample_rate * diff) / 1000000000.0f); - return res; + return (jack_nframes_t) floor(((float)c->sample_rate * diff) / 1000000000.0f); } SPA_EXPORT @@ -3088,16 +3086,16 @@ SPA_EXPORT jack_time_t jack_frames_to_time(const jack_client_t *client, jack_nframes_t frames) { struct client *c = (struct client *) client; - int32_t df = frames - c->jack_position.frame; - return c->jack_position.usecs + (int64_t)rint((double) df / c->sample_rate); + double df = (frames - c->jack_position.frame) * (double)SPA_USEC_PER_SEC / c->sample_rate; + return c->jack_position.usecs + (int64_t)rint(df); } SPA_EXPORT jack_nframes_t jack_time_to_frames(const jack_client_t *client, jack_time_t usecs) { struct client *c = (struct client *) client; - int64_t du = usecs - c->jack_position.usecs; - return c->jack_position.frame + (int32_t)rint((double)du * c->sample_rate); + double du = (usecs - c->jack_position.usecs) * (double)c->sample_rate / SPA_USEC_PER_SEC; + return c->jack_position.frame + (int32_t)rint(du); } SPA_EXPORT From f8c3126b525eb01d062d1744b0c8cd3d36126407 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 27 Aug 2019 14:41:15 +0200 Subject: [PATCH 090/133] jack: implement transport support --- src/pipewire-jack.c | 356 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 281 insertions(+), 75 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 41f52dd7d..2e9253804 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -261,11 +261,12 @@ struct client { void *xrun_arg; JackSyncCallback sync_callback; void *sync_arg; + unsigned int sync_emit:1; JackTimebaseCallback timebase_callback; void *timebase_arg; + unsigned int timebase_emit:1; struct spa_io_position *position; - double rate_diff; uint32_t sample_rate; uint32_t buffer_size; @@ -285,10 +286,8 @@ struct client { struct pw_node_activation *activation; uint32_t xrun_count; - bool started; - int status; + unsigned int started:1; - jack_position_t jack_position; }; @@ -670,11 +669,126 @@ static void process_tee(struct client *c) } } +static inline void debug_position(struct client *c, jack_position_t *p) +{ + pw_log_trace("usecs: %lu", p->usecs); + pw_log_trace("frame_rate: %u", p->frame_rate); + pw_log_trace("frame: %u", p->frame); + pw_log_trace("valid: %08x", p->valid); + + if (p->valid & JackPositionBBT) { + pw_log_trace("BBT"); + pw_log_trace(" bar: %u", p->bar); + pw_log_trace(" beat: %u", p->beat); + pw_log_trace(" tick: %u", p->tick); + pw_log_trace(" bar_start_tick: %f", p->bar_start_tick); + pw_log_trace(" beats_per_bar: %f", p->beats_per_bar); + pw_log_trace(" beat_type: %f", p->beat_type); + pw_log_trace(" ticks_per_beat: %f", p->ticks_per_beat); + pw_log_trace(" beats_per_minute: %f", p->beats_per_minute); + } + if (p->valid & JackPositionTimecode) { + pw_log_trace("Timecode:"); + pw_log_trace(" frame_time: %f", p->frame_time); + pw_log_trace(" next_time: %f", p->next_time); + } + if (p->valid & JackBBTFrameOffset) { + pw_log_trace("BBTFrameOffset:"); + pw_log_trace(" bbt_offset: %u", p->bbt_offset); + } + if (p->valid & JackAudioVideoRatio) { + pw_log_trace("AudioVideoRatio:"); + pw_log_trace(" audio_frames_per_video_frame: %f", p->audio_frames_per_video_frame); + } + if (p->valid & JackVideoFrameOffset) { + pw_log_trace("JackVideoFrameOffset:"); + pw_log_trace(" video_offset: %u", p->video_offset); + } +} + +static inline void jack_to_position(jack_position_t *s, struct spa_io_position *d) +{ + d->valid = 0; + if (s->valid & JackPositionBBT) { + SPA_FLAG_SET(d->valid, SPA_IO_POSITION_VALID_BAR); + + if (s->valid & JackBBTFrameOffset) + d->bar.offset = s->bbt_offset; + else + d->bar.offset = 0; + d->bar.signature_num = s->beats_per_bar; + d->bar.signature_denom = s->beat_type; + d->bar.bpm = s->beats_per_minute; + d->bar.beat = (s->bar - 1) * s->beats_per_bar + (s->beat - 1); + } +} + +static inline void position_to_jack(struct spa_io_position *s, jack_position_t *d, jack_transport_state_t *state) +{ + switch (s->state) { + default: + case SPA_IO_POSITION_STATE_STOPPED: + *state = JackTransportStopped; + break; + case SPA_IO_POSITION_STATE_STARTING: + *state = JackTransportStarting; + break; + case SPA_IO_POSITION_STATE_RUNNING: + *state = JackTransportRolling; + break; + case SPA_IO_POSITION_STATE_LOOPING: + *state = JackTransportLooping; + break; + } + + d->unique_1++; + d->usecs = s->clock.nsec / SPA_NSEC_PER_USEC; + d->frame_rate = s->clock.rate.denom; + + if (s->clock.position >= s->clock_start && + (s->clock_duration == 0 || s->clock.position < s->clock_start + s->clock_duration)) + d->frame = (s->clock.position - s->clock_start) * s->rate + s->position; + else + d->frame = s->position; + + d->valid = 0; + + if (s->valid & SPA_IO_POSITION_VALID_BAR) { + double min; + long abs_tick, abs_beat; + + d->valid |= JackPositionBBT; + + d->bbt_offset = s->bar.offset; + if (s->bar.offset) + d->valid |= JackBBTFrameOffset; + + d->beats_per_bar = s->bar.signature_num; + d->beat_type = s->bar.signature_denom; + d->ticks_per_beat = 1920.0f; + d->beats_per_minute = s->bar.bpm; + + min = d->frame / ((double) d->frame_rate * 60.0); + abs_tick = min * d->beats_per_minute * d->ticks_per_beat; + abs_beat = s->bar.beat; + + d->bar = abs_beat / d->beats_per_bar; + d->beat = abs_beat - (d->bar * d->beats_per_bar) + 1; + d->tick = abs_tick - (abs_beat * d->ticks_per_beat); + d->bar_start_tick = d->bar * d->beats_per_bar * + d->ticks_per_beat; + d->bar++; + } + d->unique_2 = d->unique_1; +} + static void on_rtsocket_condition(void *data, int fd, uint32_t mask) { struct client *c = data; struct timespec ts; + jack_position_t jack_position; + jack_transport_state_t jack_state; if (mask & (SPA_IO_ERR | SPA_IO_HUP)) { pw_log_warn(NAME" %p: got error", c); @@ -683,42 +797,26 @@ on_rtsocket_condition(void *data, int fd, uint32_t mask) } if (mask & SPA_IO_IN) { - uint64_t cmd, nsec, frame; - int64_t delay; + uint64_t cmd, nsec; uint32_t buffer_size, sample_rate; struct link *l; + struct spa_io_position *pos = c->position; if (read(fd, &cmd, sizeof(cmd)) != sizeof(cmd)) pw_log_warn(NAME" %p: read failed %m", c); if (cmd > 1) pw_log_warn(NAME" %p: missed %"PRIu64" wakeups", c, cmd - 1); - if (c->position) { - struct spa_io_position *pos = c->position; - - buffer_size = pos->size; - if (pos->clock.rate.num != 0 && pos->clock.rate.denom != 0) - sample_rate = pos->clock.rate.denom / pos->clock.rate.num; - else - sample_rate = c->sample_rate; - c->rate_diff = pos->clock.rate_diff; - frame = pos->clock.position; - delay = pos->clock.delay; - nsec = pos->clock.nsec; - } - else { - buffer_size = DEFAULT_BUFFER_SIZE; - sample_rate = DEFAULT_SAMPLE_RATE; - c->rate_diff = 1.0; - frame = c->jack_position.frame + buffer_size; - delay = 0; - clock_gettime(CLOCK_MONOTONIC, &ts); - nsec = SPA_TIMESPEC_TO_NSEC(&ts); + if (pos == NULL) { + pw_log_error(NAME" %p: missing position", c); + return; } + nsec = pos->clock.nsec; c->activation->status = AWAKE; c->activation->awake_time = nsec; + buffer_size = pos->clock.duration; if (buffer_size != c->buffer_size) { pw_log_info(NAME" %p: buffersize %d", c, buffer_size); c->buffer_size = buffer_size; @@ -726,6 +824,7 @@ on_rtsocket_condition(void *data, int fd, uint32_t mask) c->bufsize_callback(c->buffer_size, c->bufsize_arg); } + sample_rate = pos->clock.rate.denom; if (sample_rate != c->sample_rate) { pw_log_info(NAME" %p: sample_rate %d", c, sample_rate); c->sample_rate = sample_rate; @@ -733,16 +832,12 @@ on_rtsocket_condition(void *data, int fd, uint32_t mask) c->srate_callback(c->sample_rate, c->srate_arg); } - c->jack_position.unique_1++; - c->jack_position.usecs = nsec / SPA_NSEC_PER_USEC; - c->jack_position.frame_rate = sample_rate; - c->jack_position.frame = frame; - c->jack_position.valid = 0; - c->jack_position.unique_2 = c->jack_position.unique_1; + position_to_jack(pos, &jack_position, &jack_state); - if (c->sync_callback) { - c->sync_callback(JackTransportRolling, - &c->jack_position, c->sync_arg); + if (c->sync_callback && c->sync_emit) { + c->sync_callback(jack_state, + &jack_position, c->sync_arg); + c->sync_emit = false; } if (c->driver_activation) { @@ -753,20 +848,24 @@ on_rtsocket_condition(void *data, int fd, uint32_t mask) c->xrun_count = a->xrun_count; } - pw_log_trace(NAME" %p: do process %"PRIu64" %d %d %d %"PRIi64" %f %p", c, + pw_log_trace(NAME" %p: do process %"PRIu64" %d %d %d %"PRIi64" %f", c, nsec, c->buffer_size, c->sample_rate, - c->jack_position.frame, delay, c->rate_diff, - c->position); + jack_position.frame, pos->clock.delay, pos->clock.rate_diff); if (c->process_callback) c->process_callback(c->buffer_size, c->process_arg); if (c->timebase_callback) { - c->timebase_callback(JackTransportRolling, + c->timebase_callback(jack_state, buffer_size, - &c->jack_position, - false, + &jack_position, + c->timebase_emit, c->timebase_arg); + + c->timebase_emit = false; + + debug_position(c, &jack_position); + jack_to_position(&jack_position, pos); } process_tee(c); @@ -1695,7 +1794,7 @@ jack_client_t * jack_client_open (const char *client_name, struct client *client; bool busy = true; struct spa_dict props; - struct spa_dict_item items[5]; + struct spa_dict_item items[6]; const struct spa_support *support; uint32_t n_support; const char *str; @@ -1802,6 +1901,7 @@ jack_client_t * jack_client_open (const char *client_name, if ((str = getenv("PIPEWIRE_LATENCY")) == NULL) str = DEFAULT_LATENCY; items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_KEY_NODE_LATENCY, str); + items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_KEY_NODE_ALWAYS_PROCESS, "1"); client->node_proxy = pw_core_proxy_create_object(client->core_proxy, "client-node", @@ -1965,6 +2065,8 @@ int jack_activate (jack_client_t *client) pw_thread_loop_lock(c->context.loop); pw_client_node_proxy_set_active(c->node_proxy, true); + c->timebase_emit = true; + res = do_sync(c); pw_thread_loop_unlock(c->context.loop); @@ -1984,6 +2086,8 @@ int jack_deactivate (jack_client_t *client) pw_thread_loop_lock(c->context.loop); pw_client_node_proxy_set_active(c->node_proxy, false); + c->timebase_emit = false; + res = do_sync(c); pw_thread_loop_unlock(c->context.loop); @@ -2010,8 +2114,7 @@ jack_native_thread_t jack_client_thread_id (jack_client_t *client) SPA_EXPORT int jack_is_realtime (jack_client_t *client) { - pw_log_warn(NAME" %p: not implemented", client); - return -ENOTSUP; + return 1; } SPA_EXPORT @@ -3041,12 +3144,16 @@ SPA_EXPORT jack_nframes_t jack_frames_since_cycle_start (const jack_client_t *client) { struct client *c = (struct client *) client; + struct spa_io_position *pos = c->position; struct timespec ts; uint64_t diff; + if (pos == NULL) + return 0; + clock_gettime(CLOCK_MONOTONIC, &ts); - diff = SPA_TIMESPEC_TO_USEC(&ts) - c->jack_position.usecs; - return (jack_nframes_t) floor(((float)c->sample_rate * diff) / 1000000000.0f); + diff = SPA_TIMESPEC_TO_NSEC(&ts) - pos->clock.nsec; + return (jack_nframes_t) floor(((float)c->sample_rate * diff) / SPA_NSEC_PER_SEC); } SPA_EXPORT @@ -3061,7 +3168,12 @@ SPA_EXPORT jack_nframes_t jack_last_frame_time (const jack_client_t *client) { struct client *c = (struct client *) client; - return c->jack_position.frame; + struct spa_io_position *pos = c->position; + + if (pos == NULL) + return 0; + + return pos->clock.position; } SPA_EXPORT @@ -3072,11 +3184,16 @@ int jack_get_cycle_times(const jack_client_t *client, float *period_usecs) { struct client *c = (struct client *) client; + struct spa_io_position *pos = c->position; + + if (pos == NULL) + return -1; + + *current_frames = pos->clock.position; + *current_usecs = pos->clock.nsec / SPA_NSEC_PER_USEC; + *period_usecs = pos->clock.duration * (float)SPA_USEC_PER_SEC / (c->sample_rate * pos->clock.rate_diff); + *next_usecs = pos->clock.next_nsec / SPA_NSEC_PER_USEC; - *current_frames = c->jack_position.frame; - *current_usecs = c->jack_position.usecs; - *period_usecs = c->buffer_size / (c->sample_rate * c->rate_diff); - *next_usecs = c->jack_position.usecs + (*period_usecs * 1000000.0f); pw_log_trace(NAME" %p: %d %"PRIu64" %"PRIu64" %f", c, *current_frames, *current_usecs, *next_usecs, *period_usecs); return 0; @@ -3086,16 +3203,28 @@ SPA_EXPORT jack_time_t jack_frames_to_time(const jack_client_t *client, jack_nframes_t frames) { struct client *c = (struct client *) client; - double df = (frames - c->jack_position.frame) * (double)SPA_USEC_PER_SEC / c->sample_rate; - return c->jack_position.usecs + (int64_t)rint(df); + struct spa_io_position *pos = c->position; + double df; + + if (pos == NULL) + return 0; + + df = (frames - pos->clock.position) * (double)SPA_NSEC_PER_SEC / c->sample_rate; + return (pos->clock.nsec + (int64_t)rint(df)) / SPA_NSEC_PER_USEC; } SPA_EXPORT jack_nframes_t jack_time_to_frames(const jack_client_t *client, jack_time_t usecs) { struct client *c = (struct client *) client; - double du = (usecs - c->jack_position.usecs) * (double)c->sample_rate / SPA_USEC_PER_SEC; - return c->jack_position.frame + (int32_t)rint(du); + struct spa_io_position *pos = c->position; + double du; + + if (pos == NULL) + return 0; + + du = (usecs - pos->clock.nsec/SPA_NSEC_PER_USEC) * (double)c->sample_rate / SPA_USEC_PER_SEC; + return pos->clock.position + (int32_t)rint(du); } SPA_EXPORT @@ -3127,8 +3256,11 @@ void jack_free(void* ptr) SPA_EXPORT int jack_release_timebase (jack_client_t *client) { - pw_log_warn(NAME" %p: not implemented", client); - return -ENOTSUP; + struct client *c = (struct client *) client; + c->timebase_callback = NULL; + c->timebase_arg = NULL; + c->timebase_emit = false; + return 0; } SPA_EXPORT @@ -3139,6 +3271,7 @@ int jack_set_sync_callback (jack_client_t *client, struct client *c = (struct client *) client; c->sync_callback = sync_callback; c->sync_arg = arg; + c->sync_emit = true; return 0; } @@ -3159,6 +3292,7 @@ int jack_set_timebase_callback (jack_client_t *client, struct client *c = (struct client *) client; c->timebase_callback = timebase_callback; c->timebase_arg = arg; + c->timebase_emit = true; return 0; } @@ -3166,8 +3300,10 @@ SPA_EXPORT int jack_transport_locate (jack_client_t *client, jack_nframes_t frame) { - pw_log_warn(NAME" %p: not implemented %d", client, frame); - return -ENOTSUP; + jack_position_t pos; + pos.frame = frame; + pos.valid = (jack_position_bits_t)0; + return jack_transport_reposition(client, &pos); } SPA_EXPORT @@ -3175,52 +3311,122 @@ jack_transport_state_t jack_transport_query (const jack_client_t *client, jack_position_t *pos) { struct client *c = (struct client *) client; - if (pos != NULL) - memcpy(pos, &c->jack_position, sizeof(jack_position_t)); - return JackTransportRolling; + struct pw_node_activation *a = c->driver_activation; + jack_transport_state_t jack_state = JackTransportStopped; + + if (a != NULL && pos != NULL) + position_to_jack(&a->position, pos, &jack_state); + else + memset(pos, 0, sizeof(jack_position_t)); + + return jack_state; } SPA_EXPORT jack_nframes_t jack_get_current_transport_frame (const jack_client_t *client) { - pw_log_warn(NAME" %p: not implemented", client); - return 0; + struct client *c = (struct client *) client; + struct pw_node_activation *a = c->driver_activation; + struct spa_io_position *pos; + uint64_t clock_position; + if (!a) + return -1; + + pos = &a->position; + + if (pos->state == SPA_IO_POSITION_STATE_RUNNING || + pos->state == SPA_IO_POSITION_STATE_LOOPING) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + uint64_t nsecs = SPA_TIMESPEC_TO_NSEC(&ts) - pos->clock.nsec; + uint64_t elapsed = (uint64_t)floor((((float) c->sample_rate) / SPA_NSEC_PER_SEC) * nsecs); + clock_position = pos->clock.position + elapsed; + } else { + clock_position = pos->clock.position; + } + return (pos->clock_start - clock_position) * pos->rate + pos->position; } SPA_EXPORT int jack_transport_reposition (jack_client_t *client, const jack_position_t *pos) { - pw_log_warn(NAME" %p: not implemented", client); - return -ENOTSUP; + struct client *c = (struct client *) client; + struct pw_node_activation *a = c->driver_activation; + uint32_t seq1, seq2; + if (!a) + return -EIO; + + if (pos->valid & ~(JackPositionBBT|JackPositionTimecode)) + return -EINVAL; + + pw_log_debug("frame:%u", pos->frame); + + do { + seq1 = SEQ_WRITE(&a->pending.seq); + a->pending.change_mask |= UPDATE_POSITION; + a->pending.position.position = pos->frame; + a->pending.position.clock_start = 0; + a->pending.position.clock_duration = 0; + a->pending.position.rate = 1.0; + seq2 = SEQ_WRITE(&a->pending.seq); + } while (!SEQ_WRITE_SUCCESS(seq1, seq2)); + + return 0; } SPA_EXPORT void jack_transport_start (jack_client_t *client) { - pw_log_warn(NAME" %p: not implemented", client); + struct client *c = (struct client *) client; + struct pw_node_activation *a = c->driver_activation; + uint32_t seq1, seq2; + + if (!a) + return; + + do { + seq1 = SEQ_WRITE(&a->pending.seq); + a->pending.change_mask |= UPDATE_STATE; + a->pending.state = SPA_IO_POSITION_STATE_STARTING; + seq2 = SEQ_WRITE(&a->pending.seq); + } while (!SEQ_WRITE_SUCCESS(seq1, seq2)); } SPA_EXPORT void jack_transport_stop (jack_client_t *client) { - pw_log_warn(NAME" %p: not implemented", client); + struct client *c = (struct client *) client; + struct pw_node_activation *a = c->driver_activation; + uint32_t seq1, seq2; + + if (!a) + return; + + do { + seq1 = SEQ_WRITE(&a->pending.seq); + a->pending.change_mask |= UPDATE_STATE; + a->pending.state = SPA_IO_POSITION_STATE_STOPPED; + seq2 = SEQ_WRITE(&a->pending.seq); + } while (!SEQ_WRITE_SUCCESS(seq1, seq2)); } SPA_EXPORT void jack_get_transport_info (jack_client_t *client, jack_transport_info_t *tinfo) { - static jack_transport_info_t dummy; - memcpy(tinfo, &dummy, sizeof(jack_transport_info_t)); - pw_log_warn(NAME" %p: not implemented", client); + pw_log_error(NAME" %p: deprecated", client); + if (tinfo) + memset(tinfo, 0, sizeof(jack_transport_info_t)); } SPA_EXPORT void jack_set_transport_info (jack_client_t *client, jack_transport_info_t *tinfo) { - pw_log_warn(NAME" %p: not implemented", client); + pw_log_error(NAME" %p: deprecated", client); + if (tinfo) + memset(tinfo, 0, sizeof(jack_transport_info_t)); } SPA_EXPORT @@ -3260,7 +3466,7 @@ SPA_EXPORT int jack_client_real_time_priority (jack_client_t * client) { pw_log_warn(NAME" %p: not implemented", client); - return -ENOTSUP; + return 20; } SPA_EXPORT @@ -3297,7 +3503,7 @@ SPA_EXPORT int jack_client_create_thread (jack_client_t* client, jack_native_thread_t *thread, int priority, - int realtime, /* boolean */ + int realtime, /* boolean */ void *(*start_routine)(void*), void *arg) { From 3daf9f85d27a8af8f73daf975d273c01be1797bd Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 27 Aug 2019 21:35:28 +0200 Subject: [PATCH 091/133] update for segments --- src/pipewire-jack.c | 123 +++++++++++++++++++++++++------------------- 1 file changed, 71 insertions(+), 52 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 2e9253804..0dd8dbf56 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -706,11 +706,13 @@ static inline void debug_position(struct client *c, jack_position_t *p) } } -static inline void jack_to_position(jack_position_t *s, struct spa_io_position *d) +static inline void jack_to_position(jack_position_t *s, struct spa_io_position *pos) { + struct spa_io_segment *d = &pos->segments[0]; + d->valid = 0; if (s->valid & JackPositionBBT) { - SPA_FLAG_SET(d->valid, SPA_IO_POSITION_VALID_BAR); + SPA_FLAG_SET(d->valid, SPA_IO_SEGMENT_VALID_BAR); if (s->valid & JackBBTFrameOffset) d->bar.offset = s->bbt_offset; @@ -723,54 +725,59 @@ static inline void jack_to_position(jack_position_t *s, struct spa_io_position * } } -static inline void position_to_jack(struct spa_io_position *s, jack_position_t *d, jack_transport_state_t *state) +static inline jack_transport_state_t position_to_jack(struct spa_io_position *s, jack_position_t *d) { + jack_transport_state_t state; + struct spa_io_segment *seg = &s->segments[0]; + switch (s->state) { default: case SPA_IO_POSITION_STATE_STOPPED: - *state = JackTransportStopped; + state = JackTransportStopped; break; case SPA_IO_POSITION_STATE_STARTING: - *state = JackTransportStarting; + state = JackTransportStarting; break; case SPA_IO_POSITION_STATE_RUNNING: - *state = JackTransportRolling; - break; - case SPA_IO_POSITION_STATE_LOOPING: - *state = JackTransportLooping; + if (seg->flags & SPA_IO_SEGMENT_FLAG_LOOPING) + state = JackTransportLooping; + else + state = JackTransportRolling; break; } + if (d == NULL) + return state; + d->unique_1++; d->usecs = s->clock.nsec / SPA_NSEC_PER_USEC; d->frame_rate = s->clock.rate.denom; - if (s->clock.position >= s->clock_start && - (s->clock_duration == 0 || s->clock.position < s->clock_start + s->clock_duration)) - d->frame = (s->clock.position - s->clock_start) * s->rate + s->position; + if (s->clock.position >= seg->clock_start && + (seg->clock_duration == 0 || s->clock.position < seg->clock_start + seg->clock_duration)) + d->frame = (s->clock.position - seg->clock_start) * seg->rate + seg->position; else - d->frame = s->position; + d->frame = seg->position; d->valid = 0; - - if (s->valid & SPA_IO_POSITION_VALID_BAR) { + if (seg->valid & SPA_IO_SEGMENT_VALID_BAR) { double min; long abs_tick, abs_beat; d->valid |= JackPositionBBT; - d->bbt_offset = s->bar.offset; - if (s->bar.offset) + d->bbt_offset = seg->bar.offset; + if (seg->bar.offset) d->valid |= JackBBTFrameOffset; - d->beats_per_bar = s->bar.signature_num; - d->beat_type = s->bar.signature_denom; + d->beats_per_bar = seg->bar.signature_num; + d->beat_type = seg->bar.signature_denom; d->ticks_per_beat = 1920.0f; - d->beats_per_minute = s->bar.bpm; + d->beats_per_minute = seg->bar.bpm; min = d->frame / ((double) d->frame_rate * 60.0); abs_tick = min * d->beats_per_minute * d->ticks_per_beat; - abs_beat = s->bar.beat; + abs_beat = seg->bar.beat; d->bar = abs_beat / d->beats_per_bar; d->beat = abs_beat - (d->bar * d->beats_per_bar) + 1; @@ -780,6 +787,7 @@ static inline void position_to_jack(struct spa_io_position *s, jack_position_t * d->bar++; } d->unique_2 = d->unique_1; + return state; } static void @@ -813,7 +821,7 @@ on_rtsocket_condition(void *data, int fd, uint32_t mask) } nsec = pos->clock.nsec; - c->activation->status = AWAKE; + c->activation->status = PW_NODE_ACTIVATION_AWAKE; c->activation->awake_time = nsec; buffer_size = pos->clock.duration; @@ -832,7 +840,7 @@ on_rtsocket_condition(void *data, int fd, uint32_t mask) c->srate_callback(c->sample_rate, c->srate_arg); } - position_to_jack(pos, &jack_position, &jack_state); + jack_state = position_to_jack(pos, &jack_position); if (c->sync_callback && c->sync_emit) { c->sync_callback(jack_state, @@ -871,7 +879,7 @@ on_rtsocket_condition(void *data, int fd, uint32_t mask) clock_gettime(CLOCK_MONOTONIC, &ts); nsec = SPA_TIMESPEC_TO_NSEC(&ts); - c->activation->status = FINISHED; + c->activation->status = PW_NODE_ACTIVATION_FINISHED; c->activation->finish_time = nsec; cmd = 1; @@ -887,7 +895,7 @@ on_rtsocket_condition(void *data, int fd, uint32_t mask) state->pending, state->required); if (pw_node_activation_state_dec(state, 1)) { - l->activation->status = TRIGGERED; + l->activation->status = PW_NODE_ACTIVATION_TRIGGERED; l->activation->signal_time = nsec; pw_log_trace(NAME" %p: signal %p %p", c, l, state); @@ -2345,8 +2353,8 @@ jack_nframes_t jack_get_buffer_size (jack_client_t *client) SPA_EXPORT int jack_engine_takeover_timebase (jack_client_t *client) { - pw_log_warn(NAME" %p: not implemented", client); - return -ENOTSUP; + pw_log_error(NAME" %p: deprecated", client); + return 0; } SPA_EXPORT @@ -3290,6 +3298,14 @@ int jack_set_timebase_callback (jack_client_t *client, void *arg) { struct client *c = (struct client *) client; + struct pw_node_activation *a = c->driver_activation; + + if (a == NULL) + return -EIO; + + + + c->timebase_callback = timebase_callback; c->timebase_arg = arg; c->timebase_emit = true; @@ -3314,9 +3330,9 @@ jack_transport_state_t jack_transport_query (const jack_client_t *client, struct pw_node_activation *a = c->driver_activation; jack_transport_state_t jack_state = JackTransportStopped; - if (a != NULL && pos != NULL) - position_to_jack(&a->position, pos, &jack_state); - else + if (a != NULL) + jack_state = position_to_jack(&a->position, pos); + else if (pos != NULL) memset(pos, 0, sizeof(jack_position_t)); return jack_state; @@ -3328,14 +3344,14 @@ jack_nframes_t jack_get_current_transport_frame (const jack_client_t *client) struct client *c = (struct client *) client; struct pw_node_activation *a = c->driver_activation; struct spa_io_position *pos; + struct spa_io_segment *seg; uint64_t clock_position; if (!a) return -1; pos = &a->position; - if (pos->state == SPA_IO_POSITION_STATE_RUNNING || - pos->state == SPA_IO_POSITION_STATE_LOOPING) { + if (pos->state == SPA_IO_POSITION_STATE_RUNNING) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); uint64_t nsecs = SPA_TIMESPEC_TO_NSEC(&ts) - pos->clock.nsec; @@ -3344,7 +3360,9 @@ jack_nframes_t jack_get_current_transport_frame (const jack_client_t *client) } else { clock_position = pos->clock.position; } - return (pos->clock_start - clock_position) * pos->rate + pos->position; + seg = &pos->segments[0]; + + return (seg->clock_start - clock_position) * seg->rate + seg->position; } SPA_EXPORT @@ -3364,33 +3382,40 @@ int jack_transport_reposition (jack_client_t *client, do { seq1 = SEQ_WRITE(&a->pending.seq); - a->pending.change_mask |= UPDATE_POSITION; - a->pending.position.position = pos->frame; - a->pending.position.clock_start = 0; - a->pending.position.clock_duration = 0; - a->pending.position.rate = 1.0; + a->pending.change_mask |= PW_NODE_ACTIVATION_UPDATE_REPOSITION; + a->pending.segment.flags = 0; + a->pending.segment.clock_start = 0; + a->pending.segment.clock_duration = 0; + a->pending.segment.position = pos->frame; + a->pending.segment.rate = 1.0; seq2 = SEQ_WRITE(&a->pending.seq); } while (!SEQ_WRITE_SUCCESS(seq1, seq2)); return 0; } +static void update_state(struct pw_node_activation *a, enum spa_io_position_state state) +{ + uint32_t seq1, seq2; + + do { + seq1 = SEQ_WRITE(&a->pending.seq); + a->pending.change_mask |= PW_NODE_ACTIVATION_UPDATE_STATE; + a->pending.state = state; + seq2 = SEQ_WRITE(&a->pending.seq); + } while (!SEQ_WRITE_SUCCESS(seq1, seq2)); +} + SPA_EXPORT void jack_transport_start (jack_client_t *client) { struct client *c = (struct client *) client; struct pw_node_activation *a = c->driver_activation; - uint32_t seq1, seq2; if (!a) return; - do { - seq1 = SEQ_WRITE(&a->pending.seq); - a->pending.change_mask |= UPDATE_STATE; - a->pending.state = SPA_IO_POSITION_STATE_STARTING; - seq2 = SEQ_WRITE(&a->pending.seq); - } while (!SEQ_WRITE_SUCCESS(seq1, seq2)); + update_state(a, SPA_IO_POSITION_STATE_STARTING); } SPA_EXPORT @@ -3398,17 +3423,11 @@ void jack_transport_stop (jack_client_t *client) { struct client *c = (struct client *) client; struct pw_node_activation *a = c->driver_activation; - uint32_t seq1, seq2; if (!a) return; - do { - seq1 = SEQ_WRITE(&a->pending.seq); - a->pending.change_mask |= UPDATE_STATE; - a->pending.state = SPA_IO_POSITION_STATE_STOPPED; - seq2 = SEQ_WRITE(&a->pending.seq); - } while (!SEQ_WRITE_SUCCESS(seq1, seq2)); + update_state(a, SPA_IO_POSITION_STATE_STOPPED); } SPA_EXPORT From 796278b207e686403638d1c850ce01fa0a0fee48 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 27 Aug 2019 21:48:25 +0200 Subject: [PATCH 092/133] jack: rename --- src/pipewire-jack.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 0dd8dbf56..b222315a1 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -753,9 +753,9 @@ static inline jack_transport_state_t position_to_jack(struct spa_io_position *s, d->usecs = s->clock.nsec / SPA_NSEC_PER_USEC; d->frame_rate = s->clock.rate.denom; - if (s->clock.position >= seg->clock_start && - (seg->clock_duration == 0 || s->clock.position < seg->clock_start + seg->clock_duration)) - d->frame = (s->clock.position - seg->clock_start) * seg->rate + seg->position; + if (s->clock.position >= seg->start && + (seg->duration == 0 || s->clock.position < seg->start + seg->duration)) + d->frame = (s->clock.position - seg->start) * seg->rate + seg->position; else d->frame = seg->position; @@ -3345,7 +3345,7 @@ jack_nframes_t jack_get_current_transport_frame (const jack_client_t *client) struct pw_node_activation *a = c->driver_activation; struct spa_io_position *pos; struct spa_io_segment *seg; - uint64_t clock_position; + uint64_t position; if (!a) return -1; @@ -3356,13 +3356,13 @@ jack_nframes_t jack_get_current_transport_frame (const jack_client_t *client) clock_gettime(CLOCK_MONOTONIC, &ts); uint64_t nsecs = SPA_TIMESPEC_TO_NSEC(&ts) - pos->clock.nsec; uint64_t elapsed = (uint64_t)floor((((float) c->sample_rate) / SPA_NSEC_PER_SEC) * nsecs); - clock_position = pos->clock.position + elapsed; + position = pos->clock.position + elapsed; } else { - clock_position = pos->clock.position; + position = pos->clock.position; } seg = &pos->segments[0]; - return (seg->clock_start - clock_position) * seg->rate + seg->position; + return (seg->start - position) * seg->rate + seg->position; } SPA_EXPORT @@ -3384,8 +3384,8 @@ int jack_transport_reposition (jack_client_t *client, seq1 = SEQ_WRITE(&a->pending.seq); a->pending.change_mask |= PW_NODE_ACTIVATION_UPDATE_REPOSITION; a->pending.segment.flags = 0; - a->pending.segment.clock_start = 0; - a->pending.segment.clock_duration = 0; + a->pending.segment.start = 0; + a->pending.segment.duration = 0; a->pending.segment.position = pos->frame; a->pending.segment.rate = 1.0; seq2 = SEQ_WRITE(&a->pending.seq); From a52ad27a169ffd6abb4090e1d77385bd879a1d43 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 28 Aug 2019 10:27:26 +0200 Subject: [PATCH 093/133] jack: update for running time --- src/pipewire-jack.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index b222315a1..9e33e7444 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -729,6 +729,7 @@ static inline jack_transport_state_t position_to_jack(struct spa_io_position *s, { jack_transport_state_t state; struct spa_io_segment *seg = &s->segments[0]; + uint64_t running; switch (s->state) { default: @@ -753,9 +754,11 @@ static inline jack_transport_state_t position_to_jack(struct spa_io_position *s, d->usecs = s->clock.nsec / SPA_NSEC_PER_USEC; d->frame_rate = s->clock.rate.denom; - if (s->clock.position >= seg->start && - (seg->duration == 0 || s->clock.position < seg->start + seg->duration)) - d->frame = (s->clock.position - seg->start) * seg->rate + seg->position; + running = s->clock.position - s->offset; + + if (running >= seg->start && + (seg->duration == 0 || running < seg->start + seg->duration)) + d->frame = (running - seg->start) * seg->rate + seg->position; else d->frame = seg->position; @@ -3345,24 +3348,22 @@ jack_nframes_t jack_get_current_transport_frame (const jack_client_t *client) struct pw_node_activation *a = c->driver_activation; struct spa_io_position *pos; struct spa_io_segment *seg; - uint64_t position; + uint64_t running; if (!a) return -1; pos = &a->position; + running = pos->clock.position - pos->offset; if (pos->state == SPA_IO_POSITION_STATE_RUNNING) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); uint64_t nsecs = SPA_TIMESPEC_TO_NSEC(&ts) - pos->clock.nsec; - uint64_t elapsed = (uint64_t)floor((((float) c->sample_rate) / SPA_NSEC_PER_SEC) * nsecs); - position = pos->clock.position + elapsed; - } else { - position = pos->clock.position; + running += (uint64_t)floor((((float) c->sample_rate) / SPA_NSEC_PER_SEC) * nsecs); } seg = &pos->segments[0]; - return (seg->start - position) * seg->rate + seg->position; + return (running - seg->start) * seg->rate + seg->position; } SPA_EXPORT From 0c2c2c72d04335a7013159ca8c7e284f4676e5df Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 28 Aug 2019 13:55:50 +0200 Subject: [PATCH 094/133] jack: add support for timebase master and sync --- src/pipewire-jack.c | 95 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 72 insertions(+), 23 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 9e33e7444..eb50d7424 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -261,10 +261,11 @@ struct client { void *xrun_arg; JackSyncCallback sync_callback; void *sync_arg; + uint32_t sync_version; unsigned int sync_emit:1; JackTimebaseCallback timebase_callback; void *timebase_arg; - unsigned int timebase_emit:1; + unsigned int timebase_new_pos:1; struct spa_io_position *position; uint32_t sample_rate; @@ -812,6 +813,7 @@ on_rtsocket_condition(void *data, int fd, uint32_t mask) uint32_t buffer_size, sample_rate; struct link *l; struct spa_io_position *pos = c->position; + struct pw_node_activation *a = c->driver_activation; if (read(fd, &cmd, sizeof(cmd)) != sizeof(cmd)) pw_log_warn(NAME" %p: read failed %m", c); @@ -845,14 +847,19 @@ on_rtsocket_condition(void *data, int fd, uint32_t mask) jack_state = position_to_jack(pos, &jack_position); - if (c->sync_callback && c->sync_emit) { - c->sync_callback(jack_state, - &jack_position, c->sync_arg); - c->sync_emit = false; - } - - if (c->driver_activation) { - struct pw_node_activation *a = c->driver_activation; + if (a) { + if (a->sync_version != c->sync_version) { + c->sync_emit = true; + c->timebase_new_pos = true; + c->sync_version = a->sync_version; + } + if (c->sync_emit) { + if (c->sync_callback && + c->sync_callback(jack_state, &jack_position, c->sync_arg)) { + ATOMIC_DEC(a->sync_pending); + } + c->sync_emit = false; + } if (c->xrun_count != a->xrun_count && c->xrun_count != 0 && c->xrun_callback) c->xrun_callback(c->xrun_arg); @@ -866,14 +873,14 @@ on_rtsocket_condition(void *data, int fd, uint32_t mask) if (c->process_callback) c->process_callback(c->buffer_size, c->process_arg); - if (c->timebase_callback) { + if (c->timebase_callback && a->segment_master[1] == c->node_id) { c->timebase_callback(jack_state, buffer_size, &jack_position, - c->timebase_emit, + c->timebase_new_pos, c->timebase_arg); - c->timebase_emit = false; + c->timebase_new_pos = false; debug_position(c, &jack_position); jack_to_position(&jack_position, pos); @@ -2076,7 +2083,8 @@ int jack_activate (jack_client_t *client) pw_thread_loop_lock(c->context.loop); pw_client_node_proxy_set_active(c->node_proxy, true); - c->timebase_emit = true; + c->timebase_new_pos = true; + c->sync_emit = true; res = do_sync(c); @@ -2097,7 +2105,8 @@ int jack_deactivate (jack_client_t *client) pw_thread_loop_lock(c->context.loop); pw_client_node_proxy_set_active(c->node_proxy, false); - c->timebase_emit = false; + c->timebase_new_pos = false; + c->sync_emit = false; res = do_sync(c); @@ -3268,9 +3277,20 @@ SPA_EXPORT int jack_release_timebase (jack_client_t *client) { struct client *c = (struct client *) client; + struct pw_node_activation *a = c->driver_activation; + + if (a == NULL) + return -EIO; + + if (!ATOMIC_CAS(a->segment_master[1], c->node_id, 0)) + return -EINVAL; + + SPA_FLAG_UNSET(a->position.segments[0].valid, SPA_IO_SEGMENT_VALID_BAR); + c->timebase_callback = NULL; c->timebase_arg = NULL; - c->timebase_emit = false; + c->timebase_new_pos = false; + return 0; } @@ -3280,9 +3300,21 @@ int jack_set_sync_callback (jack_client_t *client, void *arg) { struct client *c = (struct client *) client; + struct pw_node_activation *a = c->driver_activation; + + if (a == NULL) + return -EIO; + + if (sync_callback && c->sync_callback == NULL) { + ATOMIC_INC(a->sync_total); + } else if (sync_callback == NULL && c->sync_callback) { + ATOMIC_DEC(a->sync_total); + } + c->sync_callback = sync_callback; c->sync_arg = arg; c->sync_emit = true; + return 0; } @@ -3290,8 +3322,15 @@ SPA_EXPORT int jack_set_sync_timeout (jack_client_t *client, jack_time_t timeout) { - pw_log_warn(NAME" %p: not implemented %lu", client, timeout); - return -ENOTSUP; + struct client *c = (struct client *) client; + struct pw_node_activation *a = c->driver_activation; + + if (a == NULL) + return -EIO; + + ATOMIC_STORE(a->sync_timeout, timeout); + + return 0; } SPA_EXPORT @@ -3306,12 +3345,22 @@ int jack_set_timebase_callback (jack_client_t *client, if (a == NULL) return -EIO; + /* was ok */ + if (ATOMIC_LOAD(a->segment_master[1]) == c->node_id) + return 0; - + /* try to become master */ + if (conditional) { + if (!ATOMIC_CAS(a->segment_master[1], 0, c->node_id)) + return -EBUSY; + } else { + ATOMIC_STORE(a->segment_master[1], c->node_id); + } c->timebase_callback = timebase_callback; c->timebase_arg = arg; - c->timebase_emit = true; + c->timebase_new_pos = true; + return 0; } @@ -3382,14 +3431,14 @@ int jack_transport_reposition (jack_client_t *client, pw_log_debug("frame:%u", pos->frame); do { - seq1 = SEQ_WRITE(&a->pending.seq); + seq1 = SEQ_WRITE(a->pending.seq); a->pending.change_mask |= PW_NODE_ACTIVATION_UPDATE_REPOSITION; a->pending.segment.flags = 0; a->pending.segment.start = 0; a->pending.segment.duration = 0; a->pending.segment.position = pos->frame; a->pending.segment.rate = 1.0; - seq2 = SEQ_WRITE(&a->pending.seq); + seq2 = SEQ_WRITE(a->pending.seq); } while (!SEQ_WRITE_SUCCESS(seq1, seq2)); return 0; @@ -3400,10 +3449,10 @@ static void update_state(struct pw_node_activation *a, enum spa_io_position_stat uint32_t seq1, seq2; do { - seq1 = SEQ_WRITE(&a->pending.seq); + seq1 = SEQ_WRITE(a->pending.seq); a->pending.change_mask |= PW_NODE_ACTIVATION_UPDATE_STATE; a->pending.state = state; - seq2 = SEQ_WRITE(&a->pending.seq); + seq2 = SEQ_WRITE(a->pending.seq); } while (!SEQ_WRITE_SUCCESS(seq1, seq2)); } From b4243991b3599237953611cd27ab5c3a62a426bd Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 29 Aug 2019 14:01:20 +0200 Subject: [PATCH 095/133] jack: update for sync --- src/pipewire-jack.c | 213 ++++++++++++++++++++++++++++---------------- 1 file changed, 134 insertions(+), 79 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index eb50d7424..ee7ccf0d9 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -255,17 +255,16 @@ struct client { void *portregistration_arg; JackPortConnectCallback connect_callback; void *connect_arg; + JackPortRenameCallback rename_callback; + void *rename_arg; JackGraphOrderCallback graph_callback; void *graph_arg; JackXRunCallback xrun_callback; void *xrun_arg; JackSyncCallback sync_callback; void *sync_arg; - uint32_t sync_version; - unsigned int sync_emit:1; JackTimebaseCallback timebase_callback; void *timebase_arg; - unsigned int timebase_new_pos:1; struct spa_io_position *position; uint32_t sample_rate; @@ -813,7 +812,8 @@ on_rtsocket_condition(void *data, int fd, uint32_t mask) uint32_t buffer_size, sample_rate; struct link *l; struct spa_io_position *pos = c->position; - struct pw_node_activation *a = c->driver_activation; + struct pw_node_activation *activation = c->activation; + struct pw_node_activation *driver = c->driver_activation; if (read(fd, &cmd, sizeof(cmd)) != sizeof(cmd)) pw_log_warn(NAME" %p: read failed %m", c); @@ -826,8 +826,8 @@ on_rtsocket_condition(void *data, int fd, uint32_t mask) } nsec = pos->clock.nsec; - c->activation->status = PW_NODE_ACTIVATION_AWAKE; - c->activation->awake_time = nsec; + activation->status = PW_NODE_ACTIVATION_AWAKE; + activation->awake_time = nsec; buffer_size = pos->clock.duration; if (buffer_size != c->buffer_size) { @@ -847,23 +847,16 @@ on_rtsocket_condition(void *data, int fd, uint32_t mask) jack_state = position_to_jack(pos, &jack_position); - if (a) { - if (a->sync_version != c->sync_version) { - c->sync_emit = true; - c->timebase_new_pos = true; - c->sync_version = a->sync_version; + if (driver) { + if (activation->pending_sync) { + if (c->sync_callback == NULL || + c->sync_callback(jack_state, &jack_position, c->sync_arg)) + activation->pending_sync = false; } - if (c->sync_emit) { - if (c->sync_callback && - c->sync_callback(jack_state, &jack_position, c->sync_arg)) { - ATOMIC_DEC(a->sync_pending); - } - c->sync_emit = false; - } - if (c->xrun_count != a->xrun_count && + if (c->xrun_count != driver->xrun_count && c->xrun_count != 0 && c->xrun_callback) c->xrun_callback(c->xrun_arg); - c->xrun_count = a->xrun_count; + c->xrun_count = driver->xrun_count; } pw_log_trace(NAME" %p: do process %"PRIu64" %d %d %d %"PRIi64" %f", c, @@ -873,24 +866,28 @@ on_rtsocket_condition(void *data, int fd, uint32_t mask) if (c->process_callback) c->process_callback(c->buffer_size, c->process_arg); - if (c->timebase_callback && a->segment_master[1] == c->node_id) { - c->timebase_callback(jack_state, - buffer_size, - &jack_position, - c->timebase_new_pos, - c->timebase_arg); + if (c->timebase_callback && driver && driver->segment_master[1] == c->node_id) { + if (activation->pending_new_pos || + jack_state == JackTransportRolling || + jack_state == JackTransportLooping) { + c->timebase_callback(jack_state, + buffer_size, + &jack_position, + activation->pending_new_pos, + c->timebase_arg); - c->timebase_new_pos = false; + activation->pending_new_pos = false; - debug_position(c, &jack_position); - jack_to_position(&jack_position, pos); + debug_position(c, &jack_position); + jack_to_position(&jack_position, pos); + } } process_tee(c); clock_gettime(CLOCK_MONOTONIC, &ts); nsec = SPA_TIMESPEC_TO_NSEC(&ts); - c->activation->status = PW_NODE_ACTIVATION_FINISHED; - c->activation->finish_time = nsec; + activation->status = PW_NODE_ACTIVATION_FINISHED; + activation->finish_time = nsec; cmd = 1; pw_array_for_each(l, &c->links) { @@ -985,7 +982,6 @@ static int client_node_set_param(void *object, static int update_driver_activation(struct client *c) { struct link *link; - pw_log_debug(NAME" %p: driver %d", c, c->driver_id); link = find_activation(&c->links, c->driver_id); @@ -2078,44 +2074,51 @@ SPA_EXPORT int jack_activate (jack_client_t *client) { struct client *c = (struct client *) client; - int res = 0; + int res; + + if (c->active) + return 0; pw_thread_loop_lock(c->context.loop); pw_client_node_proxy_set_active(c->node_proxy, true); - c->timebase_new_pos = true; - c->sync_emit = true; - res = do_sync(c); pw_thread_loop_unlock(c->context.loop); - if (res > 0) - c->active = true; + if (res < 0) + return res; - return res; + c->activation->pending_new_pos = true; + c->activation->pending_sync = true; + c->active = true; + return 0; } SPA_EXPORT int jack_deactivate (jack_client_t *client) { struct client *c = (struct client *) client; - int res = 0; + int res; + + if (!c->active) + return 0; pw_thread_loop_lock(c->context.loop); pw_client_node_proxy_set_active(c->node_proxy, false); - c->timebase_new_pos = false; - c->sync_emit = false; + c->activation->pending_new_pos = false; + c->activation->pending_sync = false; res = do_sync(c); pw_thread_loop_unlock(c->context.loop); - if (res > 0) - c->active = false; + if (res < 0) + return res; - return res; + c->active = false; + return 0; } SPA_EXPORT @@ -2191,8 +2194,13 @@ void jack_on_shutdown (jack_client_t *client, JackShutdownCallback shutdown_callback, void *arg) { struct client *c = (struct client *) client; - c->shutdown_callback = shutdown_callback; - c->shutdown_arg = arg; + + if (c->active) { + pw_log_error(NAME" %p: can't set callback on active client", c); + } else { + c->shutdown_callback = shutdown_callback; + c->shutdown_arg = arg; + } } SPA_EXPORT @@ -2200,8 +2208,13 @@ void jack_on_info_shutdown (jack_client_t *client, JackInfoShutdownCallback shutdown_callback, void *arg) { struct client *c = (struct client *) client; - c->info_shutdown_callback = shutdown_callback; - c->info_shutdown_arg = arg; + + if (c->active) { + pw_log_error(NAME" %p: can't set callback on active client", c); + } else { + c->info_shutdown_callback = shutdown_callback; + c->info_shutdown_arg = arg; + } } SPA_EXPORT @@ -2231,6 +2244,10 @@ int jack_set_freewheel_callback (jack_client_t *client, void *arg) { struct client *c = (struct client *) client; + if (c->active) { + pw_log_error(NAME" %p: can't set callback on active client", c); + return -EIO; + } c->freewheel_callback = freewheel_callback; c->freewheel_arg = arg; return 0; @@ -2242,6 +2259,10 @@ int jack_set_buffer_size_callback (jack_client_t *client, void *arg) { struct client *c = (struct client *) client; + if (c->active) { + pw_log_error(NAME" %p: can't set callback on active client", c); + return -EIO; + } c->bufsize_callback = bufsize_callback; c->bufsize_arg = arg; return 0; @@ -2253,6 +2274,10 @@ int jack_set_sample_rate_callback (jack_client_t *client, void *arg) { struct client *c = (struct client *) client; + if (c->active) { + pw_log_error(NAME" %p: can't set callback on active client", c); + return -EIO; + } c->srate_callback = srate_callback; c->srate_arg = arg; return 0; @@ -2264,6 +2289,10 @@ int jack_set_client_registration_callback (jack_client_t *client, registration_callback, void *arg) { struct client *c = (struct client *) client; + if (c->active) { + pw_log_error(NAME" %p: can't set callback on active client", c); + return -EIO; + } c->registration_callback = registration_callback; c->registration_arg = arg; return 0; @@ -2275,6 +2304,10 @@ int jack_set_port_registration_callback (jack_client_t *client, registration_callback, void *arg) { struct client *c = (struct client *) client; + if (c->active) { + pw_log_error(NAME" %p: can't set callback on active client", c); + return -EIO; + } c->portregistration_callback = registration_callback; c->portregistration_arg = arg; return 0; @@ -2287,6 +2320,10 @@ int jack_set_port_connect_callback (jack_client_t *client, connect_callback, void *arg) { struct client *c = (struct client *) client; + if (c->active) { + pw_log_error(NAME" %p: can't set callback on active client", c); + return -EIO; + } c->connect_callback = connect_callback; c->connect_arg = arg; return 0; @@ -2294,9 +2331,16 @@ int jack_set_port_connect_callback (jack_client_t *client, SPA_EXPORT int jack_set_port_rename_callback (jack_client_t *client, - JackPortRenameCallback - rename_callback, void *arg) + JackPortRenameCallback rename_callback, + void *arg) { + struct client *c = (struct client *) client; + if (c->active) { + pw_log_error(NAME" %p: can't set callback on active client", c); + return -EIO; + } + c->rename_callback = rename_callback; + c->rename_arg = arg; return 0; } @@ -2306,6 +2350,10 @@ int jack_set_graph_order_callback (jack_client_t *client, void *data) { struct client *c = (struct client *) client; + if (c->active) { + pw_log_error(NAME" %p: can't set callback on active client", c); + return -1; + } c->graph_callback = graph_callback; c->graph_arg = data; return 0; @@ -2316,6 +2364,10 @@ int jack_set_xrun_callback (jack_client_t *client, JackXRunCallback xrun_callback, void *arg) { struct client *c = (struct client *) client; + if (c->active) { + pw_log_error(NAME" %p: can't set callback on active client", c); + return -1; + } c->xrun_callback = xrun_callback; c->xrun_arg = arg; return 0; @@ -2326,6 +2378,11 @@ int jack_set_latency_callback (jack_client_t *client, JackLatencyCallback latency_callback, void *data) { + struct client *c = (struct client *) client; + if (c->active) { + pw_log_error(NAME" %p: can't set callback on active client", c); + return -EIO; + } pw_log_warn(NAME" %p: not implemented", client); return -ENOTSUP; } @@ -3289,7 +3346,7 @@ int jack_release_timebase (jack_client_t *client) c->timebase_callback = NULL; c->timebase_arg = NULL; - c->timebase_new_pos = false; + c->activation->pending_new_pos = false; return 0; } @@ -3299,22 +3356,16 @@ int jack_set_sync_callback (jack_client_t *client, JackSyncCallback sync_callback, void *arg) { + int res; struct client *c = (struct client *) client; - struct pw_node_activation *a = c->driver_activation; - - if (a == NULL) - return -EIO; - - if (sync_callback && c->sync_callback == NULL) { - ATOMIC_INC(a->sync_total); - } else if (sync_callback == NULL && c->sync_callback) { - ATOMIC_DEC(a->sync_total); - } c->sync_callback = sync_callback; c->sync_arg = arg; - c->sync_emit = true; + if ((res = jack_activate(client)) < 0) + return res; + + c->activation->pending_sync = true; return 0; } @@ -3339,6 +3390,7 @@ int jack_set_timebase_callback (jack_client_t *client, JackTimebaseCallback timebase_callback, void *arg) { + int res; struct client *c = (struct client *) client; struct pw_node_activation *a = c->driver_activation; @@ -3359,7 +3411,11 @@ int jack_set_timebase_callback (jack_client_t *client, c->timebase_callback = timebase_callback; c->timebase_arg = arg; - c->timebase_new_pos = true; + + if ((res = jack_activate(client)) < 0) + return res; + + c->activation->pending_new_pos = true; return 0; } @@ -3444,14 +3500,18 @@ int jack_transport_reposition (jack_client_t *client, return 0; } -static void update_state(struct pw_node_activation *a, enum spa_io_position_state state) +static void update_command(struct client *c, uint32_t command) { + struct pw_node_activation *a = c->driver_activation; uint32_t seq1, seq2; + if (!a) + return; + do { seq1 = SEQ_WRITE(a->pending.seq); - a->pending.change_mask |= PW_NODE_ACTIVATION_UPDATE_STATE; - a->pending.state = state; + a->pending.change_mask |= PW_NODE_ACTIVATION_UPDATE_COMMAND; + a->pending.command = command; seq2 = SEQ_WRITE(a->pending.seq); } while (!SEQ_WRITE_SUCCESS(seq1, seq2)); } @@ -3460,24 +3520,14 @@ SPA_EXPORT void jack_transport_start (jack_client_t *client) { struct client *c = (struct client *) client; - struct pw_node_activation *a = c->driver_activation; - - if (!a) - return; - - update_state(a, SPA_IO_POSITION_STATE_STARTING); + update_command(c, PW_NODE_ACTIVATION_COMMAND_START); } SPA_EXPORT void jack_transport_stop (jack_client_t *client) { struct client *c = (struct client *) client; - struct pw_node_activation *a = c->driver_activation; - - if (!a) - return; - - update_state(a, SPA_IO_POSITION_STATE_STOPPED); + update_command(c, PW_NODE_ACTIVATION_COMMAND_STOP); } SPA_EXPORT @@ -3503,6 +3553,11 @@ int jack_set_session_callback (jack_client_t *client, JackSessionCallback session_callback, void *arg) { + struct client *c = (struct client *) client; + if (c->active) { + pw_log_error(NAME" %p: can't set callback on active client", c); + return -EIO; + } pw_log_warn(NAME" %p: not implemented", client); return -ENOTSUP; } From 52afbcf07722b7ac457290f10d2e770c4b5838d4 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 29 Aug 2019 15:32:30 +0200 Subject: [PATCH 096/133] jack: only activate scheduling Only activate scheduling when setting sync or timebase callbacks. We just want to let the server call out process function but not pretend the user called jack_activate. --- src/pipewire-jack.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index ee7ccf0d9..e7e7ee7a3 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -2070,6 +2070,19 @@ void jack_internal_client_close (const char *client_name) pw_log_warn("not implemented %s", client_name); } +static int do_activate(struct client *c) +{ + int res; + + pw_thread_loop_lock(c->context.loop); + pw_client_node_proxy_set_active(c->node_proxy, true); + + res = do_sync(c); + + pw_thread_loop_unlock(c->context.loop); + return res; +} + SPA_EXPORT int jack_activate (jack_client_t *client) { @@ -2079,14 +2092,7 @@ int jack_activate (jack_client_t *client) if (c->active) return 0; - pw_thread_loop_lock(c->context.loop); - pw_client_node_proxy_set_active(c->node_proxy, true); - - res = do_sync(c); - - pw_thread_loop_unlock(c->context.loop); - - if (res < 0) + if ((res = do_activate(c)) < 0) return res; c->activation->pending_new_pos = true; @@ -3362,7 +3368,7 @@ int jack_set_sync_callback (jack_client_t *client, c->sync_callback = sync_callback; c->sync_arg = arg; - if ((res = jack_activate(client)) < 0) + if ((res = do_activate(c)) < 0) return res; c->activation->pending_sync = true; @@ -3412,7 +3418,7 @@ int jack_set_timebase_callback (jack_client_t *client, c->timebase_callback = timebase_callback; c->timebase_arg = arg; - if ((res = jack_activate(client)) < 0) + if ((res = do_activate(c)) < 0) return res; c->activation->pending_new_pos = true; From 6dfceccae24afeaf6f4793b0a25a3e3fe96b84e4 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 29 Aug 2019 16:13:02 +0200 Subject: [PATCH 097/133] jack: fix BBT calculations Bars + beats are stored in the integral part of the bar field, ticks in the fractional part. --- src/pipewire-jack.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index e7e7ee7a3..896ee5e8d 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -721,7 +721,8 @@ static inline void jack_to_position(jack_position_t *s, struct spa_io_position * d->bar.signature_num = s->beats_per_bar; d->bar.signature_denom = s->beat_type; d->bar.bpm = s->beats_per_minute; - d->bar.beat = (s->bar - 1) * s->beats_per_bar + (s->beat - 1); + d->bar.beat = (s->bar - 1) * s->beats_per_bar + (s->beat - 1) + + (s->tick / s->ticks_per_beat); } } @@ -764,8 +765,8 @@ static inline jack_transport_state_t position_to_jack(struct spa_io_position *s, d->valid = 0; if (seg->valid & SPA_IO_SEGMENT_VALID_BAR) { - double min; - long abs_tick, abs_beat; + double abs_beat; + long beats; d->valid |= JackPositionBBT; @@ -778,16 +779,16 @@ static inline jack_transport_state_t position_to_jack(struct spa_io_position *s, d->ticks_per_beat = 1920.0f; d->beats_per_minute = seg->bar.bpm; - min = d->frame / ((double) d->frame_rate * 60.0); - abs_tick = min * d->beats_per_minute * d->ticks_per_beat; abs_beat = seg->bar.beat; d->bar = abs_beat / d->beats_per_bar; - d->beat = abs_beat - (d->bar * d->beats_per_bar) + 1; - d->tick = abs_tick - (abs_beat * d->ticks_per_beat); - d->bar_start_tick = d->bar * d->beats_per_bar * - d->ticks_per_beat; + beats = d->bar * d->beats_per_bar; + d->bar_start_tick = beats * d->ticks_per_beat; + d->beat = abs_beat - beats; + beats += d->beat; + d->tick = (abs_beat - beats) * d->ticks_per_beat; d->bar++; + d->beat++; } d->unique_2 = d->unique_1; return state; From f39d2fd78a72b8087971a4dd86a8bfadef664e93 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 29 Aug 2019 18:20:40 +0200 Subject: [PATCH 098/133] jack: update for segment owner --- src/pipewire-jack.c | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 896ee5e8d..32d90d01f 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -706,14 +706,13 @@ static inline void debug_position(struct client *c, jack_position_t *p) } } -static inline void jack_to_position(jack_position_t *s, struct spa_io_position *pos) +static inline void jack_to_position(jack_position_t *s, struct pw_node_activation *a) { - struct spa_io_segment *d = &pos->segments[0]; + struct spa_io_segment *d = &a->pending.segment; - d->valid = 0; if (s->valid & JackPositionBBT) { - SPA_FLAG_SET(d->valid, SPA_IO_SEGMENT_VALID_BAR); - + SEQ_WRITE(a->pending.seq); + d->bar.flags = SPA_IO_SEGMENT_BAR_FLAG_VALID; if (s->valid & JackBBTFrameOffset) d->bar.offset = s->bbt_offset; else @@ -723,6 +722,7 @@ static inline void jack_to_position(jack_position_t *s, struct spa_io_position * d->bar.bpm = s->beats_per_minute; d->bar.beat = (s->bar - 1) * s->beats_per_bar + (s->beat - 1) + (s->tick / s->ticks_per_beat); + SEQ_WRITE(a->pending.seq); } } @@ -764,7 +764,7 @@ static inline jack_transport_state_t position_to_jack(struct spa_io_position *s, d->frame = seg->position; d->valid = 0; - if (seg->valid & SPA_IO_SEGMENT_VALID_BAR) { + if (seg->bar.owner && SPA_FLAG_CHECK(seg->bar.flags, SPA_IO_SEGMENT_BAR_FLAG_VALID)) { double abs_beat; long beats; @@ -867,7 +867,7 @@ on_rtsocket_condition(void *data, int fd, uint32_t mask) if (c->process_callback) c->process_callback(c->buffer_size, c->process_arg); - if (c->timebase_callback && driver && driver->segment_master[1] == c->node_id) { + if (c->timebase_callback && driver && driver->pending.segment.bar.owner == c->node_id) { if (activation->pending_new_pos || jack_state == JackTransportRolling || jack_state == JackTransportLooping) { @@ -880,7 +880,7 @@ on_rtsocket_condition(void *data, int fd, uint32_t mask) activation->pending_new_pos = false; debug_position(c, &jack_position); - jack_to_position(&jack_position, pos); + jack_to_position(&jack_position, driver); } } process_tee(c); @@ -3346,11 +3346,9 @@ int jack_release_timebase (jack_client_t *client) if (a == NULL) return -EIO; - if (!ATOMIC_CAS(a->segment_master[1], c->node_id, 0)) + if (!ATOMIC_CAS(a->pending.segment.bar.owner, c->node_id, 0)) return -EINVAL; - SPA_FLAG_UNSET(a->position.segments[0].valid, SPA_IO_SEGMENT_VALID_BAR); - c->timebase_callback = NULL; c->timebase_arg = NULL; c->activation->pending_new_pos = false; @@ -3405,15 +3403,15 @@ int jack_set_timebase_callback (jack_client_t *client, return -EIO; /* was ok */ - if (ATOMIC_LOAD(a->segment_master[1]) == c->node_id) + if (ATOMIC_LOAD(a->pending.segment.bar.owner) == c->node_id) return 0; /* try to become master */ if (conditional) { - if (!ATOMIC_CAS(a->segment_master[1], 0, c->node_id)) + if (!ATOMIC_CAS(a->pending.segment.bar.owner, 0, c->node_id)) return -EBUSY; } else { - ATOMIC_STORE(a->segment_master[1], c->node_id); + ATOMIC_STORE(a->pending.segment.bar.owner, c->node_id); } c->timebase_callback = timebase_callback; @@ -3496,11 +3494,11 @@ int jack_transport_reposition (jack_client_t *client, do { seq1 = SEQ_WRITE(a->pending.seq); a->pending.change_mask |= PW_NODE_ACTIVATION_UPDATE_REPOSITION; - a->pending.segment.flags = 0; - a->pending.segment.start = 0; - a->pending.segment.duration = 0; - a->pending.segment.position = pos->frame; - a->pending.segment.rate = 1.0; + a->pending.reposition.flags = 0; + a->pending.reposition.start = 0; + a->pending.reposition.duration = 0; + a->pending.reposition.position = pos->frame; + a->pending.reposition.rate = 1.0; seq2 = SEQ_WRITE(a->pending.seq); } while (!SEQ_WRITE_SUCCESS(seq1, seq2)); From 31cfd434daa566b6f9969fd4090ab88c5e8899d9 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 30 Aug 2019 15:30:11 +0200 Subject: [PATCH 099/133] jack: clear latency range To avoid invalid meaningless values. --- src/pipewire-jack.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 32d90d01f..62bc744b9 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -3079,6 +3079,8 @@ SPA_EXPORT void jack_port_get_latency_range (jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range) { pw_log_warn("not implemented %p %d", port, mode); + range->min = 0; + range->max = 0; } SPA_EXPORT From 58c68f3260abcef9e9782c72963a08c9a7b7a7df Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 30 Aug 2019 15:41:25 +0200 Subject: [PATCH 100/133] jack: only call shutdown_callback on errors --- src/pipewire-jack.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 62bc744b9..bfd2cfb31 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -231,8 +231,6 @@ struct client { uint32_t node_id; struct spa_source *socket_source; - bool active; - JackThreadCallback thread_callback; void *thread_arg; JackThreadInitCallback thread_init_callback; @@ -287,8 +285,8 @@ struct client { uint32_t xrun_count; unsigned int started:1; - - + unsigned int active:1; + unsigned int destroyed:1; }; static void init_port_pool(struct client *c, enum spa_direction direction) @@ -519,7 +517,9 @@ static void on_state_changed(void *data, enum pw_remote_state old, client->error = true; /* fallthrough*/ case PW_REMOTE_STATE_UNCONNECTED: - if (client->shutdown_callback) + /* don't call shutdown when we do client_close, only + * on unexpected errors */ + if (client->shutdown_callback && !client->destroyed) client->shutdown_callback(client->shutdown_arg); /* fallthrough*/ case PW_REMOTE_STATE_CONNECTED: @@ -1990,9 +1990,10 @@ int jack_client_close (jack_client_t *client) pw_thread_loop_stop(c->context.loop); + c->destroyed = true; pw_core_destroy(c->context.core); - pw_thread_loop_destroy(c->context.loop); - pw_main_loop_destroy(c->context.main); + pw_thread_loop_destroy(c->context.loop); + pw_main_loop_destroy(c->context.main); pw_log_debug(NAME" %p: free", client); free(c); From 926b500ceb030348a97adf33f391075cafaf6616 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 30 Aug 2019 16:59:10 +0200 Subject: [PATCH 101/133] jack: update buffer size property Add support for set_buffer_size by updating the node property. --- src/pipewire-jack.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index bfd2cfb31..0d1c071e1 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -2405,8 +2405,25 @@ int jack_set_freewheel(jack_client_t* client, int onoff) SPA_EXPORT int jack_set_buffer_size (jack_client_t *client, jack_nframes_t nframes) { - pw_log_warn(NAME" %p: not implemented %d", client, nframes); - return -ENOTSUP; + struct client *c = (struct client *) client; + struct spa_node_info ni; + struct spa_dict_item items[1]; + char latency[128]; + + snprintf(latency, sizeof(latency), "%d/%d", nframes, jack_get_sample_rate(client)); + + ni = SPA_NODE_INFO_INIT(); + ni.max_input_ports = MAX_PORTS; + ni.max_output_ports = MAX_PORTS; + ni.change_mask = SPA_NODE_CHANGE_MASK_PROPS; + items[0] = SPA_DICT_ITEM_INIT(PW_KEY_NODE_LATENCY, latency); + ni.props = &SPA_DICT_INIT_ARRAY(items); + + pw_client_node_proxy_update(c->node_proxy, + PW_CLIENT_NODE_UPDATE_INFO, + 0, NULL, &ni); + + return 0; } SPA_EXPORT From fc0b15d17b9ce7c9d5241fa4f92d989c46129f76 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 2 Sep 2019 12:02:34 +0200 Subject: [PATCH 102/133] jack: fix for transport changes Fix for the new transport. Refactor some of processing code to match jack_cycle_wait and jack_cycle_signal --- src/pipewire-jack.c | 323 ++++++++++++++++++++++++-------------------- 1 file changed, 175 insertions(+), 148 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 0d1c071e1..c59396d66 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -287,6 +287,9 @@ struct client { unsigned int started:1; unsigned int active:1; unsigned int destroyed:1; + + jack_position_t jack_position; + jack_transport_state_t jack_state; }; static void init_port_pool(struct client *c, enum spa_direction direction) @@ -497,15 +500,6 @@ jack_get_version_string(void) return "0.0.0.0"; } -static void on_sync_reply(void *data, uint32_t id, int seq) -{ - struct client *client = data; - if (id != 0) - return; - client->last_sync = seq; - pw_thread_loop_signal(client->context.loop, false); -} - static void on_state_changed(void *data, enum pw_remote_state old, enum pw_remote_state state, const char *error) { @@ -535,6 +529,15 @@ static const struct pw_remote_events remote_events = { .state_changed = on_state_changed, }; +static void on_sync_reply(void *data, uint32_t id, int seq) +{ + struct client *client = data; + if (id != 0) + return; + client->last_sync = seq; + pw_thread_loop_signal(client->context.loop, false); +} + static const struct pw_core_proxy_events core_events = { PW_VERSION_CORE_EVENTS, .done = on_sync_reply, @@ -708,10 +711,9 @@ static inline void debug_position(struct client *c, jack_position_t *p) static inline void jack_to_position(jack_position_t *s, struct pw_node_activation *a) { - struct spa_io_segment *d = &a->pending.segment; + struct spa_io_segment *d = &a->segment; if (s->valid & JackPositionBBT) { - SEQ_WRITE(a->pending.seq); d->bar.flags = SPA_IO_SEGMENT_BAR_FLAG_VALID; if (s->valid & JackBBTFrameOffset) d->bar.offset = s->bbt_offset; @@ -722,12 +724,12 @@ static inline void jack_to_position(jack_position_t *s, struct pw_node_activatio d->bar.bpm = s->beats_per_minute; d->bar.beat = (s->bar - 1) * s->beats_per_bar + (s->beat - 1) + (s->tick / s->ticks_per_beat); - SEQ_WRITE(a->pending.seq); } } -static inline jack_transport_state_t position_to_jack(struct spa_io_position *s, jack_position_t *d) +static inline jack_transport_state_t position_to_jack(struct pw_node_activation *a, jack_position_t *d) { + struct spa_io_position *s = &a->position; jack_transport_state_t state; struct spa_io_segment *seg = &s->segments[0]; uint64_t running; @@ -764,7 +766,7 @@ static inline jack_transport_state_t position_to_jack(struct spa_io_position *s, d->frame = seg->position; d->valid = 0; - if (seg->bar.owner && SPA_FLAG_CHECK(seg->bar.flags, SPA_IO_SEGMENT_BAR_FLAG_VALID)) { + if (a->segment_owner[0] && SPA_FLAG_CHECK(seg->bar.flags, SPA_IO_SEGMENT_BAR_FLAG_VALID)) { double abs_beat; long beats; @@ -794,13 +796,145 @@ static inline jack_transport_state_t position_to_jack(struct spa_io_position *s, return state; } +static inline int wait_sync(struct client *c) +{ + uint64_t cmd, nsec; + int fd = c->socket_source->fd; + struct spa_io_position *pos = c->position; + struct pw_node_activation *activation = c->activation; + + /* this is blocking if nothing ready */ + if (read(fd, &cmd, sizeof(cmd)) != sizeof(cmd)) + pw_log_warn(NAME" %p: read failed %m", c); + if (cmd > 1) + pw_log_warn(NAME" %p: missed %"PRIu64" wakeups", c, cmd - 1); + + if (pos == NULL) { + pw_log_error(NAME" %p: missing position", c); + return -1; + } + + nsec = pos->clock.nsec; + activation->status = PW_NODE_ACTIVATION_AWAKE; + activation->awake_time = nsec; + + return 0; +} + +static inline uint32_t cycle_wait(struct client *c) +{ + uint32_t buffer_size, sample_rate; + struct spa_io_position *pos = c->position; + struct pw_node_activation *activation = c->activation; + struct pw_node_activation *driver = c->driver_activation; + + if (wait_sync(c) < 0) + return 0; + + buffer_size = pos->clock.duration; + if (buffer_size != c->buffer_size) { + pw_log_info(NAME" %p: buffersize %d", c, buffer_size); + c->buffer_size = buffer_size; + if (c->bufsize_callback) + c->bufsize_callback(c->buffer_size, c->bufsize_arg); + } + + sample_rate = pos->clock.rate.denom; + if (sample_rate != c->sample_rate) { + pw_log_info(NAME" %p: sample_rate %d", c, sample_rate); + c->sample_rate = sample_rate; + if (c->srate_callback) + c->srate_callback(c->sample_rate, c->srate_arg); + } + + c->jack_state = position_to_jack(driver, &c->jack_position); + + if (driver) { + if (activation->pending_sync) { + if (c->sync_callback == NULL || + c->sync_callback(c->jack_state, &c->jack_position, c->sync_arg)) + activation->pending_sync = false; + } + if (c->xrun_count != driver->xrun_count && + c->xrun_count != 0 && c->xrun_callback) + c->xrun_callback(c->xrun_arg); + c->xrun_count = driver->xrun_count; + } + pw_log_trace(NAME" %p: wait %"PRIu64" %d %d %d %"PRIi64" %f", c, + activation->awake_time, c->buffer_size, c->sample_rate, + c->jack_position.frame, pos->clock.delay, pos->clock.rate_diff); + + return buffer_size; +} + +static inline void signal_sync(struct client *c) +{ + struct timespec ts; + uint64_t cmd, nsec; + struct link *l; + struct pw_node_activation *activation = c->activation; + + process_tee(c); + + clock_gettime(CLOCK_MONOTONIC, &ts); + nsec = SPA_TIMESPEC_TO_NSEC(&ts); + activation->status = PW_NODE_ACTIVATION_FINISHED; + activation->finish_time = nsec; + + cmd = 1; + pw_array_for_each(l, &c->links) { + struct pw_node_activation_state *state; + + if (l->activation == NULL) + continue; + + state = &l->activation->state[0]; + + pw_log_trace(NAME" %p: link %p %p %d/%d", c, l, state, + state->pending, state->required); + + if (pw_node_activation_state_dec(state, 1)) { + l->activation->status = PW_NODE_ACTIVATION_TRIGGERED; + l->activation->signal_time = nsec; + + pw_log_trace(NAME" %p: signal %p %p", c, l, state); + + if (write(l->signalfd, &cmd, sizeof(cmd)) != sizeof(cmd)) + pw_log_warn(NAME" %p: write failed %m", c); + } + } +} + +static inline void cycle_signal(struct client *c, int status) +{ + struct pw_node_activation *driver = c->driver_activation; + struct pw_node_activation *activation = c->activation; + + if (status == 0) { + if (c->timebase_callback && driver && driver->segment_owner[0] == c->node_id) { + if (activation->pending_new_pos || + c->jack_state == JackTransportRolling || + c->jack_state == JackTransportLooping) { + c->timebase_callback(c->jack_state, + c->buffer_size, + &c->jack_position, + activation->pending_new_pos, + c->timebase_arg); + + activation->pending_new_pos = false; + + debug_position(c, &c->jack_position); + jack_to_position(&c->jack_position, activation); + } + } + } + signal_sync(c); +} + static void on_rtsocket_condition(void *data, int fd, uint32_t mask) { struct client *c = data; - struct timespec ts; - jack_position_t jack_position; - jack_transport_state_t jack_state; if (mask & (SPA_IO_ERR | SPA_IO_HUP)) { pw_log_warn(NAME" %p: got error", c); @@ -809,109 +943,14 @@ on_rtsocket_condition(void *data, int fd, uint32_t mask) } if (mask & SPA_IO_IN) { - uint64_t cmd, nsec; - uint32_t buffer_size, sample_rate; - struct link *l; - struct spa_io_position *pos = c->position; - struct pw_node_activation *activation = c->activation; - struct pw_node_activation *driver = c->driver_activation; + uint32_t buffer_size; + int status; - if (read(fd, &cmd, sizeof(cmd)) != sizeof(cmd)) - pw_log_warn(NAME" %p: read failed %m", c); - if (cmd > 1) - pw_log_warn(NAME" %p: missed %"PRIu64" wakeups", c, cmd - 1); + buffer_size = cycle_wait(c); - if (pos == NULL) { - pw_log_error(NAME" %p: missing position", c); - return; - } + status = c->process_callback ? c->process_callback(buffer_size, c->process_arg) : 0; - nsec = pos->clock.nsec; - activation->status = PW_NODE_ACTIVATION_AWAKE; - activation->awake_time = nsec; - - buffer_size = pos->clock.duration; - if (buffer_size != c->buffer_size) { - pw_log_info(NAME" %p: buffersize %d", c, buffer_size); - c->buffer_size = buffer_size; - if (c->bufsize_callback) - c->bufsize_callback(c->buffer_size, c->bufsize_arg); - } - - sample_rate = pos->clock.rate.denom; - if (sample_rate != c->sample_rate) { - pw_log_info(NAME" %p: sample_rate %d", c, sample_rate); - c->sample_rate = sample_rate; - if (c->srate_callback) - c->srate_callback(c->sample_rate, c->srate_arg); - } - - jack_state = position_to_jack(pos, &jack_position); - - if (driver) { - if (activation->pending_sync) { - if (c->sync_callback == NULL || - c->sync_callback(jack_state, &jack_position, c->sync_arg)) - activation->pending_sync = false; - } - if (c->xrun_count != driver->xrun_count && - c->xrun_count != 0 && c->xrun_callback) - c->xrun_callback(c->xrun_arg); - c->xrun_count = driver->xrun_count; - } - - pw_log_trace(NAME" %p: do process %"PRIu64" %d %d %d %"PRIi64" %f", c, - nsec, c->buffer_size, c->sample_rate, - jack_position.frame, pos->clock.delay, pos->clock.rate_diff); - - if (c->process_callback) - c->process_callback(c->buffer_size, c->process_arg); - - if (c->timebase_callback && driver && driver->pending.segment.bar.owner == c->node_id) { - if (activation->pending_new_pos || - jack_state == JackTransportRolling || - jack_state == JackTransportLooping) { - c->timebase_callback(jack_state, - buffer_size, - &jack_position, - activation->pending_new_pos, - c->timebase_arg); - - activation->pending_new_pos = false; - - debug_position(c, &jack_position); - jack_to_position(&jack_position, driver); - } - } - process_tee(c); - - clock_gettime(CLOCK_MONOTONIC, &ts); - nsec = SPA_TIMESPEC_TO_NSEC(&ts); - activation->status = PW_NODE_ACTIVATION_FINISHED; - activation->finish_time = nsec; - - cmd = 1; - pw_array_for_each(l, &c->links) { - struct pw_node_activation_state *state; - - if (l->activation == NULL) - continue; - - state = &l->activation->state[0]; - - pw_log_trace(NAME" %p: link %p %p %d/%d", c, l, state, - state->pending, state->required); - - if (pw_node_activation_state_dec(state, 1)) { - l->activation->status = PW_NODE_ACTIVATION_TRIGGERED; - l->activation->signal_time = nsec; - - pw_log_trace(NAME" %p: signal %p %p", c, l, state); - - if (write(l->signalfd, &cmd, sizeof(cmd)) != sizeof(cmd)) - pw_log_warn(NAME" %p: write failed %m", c); - } - } + cycle_signal(c, status); } } @@ -2151,21 +2190,22 @@ int jack_is_realtime (jack_client_t *client) SPA_EXPORT jack_nframes_t jack_thread_wait (jack_client_t *client, int status) { - pw_log_warn(NAME" %p: not implemented %d", client, status); + pw_log_error(NAME" %p: jack_thread_wait: deprecated, use jack_cycle_wait/jack_cycle_signal", client); return 0; } SPA_EXPORT jack_nframes_t jack_cycle_wait (jack_client_t* client) { - pw_log_warn(NAME" %p: not implemented", client); - return 0; + struct client *c = (struct client *) client; + return cycle_wait(c); } SPA_EXPORT void jack_cycle_signal (jack_client_t* client, int status) { - pw_log_warn(NAME" %p: not implemented %d", client, status); + struct client *c = (struct client *) client; + cycle_signal(c, status); } SPA_EXPORT @@ -3366,7 +3406,7 @@ int jack_release_timebase (jack_client_t *client) if (a == NULL) return -EIO; - if (!ATOMIC_CAS(a->pending.segment.bar.owner, c->node_id, 0)) + if (!ATOMIC_CAS(a->segment_owner[0], c->node_id, 0)) return -EINVAL; c->timebase_callback = NULL; @@ -3423,15 +3463,15 @@ int jack_set_timebase_callback (jack_client_t *client, return -EIO; /* was ok */ - if (ATOMIC_LOAD(a->pending.segment.bar.owner) == c->node_id) + if (ATOMIC_LOAD(a->segment_owner[0]) == c->node_id) return 0; /* try to become master */ if (conditional) { - if (!ATOMIC_CAS(a->pending.segment.bar.owner, 0, c->node_id)) + if (!ATOMIC_CAS(a->segment_owner[0], 0, c->node_id)) return -EBUSY; } else { - ATOMIC_STORE(a->pending.segment.bar.owner, c->node_id); + ATOMIC_STORE(a->segment_owner[0], c->node_id); } c->timebase_callback = timebase_callback; @@ -3464,7 +3504,7 @@ jack_transport_state_t jack_transport_query (const jack_client_t *client, jack_transport_state_t jack_state = JackTransportStopped; if (a != NULL) - jack_state = position_to_jack(&a->position, pos); + jack_state = position_to_jack(a, pos); else if (pos != NULL) memset(pos, 0, sizeof(jack_position_t)); @@ -3502,25 +3542,20 @@ int jack_transport_reposition (jack_client_t *client, { struct client *c = (struct client *) client; struct pw_node_activation *a = c->driver_activation; - uint32_t seq1, seq2; - if (!a) + struct pw_node_activation *na = c->activation; + if (!a || !na) return -EIO; if (pos->valid & ~(JackPositionBBT|JackPositionTimecode)) return -EINVAL; pw_log_debug("frame:%u", pos->frame); - - do { - seq1 = SEQ_WRITE(a->pending.seq); - a->pending.change_mask |= PW_NODE_ACTIVATION_UPDATE_REPOSITION; - a->pending.reposition.flags = 0; - a->pending.reposition.start = 0; - a->pending.reposition.duration = 0; - a->pending.reposition.position = pos->frame; - a->pending.reposition.rate = 1.0; - seq2 = SEQ_WRITE(a->pending.seq); - } while (!SEQ_WRITE_SUCCESS(seq1, seq2)); + na->reposition.flags = 0; + na->reposition.start = 0; + na->reposition.duration = 0; + na->reposition.position = pos->frame; + na->reposition.rate = 1.0; + ATOMIC_STORE(a->reposition_owner, c->node_id); return 0; } @@ -3528,17 +3563,9 @@ int jack_transport_reposition (jack_client_t *client, static void update_command(struct client *c, uint32_t command) { struct pw_node_activation *a = c->driver_activation; - uint32_t seq1, seq2; - if (!a) return; - - do { - seq1 = SEQ_WRITE(a->pending.seq); - a->pending.change_mask |= PW_NODE_ACTIVATION_UPDATE_COMMAND; - a->pending.command = command; - seq2 = SEQ_WRITE(a->pending.seq); - } while (!SEQ_WRITE_SUCCESS(seq1, seq2)); + ATOMIC_STORE(a->command, command); } SPA_EXPORT From edaeac6975d4ed1e0ef3e2b4d0c6eee97ec8d7d4 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 2 Sep 2019 17:16:57 +0200 Subject: [PATCH 103/133] jack: improve some latency values --- src/pipewire-jack.c | 58 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index c59396d66..b2f671748 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -107,6 +107,8 @@ struct object { uint32_t type_id; uint32_t node_id; uint32_t port_id; + jack_latency_range_t capture_latency; + jack_latency_range_t playback_latency; } port; }; }; @@ -259,6 +261,8 @@ struct client { void *graph_arg; JackXRunCallback xrun_callback; void *xrun_arg; + JackLatencyCallback latency_callback; + void *latency_arg; JackSyncCallback sync_callback; void *sync_arg; JackTimebaseCallback timebase_callback; @@ -1745,6 +1749,14 @@ static void registry_event_global(void *data, uint32_t id, o->port.type_id = type_id; o->port.node_id = node_id; + if (o->port.flags & JackPortIsOutput) { + o->port.capture_latency.min = 1024; + o->port.capture_latency.max = 1024; + } else { + o->port.playback_latency.min = 1024; + o->port.playback_latency.max = 1024; + } + pw_log_debug(NAME" %p: add port %d %s %d", c, id, o->port.name, type_id); break; } @@ -2431,8 +2443,10 @@ int jack_set_latency_callback (jack_client_t *client, pw_log_error(NAME" %p: can't set callback on active client", c); return -EIO; } - pw_log_warn(NAME" %p: not implemented", client); - return -ENOTSUP; + pw_log_debug(NAME" %p: %p %p", c, latency_callback, data); + c->latency_callback = latency_callback; + c->latency_arg = data; + return 0; } SPA_EXPORT @@ -3130,35 +3144,57 @@ size_t jack_port_type_get_buffer_size (jack_client_t *client, const char *port_t SPA_EXPORT void jack_port_set_latency (jack_port_t *port, jack_nframes_t frames) { - pw_log_warn("not implemented %p %d", port, frames); + struct object *o = (struct object *) port; + jack_latency_range_t range = { frames, frames }; + if (o->port.flags & JackPortIsOutput) { + jack_port_set_latency_range(port, JackCaptureLatency, &range); + } + if (o->port.flags & JackPortIsInput) { + jack_port_set_latency_range(port, JackPlaybackLatency, &range); + } } SPA_EXPORT void jack_port_get_latency_range (jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range) { - pw_log_warn("not implemented %p %d", port, mode); - range->min = 0; - range->max = 0; + struct object *o = (struct object *) port; + if (mode == JackCaptureLatency) { + *range = o->port.capture_latency; + } else { + *range = o->port.playback_latency; + } } SPA_EXPORT void jack_port_set_latency_range (jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range) { - pw_log_warn("not implemented %p %d", port, mode); + struct object *o = (struct object *) port; + if (mode == JackCaptureLatency) { + o->port.capture_latency = *range; + } else { + o->port.playback_latency = *range; + } } SPA_EXPORT int jack_recompute_total_latencies (jack_client_t *client) { pw_log_warn(NAME" %p: not implemented", client); - return -ENOTSUP; + return 0; } SPA_EXPORT jack_nframes_t jack_port_get_latency (jack_port_t *port) { - pw_log_warn("not implemented %p", port); - return 0; + struct object *o = (struct object *) port; + jack_latency_range_t range; + if (o->port.flags & JackPortIsOutput) { + jack_port_get_latency_range(port, JackCaptureLatency, &range); + } + if (o->port.flags & JackPortIsInput) { + jack_port_get_latency_range(port, JackPlaybackLatency, &range); + } + return (range.min + range.max) / 2; } SPA_EXPORT @@ -3173,7 +3209,7 @@ SPA_EXPORT int jack_recompute_total_latency (jack_client_t *client, jack_port_t* port) { pw_log_warn(NAME" %p: not implemented %p", client, port); - return -ENOTSUP; + return 0; } SPA_EXPORT From 47b7d73b2a5ecba2b8b2b1f0cf96196dea2b89fe Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 2 Sep 2019 17:18:02 +0200 Subject: [PATCH 104/133] jack: improve debug --- src/pipewire-jack.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index b2f671748..a91761e2e 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -2001,6 +2001,7 @@ jack_client_t * jack_client_open (const char *client_name, if (status) *status = 0; + pw_log_trace(NAME" %p: new", client); return (jack_client_t *)client; init_failed: @@ -2055,6 +2056,7 @@ int jack_client_close (jack_client_t *client) SPA_EXPORT int jack_client_name_size (void) { + pw_log_trace("%d", JACK_CLIENT_NAME_SIZE); return JACK_CLIENT_NAME_SIZE; } @@ -2062,6 +2064,7 @@ SPA_EXPORT char * jack_get_client_name (jack_client_t *client) { struct client *c = (struct client *) client; + pw_log_trace(NAME" %p: %s", c, c->name); return c->name; } @@ -2128,6 +2131,7 @@ static int do_activate(struct client *c) int res; pw_thread_loop_lock(c->context.loop); + pw_log_debug(NAME" %p: activate", c); pw_client_node_proxy_set_active(c->node_proxy, true); res = do_sync(c); @@ -2151,6 +2155,7 @@ int jack_activate (jack_client_t *client) c->activation->pending_new_pos = true; c->activation->pending_sync = true; c->active = true; + return 0; } @@ -2164,6 +2169,7 @@ int jack_deactivate (jack_client_t *client) return 0; pw_thread_loop_lock(c->context.loop); + pw_log_debug(NAME" %p: deactivate", c); pw_client_node_proxy_set_active(c->node_proxy, false); c->activation->pending_new_pos = false; @@ -2210,13 +2216,17 @@ SPA_EXPORT jack_nframes_t jack_cycle_wait (jack_client_t* client) { struct client *c = (struct client *) client; - return cycle_wait(c); + jack_nframes_t res; + res = cycle_wait(c); + pw_log_trace(NAME" %p: result:%d", c, res); + return res; } SPA_EXPORT void jack_cycle_signal (jack_client_t* client, int status) { struct client *c = (struct client *) client; + pw_log_trace(NAME" %p: status:%d", c, status); cycle_signal(c, status); } @@ -2232,7 +2242,7 @@ int jack_set_process_thread(jack_client_t* client, JackThreadCallback thread_cal pw_log_error(NAME" %p: process callback was already set", c); return -EIO; } - + pw_log_debug(NAME" %p: %p %p", c, thread_callback, arg); c->thread_callback = thread_callback; c->thread_arg = arg; return 0; @@ -2244,6 +2254,7 @@ int jack_set_thread_init_callback (jack_client_t *client, void *arg) { struct client *c = (struct client *) client; + pw_log_debug(NAME" %p: %p %p", c, thread_init_callback, arg); c->thread_init_callback = thread_init_callback; c->thread_init_arg = arg; return 0; @@ -2258,6 +2269,7 @@ void jack_on_shutdown (jack_client_t *client, if (c->active) { pw_log_error(NAME" %p: can't set callback on active client", c); } else { + pw_log_debug(NAME" %p: %p %p", c, shutdown_callback, arg); c->shutdown_callback = shutdown_callback; c->shutdown_arg = arg; } @@ -2272,6 +2284,7 @@ void jack_on_info_shutdown (jack_client_t *client, if (c->active) { pw_log_error(NAME" %p: can't set callback on active client", c); } else { + pw_log_debug(NAME" %p: %p %p", c, shutdown_callback, arg); c->info_shutdown_callback = shutdown_callback; c->info_shutdown_arg = arg; } @@ -2308,6 +2321,7 @@ int jack_set_freewheel_callback (jack_client_t *client, pw_log_error(NAME" %p: can't set callback on active client", c); return -EIO; } + pw_log_debug(NAME" %p: %p %p", c, freewheel_callback, arg); c->freewheel_callback = freewheel_callback; c->freewheel_arg = arg; return 0; @@ -2323,6 +2337,7 @@ int jack_set_buffer_size_callback (jack_client_t *client, pw_log_error(NAME" %p: can't set callback on active client", c); return -EIO; } + pw_log_debug(NAME" %p: %p %p", c, bufsize_callback, arg); c->bufsize_callback = bufsize_callback; c->bufsize_arg = arg; return 0; @@ -2338,6 +2353,7 @@ int jack_set_sample_rate_callback (jack_client_t *client, pw_log_error(NAME" %p: can't set callback on active client", c); return -EIO; } + pw_log_debug(NAME" %p: %p %p", c, srate_callback, arg); c->srate_callback = srate_callback; c->srate_arg = arg; return 0; @@ -2353,6 +2369,7 @@ int jack_set_client_registration_callback (jack_client_t *client, pw_log_error(NAME" %p: can't set callback on active client", c); return -EIO; } + pw_log_debug(NAME" %p: %p %p", c, registration_callback, arg); c->registration_callback = registration_callback; c->registration_arg = arg; return 0; @@ -2368,6 +2385,7 @@ int jack_set_port_registration_callback (jack_client_t *client, pw_log_error(NAME" %p: can't set callback on active client", c); return -EIO; } + pw_log_debug(NAME" %p: %p %p", c, registration_callback, arg); c->portregistration_callback = registration_callback; c->portregistration_arg = arg; return 0; @@ -2384,6 +2402,7 @@ int jack_set_port_connect_callback (jack_client_t *client, pw_log_error(NAME" %p: can't set callback on active client", c); return -EIO; } + pw_log_debug(NAME" %p: %p %p", c, connect_callback, arg); c->connect_callback = connect_callback; c->connect_arg = arg; return 0; @@ -2399,6 +2418,7 @@ int jack_set_port_rename_callback (jack_client_t *client, pw_log_error(NAME" %p: can't set callback on active client", c); return -EIO; } + pw_log_debug(NAME" %p: %p %p", c, rename_callback, arg); c->rename_callback = rename_callback; c->rename_arg = arg; return 0; @@ -2414,6 +2434,7 @@ int jack_set_graph_order_callback (jack_client_t *client, pw_log_error(NAME" %p: can't set callback on active client", c); return -1; } + pw_log_trace(NAME" %p: %p %p", c, graph_callback, data); c->graph_callback = graph_callback; c->graph_arg = data; return 0; @@ -2428,6 +2449,7 @@ int jack_set_xrun_callback (jack_client_t *client, pw_log_error(NAME" %p: can't set callback on active client", c); return -1; } + pw_log_debug(NAME" %p: %p %p", c, xrun_callback, arg); c->xrun_callback = xrun_callback; c->xrun_arg = arg; return 0; From b1d2b9fb39d4b69075606a3d8c32d2d91a0053b1 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 2 Sep 2019 17:18:19 +0200 Subject: [PATCH 105/133] jack: fix buffer_size Make sure we never return -1 and properly scale the size of the buffer. --- src/pipewire-jack.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index a91761e2e..9cbb150dc 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -3154,9 +3154,8 @@ int jack_port_type_size(void) SPA_EXPORT size_t jack_port_type_get_buffer_size (jack_client_t *client, const char *port_type) { - struct client *c = (struct client *) client; if (!strcmp(JACK_DEFAULT_AUDIO_TYPE, port_type)) - return c->buffer_size; + return jack_get_buffer_size(client) * sizeof(float); else if (!strcmp(JACK_DEFAULT_MIDI_TYPE, port_type)) return BUFFER_SIZE_MAX; else From 89ce3eac355107eaf8a9b323b3db29e63f9194b3 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 3 Sep 2019 13:44:12 +0200 Subject: [PATCH 106/133] jack: implement monitor requests --- src/pipewire-jack.c | 43 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 9cbb150dc..655a5200e 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -107,6 +107,7 @@ struct object { uint32_t type_id; uint32_t node_id; uint32_t port_id; + uint32_t monitor_requests; jack_latency_range_t capture_latency; jack_latency_range_t playback_latency; } port; @@ -1495,7 +1496,6 @@ static int client_node_port_set_io(void *object, pw_memmap_free(mm); if (mem_id == SPA_ID_INVALID) { - mm = ptr = NULL; size = 0; } @@ -2996,30 +2996,55 @@ int jack_port_get_aliases (const jack_port_t *port, char* const aliases[2]) SPA_EXPORT int jack_port_request_monitor (jack_port_t *port, int onoff) { - pw_log_warn("not implemented %p %d", port, onoff); - return -ENOTSUP; + struct object *o = (struct object *) port; + if (onoff) + o->port.monitor_requests++; + else if (o->port.monitor_requests > 0) + o->port.monitor_requests--; + return 0; } SPA_EXPORT int jack_port_request_monitor_by_name (jack_client_t *client, const char *port_name, int onoff) { - pw_log_warn(NAME" %p: not implemented %s %d", client, port_name, onoff); - return -ENOTSUP; + struct client *c = (struct client *) client; + struct object *p; + + pw_thread_loop_lock(c->context.loop); + + p = find_port(c, port_name); + + pw_thread_loop_unlock(c->context.loop); + + if (p == NULL) { + pw_log_error(NAME" %p: jack_port_request_monitor_by_name called" + " with an incorrect port %s", client, port_name); + return -1; + } + + return jack_port_request_monitor((jack_port_t*)p, onoff); } SPA_EXPORT int jack_port_ensure_monitor (jack_port_t *port, int onoff) { - pw_log_warn("not implemented %p %d", port, onoff); - return -ENOTSUP; + struct object *o = (struct object *) port; + if (onoff) { + if (o->port.monitor_requests == 0) + o->port.monitor_requests++; + } else { + if (o->port.monitor_requests > 0) + o->port.monitor_requests = 0; + } + return 0; } SPA_EXPORT int jack_port_monitoring_input (jack_port_t *port) { - pw_log_warn("not implemented %p", port); - return -ENOTSUP; + struct object *o = (struct object *) port; + return o->port.monitor_requests > 0; } SPA_EXPORT From 46b2d1701b54a6032ce7f6e7b87c644ff5f43a3a Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 3 Sep 2019 13:44:47 +0200 Subject: [PATCH 107/133] jack: remove unused fields --- src/pipewire-jack.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 655a5200e..f56f55a1a 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -273,7 +273,6 @@ struct client { uint32_t sample_rate; uint32_t buffer_size; - struct spa_list mix[2]; struct mix mix_pool[MAX_MIX]; struct spa_list free_mix; @@ -352,7 +351,6 @@ static struct mix *ensure_mix(struct client *c, struct port *port, uint32_t mix_ mix = spa_list_first(&c->free_mix, struct mix, link); spa_list_remove(&mix->link); - spa_list_append(&c->mix[port->direction], &mix->link); spa_list_append(&port->mix, &mix->port_link); mix->id = mix_id; @@ -1904,8 +1902,6 @@ jack_client_t * jack_client_open (const char *client_name, client->buffer_size = (uint32_t)-1; client->sample_rate = (uint32_t)-1; - spa_list_init(&client->mix[SPA_DIRECTION_INPUT]); - spa_list_init(&client->mix[SPA_DIRECTION_OUTPUT]); spa_list_init(&client->free_mix); for (i = 0; i < MAX_MIX; i++) spa_list_append(&client->free_mix, &client->mix_pool[i].link); From a9640a826d01b18757563b82dd2cede8633a4fe2 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 3 Sep 2019 13:45:13 +0200 Subject: [PATCH 108/133] jack: call thread_init --- src/pipewire-jack.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index f56f55a1a..780a5d8ce 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -291,6 +291,7 @@ struct client { unsigned int started:1; unsigned int active:1; unsigned int destroyed:1; + unsigned int first:1; jack_position_t jack_position; jack_transport_state_t jack_state; @@ -834,6 +835,12 @@ static inline uint32_t cycle_wait(struct client *c) if (wait_sync(c) < 0) return 0; + if (c->first) { + if (c->thread_init_callback) + c->thread_init_callback(c->thread_init_arg); + c->first = false; + } + buffer_size = pos->clock.duration; if (buffer_size != c->buffer_size) { pw_log_info(NAME" %p: buffersize %d", c, buffer_size); @@ -944,6 +951,10 @@ on_rtsocket_condition(void *data, int fd, uint32_t mask) unhandle_socket(c); return; } + if (c->thread_callback) { + c->thread_callback(c->thread_arg); + return; + } if (mask & SPA_IO_IN) { uint32_t buffer_size; @@ -1102,6 +1113,7 @@ static int client_node_command(void *object, const struct spa_command *command) c->socket_source, SPA_IO_IN | SPA_IO_ERR | SPA_IO_HUP); c->started = true; + c->first = true; } break; default: From dde5a0dee8c4806d4660e4b73be84e4aef55c824 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 3 Sep 2019 13:45:33 +0200 Subject: [PATCH 109/133] jack: improve output port buffers The output buffers are always set on mix -1, so find that one and then copy the io area to all other mix outputs. --- src/pipewire-jack.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 780a5d8ce..fa3d2b9cb 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -338,7 +338,7 @@ static void free_object(struct client *c, struct object *o) spa_list_append(&c->context.free_objects, &o->link); } -static struct mix *ensure_mix(struct client *c, struct port *port, uint32_t mix_id) +static struct mix *find_mix(struct client *c, struct port *port, uint32_t mix_id) { struct mix *mix; @@ -346,6 +346,16 @@ static struct mix *ensure_mix(struct client *c, struct port *port, uint32_t mix_ if (mix->id == mix_id) return mix; } + return NULL; +} + +static struct mix *ensure_mix(struct client *c, struct port *port, uint32_t mix_id) +{ + struct mix *mix; + + if ((mix = find_mix(c, port, mix_id)) != NULL) + return mix; + if (spa_list_is_empty(&c->free_mix)) return NULL; @@ -2164,6 +2174,7 @@ int jack_activate (jack_client_t *client) c->activation->pending_sync = true; c->active = true; + return 0; } @@ -2746,14 +2757,11 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) io.status = -EPIPE; io.buffer_id = SPA_ID_INVALID; - spa_list_for_each(mix, &p->mix, port_link) { + if ((mix = find_mix(c, p, -1)) != NULL) { struct buffer *b; - pw_log_trace(NAME" %p: port %p mix %d.%d get buffer %d io:%p n_buffers:%d", - c, p, p->id, mix->id, frames, mix->io, mix->n_buffers); - - if (mix->n_buffers == 0) - continue; + pw_log_trace(NAME" %p: port %p %d get buffer %d n_buffers:%d", + c, p, p->id, frames, mix->n_buffers); if ((b = dequeue_buffer(mix)) == NULL) { pw_log_warn("port %p: out of buffers", p); @@ -2768,17 +2776,18 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) io.status = SPA_STATUS_HAVE_BUFFER; io.buffer_id = b->id; - break; } + done: spa_list_for_each(mix, &p->mix, port_link) { struct spa_io_buffers *mio = mix->io; if (mio == NULL) continue; + pw_log_trace(NAME" %p: port %p tee %d.%d get buffer %d io:%p", + c, p, p->id, mix->id, frames, mio); *mio = io; } } - done: if (ptr == NULL) { ptr = p->emptyptr; if (!p->zeroed) { From 2abcc287627568a88424782c05ff527b5817ba56 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 5 Sep 2019 13:17:00 +0200 Subject: [PATCH 110/133] jack: implement thread stop and kill --- src/pipewire-jack.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index fa3d2b9cb..6506b6bfc 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -3798,15 +3798,26 @@ int jack_drop_real_time_scheduling (jack_native_thread_t thread) SPA_EXPORT int jack_client_stop_thread(jack_client_t* client, jack_native_thread_t thread) { - pw_log_warn(NAME" %p: not implemented %lu", client, thread); - return -ENOTSUP; + void* status; + + if (thread == (jack_native_thread_t)NULL) + return -1; + + pthread_join(thread, &status); + return 0; } SPA_EXPORT int jack_client_kill_thread(jack_client_t* client, jack_native_thread_t thread) { - pw_log_warn(NAME" %p: not implemented %lu", client, thread); - return -ENOTSUP; + void* status; + + if (thread == (jack_native_thread_t)NULL) + return -1; + + pthread_cancel(thread); + pthread_join(thread, &status); + return 0; } SPA_EXPORT From 7f7ed1e0395a488fd06b7e2e2be541fd77970d90 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 9 Sep 2019 17:15:52 +0200 Subject: [PATCH 111/133] jack: implement some more functions --- src/pipewire-jack.c | 136 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 123 insertions(+), 13 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 6506b6bfc..a2d3f1704 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -2208,8 +2208,8 @@ int jack_deactivate (jack_client_t *client) SPA_EXPORT int jack_get_client_pid (const char *name) { - pw_log_warn("not implemented %s", name); - return -ENOTSUP; + pw_log_error("not implemented on library side"); + return 0; } SPA_EXPORT @@ -2963,29 +2963,135 @@ int jack_port_untie (jack_port_t *port) SPA_EXPORT int jack_port_set_name (jack_port_t *port, const char *port_name) { - pw_log_warn("not implemented %p %s", port, port_name); - return -ENOTSUP; + pw_log_warn("deprecated"); + return 0; } SPA_EXPORT int jack_port_rename (jack_client_t* client, jack_port_t *port, const char *port_name) { - pw_log_warn(NAME" %p: not implemented %p %s", client, port, port_name); - return -ENOTSUP; + struct client *c = (struct client *) client; + struct object *o = (struct object *) port; + struct port *p; + struct spa_port_info port_info; + struct spa_dict dict; + struct spa_dict_item items[1]; + + pw_thread_loop_lock(c->context.loop); + + p = GET_PORT(c, GET_DIRECTION(o->port.flags), o->port.port_id); + + port_info = SPA_PORT_INFO_INIT(); + port_info.change_mask |= SPA_PORT_CHANGE_MASK_PROPS; + dict = SPA_DICT_INIT(items, 0); + items[dict.n_items++] = SPA_DICT_ITEM_INIT(PW_KEY_PORT_NAME, port_name); + port_info.props = &dict; + + pw_client_node_proxy_port_update(c->node_proxy, + p->direction, + p->id, + PW_CLIENT_NODE_PORT_UPDATE_INFO, + 0, NULL, + &port_info); + pw_thread_loop_unlock(c->context.loop); + + return 0; } SPA_EXPORT int jack_port_set_alias (jack_port_t *port, const char *alias) { - pw_log_warn("not implemented %p %s", port, alias); - return -ENOTSUP; + struct object *o = (struct object *) port; + struct client *c = o->client; + struct port *p; + struct spa_port_info port_info; + struct spa_dict dict; + struct spa_dict_item items[1]; + const char *key; + + if (c == NULL) + return -1; + + pw_thread_loop_lock(c->context.loop); + + if (o->port.alias1[0] == '\0') { + key = PW_KEY_PORT_ALIAS1; + snprintf(o->port.alias1, sizeof(o->port.alias1), "%s", alias); + } + else if (o->port.alias2[0] == '\0') { + key = PW_KEY_PORT_ALIAS2; + snprintf(o->port.alias2, sizeof(o->port.alias2), "%s", alias); + } + else + goto error; + + p = GET_PORT(c, GET_DIRECTION(o->port.flags), o->port.port_id); + + port_info = SPA_PORT_INFO_INIT(); + port_info.change_mask |= SPA_PORT_CHANGE_MASK_PROPS; + dict = SPA_DICT_INIT(items, 0); + items[dict.n_items++] = SPA_DICT_ITEM_INIT(key, alias); + port_info.props = &dict; + + pw_client_node_proxy_port_update(c->node_proxy, + p->direction, + p->id, + PW_CLIENT_NODE_PORT_UPDATE_INFO, + 0, NULL, + &port_info); + pw_thread_loop_unlock(c->context.loop); + + return 0; + +error: + pw_thread_loop_unlock(c->context.loop); + return -1; } SPA_EXPORT int jack_port_unset_alias (jack_port_t *port, const char *alias) { - pw_log_warn("not implemented %p %s", port, alias); - return -ENOTSUP; + struct object *o = (struct object *) port; + struct client *c = o->client; + struct port *p; + struct spa_port_info port_info; + struct spa_dict dict; + struct spa_dict_item items[1]; + const char *key; + + if (c == NULL) + return -1; + + pw_thread_loop_lock(c->context.loop); + + if (strcmp(o->port.alias1, alias) == 0) + key = PW_KEY_PORT_ALIAS1; + else if (strcmp(o->port.alias2, alias) == 0) + key = PW_KEY_PORT_ALIAS2; + else + goto error; + + p = GET_PORT(c, GET_DIRECTION(o->port.flags), o->port.port_id); + + port_info = SPA_PORT_INFO_INIT(); + port_info.change_mask |= SPA_PORT_CHANGE_MASK_PROPS; + dict = SPA_DICT_INIT(items, 0); + items[dict.n_items++] = SPA_DICT_ITEM_INIT(key, NULL); + port_info.props = &dict; + + pw_client_node_proxy_port_update(c->node_proxy, + p->direction, + p->id, + PW_CLIENT_NODE_PORT_UPDATE_INFO, + 0, NULL, + &port_info); + pw_thread_loop_unlock(c->context.loop); + + return 0; + +error: + pw_thread_loop_unlock(c->context.loop); + return -1; } SPA_EXPORT @@ -3740,15 +3846,13 @@ char *jack_client_get_uuid (jack_client_t *client) SPA_EXPORT int jack_client_real_time_priority (jack_client_t * client) { - pw_log_warn(NAME" %p: not implemented", client); return 20; } SPA_EXPORT int jack_client_max_real_time_priority (jack_client_t *client) { - pw_log_warn(NAME" %p: not implemented", client); - return -ENOTSUP; + return 20; } SPA_EXPORT @@ -3785,6 +3889,7 @@ int jack_client_create_thread (jack_client_t* client, if (globals.creator == NULL) globals.creator = pthread_create; + pw_log_info("client %p: create thread", client); return globals.creator(thread, NULL, start_routine, arg); } @@ -3803,7 +3908,9 @@ int jack_client_stop_thread(jack_client_t* client, jack_native_thread_t thread) if (thread == (jack_native_thread_t)NULL) return -1; + pw_log_warn("join thread %lu", thread); pthread_join(thread, &status); + pw_log_warn("stopped thread %lu", thread); return 0; } @@ -3815,8 +3922,11 @@ int jack_client_kill_thread(jack_client_t* client, jack_native_thread_t thread) if (thread == (jack_native_thread_t)NULL) return -1; + pw_log_warn("cancel thread %lu", thread); pthread_cancel(thread); + pw_log_warn("join thread %lu", thread); pthread_join(thread, &status); + pw_log_warn("stopped thread %lu", thread); return 0; } From 2736c227a19b1db48dd849af6f7f3e929bb53c00 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 9 Sep 2019 17:16:18 +0200 Subject: [PATCH 112/133] jack: use our own per client loop, like jack. --- src/pipewire-jack.c | 100 +++++++++++++++++++++++++++----------------- 1 file changed, 62 insertions(+), 38 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index a2d3f1704..32e7fd6c4 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -40,6 +40,7 @@ #include #include +#include #include "extensions/client-node.h" @@ -216,6 +217,8 @@ struct client { struct context context; + struct pw_data_loop *loop; + struct pw_remote *remote; struct spa_hook remote_listener; @@ -292,6 +295,7 @@ struct client { unsigned int active:1; unsigned int destroyed:1; unsigned int first:1; + unsigned int thread_entered:1; jack_position_t jack_position; jack_transport_state_t jack_state; @@ -607,7 +611,7 @@ do_remove_sources(struct spa_loop *loop, struct client *c = user_data; if (c->socket_source) { - pw_loop_destroy_source(c->context.core->data_loop, c->socket_source); + pw_loop_destroy_source(c->loop->loop, c->socket_source); c->socket_source = NULL; } return 0; @@ -615,7 +619,7 @@ do_remove_sources(struct spa_loop *loop, static void unhandle_socket(struct client *c) { - pw_loop_invoke(c->context.core->data_loop, + pw_loop_invoke(c->loop->loop, do_remove_sources, 1, NULL, 0, true, c); } @@ -810,41 +814,32 @@ static inline jack_transport_state_t position_to_jack(struct pw_node_activation return state; } -static inline int wait_sync(struct client *c) +static inline uint32_t cycle_run(struct client *c) { uint64_t cmd, nsec; int fd = c->socket_source->fd; - struct spa_io_position *pos = c->position; - struct pw_node_activation *activation = c->activation; - - /* this is blocking if nothing ready */ - if (read(fd, &cmd, sizeof(cmd)) != sizeof(cmd)) - pw_log_warn(NAME" %p: read failed %m", c); - if (cmd > 1) - pw_log_warn(NAME" %p: missed %"PRIu64" wakeups", c, cmd - 1); - - if (pos == NULL) { - pw_log_error(NAME" %p: missing position", c); - return -1; - } - - nsec = pos->clock.nsec; - activation->status = PW_NODE_ACTIVATION_AWAKE; - activation->awake_time = nsec; - - return 0; -} - -static inline uint32_t cycle_wait(struct client *c) -{ uint32_t buffer_size, sample_rate; struct spa_io_position *pos = c->position; struct pw_node_activation *activation = c->activation; struct pw_node_activation *driver = c->driver_activation; - if (wait_sync(c) < 0) - return 0; + /* this is blocking if nothing ready */ + if (read(fd, &cmd, sizeof(cmd)) != sizeof(cmd)) { + pw_log_warn(NAME" %p: read failed %m", c); + if (errno == EWOULDBLOCK) + return 0; + } + if (cmd > 1) + pw_log_warn(NAME" %p: missed %"PRIu64" wakeups", c, cmd - 1); + if (pos == NULL) { + pw_log_error(NAME" %p: missing position", c); + return 0; + } + + nsec = pos->clock.nsec; + activation->status = PW_NODE_ACTIVATION_AWAKE; + activation->awake_time = nsec; if (c->first) { if (c->thread_init_callback) c->thread_init_callback(c->thread_init_arg); @@ -887,6 +882,18 @@ static inline uint32_t cycle_wait(struct client *c) return buffer_size; } +static inline uint32_t cycle_wait(struct client *c) +{ + int res; + + res = pw_data_loop_wait(c->loop, -1); + if (res <= 0) { + pw_log_warn(NAME" %p: wait error %m", c); + return 0; + } + return cycle_run(c); +} + static inline void signal_sync(struct client *c) { struct timespec ts; @@ -962,15 +969,16 @@ on_rtsocket_condition(void *data, int fd, uint32_t mask) return; } if (c->thread_callback) { - c->thread_callback(c->thread_arg); + if (!c->thread_entered) { + c->thread_entered = true; + c->thread_callback(c->thread_arg); + } return; - } - - if (mask & SPA_IO_IN) { + } else if (mask & SPA_IO_IN) { uint32_t buffer_size; int status; - buffer_size = cycle_wait(c); + buffer_size = cycle_run(c); status = c->process_callback ? c->process_callback(buffer_size, c->process_arg) : 0; @@ -993,6 +1001,8 @@ static void clean_transport(struct client *c) if (c->node_id == SPA_ID_INVALID) return; + pw_data_loop_stop(c->loop); + unhandle_socket(c); pw_array_for_each(l, &c->links) @@ -1009,7 +1019,6 @@ static int client_node_transport(void *object, uint32_t mem_id, uint32_t offset, uint32_t size) { struct client *c = (struct client *) object; - struct pw_core *core = c->context.core; clean_transport(c); @@ -1027,7 +1036,7 @@ static int client_node_transport(void *object, c, readfd, writefd, node_id); close(writefd); - c->socket_source = pw_loop_add_io(core->data_loop, + c->socket_source = pw_loop_add_io(c->loop->loop, readfd, SPA_IO_ERR | SPA_IO_HUP, true, on_rtsocket_condition, c); @@ -1110,7 +1119,7 @@ static int client_node_command(void *object, const struct spa_command *command) switch (SPA_NODE_COMMAND_ID(command)) { case SPA_NODE_COMMAND_Pause: if (c->started) { - pw_loop_update_io(c->context.core->data_loop, + pw_loop_update_io(c->loop->loop, c->socket_source, SPA_IO_ERR | SPA_IO_HUP); c->started = false; @@ -1119,11 +1128,12 @@ static int client_node_command(void *object, const struct spa_command *command) case SPA_NODE_COMMAND_Start: if (!c->started) { - pw_loop_update_io(c->context.core->data_loop, + pw_loop_update_io(c->loop->loop, c->socket_source, SPA_IO_IN | SPA_IO_ERR | SPA_IO_HUP); c->started = true; c->first = true; + c->thread_entered = false; } break; default: @@ -1808,6 +1818,8 @@ static void registry_event_global(void *data, uint32_t id, pw_map_insert_at(&c->context.globals, size++, NULL); pw_map_insert_at(&c->context.globals, id, o); + pw_thread_loop_unlock(c->context.loop); + switch (type) { case PW_TYPE_INTERFACE_Node: if (c->registration_callback) @@ -1824,6 +1836,8 @@ static void registry_event_global(void *data, uint32_t id, c->connect_callback(o->port_link.src, o->port_link.dst, 1, c->connect_arg); break; } + pw_thread_loop_lock(c->context.loop); + exit: return; exit_free: @@ -1842,6 +1856,8 @@ static void registry_event_global_remove(void *object, uint32_t id) if (o == NULL) return; + pw_thread_loop_unlock(c->context.loop); + switch (o->type) { case PW_TYPE_INTERFACE_Node: if (c->registration_callback) @@ -1856,6 +1872,7 @@ static void registry_event_global_remove(void *object, uint32_t id) c->connect_callback(o->port_link.src, o->port_link.dst, 0, c->connect_arg); break; } + pw_thread_loop_lock(c->context.loop); /* JACK clients expect the objects to hang around after * they are unregistered. We keep them in the map but reuse the @@ -1919,6 +1936,10 @@ jack_client_t * jack_client_open (const char *client_name, #endif } + client->loop = pw_data_loop_new(NULL); + if (client->loop == NULL) + goto init_failed; + pw_array_init(&client->links, 64); client->buffer_size = (uint32_t)-1; @@ -2148,6 +2169,8 @@ static int do_activate(struct client *c) { int res; + pw_data_loop_start(c->loop); + pw_thread_loop_lock(c->context.loop); pw_log_debug(NAME" %p: activate", c); pw_client_node_proxy_set_active(c->node_proxy, true); @@ -2174,7 +2197,6 @@ int jack_activate (jack_client_t *client) c->activation->pending_sync = true; c->active = true; - return 0; } @@ -2198,6 +2220,8 @@ int jack_deactivate (jack_client_t *client) pw_thread_loop_unlock(c->context.loop); + pw_data_loop_stop(c->loop); + if (res < 0) return res; From 35bf6bfa5c3969b65e63dbfe48578c5c040849d4 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 10 Sep 2019 09:58:36 +0200 Subject: [PATCH 113/133] jack: map buffer readwrite and private for input qtractor writes to the input buffer so make them writable but with a private mapping. --- src/pipewire-jack.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 32e7fd6c4..e7ef14245 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -1395,7 +1395,10 @@ static int client_node_port_use_buffers(void *object, pw_log_debug(NAME" %p: port %p %d %d.%d use_buffers %d", c, p, direction, port_id, mix_id, n_buffers); - fl = PW_MEMMAP_FLAG_READ | (direction == SPA_DIRECTION_OUTPUT ? PW_MEMMAP_FLAG_WRITE : 0); + + /* some apps write to the input buffer so we want everything writable + * but for input buffers, the changes are private */ + fl = PW_MEMMAP_FLAG_READWRITE | (direction == SPA_DIRECTION_INPUT ? PW_MEMMAP_FLAG_PRIVATE : 0); /* clear previous buffers */ clear_buffers(c, mix); From ad086e33498cbde349bb9452807f7b561b473d41 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 10 Sep 2019 11:56:25 +0200 Subject: [PATCH 114/133] jack: don't use private flag Some input buffers are also used as output buffers in case of feedback. --- src/pipewire-jack.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index e7ef14245..e53e500fc 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -1396,9 +1396,8 @@ static int client_node_port_use_buffers(void *object, port_id, mix_id, n_buffers); - /* some apps write to the input buffer so we want everything writable - * but for input buffers, the changes are private */ - fl = PW_MEMMAP_FLAG_READWRITE | (direction == SPA_DIRECTION_INPUT ? PW_MEMMAP_FLAG_PRIVATE : 0); + /* some apps write to the input buffer so we want everything readwrite */ + fl = PW_MEMMAP_FLAG_READWRITE; /* clear previous buffers */ clear_buffers(c, mix); From 4bd1e9a9ff70bf50611e85d9244e70da04dfd13c Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 10 Sep 2019 18:49:19 +0200 Subject: [PATCH 115/133] jack: init midi buffer correctly Init the midi event buffer at the right memory. Add some more debug. --- src/pipewire-jack.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index e53e500fc..1bd8deb06 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -1357,6 +1357,7 @@ static void init_buffer(struct port *p, void *data, size_t maxsize) { if (p->object->port.type_id == 1) { struct midi_buffer *mb = data; + pw_log_debug("port %p: init midi buffer %p size:%zd", p, data, maxsize); mb->magic = MIDI_BUFFER_MAGIC; mb->buffer_size = maxsize; mb->nframes = maxsize / sizeof(float); @@ -1727,9 +1728,11 @@ static void registry_event_global(void *data, uint32_t id, spa_dict_for_each(item, props) { if (!strcmp(item->key, PW_KEY_PORT_DIRECTION)) { - if (!strcmp(item->value, "in")) + if (strcmp(item->value, "in") == 0 || + strcmp(item->value, "control") == 0) flags |= JackPortIsInput; - else if (!strcmp(item->value, "out")) + else if (strcmp(item->value, "out") == 0 || + strcmp(item->value, "notify") == 0) flags |= JackPortIsOutput; } else if (!strcmp(item->key, PW_KEY_PORT_PHYSICAL)) { @@ -2609,7 +2612,7 @@ jack_port_t * jack_port_register (jack_client_t *client, struct port *p; int res; - pw_log_debug(NAME" %p: port register \"%s\" \"%s\" %ld %ld", + pw_log_debug(NAME" %p: port register \"%s\" \"%s\" %08lx %ld", c, port_name, port_type, flags, buffer_size); if (flags & JackPortIsInput) @@ -2757,11 +2760,16 @@ SPA_EXPORT void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) { struct object *o = (struct object *) port; - struct client *c = o->client; + struct client *c; struct port *p; struct mix *mix; void *ptr = NULL; + if (o == NULL) + return NULL; + + c = o->client; + if (o->type != PW_TYPE_INTERFACE_Port || o->port.port_id == SPA_ID_INVALID) { pw_log_error(NAME" %p: invalid port %p", c, port); return NULL; @@ -2817,7 +2825,7 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) if (ptr == NULL) { ptr = p->emptyptr; if (!p->zeroed) { - init_buffer(p, p->empty, sizeof(p->empty)); + init_buffer(p, ptr, BUFFER_SIZE_MAX * sizeof(float)); p->zeroed = true; } } @@ -4041,16 +4049,22 @@ jack_midi_data_t* jack_midi_event_reserve(void *port_buffer, struct midi_event *events = SPA_MEMBER(mb, sizeof(*mb), struct midi_event); size_t buffer_size = mb->buffer_size; - if (time < 0 || time >= mb->nframes) - goto failed; + if (time < 0 || time >= mb->nframes) { + pw_log_warn("midi %p: time:%d frames:%d", port_buffer, time, mb->nframes); + goto failed; + } - if (mb->event_count > 0 && time < events[mb->event_count - 1].time) - goto failed; + if (mb->event_count > 0 && time < events[mb->event_count - 1].time) { + pw_log_warn("midi %p: time:%d ev:%d", port_buffer, time, mb->event_count); + goto failed; + } /* Check if data_size is >0 and there is enough space in the buffer for the event. */ if (data_size <= 0) { + pw_log_warn("midi %p: data_size:%zd", port_buffer, data_size); goto failed; // return NULL? } else if (jack_midi_max_event_size (port_buffer) < data_size) { + pw_log_warn("midi %p: event too large: data_size:%zd", port_buffer, data_size); goto failed; } else { struct midi_event *ev = &events[mb->event_count]; From 1d2db89b9c80153f94ace12572f16678694fafc9 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 16 Sep 2019 12:53:53 +0200 Subject: [PATCH 116/133] jack: update for HAVE/NEED_BUFFER -> HAVE/NEED_DATA --- src/pipewire-jack.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 1bd8deb06..960065576 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -2723,7 +2723,7 @@ static void *mix_audio(struct client *c, struct port *p, jack_nframes_t frames) if (io == NULL || io->buffer_id >= mix->n_buffers) continue; - io->status = SPA_STATUS_NEED_BUFFER; + io->status = SPA_STATUS_NEED_DATA; b = &mix->buffers[io->buffer_id]; if (layer++ == 0) ptr = b->datas[0].data; @@ -2808,7 +2808,7 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) b->datas[0].chunk->size = frames * sizeof(float); b->datas[0].chunk->stride = sizeof(float); - io.status = SPA_STATUS_HAVE_BUFFER; + io.status = SPA_STATUS_HAVE_DATA; io.buffer_id = b->id; } done: From cd2c8ceb3117e362b7abfd6dcd75ab128eb72ea0 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 16 Sep 2019 21:08:35 +0200 Subject: [PATCH 117/133] jack: use buffers for midi Place the midi in application/control buffers instead of control IO. --- src/pipewire-jack.c | 220 ++++++++++++++++++++++++-------------------- 1 file changed, 118 insertions(+), 102 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 960065576..312a56182 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -164,10 +164,6 @@ struct mix { struct port *port; struct spa_io_buffers *io; - struct spa_io_sequence *notify; - size_t notify_size; - struct spa_io_sequence *control; - size_t control_size; struct buffer buffers[MAX_BUFFERS]; uint32_t n_buffers; @@ -184,6 +180,7 @@ struct port { uint32_t id; struct object *object; + struct spa_io_buffers io; struct spa_list mix; bool have_format; @@ -661,8 +658,6 @@ static void convert_to_midi(struct spa_pod_sequence *seq, void *midi) { struct spa_pod_control *c; - jack_midi_reset_buffer(midi); - SPA_POD_SEQUENCE_FOREACH(seq, c) { switch(c->type) { case SPA_CONTROL_Midi: @@ -675,18 +670,57 @@ static void convert_to_midi(struct spa_pod_sequence *seq, void *midi) } } +static void *get_buffer_output(struct client *c, struct port *p, uint32_t frames, uint32_t stride) +{ + struct mix *mix; + void *ptr = NULL; + + p->io.status = -EPIPE; + p->io.buffer_id = SPA_ID_INVALID; + + if ((mix = find_mix(c, p, -1)) != NULL) { + struct buffer *b; + + pw_log_trace(NAME" %p: port %p %d get buffer %d n_buffers:%d", + c, p, p->id, frames, mix->n_buffers); + + if ((b = dequeue_buffer(mix)) == NULL) { + pw_log_warn("port %p: out of buffers", p); + goto done; + } + reuse_buffer(c, mix, b->id); + ptr = b->datas[0].data; + + b->datas[0].chunk->offset = 0; + b->datas[0].chunk->size = frames * stride; + b->datas[0].chunk->stride = stride; + + p->io.status = SPA_STATUS_HAVE_DATA; + p->io.buffer_id = b->id; + } +done: + spa_list_for_each(mix, &p->mix, port_link) { + struct spa_io_buffers *mio = mix->io; + if (mio == NULL) + continue; + pw_log_trace(NAME" %p: port %p tee %d.%d get buffer %d io:%p", + c, p, p->id, mix->id, frames, mio); + *mio = p->io; + } + return ptr; +} + static void process_tee(struct client *c) { struct port *p; + spa_list_for_each(p, &c->ports[SPA_DIRECTION_OUTPUT], link) { - struct mix *mix; - spa_list_for_each(mix, &p->mix, port_link) { - if (mix->notify == NULL) - continue; - convert_from_midi(p->emptyptr, mix->notify, mix->notify_size); - break; - } + if (p->object->port.type_id != 1) + continue; + void *ptr = get_buffer_output(c, p, BUFFER_SIZE_MAX, 1); + if (ptr != NULL) + convert_from_midi(p->emptyptr, ptr, BUFFER_SIZE_MAX); } } @@ -1199,8 +1233,8 @@ static int param_enum_format(struct client *c, struct port *p, case 1: *param = spa_pod_builder_add_object(b, SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, - SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_stream), - SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_midi)); + SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_application), + SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_control)); break; default: return -EINVAL; @@ -1236,8 +1270,8 @@ static int param_format(struct client *c, struct port *p, case 1: *param = spa_pod_builder_add_object(b, SPA_TYPE_OBJECT_Format, SPA_PARAM_Format, - SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_stream), - SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_midi)); + SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_application), + SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_control)); break; default: return -EINVAL; @@ -1261,27 +1295,10 @@ static int param_buffers(struct client *c, struct port *p, static int param_io(struct client *c, struct port *p, struct spa_pod **param, struct spa_pod_builder *b) { - switch (p->object->port.type_id) { - case 0: - *param = spa_pod_builder_add_object(b, - SPA_TYPE_OBJECT_ParamIO, SPA_PARAM_IO, - SPA_PARAM_IO_id, SPA_POD_Id(SPA_IO_Buffers), - SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_buffers))); - break; - case 1: - if (p->direction == SPA_DIRECTION_OUTPUT) { - *param = spa_pod_builder_add_object(b, - SPA_TYPE_OBJECT_ParamIO, SPA_PARAM_IO, - SPA_PARAM_IO_id, SPA_POD_Id(SPA_IO_Notify), - SPA_PARAM_IO_size, SPA_POD_Int(BUFFER_SIZE_MAX)); - } else { - *param = spa_pod_builder_add_object(b, - SPA_TYPE_OBJECT_ParamIO, SPA_PARAM_IO, - SPA_PARAM_IO_id, SPA_POD_Id(SPA_IO_Control), - SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_sequence))); - } - break; - } + *param = spa_pod_builder_add_object(b, + SPA_TYPE_OBJECT_ParamIO, SPA_PARAM_IO, + SPA_PARAM_IO_id, SPA_POD_Id(SPA_IO_Buffers), + SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_buffers))); return 1; } @@ -1313,7 +1330,9 @@ static int port_set_format(struct client *c, struct port *p, p->rate = info.info.raw.rate; break; - case SPA_MEDIA_TYPE_stream: + case SPA_MEDIA_TYPE_application: + if (info.media_subtype != SPA_MEDIA_SUBTYPE_control) + return -EINVAL; break; default: return -EINVAL; @@ -1486,11 +1505,12 @@ static int client_node_port_use_buffers(void *object, d->data, d->maxsize); } + init_buffer(p, p->emptyptr, BUFFER_SIZE_MAX); + p->zeroed = true; + SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT); - if (direction == SPA_DIRECTION_OUTPUT) { - init_buffer(p, b->datas[0].data, b->datas[0].maxsize); + if (direction == SPA_DIRECTION_OUTPUT) reuse_buffer(c, mix, b->id); - } } pw_log_debug(NAME" %p: have %d buffers", c, n_buffers); @@ -1550,14 +1570,6 @@ static int client_node_port_set_io(void *object, case SPA_IO_Buffers: mix->io = ptr; break; - case SPA_IO_Notify: - mix->notify = ptr; - mix->notify_size = size; - break; - case SPA_IO_Control: - mix->control = ptr; - mix->control_size = size; - break; default: break; } @@ -1728,11 +1740,9 @@ static void registry_event_global(void *data, uint32_t id, spa_dict_for_each(item, props) { if (!strcmp(item->key, PW_KEY_PORT_DIRECTION)) { - if (strcmp(item->value, "in") == 0 || - strcmp(item->value, "control") == 0) + if (strcmp(item->value, "in") == 0) flags |= JackPortIsInput; - else if (strcmp(item->value, "out") == 0 || - strcmp(item->value, "notify") == 0) + else if (strcmp(item->value, "out") == 0) flags |= JackPortIsOutput; } else if (!strcmp(item->key, PW_KEY_PORT_PHYSICAL)) { @@ -2739,30 +2749,62 @@ static void *mix_audio(struct client *c, struct port *p, jack_nframes_t frames) static void *mix_midi(struct client *c, struct port *p, jack_nframes_t frames) { struct mix *mix; - struct spa_io_sequence *io; - void *ptr = NULL; + struct buffer *b; + struct spa_io_buffers *io; + void *ptr = p->emptyptr; + + jack_midi_reset_buffer(ptr); spa_list_for_each(mix, &p->mix, port_link) { pw_log_trace(NAME" %p: port %p mix %d.%d get buffer %d", c, p, p->id, mix->id, frames); - io = mix->control; - if (io == NULL) + + io = mix->io; + if (io == NULL || io->buffer_id >= mix->n_buffers) continue; - ptr = p->emptyptr; - convert_to_midi(&io->sequence, ptr); + io->status = SPA_STATUS_NEED_DATA; + b = &mix->buffers[io->buffer_id]; + /* FIXME, actually mix the midi */ + convert_to_midi(b->datas[0].data, ptr); + p->zeroed = false; break; } return ptr; } +static inline void *get_buffer_input_float(struct client *c, struct port *p, jack_nframes_t frames) +{ + return mix_audio(c, p, frames); +} + +static inline void *get_buffer_input_midi(struct client *c, struct port *p, jack_nframes_t frames) +{ + return mix_midi(c, p, frames); +} + +static inline void *get_buffer_output_float(struct client *c, struct port *p, jack_nframes_t frames) +{ + void *ptr; + + ptr = get_buffer_output(c, p, frames, sizeof(float)); + if (ptr == NULL) + ptr = p->emptyptr; + + return ptr; +} + +static inline void *get_buffer_output_midi(struct client *c, struct port *p, jack_nframes_t frames) +{ + return p->emptyptr; +} + SPA_EXPORT void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) { struct object *o = (struct object *) port; struct client *c; struct port *p; - struct mix *mix; void *ptr = NULL; if (o == NULL) @@ -2779,56 +2821,30 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) if (p->direction == SPA_DIRECTION_INPUT) { switch (p->object->port.type_id) { case 0: - ptr = mix_audio(c, p, frames); + ptr = get_buffer_input_float(c, p, frames); break; case 1: - ptr = mix_midi(c, p, frames); + ptr = get_buffer_input_midi(c, p, frames); break; } - } else { - struct spa_io_buffers io; - - io.status = -EPIPE; - io.buffer_id = SPA_ID_INVALID; - - if ((mix = find_mix(c, p, -1)) != NULL) { - struct buffer *b; - - pw_log_trace(NAME" %p: port %p %d get buffer %d n_buffers:%d", - c, p, p->id, frames, mix->n_buffers); - - if ((b = dequeue_buffer(mix)) == NULL) { - pw_log_warn("port %p: out of buffers", p); - goto done; + if (ptr == NULL) { + ptr = p->emptyptr; + if (!p->zeroed) { + init_buffer(p, ptr, BUFFER_SIZE_MAX * sizeof(float)); + p->zeroed = true; } - reuse_buffer(c, mix, b->id); - ptr = b->datas[0].data; - - b->datas[0].chunk->offset = 0; - b->datas[0].chunk->size = frames * sizeof(float); - b->datas[0].chunk->stride = sizeof(float); - - io.status = SPA_STATUS_HAVE_DATA; - io.buffer_id = b->id; } - done: - spa_list_for_each(mix, &p->mix, port_link) { - struct spa_io_buffers *mio = mix->io; - if (mio == NULL) - continue; - pw_log_trace(NAME" %p: port %p tee %d.%d get buffer %d io:%p", - c, p, p->id, mix->id, frames, mio); - *mio = io; + } else { + switch (p->object->port.type_id) { + case 0: + ptr = get_buffer_output_float(c, p, frames); + break; + case 1: + ptr = get_buffer_output_midi(c, p, frames); + break; } } - if (ptr == NULL) { - ptr = p->emptyptr; - if (!p->zeroed) { - init_buffer(p, ptr, BUFFER_SIZE_MAX * sizeof(float)); - p->zeroed = true; - } - } pw_log_trace(NAME" %p: port %p buffer %p", c, p, ptr); return ptr; } From f777c04ea683d0922f72fba25e74ff9535228609 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 19 Sep 2019 13:16:42 +0200 Subject: [PATCH 118/133] jack: only allow connection of same type --- src/pipewire-jack.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 312a56182..e9e4655d9 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -3241,7 +3241,8 @@ int jack_connect (jack_client_t *client, if (src == NULL || dst == NULL || !(src->port.flags & JackPortIsOutput) || - !(dst->port.flags & JackPortIsInput)) { + !(dst->port.flags & JackPortIsInput) || + src->port.type_id != dst->port.type_id) { res = -EINVAL; goto exit; } From b85b88b53d2d341bc873d4feca5ef7648e559a65 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 19 Sep 2019 15:26:06 +0200 Subject: [PATCH 119/133] jack: sort midi ports before audio ports for compatibility reasons return the audio ports before the midi ports. Most apps just try to link the first hw ports they see and jack always lists the hw audio ports first. --- src/pipewire-jack.c | 46 +++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index e9e4655d9..32a4f1dd9 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -3443,7 +3443,7 @@ const char ** jack_get_ports (jack_client_t *client, int count = 0; struct object *o; const char *str; - uint32_t id; + uint32_t i, id; regex_t port_regex, type_regex; if ((str = getenv("PIPEWIRE_NODE")) != NULL) @@ -3461,30 +3461,32 @@ const char ** jack_get_ports (jack_client_t *client, pw_log_debug(NAME" %p: ports id:%d name:%s type:%s flags:%08lx", c, id, port_name_pattern, type_name_pattern, flags); - spa_list_for_each(o, &c->context.ports, link) { - pw_log_debug(NAME" %p: check port type:%d flags:%08lx name:%s", c, - o->port.type_id, o->port.flags, o->port.name); - if (o->port.type_id == 2) - continue; - if (!SPA_FLAG_CHECK(o->port.flags, flags)) - continue; - if (id != SPA_ID_INVALID && o->port.node_id != id) - continue; - - if (port_name_pattern && port_name_pattern[0]) { - if (regexec(&port_regex, o->port.name, 0, NULL, 0) == REG_NOMATCH) + for (i = 0; i < 2; i++) { + spa_list_for_each(o, &c->context.ports, link) { + pw_log_debug(NAME" %p: check port type:%d flags:%08lx name:%s", c, + o->port.type_id, o->port.flags, o->port.name); + if (count == JACK_PORT_MAX) + break; + if (o->port.type_id != i) continue; - } - if (type_name_pattern && type_name_pattern[0]) { - if (regexec(&type_regex, type_to_string(o->port.type_id), - 0, NULL, 0) == REG_NOMATCH) + if (!SPA_FLAG_CHECK(o->port.flags, flags)) + continue; + if (id != SPA_ID_INVALID && o->port.node_id != id) continue; - } - pw_log_debug(NAME" %p: port %s matches (%d)", c, o->port.name, count); - res[count++] = o->port.name; - if (count == JACK_PORT_MAX) - break; + if (port_name_pattern && port_name_pattern[0]) { + if (regexec(&port_regex, o->port.name, 0, NULL, 0) == REG_NOMATCH) + continue; + } + if (type_name_pattern && type_name_pattern[0]) { + if (regexec(&type_regex, type_to_string(o->port.type_id), + 0, NULL, 0) == REG_NOMATCH) + continue; + } + + pw_log_debug(NAME" %p: port %s matches (%d)", c, o->port.name, count); + res[count++] = o->port.name; + } } pw_thread_loop_unlock(c->context.loop); From 8d8ef587f5c6475df2887422bd268e5065227411 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 23 Sep 2019 20:50:12 +0200 Subject: [PATCH 120/133] jack: improve properties --- src/pipewire-jack.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 32a4f1dd9..fe0336dda 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -1702,8 +1702,8 @@ static void registry_event_global(void *data, uint32_t id, switch (type) { case PW_TYPE_INTERFACE_Node: - if ((str = spa_dict_lookup(props, PW_KEY_NODE_NICK)) == NULL && - (str = spa_dict_lookup(props, PW_KEY_NODE_DESCRIPTION)) == NULL && + if ((str = spa_dict_lookup(props, PW_KEY_NODE_DESCRIPTION)) == NULL && + (str = spa_dict_lookup(props, PW_KEY_NODE_NICK)) == NULL && (str = spa_dict_lookup(props, PW_KEY_NODE_NAME)) == NULL) { str = "node"; } @@ -1780,12 +1780,12 @@ static void registry_event_global(void *data, uint32_t id, o->port.port_id = SPA_ID_INVALID; } - if ((str = spa_dict_lookup(props, PW_KEY_PORT_ALIAS1)) != NULL) + if ((str = spa_dict_lookup(props, PW_KEY_OBJECT_PATH)) != NULL) snprintf(o->port.alias1, sizeof(o->port.alias1), "%s", str); else o->port.alias1[0] = '\0'; - if ((str = spa_dict_lookup(props, PW_KEY_PORT_ALIAS2)) != NULL) + if ((str = spa_dict_lookup(props, PW_KEY_PORT_ALIAS)) != NULL) snprintf(o->port.alias2, sizeof(o->port.alias2), "%s", str); else o->port.alias2[0] = '\0'; @@ -3065,11 +3065,11 @@ int jack_port_set_alias (jack_port_t *port, const char *alias) pw_thread_loop_lock(c->context.loop); if (o->port.alias1[0] == '\0') { - key = PW_KEY_PORT_ALIAS1; + key = PW_KEY_OBJECT_PATH; snprintf(o->port.alias1, sizeof(o->port.alias1), "%s", alias); } else if (o->port.alias2[0] == '\0') { - key = PW_KEY_PORT_ALIAS2; + key = PW_KEY_PORT_ALIAS; snprintf(o->port.alias2, sizeof(o->port.alias2), "%s", alias); } else @@ -3115,9 +3115,9 @@ int jack_port_unset_alias (jack_port_t *port, const char *alias) pw_thread_loop_lock(c->context.loop); if (strcmp(o->port.alias1, alias) == 0) - key = PW_KEY_PORT_ALIAS1; + key = PW_KEY_OBJECT_PATH; else if (strcmp(o->port.alias2, alias) == 0) - key = PW_KEY_PORT_ALIAS2; + key = PW_KEY_PORT_ALIAS; else goto error; From 274d616fa6adef0a917f16e1001cf66fdca88e64 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 26 Sep 2019 11:56:25 +0200 Subject: [PATCH 121/133] jack: implement midi mixing --- src/pipewire-jack.c | 60 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 14 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index fe0336dda..ae74d1c80 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -48,7 +48,7 @@ #define JACK_PORT_NAME_SIZE 256 #define JACK_PORT_MAX 4096 #define JACK_PORT_TYPE_SIZE 32 -#define CONNECTION_NUM_FOR_PORT 2048 +#define CONNECTION_NUM_FOR_PORT 1024 #define BUFFER_SIZE_MAX 8192 @@ -654,22 +654,45 @@ static void convert_from_midi(void *midi, void *buffer, size_t size) spa_pod_builder_pop(&b, &f); } -static void convert_to_midi(struct spa_pod_sequence *seq, void *midi) +static void convert_to_midi(struct spa_pod_sequence **seq, uint32_t n_seq, void *midi) { - struct spa_pod_control *c; + struct spa_pod_control *c[n_seq]; + uint32_t i; - SPA_POD_SEQUENCE_FOREACH(seq, c) { - switch(c->type) { + for (i = 0; i < n_seq; i++) { + c[i] = spa_pod_control_first(&seq[i]->body); + } + + while (true) { + struct spa_pod_control *next = NULL; + uint32_t next_index = 0; + + for (i = 0; i < n_seq; i++) { + if (!spa_pod_control_is_inside(&seq[i]->body, + SPA_POD_BODY_SIZE(seq[i]), c[i])) + continue; + + if (next == NULL || c[i]->offset < next->offset) { + next = c[i]; + next_index = i; + } + } + if (next == NULL) + break; + + switch(next->type) { case SPA_CONTROL_Midi: jack_midi_event_write(midi, - c->offset, - SPA_POD_BODY(&c->value), - SPA_POD_BODY_SIZE(&c->value)); + next->offset, + SPA_POD_BODY(&next->value), + SPA_POD_BODY_SIZE(&next->value)); break; } + c[next_index] = spa_pod_control_next(c[next_index]); } } + static void *get_buffer_output(struct client *c, struct port *p, uint32_t frames, uint32_t stride) { struct mix *mix; @@ -2749,13 +2772,17 @@ static void *mix_audio(struct client *c, struct port *p, jack_nframes_t frames) static void *mix_midi(struct client *c, struct port *p, jack_nframes_t frames) { struct mix *mix; - struct buffer *b; struct spa_io_buffers *io; void *ptr = p->emptyptr; + struct spa_pod_sequence *seq[CONNECTION_NUM_FOR_PORT]; + uint32_t n_seq = 0; jack_midi_reset_buffer(ptr); spa_list_for_each(mix, &p->mix, port_link) { + struct spa_data *d; + void *pod; + pw_log_trace(NAME" %p: port %p mix %d.%d get buffer %d", c, p, p->id, mix->id, frames); @@ -2764,12 +2791,17 @@ static void *mix_midi(struct client *c, struct port *p, jack_nframes_t frames) continue; io->status = SPA_STATUS_NEED_DATA; - b = &mix->buffers[io->buffer_id]; - /* FIXME, actually mix the midi */ - convert_to_midi(b->datas[0].data, ptr); - p->zeroed = false; - break; + d = &mix->buffers[io->buffer_id].datas[0]; + + if ((pod = spa_pod_from_data(d->data, d->maxsize, d->chunk->offset, d->chunk->size)) == NULL) + continue; + if (!spa_pod_is_sequence(pod)) + continue; + + seq[n_seq++] = pod; } + convert_to_midi(seq, n_seq, ptr); + return ptr; } From e196e56287da8805dd395dbde17fbf8cf59237fb Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 1 Oct 2019 16:27:09 +0200 Subject: [PATCH 122/133] jack: fix mixing.. --- src/pipewire-jack.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index ae74d1c80..b874e2f25 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -2761,8 +2761,8 @@ static void *mix_audio(struct client *c, struct port *p, jack_nframes_t frames) if (layer++ == 0) ptr = b->datas[0].data; else { + mix2(p->emptyptr, ptr, b->datas[0].data, frames); ptr = p->emptyptr; - mix2(ptr, ptr, b->datas[0].data, frames); p->zeroed = false; } } @@ -3748,18 +3748,24 @@ int jack_set_timebase_callback (jack_client_t *client, int res; struct client *c = (struct client *) client; struct pw_node_activation *a = c->driver_activation; + uint32_t owner; + + pw_log_debug(NAME" %p: activation %p", c, a); if (a == NULL) return -EIO; /* was ok */ - if (ATOMIC_LOAD(a->segment_owner[0]) == c->node_id) + owner = ATOMIC_LOAD(a->segment_owner[0]); + if (owner == c->node_id) return 0; /* try to become master */ if (conditional) { - if (!ATOMIC_CAS(a->segment_owner[0], 0, c->node_id)) + if (!ATOMIC_CAS(a->segment_owner[0], 0, c->node_id)) { + pw_log_debug(NAME" %p: owner:%u id:%u", c, owner, c->node_id); return -EBUSY; + } } else { ATOMIC_STORE(a->segment_owner[0], c->node_id); } @@ -3767,6 +3773,8 @@ int jack_set_timebase_callback (jack_client_t *client, c->timebase_callback = timebase_callback; c->timebase_arg = arg; + pw_log_debug(NAME" %p: timebase set id:%u", c, c->node_id); + if ((res = do_activate(c)) < 0) return res; From 0bc34d53018af1e4194dfb08b6c1aea737c2afb0 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 2 Oct 2019 17:54:49 +0200 Subject: [PATCH 123/133] flag update --- src/pipewire-jack.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index b874e2f25..ac25e03e9 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -626,10 +626,10 @@ static void reuse_buffer(struct client *c, struct mix *mix, uint32_t id) b = &mix->buffers[id]; - if (SPA_FLAG_CHECK(b->flags, BUFFER_FLAG_OUT)) { + if (SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_OUT)) { pw_log_trace(NAME" %p: port %p: recycle buffer %d", c, mix->port, id); spa_list_append(&mix->queue, &b->link); - SPA_FLAG_UNSET(b->flags, BUFFER_FLAG_OUT); + SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_OUT); } } @@ -841,7 +841,7 @@ static inline jack_transport_state_t position_to_jack(struct pw_node_activation d->frame = seg->position; d->valid = 0; - if (a->segment_owner[0] && SPA_FLAG_CHECK(seg->bar.flags, SPA_IO_SEGMENT_BAR_FLAG_VALID)) { + if (a->segment_owner[0] && SPA_FLAG_IS_SET(seg->bar.flags, SPA_IO_SEGMENT_BAR_FLAG_VALID)) { double abs_beat; long beats; @@ -3501,7 +3501,7 @@ const char ** jack_get_ports (jack_client_t *client, break; if (o->port.type_id != i) continue; - if (!SPA_FLAG_CHECK(o->port.flags, flags)) + if (!SPA_FLAG_IS_SET(o->port.flags, flags)) continue; if (id != SPA_ID_INVALID && o->port.node_id != id) continue; From f50a5ca2154506fcfe9a407329d6263cb7680a2d Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 21 Oct 2019 17:23:26 +0200 Subject: [PATCH 124/133] jack: handle suspend --- src/pipewire-jack.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index ac25e03e9..2558f7f93 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -1174,6 +1174,7 @@ static int client_node_command(void *object, const struct spa_command *command) pw_log_debug(NAME" %p: got command %d", c, SPA_COMMAND_TYPE(command)); switch (SPA_NODE_COMMAND_ID(command)) { + case SPA_NODE_COMMAND_Suspend: case SPA_NODE_COMMAND_Pause: if (c->started) { pw_loop_update_io(c->loop->loop, From 8997a078aa0fc4e6e6a194e5dcec8784d0074e64 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 22 Oct 2019 09:17:38 +0200 Subject: [PATCH 125/133] jack: skip when there are no buffers on a mixer --- src/pipewire-jack.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 2558f7f93..61452b722 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -704,6 +704,9 @@ static void *get_buffer_output(struct client *c, struct port *p, uint32_t frames if ((mix = find_mix(c, p, -1)) != NULL) { struct buffer *b; + if (mix->n_buffers == 0) + goto done; + pw_log_trace(NAME" %p: port %p %d get buffer %d n_buffers:%d", c, p, p->id, frames, mix->n_buffers); From d9e9c6b7c8764425d5b5ce384b018da488fb2744 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 22 Oct 2019 12:21:37 +0200 Subject: [PATCH 126/133] jack: do better port sorting First sort by type, then by master priority, then by port id. --- src/pipewire-jack.c | 113 ++++++++++++++++++++++++-------------------- 1 file changed, 63 insertions(+), 50 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 61452b722..9cab33778 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -95,6 +95,7 @@ struct object { union { struct { char name[JACK_CLIENT_NAME_SIZE+1]; + int32_t priority; } node; struct { uint32_t src; @@ -111,6 +112,7 @@ struct object { uint32_t monitor_requests; jack_latency_range_t capture_latency; jack_latency_range_t playback_latency; + int32_t priority; } port; }; }; @@ -740,7 +742,6 @@ static void process_tee(struct client *c) { struct port *p; - spa_list_for_each(p, &c->ports[SPA_DIRECTION_OUTPUT], link) { if (p->object->port.type_id != 1) continue; @@ -1729,17 +1730,20 @@ static void registry_event_global(void *data, uint32_t id, switch (type) { case PW_TYPE_INTERFACE_Node: + o = alloc_object(c); + if ((str = spa_dict_lookup(props, PW_KEY_NODE_DESCRIPTION)) == NULL && (str = spa_dict_lookup(props, PW_KEY_NODE_NICK)) == NULL && (str = spa_dict_lookup(props, PW_KEY_NODE_NAME)) == NULL) { str = "node"; } - - o = alloc_object(c); - spa_list_append(&c->context.nodes, &o->link); - snprintf(o->node.name, sizeof(o->node.name), "%s/%d", str, id); + + if ((str = spa_dict_lookup(props, PW_KEY_PRIORITY_MASTER)) != NULL) + o->node.priority = pw_properties_parse_int(str); + pw_log_debug(NAME" %p: add node %d", c, id); + spa_list_append(&c->context.nodes, &o->link); break; case PW_TYPE_INTERFACE_Port: @@ -1805,6 +1809,7 @@ static void registry_event_global(void *data, uint32_t id, snprintf(o->port.name, sizeof(o->port.name), "%s:%s", ot->node.name, str); o->port.port_id = SPA_ID_INVALID; + o->port.priority = ot->node.priority; } if ((str = spa_dict_lookup(props, PW_KEY_OBJECT_PATH)) != NULL) @@ -2745,7 +2750,7 @@ int jack_port_unregister (jack_client_t *client, jack_port_t *port) return res; } -static void *mix_audio(struct client *c, struct port *p, jack_nframes_t frames) +static inline void *get_buffer_input_float(struct client *c, struct port *p, jack_nframes_t frames) { struct mix *mix; struct buffer *b; @@ -2773,7 +2778,7 @@ static void *mix_audio(struct client *c, struct port *p, jack_nframes_t frames) return ptr; } -static void *mix_midi(struct client *c, struct port *p, jack_nframes_t frames) +static inline void *get_buffer_input_midi(struct client *c, struct port *p, jack_nframes_t frames) { struct mix *mix; struct spa_io_buffers *io; @@ -2809,16 +2814,6 @@ static void *mix_midi(struct client *c, struct port *p, jack_nframes_t frames) return ptr; } -static inline void *get_buffer_input_float(struct client *c, struct port *p, jack_nframes_t frames) -{ - return mix_audio(c, p, frames); -} - -static inline void *get_buffer_input_midi(struct client *c, struct port *p, jack_nframes_t frames) -{ - return mix_midi(c, p, frames); -} - static inline void *get_buffer_output_float(struct client *c, struct port *p, jack_nframes_t frames) { void *ptr; @@ -3468,6 +3463,19 @@ int jack_recompute_total_latency (jack_client_t *client, jack_port_t* port) return 0; } +static int port_compare_func(const void *v1, const void *v2) +{ + const struct object *const*o1 = v1, *const*o2 = v2; + + if ((*o1)->port.type_id != (*o2)->port.type_id) + return (*o1)->port.type_id - (*o2)->port.type_id; + + if ((*o1)->port.priority != (*o2)->port.priority) + return (*o2)->port.priority - (*o1)->port.priority; + + return (*o1)->id - (*o2)->id; +} + SPA_EXPORT const char ** jack_get_ports (jack_client_t *client, const char *port_name_pattern, @@ -3475,11 +3483,11 @@ const char ** jack_get_ports (jack_client_t *client, unsigned long flags) { struct client *c = (struct client *) client; - const char **res = malloc(sizeof(char*) * (JACK_PORT_MAX + 1)); - int count = 0; + const char **res; struct object *o; + struct object *tmp[JACK_PORT_MAX]; const char *str; - uint32_t i, id; + uint32_t i, count, id; regex_t port_regex, type_regex; if ((str = getenv("PIPEWIRE_NODE")) != NULL) @@ -3497,40 +3505,45 @@ const char ** jack_get_ports (jack_client_t *client, pw_log_debug(NAME" %p: ports id:%d name:%s type:%s flags:%08lx", c, id, port_name_pattern, type_name_pattern, flags); - for (i = 0; i < 2; i++) { - spa_list_for_each(o, &c->context.ports, link) { - pw_log_debug(NAME" %p: check port type:%d flags:%08lx name:%s", c, - o->port.type_id, o->port.flags, o->port.name); - if (count == JACK_PORT_MAX) - break; - if (o->port.type_id != i) - continue; - if (!SPA_FLAG_IS_SET(o->port.flags, flags)) - continue; - if (id != SPA_ID_INVALID && o->port.node_id != id) - continue; + count = 0; + spa_list_for_each(o, &c->context.ports, link) { + pw_log_debug(NAME" %p: check port type:%d flags:%08lx name:%s", c, + o->port.type_id, o->port.flags, o->port.name); + if (count == JACK_PORT_MAX) + break; + if (o->port.type_id > 1) + continue; + if (!SPA_FLAG_IS_SET(o->port.flags, flags)) + continue; + if (id != SPA_ID_INVALID && o->port.node_id != id) + continue; - if (port_name_pattern && port_name_pattern[0]) { - if (regexec(&port_regex, o->port.name, 0, NULL, 0) == REG_NOMATCH) - continue; - } - if (type_name_pattern && type_name_pattern[0]) { - if (regexec(&type_regex, type_to_string(o->port.type_id), - 0, NULL, 0) == REG_NOMATCH) - continue; - } - - pw_log_debug(NAME" %p: port %s matches (%d)", c, o->port.name, count); - res[count++] = o->port.name; + if (port_name_pattern && port_name_pattern[0]) { + if (regexec(&port_regex, o->port.name, 0, NULL, 0) == REG_NOMATCH) + continue; + } + if (type_name_pattern && type_name_pattern[0]) { + if (regexec(&type_regex, type_to_string(o->port.type_id), + 0, NULL, 0) == REG_NOMATCH) + continue; } - } - pw_thread_loop_unlock(c->context.loop); - if (count == 0) { - free(res); - res = NULL; - } else + pw_log_debug(NAME" %p: port %s prio:%d matches (%d)", + c, o->port.name, o->port.priority, count); + tmp[count++] = o; + } + if (count > 0) { + qsort(tmp, count, sizeof(struct object *), port_compare_func); + + res = malloc(sizeof(char*) * (count + 1)); + for (i = 0; i < count; i++) + res[i] = tmp[i]->port.name; res[count] = NULL; + } else { + res = NULL; + } + + pw_thread_loop_unlock(c->context.loop); if (port_name_pattern && port_name_pattern[0]) regfree(&port_regex); From d4ed1f6832ec1068e4fce2d5910446e303f3c1ae Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 24 Oct 2019 10:23:01 +0200 Subject: [PATCH 127/133] jack: fix confusion between frames and bytes --- src/pipewire-jack.c | 86 +++++++++++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 39 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 9cab33778..16cf3b261 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -50,8 +50,9 @@ #define JACK_PORT_TYPE_SIZE 32 #define CONNECTION_NUM_FOR_PORT 1024 -#define BUFFER_SIZE_MAX 8192 +#define MAX_BUFFER_FRAMES 8192 +#define MAX_ALIGN 16 #define MAX_OBJECTS 8192 #define MAX_PORTS 1024 #define MAX_BUFFERS 2 @@ -61,9 +62,8 @@ #define MAX_IO 32 #define DEFAULT_SAMPLE_RATE 48000 -#define DEFAULT_BUFFER_SIZE 1024 -#define MAX_BUFFER_SIZE 2048 -#define DEFAULT_LATENCY SPA_STRINGIFY(DEFAULT_BUFFER_SIZE/DEFAULT_SAMPLE_RATE) +#define DEFAULT_BUFFER_FRAMES 1024 +#define DEFAULT_LATENCY SPA_STRINGIFY(DEFAULT_BUFFER_FRAMES/DEFAULT_SAMPLE_RATE) #define REAL_JACK_PORT_NAME_SIZE (JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE) @@ -190,7 +190,7 @@ struct port { bool zeroed; float *emptyptr; - float empty[BUFFER_SIZE_MAX + 15]; + float empty[MAX_BUFFER_FRAMES + MAX_ALIGN]; }; struct context { @@ -273,7 +273,7 @@ struct client { struct spa_io_position *position; uint32_t sample_rate; - uint32_t buffer_size; + uint32_t buffer_frames; struct mix mix_pool[MAX_MIX]; struct spa_list free_mix; @@ -310,7 +310,7 @@ static void init_port_pool(struct client *c, enum spa_direction direction) c->port_pool[direction][i].direction = direction; c->port_pool[direction][i].id = i; c->port_pool[direction][i].emptyptr = - SPA_PTR_ALIGN(c->port_pool[direction][i].empty, 16, float); + SPA_PTR_ALIGN(c->port_pool[direction][i].empty, MAX_ALIGN, float); spa_list_append(&c->free_ports[direction], &c->port_pool[direction][i].link); } } @@ -720,7 +720,7 @@ static void *get_buffer_output(struct client *c, struct port *p, uint32_t frames ptr = b->datas[0].data; b->datas[0].chunk->offset = 0; - b->datas[0].chunk->size = frames * stride; + b->datas[0].chunk->size = frames * sizeof(float); b->datas[0].chunk->stride = stride; p->io.status = SPA_STATUS_HAVE_DATA; @@ -745,9 +745,9 @@ static void process_tee(struct client *c) spa_list_for_each(p, &c->ports[SPA_DIRECTION_OUTPUT], link) { if (p->object->port.type_id != 1) continue; - void *ptr = get_buffer_output(c, p, BUFFER_SIZE_MAX, 1); + void *ptr = get_buffer_output(c, p, MAX_BUFFER_FRAMES, 1); if (ptr != NULL) - convert_from_midi(p->emptyptr, ptr, BUFFER_SIZE_MAX); + convert_from_midi(p->emptyptr, ptr, MAX_BUFFER_FRAMES * sizeof(float)); } } @@ -879,7 +879,7 @@ static inline uint32_t cycle_run(struct client *c) { uint64_t cmd, nsec; int fd = c->socket_source->fd; - uint32_t buffer_size, sample_rate; + uint32_t buffer_frames, sample_rate; struct spa_io_position *pos = c->position; struct pw_node_activation *activation = c->activation; struct pw_node_activation *driver = c->driver_activation; @@ -907,12 +907,12 @@ static inline uint32_t cycle_run(struct client *c) c->first = false; } - buffer_size = pos->clock.duration; - if (buffer_size != c->buffer_size) { - pw_log_info(NAME" %p: buffersize %d", c, buffer_size); - c->buffer_size = buffer_size; + buffer_frames = pos->clock.duration; + if (buffer_frames != c->buffer_frames) { + pw_log_info(NAME" %p: bufferframes %d", c, buffer_frames); + c->buffer_frames = buffer_frames; if (c->bufsize_callback) - c->bufsize_callback(c->buffer_size, c->bufsize_arg); + c->bufsize_callback(c->buffer_frames, c->bufsize_arg); } sample_rate = pos->clock.rate.denom; @@ -936,11 +936,11 @@ static inline uint32_t cycle_run(struct client *c) c->xrun_callback(c->xrun_arg); c->xrun_count = driver->xrun_count; } - pw_log_trace(NAME" %p: wait %"PRIu64" %d %d %d %"PRIi64" %f", c, - activation->awake_time, c->buffer_size, c->sample_rate, + pw_log_trace(NAME" %p: wait %"PRIu64" frames:%d rate:%d pos:%d delay:%"PRIi64" corr:%f", c, + activation->awake_time, c->buffer_frames, c->sample_rate, c->jack_position.frame, pos->clock.delay, pos->clock.rate_diff); - return buffer_size; + return buffer_frames; } static inline uint32_t cycle_wait(struct client *c) @@ -1004,7 +1004,7 @@ static inline void cycle_signal(struct client *c, int status) c->jack_state == JackTransportRolling || c->jack_state == JackTransportLooping) { c->timebase_callback(c->jack_state, - c->buffer_size, + c->buffer_frames, &c->jack_position, activation->pending_new_pos, c->timebase_arg); @@ -1036,12 +1036,12 @@ on_rtsocket_condition(void *data, int fd, uint32_t mask) } return; } else if (mask & SPA_IO_IN) { - uint32_t buffer_size; + uint32_t buffer_frames; int status; - buffer_size = cycle_run(c); + buffer_frames = cycle_run(c); - status = c->process_callback ? c->process_callback(buffer_size, c->process_arg) : 0; + status = c->process_callback ? c->process_callback(buffer_frames, c->process_arg) : 0; cycle_signal(c, status); } @@ -1314,7 +1314,11 @@ static int param_buffers(struct client *c, struct port *p, SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(1, 1, MAX_BUFFERS), SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1), - SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_STEP_Int(MAX_BUFFER_SIZE * sizeof(float), 4, INT32_MAX, 4), + SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_STEP_Int( + MAX_BUFFER_FRAMES * sizeof(float), + sizeof(float), + MAX_BUFFER_FRAMES * sizeof(float), + sizeof(float)), SPA_PARAM_BUFFERS_stride, SPA_POD_Int(4), SPA_PARAM_BUFFERS_align, SPA_POD_Int(16)); return 1; @@ -1382,6 +1386,8 @@ static int client_node_port_set_param(void *object, uint8_t buffer[4096]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); + pw_log_debug("port %p: %d.%d id:%d %p", p, direction, port_id, id, param); + if (id == SPA_PARAM_Format) { port_set_format(c, p, flags, param); } @@ -1400,20 +1406,20 @@ static int client_node_port_set_param(void *object, NULL); } -static void init_buffer(struct port *p, void *data, size_t maxsize) +static void init_buffer(struct port *p, void *data, size_t maxframes) { if (p->object->port.type_id == 1) { struct midi_buffer *mb = data; - pw_log_debug("port %p: init midi buffer %p size:%zd", p, data, maxsize); mb->magic = MIDI_BUFFER_MAGIC; - mb->buffer_size = maxsize; - mb->nframes = maxsize / sizeof(float); + mb->buffer_size = MAX_BUFFER_FRAMES * sizeof(float); + mb->nframes = maxframes; mb->write_pos = 0; mb->event_count = 0; mb->lost_events = 0; + pw_log_debug("port %p: init midi buffer %p size:%d", p, data, mb->buffer_size); } else - memset(data, 0, maxsize); + memset(data, 0, maxframes * sizeof(float)); } static int client_node_port_use_buffers(void *object, @@ -1533,7 +1539,7 @@ static int client_node_port_use_buffers(void *object, d->data, d->maxsize); } - init_buffer(p, p->emptyptr, BUFFER_SIZE_MAX); + init_buffer(p, p->emptyptr, MAX_BUFFER_FRAMES); p->zeroed = true; SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT); @@ -1989,7 +1995,7 @@ jack_client_t * jack_client_open (const char *client_name, pw_array_init(&client->links, 64); - client->buffer_size = (uint32_t)-1; + client->buffer_frames = (uint32_t)-1; client->sample_rate = (uint32_t)-1; spa_list_init(&client->free_mix); @@ -2605,9 +2611,9 @@ SPA_EXPORT jack_nframes_t jack_get_buffer_size (jack_client_t *client) { struct client *c = (struct client *) client; - if (c->buffer_size == (uint32_t)-1) - return DEFAULT_BUFFER_SIZE; - return c->buffer_size; + if (c->buffer_frames == (uint32_t)-1) + return DEFAULT_BUFFER_FRAMES; + return c->buffer_frames; } SPA_EXPORT @@ -2637,7 +2643,7 @@ jack_port_t * jack_port_register (jack_client_t *client, const char *port_name, const char *port_type, unsigned long flags, - unsigned long buffer_size) + unsigned long buffer_frames) { struct client *c = (struct client *) client; enum spa_direction direction; @@ -2655,7 +2661,7 @@ jack_port_t * jack_port_register (jack_client_t *client, int res; pw_log_debug(NAME" %p: port register \"%s\" \"%s\" %08lx %ld", - c, port_name, port_type, flags, buffer_size); + c, port_name, port_type, flags, buffer_frames); if (flags & JackPortIsInput) direction = PW_DIRECTION_INPUT; @@ -2675,6 +2681,8 @@ jack_port_t * jack_port_register (jack_client_t *client, snprintf(o->port.name, sizeof(o->port.name), "%s:%s", c->name, port_name); o->port.type_id = type_id; + pw_log_debug(NAME" %p: port %p", c, p); + spa_list_init(&p->mix); port_info = SPA_PORT_INFO_INIT(); @@ -2861,7 +2869,7 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) if (ptr == NULL) { ptr = p->emptyptr; if (!p->zeroed) { - init_buffer(p, ptr, BUFFER_SIZE_MAX * sizeof(float)); + init_buffer(p, ptr, MAX_BUFFER_FRAMES); p->zeroed = true; } } @@ -3387,7 +3395,7 @@ size_t jack_port_type_get_buffer_size (jack_client_t *client, const char *port_t if (!strcmp(JACK_DEFAULT_AUDIO_TYPE, port_type)) return jack_get_buffer_size(client) * sizeof(float); else if (!strcmp(JACK_DEFAULT_MIDI_TYPE, port_type)) - return BUFFER_SIZE_MAX; + return MAX_BUFFER_FRAMES * sizeof(float); else return 0; } @@ -3473,7 +3481,7 @@ static int port_compare_func(const void *v1, const void *v2) if ((*o1)->port.priority != (*o2)->port.priority) return (*o2)->port.priority - (*o1)->port.priority; - return (*o1)->id - (*o2)->id; + return (*o1)->id - (*o2)->id; } SPA_EXPORT From 4f47d6e05221dfd2f9cc5ef16243004a40e56d9a Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 28 Oct 2019 13:19:46 +0100 Subject: [PATCH 128/133] jack: also return video DSP ports --- src/pipewire-jack.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 16cf3b261..6a299c7e3 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -44,6 +44,8 @@ #include "extensions/client-node.h" +#define JACK_DEFAULT_VIDEO_TYPE "32 bit float RGBA video" + #define JACK_CLIENT_NAME_SIZE 64 #define JACK_PORT_NAME_SIZE 256 #define JACK_PORT_MAX 4096 @@ -1702,8 +1704,10 @@ static jack_port_type_id_t string_to_type(const char *port_type) return 0; else if (!strcmp(JACK_DEFAULT_MIDI_TYPE, port_type)) return 1; - else if (!strcmp("other", port_type)) + else if (!strcmp(JACK_DEFAULT_VIDEO_TYPE, port_type)) return 2; + else if (!strcmp("other", port_type)) + return 3; else return SPA_ID_INVALID; } @@ -1716,6 +1720,8 @@ static const char* type_to_string(jack_port_type_id_t type_id) case 1: return JACK_DEFAULT_MIDI_TYPE; case 2: + return JACK_DEFAULT_VIDEO_TYPE; + case 3: return "other"; default: return NULL; @@ -1760,12 +1766,10 @@ static void registry_event_global(void *data, uint32_t id, uint32_t node_id; char full_name[1024]; - if ((str = spa_dict_lookup(props, PW_KEY_FORMAT_DSP)) == NULL) { - type_id = 2; - } - else - if ((type_id = string_to_type(str)) == SPA_ID_INVALID) - goto exit; + if ((str = spa_dict_lookup(props, PW_KEY_FORMAT_DSP)) == NULL) + str = "other"; + if ((type_id = string_to_type(str)) == SPA_ID_INVALID) + goto exit; if ((str = spa_dict_lookup(props, PW_KEY_NODE_ID)) == NULL) goto exit; @@ -3519,7 +3523,7 @@ const char ** jack_get_ports (jack_client_t *client, o->port.type_id, o->port.flags, o->port.name); if (count == JACK_PORT_MAX) break; - if (o->port.type_id > 1) + if (o->port.type_id > 2) continue; if (!SPA_FLAG_IS_SET(o->port.flags, flags)) continue; From e3e92bb44323ef343ce7ceb5adc1d4fe3eef5ed0 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 28 Oct 2019 15:03:31 +0100 Subject: [PATCH 129/133] jack: add some more video type handling --- src/pipewire-jack.c | 96 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 82 insertions(+), 14 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 6a299c7e3..2ca6310f9 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -35,6 +35,7 @@ #include #include +#include #include #include @@ -1266,6 +1267,21 @@ static int param_enum_format(struct client *c, struct port *p, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_application), SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_control)); break; + case 2: + *param = spa_pod_builder_add_object(b, + SPA_TYPE_OBJECT_Format, SPA_PARAM_Format, + SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), + SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), + SPA_FORMAT_VIDEO_format, SPA_POD_Id(SPA_VIDEO_FORMAT_RGBA_F32), + SPA_FORMAT_VIDEO_size, SPA_POD_CHOICE_RANGE_Rectangle( + &SPA_RECTANGLE(320, 240), + &SPA_RECTANGLE(1,1), + &SPA_RECTANGLE(INT32_MAX, INT32_MAX)), + SPA_FORMAT_VIDEO_framerate, SPA_POD_CHOICE_RANGE_Fraction( + &SPA_FRACTION(25,1), + &SPA_FRACTION(0,1), + &SPA_FRACTION(INT32_MAX,1))); + break; default: return -EINVAL; } @@ -1303,6 +1319,21 @@ static int param_format(struct client *c, struct port *p, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_application), SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_control)); break; + case 2: + *param = spa_pod_builder_add_object(b, + SPA_TYPE_OBJECT_Format, SPA_PARAM_Format, + SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), + SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), + SPA_FORMAT_VIDEO_format, SPA_POD_Id(SPA_VIDEO_FORMAT_RGBA_F32), + SPA_FORMAT_VIDEO_size, SPA_POD_CHOICE_RANGE_Rectangle( + &SPA_RECTANGLE(320, 240), + &SPA_RECTANGLE(1,1), + &SPA_RECTANGLE(INT32_MAX, INT32_MAX)), + SPA_FORMAT_VIDEO_framerate, SPA_POD_CHOICE_RANGE_Fraction( + &SPA_FRACTION(25,1), + &SPA_FRACTION(0,1), + &SPA_FRACTION(INT32_MAX,1))); + break; default: return -EINVAL; } @@ -1312,17 +1343,36 @@ static int param_format(struct client *c, struct port *p, static int param_buffers(struct client *c, struct port *p, struct spa_pod **param, struct spa_pod_builder *b) { - *param = spa_pod_builder_add_object(b, - SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, - SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(1, 1, MAX_BUFFERS), - SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1), - SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_STEP_Int( - MAX_BUFFER_FRAMES * sizeof(float), - sizeof(float), - MAX_BUFFER_FRAMES * sizeof(float), - sizeof(float)), - SPA_PARAM_BUFFERS_stride, SPA_POD_Int(4), - SPA_PARAM_BUFFERS_align, SPA_POD_Int(16)); + switch (p->object->port.type_id) { + case 0: + case 1: + *param = spa_pod_builder_add_object(b, + SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, + SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(1, 1, MAX_BUFFERS), + SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1), + SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_STEP_Int( + MAX_BUFFER_FRAMES * sizeof(float), + sizeof(float), + MAX_BUFFER_FRAMES * sizeof(float), + sizeof(float)), + SPA_PARAM_BUFFERS_stride, SPA_POD_Int(4), + SPA_PARAM_BUFFERS_align, SPA_POD_Int(16)); + break; + case 2: + *param = spa_pod_builder_add_object(b, + SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, + SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(1, 1, MAX_BUFFERS), + SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1), + SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_RANGE_Int( + 320 * 240 * 4 * 4, + 0, + INT32_MAX), + SPA_PARAM_BUFFERS_stride, SPA_POD_CHOICE_RANGE_Int(4, 4, INT32_MAX), + SPA_PARAM_BUFFERS_align, SPA_POD_Int(16)); + break; + default: + return -EINVAL; + } return 1; } @@ -1350,11 +1400,11 @@ static int port_set_format(struct client *c, struct port *p, } else { struct spa_audio_info info = { 0 }; - spa_format_parse(param, &info.media_type, &info.media_subtype); switch (info.media_type) { case SPA_MEDIA_TYPE_audio: + { if (info.media_subtype != SPA_MEDIA_SUBTYPE_raw) return -EINVAL; @@ -1363,11 +1413,21 @@ static int port_set_format(struct client *c, struct port *p, p->rate = info.info.raw.rate; break; - + } case SPA_MEDIA_TYPE_application: if (info.media_subtype != SPA_MEDIA_SUBTYPE_control) return -EINVAL; break; + case SPA_MEDIA_TYPE_video: + { + struct spa_video_info info = { 0 }; + + if (info.media_subtype != SPA_MEDIA_SUBTYPE_raw) + return -EINVAL; + if (spa_format_video_raw_parse(param, &info.info.raw) < 0) + return -EINVAL; + break; + } default: return -EINVAL; } @@ -2798,7 +2858,7 @@ static inline void *get_buffer_input_midi(struct client *c, struct port *p, jack struct spa_pod_sequence *seq[CONNECTION_NUM_FOR_PORT]; uint32_t n_seq = 0; - jack_midi_reset_buffer(ptr); + jack_midi_clear_buffer(ptr); spa_list_for_each(mix, &p->mix, port_link) { struct spa_data *d; @@ -2869,6 +2929,9 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) case 1: ptr = get_buffer_input_midi(c, p, frames); break; + case 2: + ptr = get_buffer_input_float(c, p, frames); + break; } if (ptr == NULL) { ptr = p->emptyptr; @@ -2885,6 +2948,9 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) case 1: ptr = get_buffer_output_midi(c, p, frames); break; + case 2: + ptr = get_buffer_output_float(c, p, frames); + break; } } @@ -3400,6 +3466,8 @@ size_t jack_port_type_get_buffer_size (jack_client_t *client, const char *port_t return jack_get_buffer_size(client) * sizeof(float); else if (!strcmp(JACK_DEFAULT_MIDI_TYPE, port_type)) return MAX_BUFFER_FRAMES * sizeof(float); + else if (!strcmp(JACK_DEFAULT_VIDEO_TYPE, port_type)) + return 320 * 240 * 4 * sizeof(float); else return 0; } From 42a33a472e6ab0533a00d171ebd4548591674b62 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 28 Oct 2019 18:03:46 +0100 Subject: [PATCH 130/133] jack: fix video issues --- src/pipewire-jack.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 2ca6310f9..92c85cd94 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -1269,7 +1269,7 @@ static int param_enum_format(struct client *c, struct port *p, break; case 2: *param = spa_pod_builder_add_object(b, - SPA_TYPE_OBJECT_Format, SPA_PARAM_Format, + SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), SPA_FORMAT_VIDEO_format, SPA_POD_Id(SPA_VIDEO_FORMAT_RGBA_F32), @@ -1420,11 +1420,11 @@ static int port_set_format(struct client *c, struct port *p, break; case SPA_MEDIA_TYPE_video: { - struct spa_video_info info = { 0 }; + struct spa_video_info vinfo = { 0 }; if (info.media_subtype != SPA_MEDIA_SUBTYPE_raw) return -EINVAL; - if (spa_format_video_raw_parse(param, &info.info.raw) < 0) + if (spa_format_video_raw_parse(param, &vinfo.info.raw) < 0) return -EINVAL; break; } @@ -1511,9 +1511,12 @@ static int client_node_port_use_buffers(void *object, pw_log_debug(NAME" %p: port %p %d %d.%d use_buffers %d", c, p, direction, port_id, mix_id, n_buffers); - - /* some apps write to the input buffer so we want everything readwrite */ - fl = PW_MEMMAP_FLAG_READWRITE; + if (p->object->port.type_id == 2 && direction == SPA_DIRECTION_INPUT) { + fl = PW_MEMMAP_FLAG_READ; + } else { + /* some apps write to the input buffer so we want everything readwrite */ + fl = PW_MEMMAP_FLAG_READWRITE; + } /* clear previous buffers */ clear_buffers(c, mix); From 068b7e775e4866b94009ae06b3161c1e01ee00be Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 28 Oct 2019 18:04:45 +0100 Subject: [PATCH 131/133] jack: add video example --- examples/video-dsp-play.c | 206 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 examples/video-dsp-play.c diff --git a/examples/video-dsp-play.c b/examples/video-dsp-play.c new file mode 100644 index 000000000..e67c33055 --- /dev/null +++ b/examples/video-dsp-play.c @@ -0,0 +1,206 @@ +/* PipeWire + * + * Copyright © 2019 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include + +#include + +#include + +#define WIDTH 640 +#define HEIGHT 480 +#define BPP 3 + +#define MAX_BUFFERS 64 + +#define JACK_DEFAULT_VIDEO_TYPE "32 bit float RGBA video" + +#define CLAMP(v,low,high) \ +({ \ + __typeof__(v) _v = (v); \ + __typeof__(low) _low = (low); \ + __typeof__(high) _high = (high); \ + (_v < _low) ? _low : (_v > _high) ? _high : _v; \ +}) + +struct pixel { + float r, g, b, a; +}; + +struct data { + const char *path; + + SDL_Renderer *renderer; + SDL_Window *window; + SDL_Texture *texture; + SDL_Texture *cursor; + + jack_client_t *client; + const char *client_name; + jack_port_t *in_port; + + uint32_t width; + uint32_t height; + int32_t stride; + + int counter; + SDL_Rect rect; + SDL_Rect cursor_rect; +}; + +static void handle_events(struct data *data) +{ + SDL_Event event; + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_QUIT: + exit(0); + break; + } + } +} + +static int +process (jack_nframes_t nframes, void *arg) +{ + struct data *data = (struct data*)arg; + void *sdata, *ddata; + int sstride, dstride; + uint32_t i, j; + uint8_t *src, *dst; + + sdata = jack_port_get_buffer (data->in_port, nframes); + + handle_events(data); + + if (SDL_LockTexture(data->texture, NULL, &ddata, &dstride) < 0) { + fprintf(stderr, "Couldn't lock texture: %s\n", SDL_GetError()); + goto done; + } + + /* copy video image in texture */ + sstride = data->stride; + + src = sdata; + dst = ddata; + + for (i = 0; i < data->height; i++) { + struct pixel *p = (struct pixel *) src; + for (j = 0; j < data->width; j++) { + dst[j * 4 + 0] = CLAMP(lrintf(p[j].r * 255.0f), 0, 255); + dst[j * 4 + 1] = CLAMP(lrintf(p[j].g * 255.0f), 0, 255); + dst[j * 4 + 2] = CLAMP(lrintf(p[j].b * 255.0f), 0, 255); + dst[j * 4 + 3] = CLAMP(lrintf(p[j].a * 255.0f), 0, 255); + } + src += sstride; + dst += dstride; + } + SDL_UnlockTexture(data->texture); + + SDL_RenderClear(data->renderer); + SDL_RenderCopy(data->renderer, data->texture, &data->rect, NULL); + SDL_RenderPresent(data->renderer); + + done: + return 0; +} + +int main(int argc, char *argv[]) +{ + struct data data = { 0, }; + jack_options_t options = JackNullOption; + jack_status_t status; + + data.client = jack_client_open ("video-dsp-play", options, &status); + if (data.client == NULL) { + fprintf (stderr, "jack_client_open() failed, " + "status = 0x%2.0x\n", status); + if (status & JackServerFailed) { + fprintf (stderr, "Unable to connect to JACK server\n"); + } + exit (1); + } + if (status & JackServerStarted) { + fprintf (stderr, "JACK server started\n"); + } + if (status & JackNameNotUnique) { + data.client_name = jack_get_client_name(data.client); + fprintf (stderr, "unique name `%s' assigned\n", data.client_name); + } + + jack_set_process_callback (data.client, process, &data); + + data.width = 320; + data.height = 240; + data.stride = data.width * 4 * sizeof(float); + + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + fprintf(stderr, "can't initialize SDL: %s\n", SDL_GetError()); + return -1; + } + + if (SDL_CreateWindowAndRenderer + (WIDTH, HEIGHT, SDL_WINDOW_RESIZABLE, &data.window, &data.renderer)) { + fprintf(stderr, "can't create window: %s\n", SDL_GetError()); + return -1; + } + + data.texture = SDL_CreateTexture(data.renderer, + SDL_PIXELFORMAT_RGBA32, + SDL_TEXTUREACCESS_STREAMING, + data.width, + data.height); + data.rect.x = 0; + data.rect.y = 0; + data.rect.w = data.width; + data.rect.h = data.height; + + data.in_port = jack_port_register (data.client, "input", + JACK_DEFAULT_VIDEO_TYPE, + JackPortIsInput, 0); + + if (data.in_port == NULL) { + fprintf(stderr, "no more JACK ports available\n"); + exit (1); + } + + if (jack_activate (data.client)) { + fprintf (stderr, "cannot activate client"); + exit (1); + } + + while (1) { + sleep (1); + } + + jack_client_close (data.client); + + SDL_DestroyTexture(data.texture); + SDL_DestroyRenderer(data.renderer); + SDL_DestroyWindow(data.window); + + return 0; +} From 577f06cfebf26f40e6adb40ec8be185ca62846d2 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 28 Oct 2019 19:02:04 +0100 Subject: [PATCH 132/133] Fix and use constants --- examples/video-dsp-play.c | 10 +++++----- src/meson.build | 9 +++++++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/examples/video-dsp-play.c b/examples/video-dsp-play.c index e67c33055..e503ef709 100644 --- a/examples/video-dsp-play.c +++ b/examples/video-dsp-play.c @@ -30,9 +30,9 @@ #include -#define WIDTH 640 -#define HEIGHT 480 -#define BPP 3 +#define WIDTH 320 +#define HEIGHT 240 +#define BPP 16 #define MAX_BUFFERS 64 @@ -153,8 +153,8 @@ int main(int argc, char *argv[]) jack_set_process_callback (data.client, process, &data); - data.width = 320; - data.height = 240; + data.width = WIDTH; + data.height = HEIGHT; data.stride = data.width * 4 * sizeof(float); if (SDL_Init(SDL_INIT_VIDEO) < 0) { diff --git a/src/meson.build b/src/meson.build index a2269e7b4..7287ab482 100644 --- a/src/meson.build +++ b/src/meson.build @@ -22,3 +22,12 @@ pipewire_jack = shared_library('jack', dependencies : [pipewire_dep, jack_dep, mathlib], install : false, ) + +if sdl_dep.found() + executable('video-dsp-play', + '../examples/video-dsp-play.c', + c_args : [ '-D_GNU_SOURCE' ], + install: false, + dependencies : [jack_dep, sdl_dep, mathlib], + ) +endif From 9ed29aafd4e870bbfe81cb9c6700816581dd772c Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 29 Oct 2019 10:10:07 +0100 Subject: [PATCH 133/133] jack: warn -> debug for thread functions --- src/pipewire-jack.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 92c85cd94..736f65725 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -4101,9 +4101,9 @@ int jack_client_stop_thread(jack_client_t* client, jack_native_thread_t thread) if (thread == (jack_native_thread_t)NULL) return -1; - pw_log_warn("join thread %lu", thread); + pw_log_debug("join thread %lu", thread); pthread_join(thread, &status); - pw_log_warn("stopped thread %lu", thread); + pw_log_debug("stopped thread %lu", thread); return 0; } @@ -4115,11 +4115,11 @@ int jack_client_kill_thread(jack_client_t* client, jack_native_thread_t thread) if (thread == (jack_native_thread_t)NULL) return -1; - pw_log_warn("cancel thread %lu", thread); + pw_log_debug("cancel thread %lu", thread); pthread_cancel(thread); - pw_log_warn("join thread %lu", thread); + pw_log_debug("join thread %lu", thread); pthread_join(thread, &status); - pw_log_warn("stopped thread %lu", thread); + pw_log_debug("stopped thread %lu", thread); return 0; }