├── .gitignore
├── LICENSE
├── Opensegaapi
├── premake5.lua
└── src
│ ├── Opensegaapi.rc
│ ├── opensegaapi.cpp
│ ├── opensegaapi.h
│ ├── resource.h
│ └── tsf.h
├── README.md
├── appveyor.yml
├── build.ps1
├── premake5.bat
├── premake5.exe
└── premake5.lua
/.gitignore:
--------------------------------------------------------------------------------
1 | *.sln
2 | *.vcxproj
3 | *.vcxproj.filters
4 | .vs/
5 | build/
6 | Debug/
7 |
8 | ParrotLoader/Release/
9 |
10 | Release/
11 |
12 | TeknoParrot\.sdf
13 |
14 | TeknoParrot\.v12\.suo
15 |
16 | TeknoParrot/TeknoParrot\.vcxproj\.user
17 |
18 | ParrotLoader/ParrotLoader\.vcxproj\.user
19 |
20 | *.user
21 | *.aps
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/Opensegaapi/premake5.lua:
--------------------------------------------------------------------------------
1 | project "Opensegaapi"
2 | targetname "Opensegaapi"
3 | language "C++"
4 | kind "SharedLib"
5 | removeplatforms { "x64" }
6 |
7 | files
8 | {
9 | "src/**.cpp", "src/**.h",
10 | "deps/cpp/**.cpp", "deps/inc/**.h",
11 | "src/Opensegaapi.aps", "src/Opensegaapi.rc"
12 | }
13 |
14 | includedirs { "src" }
15 |
16 | postbuildcommands {
17 | "if not exist $(TargetDir)output mkdir $(TargetDir)output",
18 | "{COPY} $(TargetDir)Opensegaapi.dll $(TargetDir)output/"
19 | }
--------------------------------------------------------------------------------
/Opensegaapi/src/Opensegaapi.rc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/teknogods/OpenSegaAPI/037bc53769dac693b7fd0cda6cd80864753dfcef/Opensegaapi/src/Opensegaapi.rc
--------------------------------------------------------------------------------
/Opensegaapi/src/opensegaapi.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the OpenParrot project - https://teknoparrot.com / https://github.com/teknogods
3 | *
4 | * See LICENSE and MENTIONS in the root of the source tree for information
5 | * regarding licensing.
6 | */
7 | extern "C" {
8 | #include "opensegaapi.h"
9 | }
10 |
11 | #include
12 | #define XAUDIO2_HELPER_FUNCTIONS
13 | #include
14 | #include
15 | #define TSF_IMPLEMENTATION
16 | #include "tsf.h"
17 | #define CHECK_HR(exp) { HRESULT hr = exp; if (FAILED(hr)) { printf("failed %s: %08x\n", #exp, hr); abort(); } }
18 | #pragma comment(lib, "xaudio2.lib")
19 |
20 | namespace WRL = Microsoft::WRL;
21 |
22 | const GUID OPEN_EAX_NULL_GUID;
23 | const GUID OPEN_EAX_FREQUENCYSHIFTER_EFFECT;
24 | const GUID OPEN_EAX_ECHO_EFFECT;
25 | const GUID OPEN_EAX_REVERB_EFFECT;
26 | const GUID OPEN_EAX_EQUALIZER_EFFECT;
27 | const GUID OPEN_EAX_DISTORTION_EFFECT;
28 | const GUID OPEN_EAX_AGCCOMPRESSOR_EFFECT;
29 | const GUID OPEN_EAX_PITCHSHIFTER_EFFECT;
30 | const GUID OPEN_EAX_FLANGER_EFFECT;
31 | const GUID OPEN_EAX_VOCALMORPHER_EFFECT;
32 | const GUID OPEN_EAX_AUTOWAH_EFFECT;
33 | const GUID OPEN_EAX_RINGMODULATOR_EFFECT;
34 | const GUID OPEN_EAX_CHORUS_EFFECT;
35 |
36 | const GUID OPEN_EAXPROPERTYID_EAX40_FXSlot0;
37 | const GUID OPEN_EAXPROPERTYID_EAX40_FXSlot1;
38 | const GUID OPEN_EAXPROPERTYID_EAX40_FXSlot2;
39 | const GUID OPEN_EAXPROPERTYID_EAX40_FXSlot3;
40 |
41 | #include
42 | #include
43 |
44 | struct OPEN_segaapiBuffer_t;
45 |
46 | #ifdef _DEBUG
47 | void info(const char* format, ...)
48 | {
49 | va_list args;
50 | char buffer[1024];
51 |
52 | va_start(args, format);
53 | int len = _vsnprintf_s(buffer, sizeof(buffer), format, args);
54 | va_end(args);
55 |
56 | buffer[len] = '\n';
57 | buffer[len + 1] = '\0';
58 |
59 | OutputDebugStringA(buffer);
60 | }
61 | #else
62 | #define info(x) {}
63 | #endif
64 |
65 | class XA2Callback : public IXAudio2VoiceCallback
66 | {
67 | public:
68 | void __stdcall OnVoiceProcessingPassStart(UINT32 BytesRequired) override
69 | {
70 | }
71 |
72 | void __stdcall OnVoiceProcessingPassEnd() override
73 | {
74 | }
75 |
76 | void __stdcall OnStreamEnd() override
77 | {
78 | }
79 |
80 | void __stdcall OnBufferStart(void*) override
81 | {
82 | }
83 |
84 | void __stdcall OnLoopEnd(void*) override
85 | {
86 | }
87 |
88 | void __stdcall OnVoiceError(void*, HRESULT) override
89 | {
90 | }
91 |
92 | void __stdcall OnBufferEnd(void* cxt) override;
93 |
94 | public:
95 | OPEN_segaapiBuffer_t * buffer;
96 | };
97 |
98 | struct OPEN_segaapiBuffer_t
99 | {
100 | void* userData;
101 | OPEN_HAWOSEGABUFFERCALLBACK callback;
102 | bool synthesizer;
103 | bool loop;
104 | unsigned int channels;
105 | unsigned int startLoop;
106 | unsigned int endLoop;
107 | unsigned int endOffset;
108 | unsigned int sampleRate;
109 | unsigned int sampleFormat;
110 | uint8_t* data;
111 | size_t size;
112 | bool playing;
113 | bool paused;
114 | bool playWithSetup;
115 |
116 | WAVEFORMATEX xaFormat;
117 |
118 | XAUDIO2_BUFFER xaBuffer;
119 | IXAudio2SourceVoice* xaVoice;
120 |
121 | float sendVolumes[7];
122 | int sendChannels[7];
123 | OPEN_HAROUTING sendRoutes[7];
124 | float channelVolumes[6];
125 |
126 | tsf* synth;
127 | tsf_region* region;
128 |
129 | XA2Callback xaCallback;
130 |
131 | concurrency::concurrent_queue> defers;
132 | };
133 |
134 | void XA2Callback::OnBufferEnd(void* cxt)
135 | {
136 | std::function entry;
137 |
138 | while (!buffer->defers.empty())
139 | {
140 | XAUDIO2_VOICE_STATE vs;
141 | buffer->xaVoice->GetState(&vs);
142 |
143 | if (vs.BuffersQueued > 0)
144 | {
145 | buffer->xaVoice->FlushSourceBuffers();
146 |
147 | return;
148 | }
149 |
150 | if (buffer->defers.try_pop(entry))
151 | {
152 | entry();
153 | }
154 | }
155 | }
156 |
157 | template
158 | void defer_buffer_call(OPEN_segaapiBuffer_t* buffer, const TFn& fn)
159 | {
160 | if (buffer->xaVoice)
161 | {
162 | XAUDIO2_VOICE_STATE vs;
163 | buffer->xaVoice->GetState(&vs);
164 |
165 | if (vs.BuffersQueued > 0)
166 | {
167 | buffer->defers.push(fn);
168 |
169 | buffer->xaVoice->FlushSourceBuffers();
170 |
171 | return;
172 | }
173 | }
174 | fn();
175 | }
176 |
177 | static void dumpWaveBuffer(const char* path, unsigned int channels, unsigned int sampleRate, unsigned int sampleBits, void* data, size_t size)
178 | {
179 | info("dumpWaveBuffer path %s channels %d sampleRate %d sampleBits %d size %d", path, channels, sampleRate, sampleBits, size);
180 |
181 | struct RIFF_Header
182 | {
183 | char chunkID[4];
184 | long chunkSize;
185 | char format[4];
186 | };
187 |
188 | struct WAVE_Format
189 | {
190 | char subChunkID[4];
191 | long subChunkSize;
192 | short audioFormat;
193 | short numChannels;
194 | long sampleRate;
195 | long byteRate;
196 | short blockAlign;
197 | short bitsPerSample;
198 | };
199 |
200 | struct WAVE_Data
201 | {
202 | char subChunkID[4];
203 | long subChunk2Size;
204 | };
205 |
206 | FILE* soundFile = NULL;
207 | struct WAVE_Format wave_format;
208 | struct RIFF_Header riff_header;
209 | struct WAVE_Data wave_data;
210 |
211 | soundFile = fopen(path, "wb");
212 |
213 | riff_header.chunkID[0] = 'R';
214 | riff_header.chunkID[1] = 'I';
215 | riff_header.chunkID[2] = 'F';
216 | riff_header.chunkID[3] = 'F';
217 | riff_header.format[0] = 'W';
218 | riff_header.format[1] = 'A';
219 | riff_header.format[2] = 'V';
220 | riff_header.format[3] = 'E';
221 |
222 | fwrite(&riff_header, sizeof(struct RIFF_Header), 1, soundFile);
223 |
224 | wave_format.subChunkID[0] = 'f';
225 | wave_format.subChunkID[1] = 'm';
226 | wave_format.subChunkID[2] = 't';
227 | wave_format.subChunkID[3] = ' ';
228 |
229 | wave_format.audioFormat = 1;
230 | wave_format.sampleRate = sampleRate;
231 | wave_format.numChannels = channels;
232 | wave_format.bitsPerSample = sampleBits;
233 | wave_format.byteRate = (sampleRate * sampleBits * channels) / 8;
234 | wave_format.blockAlign = (sampleBits * channels) / 8;
235 | wave_format.subChunkSize = 16;
236 |
237 | fwrite(&wave_format, sizeof(struct WAVE_Format), 1, soundFile);
238 |
239 | wave_data.subChunkID[0] = 'd';
240 | wave_data.subChunkID[1] = 'a';
241 | wave_data.subChunkID[2] = 't';
242 | wave_data.subChunkID[3] = 'a';
243 |
244 | wave_data.subChunk2Size = size;
245 |
246 | fwrite(&wave_data, sizeof(struct WAVE_Data), 1, soundFile);
247 | fwrite(data, wave_data.subChunk2Size, 1, soundFile);
248 |
249 | fclose(soundFile);
250 | }
251 |
252 | static unsigned int bufferSampleSize(OPEN_segaapiBuffer_t* buffer)
253 | {
254 | return buffer->channels * ((buffer->sampleFormat == OPEN_HASF_SIGNED_16PCM) ? 2 : 1);
255 | }
256 |
257 | static void updateSynthOnPlay(OPEN_segaapiBuffer_t* buffer, unsigned int offset, size_t length)
258 | {
259 | // TODO
260 | //// synth
261 | //if (buffer->synthesizer)
262 | //{
263 | // if (!buffer->synth->voices)
264 | // {
265 | // struct tsf_voice *voice = new tsf_voice;
266 | // memset(voice, 0, sizeof(tsf_voice));
267 |
268 | // auto region = buffer->region;
269 |
270 | // TSF_BOOL doLoop; float filterQDB;
271 | // voice->playingPreset = -1;
272 |
273 | // voice->region = region;
274 | // voice->noteGainDB = 0.0f - region->volume;
275 |
276 | // tsf_voice_calcpitchratio(voice, 0, buffer->synth->outSampleRate);
277 | // // The SFZ spec is silent about the pan curve, but a 3dB pan law seems common. This sqrt() curve matches what Dimension LE does; Alchemy Free seems closer to sin(adjustedPan * pi/2).
278 | // voice->panFactorLeft = TSF_SQRTF(0.5f - region->pan);
279 | // voice->panFactorRight = TSF_SQRTF(0.5f + region->pan);
280 |
281 | // // Offset/end.
282 | // voice->sourceSamplePosition = region->offset;
283 |
284 | // // Loop.
285 | // doLoop = (region->loop_mode != TSF_LOOPMODE_NONE && region->loop_start < region->loop_end);
286 | // voice->loopStart = (doLoop ? region->loop_start : 0);
287 | // voice->loopEnd = (doLoop ? region->loop_end : 0);
288 |
289 | // // Setup envelopes.
290 | // tsf_voice_envelope_setup(&voice->ampenv, ®ion->ampenv, 0, 0, TSF_TRUE, buffer->synth->outSampleRate);
291 | // tsf_voice_envelope_setup(&voice->modenv, ®ion->modenv, 0, 0, TSF_FALSE, buffer->synth->outSampleRate);
292 |
293 | // // Setup lowpass filter.
294 | // filterQDB = region->initialFilterQ / 10.0f;
295 | // voice->lowpass.QInv = 1.0 / TSF_POW(10.0, (filterQDB / 20.0));
296 | // voice->lowpass.z1 = voice->lowpass.z2 = 0;
297 | // voice->lowpass.active = (region->initialFilterFc *=*/ 13500);
298 | // if (voice->lowpass.active) tsf_voice_lowpass_setup(&voice->lowpass, tsf_cents2Hertz((float)region->initialFilterFc) / buffer->synth->outSampleRate);
299 |
300 | // // Setup LFO filters.
301 | // tsf_voice_lfo_setup(&voice->modlfo, region->delayModLFO, region->freqModLFO, buffer->synth->outSampleRate);
302 | // tsf_voice_lfo_setup(&voice->viblfo, region->delayVibLFO, region->freqVibLFO, buffer->synth->outSampleRate);
303 |
304 | // voice->pitchInputTimecents = (log(1.0) / log(2.0) * 1200);
305 | // voice->pitchOutputFactor = 1.0f;
306 |
307 | // buffer->synth->voices = voice;
308 | // }
309 |
310 | // buffer->synth->voices->region = buffer->region;
311 |
312 | // // make input
313 | // buffer->synth->outputmode = TSF_MONO;
314 |
315 | // auto soffset = offset;
316 | // auto slength = length;
317 |
318 | // if (offset == -1)
319 | // {
320 | // soffset = 0;
321 | // }
322 |
323 | // if (length == -1)
324 | // {
325 | // slength = buffer->size;
326 | // }
327 |
328 | // std::vector fontSamples(slength / bufferSampleSize(buffer));
329 | // buffer->synth->fontSamples = &fontSamples[0];
330 |
331 | // buffer->region->end = double(fontSamples.size());
332 |
333 | // for (int i = 0; i < fontSamples.size(); i++)
334 | // {
335 | // if (buffer->sampleFormat == OPEN_HASF_UNSIGNED_8PCM)
336 | // {
337 | // fontSamples[i] = (buffer->data[soffset + i] / 128.0f) - 1.0f;
338 | // }
339 | // else if (buffer->sampleFormat == OPEN_HASF_SIGNED_16PCM)
340 | // {
341 | // fontSamples[i] = (*(int16_t*)&buffer->data[soffset + (i * 2)]) / 32768.0f;
342 | // }
343 | // }
344 |
345 | // std::vector outSamples(slength / bufferSampleSize(buffer));
346 | // tsf_voice_render(buffer->synth, buffer->synth->voices, &outSamples[0], outSamples.size());
347 |
348 | // for (int i = 0; i < outSamples.size(); i++)
349 | // {
350 | // if (buffer->sampleFormat == OPEN_HASF_UNSIGNED_8PCM)
351 | // {
352 | // buffer->data[soffset + i] = (uint8_t)((outSamples[i] + 1.0f) * 128.0f);
353 | // }
354 | // else if (buffer->sampleFormat == OPEN_HASF_SIGNED_16PCM)
355 | // {
356 | // *(int16_t*)&buffer->data[soffset + (i * 2)] = outSamples[i] * 32768.0f;
357 | // }
358 | // }
359 | //}
360 | }
361 |
362 | static void resetBuffer(OPEN_segaapiBuffer_t* buffer)
363 | {
364 | buffer->startLoop = 0;
365 | buffer->endOffset = buffer->size;
366 | buffer->endLoop = buffer->size;
367 | buffer->loop = false;
368 | buffer->paused = false;
369 | buffer->playWithSetup = false;
370 | buffer->sendRoutes[0] = OPEN_HA_FRONT_LEFT_PORT;
371 | buffer->sendRoutes[1] = OPEN_HA_FRONT_RIGHT_PORT;
372 | buffer->sendRoutes[2] = OPEN_HA_UNUSED_PORT;
373 | buffer->sendRoutes[3] = OPEN_HA_UNUSED_PORT;
374 | buffer->sendRoutes[4] = OPEN_HA_UNUSED_PORT;
375 | buffer->sendRoutes[5] = OPEN_HA_UNUSED_PORT;
376 | buffer->sendRoutes[6] = OPEN_HA_UNUSED_PORT;
377 | buffer->sendVolumes[0] = 0.0f;
378 | buffer->sendVolumes[1] = 0.0f;
379 | buffer->sendVolumes[2] = 0.0f;
380 | buffer->sendVolumes[3] = 0.0f;
381 | buffer->sendVolumes[4] = 0.0f;
382 | buffer->sendVolumes[5] = 0.0f;
383 | buffer->sendVolumes[6] = 0.0f;
384 | buffer->channelVolumes[0] = 1.0f;
385 | buffer->channelVolumes[1] = 1.0f;
386 | buffer->channelVolumes[2] = 1.0f;
387 | buffer->channelVolumes[3] = 1.0f;
388 | buffer->channelVolumes[4] = 1.0f;
389 | buffer->channelVolumes[5] = 1.0f;
390 | buffer->sendChannels[0] = 0;
391 | buffer->sendChannels[1] = 1;
392 | buffer->sendChannels[2] = 0;
393 | buffer->sendChannels[3] = 0;
394 | buffer->sendChannels[4] = 0;
395 | buffer->sendChannels[5] = 0;
396 | buffer->sendChannels[6] = 0;
397 |
398 | auto res = (tsf*)TSF_MALLOC(sizeof(tsf));
399 | TSF_MEMSET(res, 0, sizeof(tsf));
400 | res->presetNum = 0;
401 | res->outSampleRate = buffer->sampleRate;
402 |
403 | buffer->synth = res;
404 |
405 | tsf_region* region = new tsf_region;
406 | memset(region, 0, sizeof(tsf_region));
407 |
408 | tsf_region_clear(region, 0);
409 |
410 | region->ampenv.delay = 0;
411 | region->ampenv.hold = 300.0f;
412 | region->ampenv.attack = 0;
413 | region->ampenv.decay = 0;
414 | region->ampenv.release = 0;
415 | region->ampenv.sustain = 0;
416 |
417 | buffer->region = region;
418 | }
419 |
420 | static WRL::ComPtr g_xa2;
421 | static IXAudio2MasteringVoice* g_masteringVoice;
422 | static IXAudio2SubmixVoice* g_submixVoices[6];
423 |
424 | static void updateBufferNew(OPEN_segaapiBuffer_t* buffer, unsigned int offset, size_t length)
425 | {
426 | // don't update with pending defers
427 | if (!buffer->defers.empty())
428 | {
429 | info("updateBufferNew: DEFER!");
430 | return;
431 | }
432 |
433 | CHECK_HR(buffer->xaVoice->FlushSourceBuffers());
434 |
435 | buffer->xaBuffer.Flags = 0;
436 | buffer->xaBuffer.AudioBytes = buffer->size;
437 | buffer->xaBuffer.pAudioData = buffer->data;
438 |
439 | if (buffer->loop)
440 | {
441 | info("updateBufferNew: loop");
442 |
443 | // Note: Sega uses byte offsets for begin and end
444 | // Xaudio2 uses start sample and length in samples
445 | buffer->xaBuffer.PlayBegin = buffer->startLoop / bufferSampleSize(buffer);
446 | buffer->xaBuffer.PlayLength = (min(buffer->endLoop, buffer->endOffset) - buffer->startLoop) / bufferSampleSize(buffer);
447 | buffer->xaBuffer.LoopBegin = buffer->xaBuffer.PlayBegin;
448 | buffer->xaBuffer.LoopLength = buffer->xaBuffer.PlayLength;
449 | buffer->xaBuffer.LoopCount = XAUDIO2_LOOP_INFINITE;
450 | buffer->xaBuffer.pContext = NULL;
451 | }
452 | else
453 | {
454 | info("updateBufferNew: no loop");
455 | buffer->xaBuffer.PlayBegin = buffer->startLoop / bufferSampleSize(buffer);
456 | buffer->xaBuffer.PlayLength = (min(buffer->endLoop, buffer->endOffset) - buffer->startLoop) / bufferSampleSize(buffer);
457 | buffer->xaBuffer.LoopBegin = 0;
458 | buffer->xaBuffer.LoopLength = 0;
459 | buffer->xaBuffer.LoopCount = 0;
460 | buffer->xaBuffer.pContext = NULL;
461 | }
462 |
463 | buffer->xaVoice->SubmitSourceBuffer(&buffer->xaBuffer);
464 |
465 | // Uncomment to dump audio buffers to wav files (super slow)
466 | /*auto sampleBits = (buffer->sampleFormat == OPEN_HASF_SIGNED_16PCM) ? 16 : 8;
467 | char path[255];
468 | sprintf(path, "C:\\dump\\%08X.wav", &buffer);
469 | dumpWaveBuffer(path, buffer->channels, buffer->sampleRate, sampleBits, buffer->data, buffer->size);*/
470 | }
471 |
472 | extern "C" {
473 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_CreateBuffer(OPEN_HAWOSEBUFFERCONFIG* pConfig, OPEN_HAWOSEGABUFFERCALLBACK pCallback, unsigned int dwFlags, void* * phHandle)
474 | {
475 | if (phHandle == NULL || pConfig == NULL)
476 | {
477 | info("SEGAAPI_CreateBuffer: Handle: %08X, Status: OPEN_SEGAERR_BAD_POINTER", phHandle);
478 | return OPEN_SEGAERR_BAD_POINTER;
479 | }
480 |
481 | OPEN_segaapiBuffer_t* buffer = new OPEN_segaapiBuffer_t;
482 |
483 | info("SEGAAPI_CreateBuffer: hHandle: %08X synth: %d, mem caller: %d, mem last: %d, mem alloc: %d, size: %d SampleRate: %d, byNumChans: %d, dwPriority: %d, dwSampleFormat: %d", buffer, (dwFlags & OPEN_HABUF_SYNTH_BUFFER), (dwFlags & OPEN_HABUF_ALLOC_USER_MEM) >> 1, (dwFlags & OPEN_HABUF_USE_MAPPED_MEM) >> 2, dwFlags == 0, pConfig->mapData.dwSize, pConfig->dwSampleRate, pConfig->byNumChans, pConfig->dwPriority, pConfig->dwSampleFormat);
484 |
485 | buffer->playing = false;
486 | buffer->callback = pCallback;
487 | buffer->synthesizer = dwFlags & OPEN_HABUF_SYNTH_BUFFER;
488 | buffer->sampleFormat = pConfig->dwSampleFormat;
489 | buffer->sampleRate = pConfig->dwSampleRate;
490 | buffer->channels = pConfig->byNumChans;
491 | buffer->userData = pConfig->hUserData;
492 | buffer->size = pConfig->mapData.dwSize;
493 | pConfig->mapData.dwOffset = 0;
494 |
495 | // Use buffer supplied by caller
496 | if (dwFlags & OPEN_HABUF_ALLOC_USER_MEM)
497 | {
498 | buffer->data = (uint8_t*)pConfig->mapData.hBufferHdr;
499 | }
500 | // Reuse buffer
501 | else if (dwFlags & OPEN_HABUF_USE_MAPPED_MEM)
502 | {
503 | buffer->data = (uint8_t*)pConfig->mapData.hBufferHdr;
504 | }
505 | // Allocate new buffer (caller will fill it later)
506 | else
507 | {
508 | buffer->data = (uint8_t*)malloc(buffer->size);
509 | }
510 |
511 | pConfig->mapData.hBufferHdr = buffer->data;
512 |
513 | auto sampleRate = pConfig->dwSampleRate;
514 | auto sampleBits = (pConfig->dwSampleFormat == OPEN_HASF_SIGNED_16PCM) ? 16 : 8;
515 | auto channels = pConfig->byNumChans;
516 |
517 | buffer->xaFormat.cbSize = sizeof(WAVEFORMATEX);
518 | buffer->xaFormat.nAvgBytesPerSec = (sampleRate * sampleBits * channels) / 8;
519 | buffer->xaFormat.nSamplesPerSec = sampleRate;
520 | buffer->xaFormat.wBitsPerSample = sampleBits;
521 | buffer->xaFormat.nChannels = channels;
522 | buffer->xaFormat.wFormatTag = 1;
523 | buffer->xaFormat.nBlockAlign = (sampleBits * channels) / 8;
524 | buffer->xaCallback.buffer = buffer;
525 |
526 | CHECK_HR(g_xa2->CreateSourceVoice(&buffer->xaVoice, &buffer->xaFormat, 0, 2.0f, &buffer->xaCallback));
527 |
528 | buffer->xaBuffer = { 0 };
529 |
530 | if (buffer->synthesizer)
531 | {
532 | // Not supported
533 | }
534 | resetBuffer(buffer);
535 |
536 | *phHandle = buffer;
537 |
538 | return OPEN_SEGA_SUCCESS;
539 | }
540 |
541 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetUserData(void* hHandle, void* hUserData)
542 | {
543 | if (hHandle == NULL)
544 | {
545 | info("SEGAAPI_SetUserData: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
546 | return OPEN_SEGAERR_BAD_HANDLE;
547 | }
548 |
549 | info("SEGAAPI_SetUserData: Handle: %08X UserData: %08X", hHandle, hUserData);
550 |
551 | OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
552 | buffer->userData = hUserData;
553 | return OPEN_SEGA_SUCCESS;
554 | }
555 |
556 |
557 | __declspec(dllexport) void* SEGAAPI_GetUserData(void* hHandle)
558 | {
559 | if (hHandle == NULL)
560 | {
561 | return nullptr;
562 | }
563 |
564 | info("SEGAAPI_GetUserData: Handle: %08X", hHandle);
565 |
566 | OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
567 | return buffer->userData;
568 | }
569 |
570 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_UpdateBuffer(void* hHandle, unsigned int dwStartOffset, unsigned int dwLength)
571 | {
572 | if (hHandle == NULL)
573 | {
574 | info("SEGAAPI_UpdateBuffer: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
575 | return OPEN_SEGAERR_BAD_HANDLE;
576 | }
577 |
578 | info("SEGAAPI_UpdateBuffer: Handle: %08X dwStartOffset: %08X, dwLength: %08X", hHandle, dwStartOffset, dwLength);
579 |
580 | OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
581 |
582 | updateBufferNew(buffer, dwStartOffset, dwLength);
583 | return OPEN_SEGA_SUCCESS;
584 | }
585 |
586 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetEndOffset(void* hHandle, unsigned int dwOffset)
587 | {
588 | if (hHandle == NULL)
589 | {
590 | info("SEGAAPI_SetEndOffset: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
591 | return OPEN_SEGAERR_BAD_HANDLE;
592 | }
593 |
594 | info("SEGAAPI_SetEndOffset: Handle: %08X dwOffset: %08X", hHandle, dwOffset);
595 |
596 | OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
597 | buffer->endOffset = dwOffset;
598 | return OPEN_SEGA_SUCCESS;
599 | }
600 |
601 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetEndLoopOffset(void* hHandle, unsigned int dwOffset)
602 | {
603 | if (hHandle == NULL)
604 | {
605 | info("SEGAAPI_SetEndLoopOffset: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
606 | return OPEN_SEGAERR_BAD_HANDLE;
607 | }
608 |
609 | info("SEGAAPI_SetEndLoopOffset: Handle: %08X dwOffset: %08X", hHandle, dwOffset);
610 |
611 | OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
612 | buffer->endLoop = dwOffset;
613 | return OPEN_SEGA_SUCCESS;
614 | }
615 |
616 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetStartLoopOffset(void* hHandle, unsigned int dwOffset)
617 | {
618 | if (hHandle == NULL)
619 | {
620 | info("SEGAAPI_SetStartLoopOffset: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
621 | return OPEN_SEGAERR_BAD_HANDLE;
622 | }
623 |
624 | info("SEGAAPI_SetStartLoopOffset: Handle: %08X dwOffset: %08X", hHandle, dwOffset);
625 |
626 | OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
627 | buffer->startLoop = dwOffset;
628 | return OPEN_SEGA_SUCCESS;
629 | }
630 |
631 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetSampleRate(void* hHandle, unsigned int dwSampleRate)
632 | {
633 | if (hHandle == NULL)
634 | {
635 | info("SEGAAPI_SetSampleRate: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
636 | return OPEN_SEGAERR_BAD_HANDLE;
637 | }
638 |
639 | info("SEGAAPI_SetSampleRate: Handle: %08X dwSampleRate: %08X", hHandle, dwSampleRate);
640 |
641 | OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
642 | buffer->sampleRate = dwSampleRate;
643 |
644 | defer_buffer_call(buffer, [=]()
645 | {
646 | buffer->xaVoice->SetSourceSampleRate(dwSampleRate);
647 | });
648 |
649 | return OPEN_SEGA_SUCCESS;
650 | }
651 |
652 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetLoopState(void* hHandle, int bDoContinuousLooping)
653 | {
654 | if (hHandle == NULL)
655 | {
656 | info("SEGAAPI_SetLoopState: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
657 | return OPEN_SEGAERR_BAD_HANDLE;
658 | }
659 |
660 | info("SEGAAPI_SetLoopState: Handle: %08X bDoContinuousLooping: %d", hHandle, bDoContinuousLooping);
661 |
662 | OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
663 | buffer->loop = bDoContinuousLooping;
664 |
665 | return OPEN_SEGA_SUCCESS;
666 | }
667 |
668 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetPlaybackPosition(void* hHandle, unsigned int dwPlaybackPos)
669 | {
670 | if (hHandle == NULL)
671 | {
672 | info("SEGAAPI_SetPlaybackPosition: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
673 | return OPEN_SEGAERR_BAD_HANDLE;
674 | }
675 |
676 | info("SEGAAPI_SetPlaybackPosition: Handle: %08X dwPlaybackPos: %08X", hHandle, dwPlaybackPos);
677 |
678 | OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
679 |
680 | if (dwPlaybackPos != 0)
681 | {
682 | }
683 |
684 | // XA2 TODO
685 | return OPEN_SEGA_SUCCESS;
686 | }
687 |
688 | __declspec(dllexport) unsigned int SEGAAPI_GetPlaybackPosition(void* hHandle)
689 | {
690 | if (hHandle == NULL)
691 | {
692 | return 0;
693 | }
694 |
695 | OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
696 |
697 | XAUDIO2_VOICE_STATE vs;
698 | buffer->xaVoice->GetState(&vs);
699 |
700 | unsigned int result = (vs.SamplesPlayed * (buffer->xaFormat.wBitsPerSample / 8) * buffer->xaFormat.nChannels) % buffer->size;
701 |
702 | info("SEGAAPI_GetPlaybackPosition: Handle: %08X Samples played: %08d BitsPerSample %08d/%08d nChannels %08d bufferSize %08d Result: %08X", hHandle, vs.SamplesPlayed, buffer->xaFormat.wBitsPerSample, (buffer->xaFormat.wBitsPerSample / 8), buffer->xaFormat.nChannels, buffer->size, result);
703 |
704 | return result;
705 | }
706 |
707 | static void updateRouting(OPEN_segaapiBuffer_t* buffer);
708 |
709 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_Play(void* hHandle)
710 | {
711 | if (hHandle == NULL)
712 | {
713 | info("SEGAAPI_Play: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
714 | return OPEN_SEGAERR_BAD_HANDLE;
715 | }
716 |
717 | info("SEGAAPI_Play: Handle: %08X", hHandle);
718 |
719 | OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
720 |
721 | updateRouting(buffer);
722 | updateBufferNew(buffer, -1, -1);
723 |
724 | buffer->playing = true;
725 | buffer->paused = false;
726 |
727 | // Uncomment to mute music
728 | //if (buffer->playWithSetup)
729 | //{
730 | CHECK_HR(buffer->xaVoice->Start());
731 | //}
732 |
733 | return OPEN_SEGA_SUCCESS;
734 | }
735 |
736 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_Stop(void* hHandle)
737 | {
738 | if (hHandle == NULL)
739 | {
740 | info("SEGAAPI_Stop: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
741 | return OPEN_SEGAERR_BAD_HANDLE;
742 | }
743 |
744 | info("SEGAAPI_Stop: Handle: %08X", hHandle);
745 |
746 | OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
747 | buffer->playing = false;
748 | buffer->paused = false;
749 | CHECK_HR(buffer->xaVoice->Stop());
750 | return OPEN_SEGA_SUCCESS;
751 | }
752 |
753 | __declspec(dllexport) OPEN_HAWOSTATUS SEGAAPI_GetPlaybackStatus(void* hHandle)
754 | {
755 | if (hHandle == NULL)
756 | {
757 | info("SEGAAPI_GetPlaybackStatus: Handle: %08X, Status: OPEN_HAWOSTATUS_INVALID", hHandle);
758 | return OPEN_HAWOSTATUS_INVALID;
759 | }
760 |
761 | OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
762 |
763 | if (buffer->paused)
764 | {
765 | info("SEGAAPI_GetPlaybackStatus: Handle: %08X, Status: OPEN_HAWOSTATUS_PAUSE, buffer is paused", hHandle);
766 | return OPEN_HAWOSTATUS_PAUSE;
767 | }
768 |
769 | // XA2 TODO
770 | XAUDIO2_VOICE_STATE vs;
771 | buffer->xaVoice->GetState(&vs);
772 |
773 | if (vs.BuffersQueued == 0)
774 | {
775 | info("SEGAAPI_GetPlaybackStatus: Handle: %08X, Status: OPEN_HAWOSTATUS_STOP, buffersqueued is 0", hHandle);
776 | return OPEN_HAWOSTATUS_STOP;
777 | }
778 |
779 | if (!buffer->loop && vs.SamplesPlayed >= (min(buffer->size, buffer->endOffset) / bufferSampleSize(buffer)))
780 | {
781 | info("SEGAAPI_GetPlaybackStatus: Handle: %08X, Status: OPEN_HAWOSTATUS_STOP, Loop false and samples played bigger", hHandle);
782 | return OPEN_HAWOSTATUS_STOP;
783 | }
784 |
785 | if (buffer->playing)
786 | {
787 | info("SEGAAPI_GetPlaybackStatus: Handle: %08X, Status: OPEN_HAWOSTATUS_ACTIVE, playing true!", hHandle);
788 | return OPEN_HAWOSTATUS_ACTIVE;
789 | }
790 | else
791 | {
792 | info("SEGAAPI_GetPlaybackStatus: Handle: %08X, Status: OPEN_HAWOSTATUS_STOP, Playing false!", hHandle);
793 | return OPEN_HAWOSTATUS_STOP;
794 | }
795 | }
796 |
797 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetReleaseState(void* hHandle, int bSet)
798 | {
799 | if (hHandle == NULL)
800 | {
801 | info("SEGAAPI_SetReleaseState: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
802 | return OPEN_SEGAERR_BAD_HANDLE;
803 | }
804 |
805 | info("SEGAAPI_SetReleaseState: Handle: %08X bSet: %08X", hHandle, bSet);
806 |
807 | OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
808 |
809 | if (bSet)
810 | {
811 | buffer->playing = false;
812 | buffer->xaVoice->FlushSourceBuffers();
813 | buffer->xaVoice->Stop();
814 | }
815 |
816 | return OPEN_SEGA_SUCCESS;
817 | }
818 |
819 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_DestroyBuffer(void* hHandle)
820 | {
821 | if (hHandle == NULL)
822 | {
823 | info("SEGAAPI_DestroyBuffer: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
824 | return OPEN_SEGAERR_BAD_HANDLE;
825 | }
826 |
827 | info("SEGAAPI_DestroyBuffer: Handle: %08X", hHandle);
828 |
829 | OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
830 |
831 | buffer->xaVoice->DestroyVoice();
832 | delete buffer;
833 | return OPEN_SEGA_SUCCESS;
834 | }
835 |
836 | __declspec(dllexport) int SEGAAPI_SetGlobalEAXProperty(GUID * guid, unsigned long ulProperty, void * pData, unsigned long ulDataSize)
837 | {
838 | info("SEGAAPI_SetGlobalEAXProperty:");
839 |
840 | // Everything is fine
841 | return TRUE;
842 | }
843 |
844 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_Init(void)
845 | {
846 | info("SEGAAPI_Init");
847 |
848 | CoInitialize(nullptr);
849 |
850 | CHECK_HR(XAudio2Create(&g_xa2));
851 |
852 | XAUDIO2_DEBUG_CONFIGURATION cfg = { 0 };
853 | cfg.TraceMask = XAUDIO2_LOG_ERRORS;
854 | //cfg.BreakMask = XAUDIO2_LOG_ERRORS;
855 | g_xa2->SetDebugConfiguration(&cfg);
856 |
857 | CHECK_HR(g_xa2->CreateMasteringVoice(&g_masteringVoice));
858 |
859 | XAUDIO2_VOICE_DETAILS vd;
860 | g_masteringVoice->GetVoiceDetails(&vd);
861 |
862 | for (auto& g_submixVoice : g_submixVoices)
863 | {
864 | CHECK_HR(g_xa2->CreateSubmixVoice(&g_submixVoice, 1, vd.InputSampleRate));
865 | }
866 |
867 | int numChannels = vd.InputChannels;
868 |
869 | auto setSubmixVoice = [=](OPEN_HAROUTING index, float frontLeft, float frontRight, float frontCenter, float lfe,
870 | float rearLeft, float rearRight)
871 | {
872 | float levelMatrix[12] = { 0 };
873 | levelMatrix[0] = frontLeft + rearLeft;
874 | levelMatrix[1] = frontRight + rearRight;
875 |
876 | if (numChannels == 2)
877 | {
878 | // TODO
879 | }
880 |
881 | levelMatrix[2] = lfe;
882 |
883 | // TODO: surround data - SetOutputMatrix order is somewhat unclear :/
884 |
885 | g_submixVoices[index]->SetOutputMatrix(g_masteringVoice, 1, numChannels, levelMatrix);
886 | };
887 |
888 | setSubmixVoice(OPEN_HA_FRONT_LEFT_PORT, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
889 | setSubmixVoice(OPEN_HA_FRONT_RIGHT_PORT, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f);
890 | setSubmixVoice(OPEN_HA_FRONT_CENTER_PORT, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
891 | setSubmixVoice(OPEN_HA_REAR_LEFT_PORT, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
892 | setSubmixVoice(OPEN_HA_REAR_RIGHT_PORT, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f);
893 | setSubmixVoice(OPEN_HA_LFE_PORT, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
894 |
895 | return OPEN_SEGA_SUCCESS;
896 | }
897 |
898 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_Exit(void)
899 | {
900 | info("SEGAAPI_Exit");
901 |
902 | for (auto& g_submixVoice : g_submixVoices)
903 | {
904 | g_submixVoice->DestroyVoice();
905 | }
906 |
907 | // TODO: deinit XA2
908 | return OPEN_SEGA_SUCCESS;
909 | }
910 |
911 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_Reset(void)
912 | {
913 | info("SEGAAPI_Reset");
914 | return OPEN_SEGA_SUCCESS;
915 | }
916 |
917 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetIOVolume(OPEN_HAPHYSICALIO dwPhysIO, unsigned int dwVolume)
918 | {
919 | info("SEGAAPI_SetIOVolume: dwPhysIO: %08X dwVolume: %08X", dwPhysIO, dwVolume);
920 | g_masteringVoice->SetVolume(dwVolume / (float)0xFFFFFFFF);
921 | return OPEN_SEGA_SUCCESS;
922 | }
923 |
924 | static void updateRouting(OPEN_segaapiBuffer_t* buffer)
925 | {
926 | float levels[7 * 2];
927 | IXAudio2SubmixVoice* outVoices[7];
928 |
929 | int numRoutes = 0;
930 |
931 | for (int i = 0; i < /*7*/2; i++)
932 | {
933 | if (buffer->sendRoutes[i] != OPEN_HA_UNUSED_PORT && buffer->sendRoutes[i] < 6)
934 | {
935 | outVoices[numRoutes] = g_submixVoices[buffer->sendRoutes[i]];
936 |
937 | int levelOff = numRoutes * buffer->channels;
938 |
939 | for (int ch = 0; ch < buffer->channels; ch++)
940 | {
941 | levels[levelOff + ch] = 0;
942 | }
943 |
944 | float level = buffer->sendVolumes[i] * buffer->channelVolumes[buffer->sendChannels[i]];
945 | levels[levelOff + buffer->sendChannels[i]] = level;
946 |
947 | ++numRoutes;
948 | }
949 | }
950 |
951 | // can't set no routes
952 | if (numRoutes == 0)
953 | {
954 | return;
955 | }
956 |
957 | XAUDIO2_SEND_DESCRIPTOR sendDescs[7];
958 | for (int i = 0; i < numRoutes; i++)
959 | {
960 | sendDescs[i].Flags = 0;
961 | sendDescs[i].pOutputVoice = outVoices[i];
962 | }
963 |
964 | XAUDIO2_VOICE_SENDS sends;
965 | sends.SendCount = numRoutes;
966 | sends.pSends = sendDescs;
967 | CHECK_HR(buffer->xaVoice->SetOutputVoices(&sends));
968 |
969 | for (int i = 0; i < numRoutes; i++)
970 | {
971 | CHECK_HR(buffer->xaVoice->SetOutputMatrix(outVoices[i], buffer->channels, 1, &levels[i * buffer->channels]));
972 | }
973 | }
974 |
975 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetSendRouting(void* hHandle, unsigned int dwChannel, unsigned int dwSend, OPEN_HAROUTING dwDest)
976 | {
977 | if (hHandle == NULL)
978 | {
979 | info("SEGAAPI_SetSendRouting: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
980 | return OPEN_SEGAERR_BAD_HANDLE;
981 | }
982 |
983 | info("SEGAAPI_SetSendRouting: hHandle: %08X dwChannel: %08X dwSend: %08X dwDest: %08X", hHandle, dwChannel, dwSend, dwDest);
984 |
985 | OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
986 | buffer->sendRoutes[dwSend] = dwDest;
987 | buffer->sendChannels[dwSend] = dwChannel;
988 |
989 | updateRouting(buffer);
990 | return OPEN_SEGA_SUCCESS;
991 | }
992 |
993 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetSendLevel(void* hHandle, unsigned int dwChannel, unsigned int dwSend, unsigned int dwLevel)
994 | {
995 | if (hHandle == NULL)
996 | {
997 | info("SEGAAPI_SetSendLevel: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
998 | return OPEN_SEGAERR_BAD_HANDLE;
999 | }
1000 |
1001 | info("SEGAAPI_SetSendLevel: hHandle: %08X dwChannel: %08X dwSend: %08X dwLevel: %08X", hHandle, dwChannel, dwSend, dwLevel);
1002 |
1003 | OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
1004 | buffer->sendVolumes[dwSend] = dwLevel / (float)0xFFFFFFFF;
1005 | buffer->sendChannels[dwSend] = dwChannel;
1006 |
1007 | updateRouting(buffer);
1008 | return OPEN_SEGA_SUCCESS;
1009 | }
1010 |
1011 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetSynthParam(void* hHandle, OPEN_HASYNTHPARAMSEXT param, int lPARWValue)
1012 | {
1013 | if (hHandle == NULL)
1014 | {
1015 | info("SEGAAPI_SetSynthParam: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
1016 | return OPEN_SEGAERR_BAD_HANDLE;
1017 | }
1018 |
1019 | info("SEGAAPI_SetSynthParam: hHandle: %08X OPEN_HASYNTHPARAMSEXT: %08X lPARWValue: %08X", hHandle, param, lPARWValue);
1020 |
1021 | enum
1022 | {
1023 | StartAddrsOffset,
1024 | EndAddrsOffset,
1025 | StartloopAddrsOffset,
1026 | EndloopAddrsOffset,
1027 | StartAddrsCoarseOffset,
1028 | ModLfoToPitch,
1029 | VibLfoToPitch,
1030 | ModEnvToPitch,
1031 | InitialFilterFc,
1032 | InitialFilterQ,
1033 | ModLfoToFilterFc,
1034 | ModEnvToFilterFc,
1035 | EndAddrsCoarseOffset,
1036 | ModLfoToVolume,
1037 | Unused1,
1038 | ChorusEffectsSend,
1039 | ReverbEffectsSend,
1040 | Pan,
1041 | Unused2,
1042 | Unused3,
1043 | Unused4,
1044 | DelayModLFO,
1045 | FreqModLFO,
1046 | DelayVibLFO,
1047 | FreqVibLFO,
1048 | DelayModEnv,
1049 | AttackModEnv,
1050 | HoldModEnv,
1051 | DecayModEnv,
1052 | SustainModEnv,
1053 | ReleaseModEnv,
1054 | KeynumToModEnvHold,
1055 | KeynumToModEnvDecay,
1056 | DelayVolEnv,
1057 | AttackVolEnv,
1058 | HoldVolEnv,
1059 | DecayVolEnv,
1060 | SustainVolEnv,
1061 | ReleaseVolEnv,
1062 | KeynumToVolEnvHold,
1063 | KeynumToVolEnvDecay,
1064 | Instrument,
1065 | Reserved1,
1066 | KeyRange,
1067 | VelRange,
1068 | StartloopAddrsCoarseOffset,
1069 | Keynum,
1070 | Velocity,
1071 | InitialAttenuation,
1072 | Reserved2,
1073 | EndloopAddrsCoarseOffset,
1074 | CoarseTune,
1075 | FineTune,
1076 | SampleID,
1077 | SampleModes,
1078 | Reserved3,
1079 | ScaleTuning,
1080 | ExclusiveClass,
1081 | OverridingRootKey,
1082 | Unused5,
1083 | EndOper
1084 | };
1085 |
1086 | int mapping[26] = {
1087 | InitialAttenuation, ///< 0, 0x00, initialAttenuation
1088 | FineTune, ///< 1, 0x01, fineTune + coarseTune * 100
1089 | InitialFilterFc, ///< 2, 0x02, initialFilterFc
1090 | InitialFilterQ, ///< 3, 0x03, initialFilterQ
1091 | DelayVolEnv, ///< 4, 0x04, delayVolEnv
1092 | AttackVolEnv, ///< 5, 0x05, attackVolEnv
1093 | HoldVolEnv, ///< 6, 0x06, holdVolEnv
1094 | DecayVolEnv, ///< 7, 0x07, decayVolEnv
1095 | SustainVolEnv, ///< 8, 0x08, sustainVolEnv
1096 | ReleaseVolEnv, ///< 9, 0x09, releaseVolEnv
1097 | DelayModEnv, ///< 10, 0x0A, delayModEnv
1098 | AttackModEnv, ///< 11, 0x0B, attackModEnv
1099 | HoldModEnv, ///< 12, 0x0C, holdModEnv
1100 | DecayModEnv, ///< 13, 0x0D, decayModEnv
1101 | SustainModEnv, ///< 14, 0x0E, sustainModEnv
1102 | ReleaseModEnv, ///< 15, 0x0F, releaseModEnv
1103 | DelayModLFO, ///< 16, 0x10, delayModLFO
1104 | FreqModLFO, ///< 17, 0x11, freqModLFO
1105 | DelayVibLFO, ///< 18, 0x12, delayVibLFO
1106 | FreqVibLFO, ///< 19, 0x13, freqVibLFO
1107 | ModLfoToPitch, ///< 20, 0x14, modLfoToPitch
1108 | VibLfoToPitch, ///< 21, 0x15, vibLfoToPitch
1109 | ModLfoToFilterFc, ///< 22, 0x16, modLfoToFilterFc
1110 | ModLfoToVolume, ///< 23, 0x17, modLfoToVolume
1111 | ModEnvToPitch, ///< 24, 0x18, modEnvToPitch
1112 | ModEnvToFilterFc ///< 25, 0x19, modEnvToFilterFc
1113 | };
1114 |
1115 | int realParam = mapping[param];
1116 |
1117 | OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
1118 | //tsf_hydra_genamount amount;
1119 | //amount.shortAmount = lPARWValue;
1120 | //tsf_region_operator(buffer->region, realParam, &amount);
1121 |
1122 | if (param == OPEN_HAVP_ATTENUATION)
1123 | {
1124 | float volume = tsf_decibelsToGain(0.0f - lPARWValue / 10.0f);
1125 |
1126 | buffer->xaVoice->SetVolume(volume);
1127 | info("SEGAAPI_SetSynthParam: OPEN_HAVP_ATTENUATION gain: %f dB: %d", volume, lPARWValue);
1128 | }
1129 | else if (param == OPEN_HAVP_PITCH)
1130 | {
1131 | float semiTones = lPARWValue / 100.0f;
1132 | float freqRatio = XAudio2SemitonesToFrequencyRatio(semiTones);
1133 |
1134 | buffer->xaVoice->SetFrequencyRatio(freqRatio);
1135 | info("SEGAAPI_SetSynthParam: OPEN_HAVP_PITCH hHandle: %08X semitones: %f freqRatio: %f", hHandle, semiTones, freqRatio);
1136 | }
1137 |
1138 | return OPEN_SEGA_SUCCESS;
1139 | }
1140 |
1141 | __declspec(dllexport) int SEGAAPI_GetSynthParam(void * hHandle, OPEN_HASYNTHPARAMSEXT param)
1142 | {
1143 | if (hHandle == NULL)
1144 | {
1145 | info("SEGAAPI_GetSynthParam: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
1146 | return OPEN_SEGAERR_BAD_HANDLE;
1147 | }
1148 |
1149 | info("SEGAAPI_GetSynthParam: hHandle: %08X OPEN_HASYNTHPARAMSEXT: %08X", hHandle, param);
1150 |
1151 | return 0; //todo not sure if actually used
1152 | }
1153 |
1154 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetSynthParamMultiple(void* hHandle, unsigned int dwNumParams, OPEN_SynthParamSet* pSynthParams)
1155 | {
1156 | if (hHandle == NULL)
1157 | {
1158 | info("SEGAAPI_SetSynthParamMultiple: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
1159 | return OPEN_SEGAERR_BAD_HANDLE;
1160 | }
1161 |
1162 | info("SEGAAPI_SetSynthParamMultiple: hHandle: %08X dwNumParams: %08X pSynthParams: %08X", hHandle, dwNumParams, pSynthParams);
1163 |
1164 | for (int i = 0; i < dwNumParams; i++)
1165 | {
1166 | SEGAAPI_SetSynthParam(hHandle, pSynthParams[i].param, pSynthParams[i].lPARWValue);
1167 | }
1168 |
1169 | return OPEN_SEGA_SUCCESS;
1170 | }
1171 |
1172 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetChannelVolume(void* hHandle, unsigned int dwChannel, unsigned int dwVolume)
1173 | {
1174 | if (hHandle == NULL)
1175 | {
1176 | info("SEGAAPI_SetChannelVolume: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
1177 | return OPEN_SEGAERR_BAD_HANDLE;
1178 | }
1179 |
1180 | info("SEGAAPI_SetChannelVolume: hHandle: %08X dwChannel: %08X dwVolume: %08X", hHandle, dwChannel, dwVolume);
1181 |
1182 | OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
1183 | buffer->channelVolumes[dwChannel] = dwVolume / (float)0xFFFFFFFF;
1184 | return OPEN_SEGA_SUCCESS;
1185 | }
1186 |
1187 | __declspec(dllexport) unsigned int SEGAAPI_GetChannelVolume(void* hHandle, unsigned int dwChannel)
1188 | {
1189 | if (hHandle == NULL)
1190 | {
1191 | info("SEGAAPI_GetChannelVolume: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
1192 | return OPEN_SEGAERR_BAD_HANDLE;
1193 | }
1194 |
1195 | info("SEGAAPI_GetChannelVolume: hHandle: %08X dwChannel: %08X", hHandle, dwChannel);
1196 |
1197 | OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
1198 | return buffer->channelVolumes[dwChannel];
1199 | }
1200 |
1201 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_Pause(void* hHandle)
1202 | {
1203 | if (hHandle == NULL)
1204 | {
1205 | info("SEGAAPI_Pause: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
1206 | return OPEN_SEGAERR_BAD_HANDLE;
1207 | }
1208 |
1209 | info("SEGAAPI_Pause: hHandle: %08X", hHandle);
1210 |
1211 | OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
1212 |
1213 | buffer->playing = false;
1214 | buffer->paused = true;
1215 | CHECK_HR(buffer->xaVoice->Stop());
1216 | return OPEN_SEGA_SUCCESS;
1217 | }
1218 |
1219 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_PlayWithSetup(
1220 | void* hHandle,
1221 | unsigned int dwNumSendRouteParams, OPEN_SendRouteParamSet* pSendRouteParams,
1222 | unsigned int dwNumSendLevelParams, OPEN_SendLevelParamSet* pSendLevelParams,
1223 | unsigned int dwNumVoiceParams, OPEN_VoiceParamSet* pVoiceParams,
1224 | unsigned int dwNumSynthParams, OPEN_SynthParamSet* pSynthParams
1225 | )
1226 | {
1227 | if (hHandle == NULL)
1228 | {
1229 | info("SEGAAPI_PlayWithSetup: Handle: %08X, Status: OPEN_SEGAERR_BAD_HANDLE", hHandle);
1230 | return OPEN_SEGAERR_BAD_HANDLE;
1231 | }
1232 |
1233 | info("SEGAAPI_PlayWithSetup: hHandle: %08X dwNumSendRouteParams: %d pSendRouteParams: %08X dwNumSendLevelParams: %d pSendLevelParams: %08X dwNumVoiceParams: %d pVoiceParams: %08X dwNumSynthParams: %d pSynthParams: %08X", hHandle, dwNumSendRouteParams, *pSendRouteParams, dwNumSendLevelParams, *pSendLevelParams, dwNumVoiceParams, *pVoiceParams, dwNumSynthParams, *pSynthParams);
1234 | info("dwNumSynthParams: %d", dwNumSynthParams);
1235 |
1236 | OPEN_segaapiBuffer_t* buffer = (OPEN_segaapiBuffer_t*)hHandle;
1237 | buffer->playWithSetup = true;
1238 |
1239 | for (int i = 0; i < dwNumSendRouteParams; i++)
1240 | {
1241 | SEGAAPI_SetSendRouting(hHandle, pSendRouteParams[i].dwChannel, pSendRouteParams[i].dwSend, pSendRouteParams[i].dwDest);
1242 | }
1243 |
1244 | for (int i = 0; i < dwNumSendLevelParams; i++)
1245 | {
1246 | SEGAAPI_SetSendLevel(hHandle, pSendLevelParams[i].dwChannel, pSendLevelParams[i].dwSend, pSendLevelParams[i].dwLevel);
1247 | }
1248 |
1249 | unsigned int loopStart = 0;
1250 | unsigned int loopEnd = 0;
1251 | unsigned int loopState = 0;
1252 | unsigned int endOffset = 0;
1253 |
1254 | for (int i = 0; i < dwNumVoiceParams; i++)
1255 | {
1256 | switch (pVoiceParams[i].VoiceIoctl)
1257 | {
1258 | case OPEN_VOICEIOCTL_SET_START_LOOP_OFFSET:
1259 | SEGAAPI_SetStartLoopOffset(hHandle, pVoiceParams[i].dwParam1);
1260 | loopStart = pVoiceParams[i].dwParam1;
1261 | break;
1262 | case OPEN_VOICEIOCTL_SET_END_LOOP_OFFSET:
1263 | SEGAAPI_SetEndLoopOffset(hHandle, pVoiceParams[i].dwParam1);
1264 | loopEnd = pVoiceParams[i].dwParam1;
1265 | break;
1266 | case OPEN_VOICEIOCTL_SET_END_OFFSET:
1267 | SEGAAPI_SetEndOffset(hHandle, pVoiceParams[i].dwParam1);
1268 | endOffset = pVoiceParams[i].dwParam1;
1269 | break;
1270 | case OPEN_VOICEIOCTL_SET_LOOP_STATE:
1271 | SEGAAPI_SetLoopState(hHandle, pVoiceParams[i].dwParam1);
1272 | loopState = pVoiceParams[i].dwParam1;
1273 | break;
1274 | case OPEN_VOICEIOCTL_SET_NOTIFICATION_POINT:
1275 | info("Unimplemented! OPEN_VOICEIOCTL_SET_NOTIFICATION_POINT");
1276 | break;
1277 | case OPEN_VOICEIOCTL_CLEAR_NOTIFICATION_POINT:
1278 | info("Unimplemented! OPEN_VOICEIOCTL_CLEAR_NOTIFICATION_POINT");
1279 | break;
1280 | case OPEN_VOICEIOCTL_SET_NOTIFICATION_FREQUENCY:
1281 | info("Unimplemented! OPEN_VOICEIOCTL_SET_NOTIFICATION_FREQUENCY");
1282 | break;
1283 | }
1284 | }
1285 |
1286 | info("Loopdata: hHandle: %08X, loopStart: %08X, loopEnd: %08X, endOffset: %08X, loopState: %d, size: %d", hHandle, loopStart, loopEnd, endOffset, loopState, buffer->size);
1287 |
1288 | for (int i = 0; i < dwNumSynthParams; i++)
1289 | {
1290 | SEGAAPI_SetSynthParam(hHandle, pSynthParams[i].param, pSynthParams[i].lPARWValue);
1291 | }
1292 |
1293 | SEGAAPI_Play(hHandle);
1294 |
1295 | return OPEN_SEGA_SUCCESS;
1296 | }
1297 |
1298 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_GetLastStatus(void)
1299 | {
1300 | info("SEGAAPI_GetLastStatus");
1301 | return OPEN_SEGA_SUCCESS;
1302 | }
1303 | }
1304 | #pragma optimize("", on)
--------------------------------------------------------------------------------
/Opensegaapi/src/opensegaapi.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the OpenParrot project - https://teknoparrot.com / https://github.com/teknogods
3 | *
4 | * See LICENSE and MENTIONS in the root of the source tree for information
5 | * regarding licensing.
6 | */
7 |
8 | #include
9 |
10 | // TODO: DOCUMENT ALL THESE ACCORDING TO ORIGINAL DOCUMENTS!!!!
11 |
12 | #define OPEN_SEGA_SUCCESS 0L
13 | typedef int OPEN_SEGASTATUS;
14 | #define OPEN_SEGARESULT_FAILURE(_x) ((1 << 31) | 0xA000 | (_x))
15 | #define OPEN_SEGAERR_FAIL OPEN_SEGARESULT_FAILURE(0) // -2147442688
16 | #define OPEN_SEGAERR_BAD_POINTER OPEN_SEGARESULT_FAILURE(3) // -2147442685
17 | #define OPEN_SEGAERR_BAD_PARAM OPEN_SEGARESULT_FAILURE(9) // -2147442679
18 | #define OPEN_SEGAERR_INVALID_SEND OPEN_SEGARESULT_FAILURE(11) // -2147442677
19 | #define OPEN_SEGAERR_BAD_HANDLE OPEN_SEGARESULT_FAILURE(18) // -2147442670
20 | #define OPEN_SEGAERR_BAD_SAMPLERATE OPEN_SEGARESULT_FAILURE(28) // -2147442660
21 | // SEGA custom EAX40 properties.
22 | // GUID: {A7FEEC3F-2BFD-4a40-891F-7423E38BAC1F}
23 | DEFINE_GUID(EAXPROPERTYID_EAX40_SEGA_Custom,
24 | 0xa7feec3f, 0x2bfd, 0x4a40, 0x89, 0x1f, 0x74, 0x23, 0xe3, 0x8b, 0xac, 0x1f);
25 |
26 | typedef enum
27 | {
28 | EAXOPENSEGA_STEREO_RETURN_FX2 = 0, // front L/R (default)
29 | EAXOPENSEGA_STEREO_RETURN_FX3 = 1 // rear L/R
30 | } EAXOPENSEGA_PROPERTY;
31 |
32 | typedef enum
33 | {
34 | OPEN_HAWOS_RESOURCE_STOLEN = 0,
35 | OPEN_HAWOS_NOTIFY = 2
36 | } OPEN_HAWOSMESSAGETYPE;
37 |
38 | typedef enum
39 | {
40 | OPEN_HAWOSTATUS_STOP,
41 | OPEN_HAWOSTATUS_ACTIVE,
42 | OPEN_HAWOSTATUS_PAUSE,
43 | OPEN_HAWOSTATUS_INVALID = -1
44 | } OPEN_HAWOSTATUS;
45 |
46 | #define OPEN_HABUF_SYNTH_BUFFER 0x00000001
47 | #define OPEN_HABUF_ALLOC_USER_MEM 0x00000002
48 | #define OPEN_HABUF_USE_MAPPED_MEM 0x00000004
49 | #define OPEN_HASF_UNSIGNED_8PCM 0x0004
50 | #define OPEN_HASF_SIGNED_16PCM 0x0020
51 |
52 | typedef struct
53 | {
54 | unsigned int dwSampleRate;
55 | unsigned int dwSampleFormat;
56 | unsigned int byNumChans;
57 | } OPEN_HAWOSEFORMAT;
58 |
59 | typedef struct
60 | {
61 | unsigned int dwSize;
62 | unsigned int dwOffset;
63 | void* hBufferHdr;
64 | } OPEN_HAWOSEMAPDATA;
65 |
66 | typedef struct
67 | {
68 | unsigned int dwPriority;
69 | unsigned int dwSampleRate;
70 | unsigned int dwSampleFormat;
71 | unsigned int byNumChans;
72 | unsigned int dwReserved;
73 | void* hUserData;
74 | OPEN_HAWOSEMAPDATA mapData;
75 | } OPEN_HAWOSEBUFFERCONFIG;
76 |
77 | #define OPEN_HAWOSEVOL_MAX 0xFFFFFFFF
78 | #define OPEN_HAWOSEP_MINIMUM 0
79 | #define OPEN_HAWOSEP_MAXIMUM 0xFFFFFFFF
80 | #define OPEN_HAWOSE_UNUSED_SEND 0xFFFF0001
81 |
82 | typedef enum OPEN_HAROUTING
83 | {
84 | OPEN_HA_UNUSED_PORT = OPEN_HAWOSE_UNUSED_SEND,
85 | OPEN_HA_FRONT_LEFT_PORT = 0,
86 | OPEN_HA_FRONT_RIGHT_PORT = 1,
87 | OPEN_HA_FRONT_CENTER_PORT = 2,
88 | OPEN_HA_LFE_PORT = 3,
89 | OPEN_HA_REAR_LEFT_PORT = 4,
90 | OPEN_HA_REAR_RIGHT_PORT = 5,
91 | OPEN_HA_FXSLOT0_PORT = 10,
92 | OPEN_HA_FXSLOT1_PORT = 11,
93 | OPEN_HA_FXSLOT2_PORT = 12,
94 | OPEN_HA_FXSLOT3_PORT = 13
95 | } OPEN_HAROUTING;
96 |
97 | typedef enum
98 | {
99 | OPEN_HASPDIFOUT_44_1KHZ = 0,
100 | OPEN_HASPDIFOUT_48KHZ,
101 | OPEN_HASPDIFOUT_96KHZ
102 | } OPEN_HASPDIFOUTRATE;
103 |
104 | typedef enum OPEN_HAPHYSICALIO
105 | {
106 | OPEN_HA_OUT_FRONT_LEFT = 0,
107 | OPEN_HA_OUT_FRONT_RIGHT = 1,
108 | OPEN_HA_OUT_FRONT_CENTER = 2,
109 | OPEN_HA_OUT_LFE_PORT = 3,
110 | OPEN_HA_OUT_REAR_LEFT = 4,
111 | OPEN_HA_OUT_REAR_RIGHT = 5,
112 | OPEN_HA_OUT_OPTICAL_LEFT = 10,
113 | OPEN_HA_OUT_OPTICAL_RIGHT = 11,
114 | OPEN_HA_IN_LINEIN_LEFT = 20,
115 | OPEN_HA_IN_LINEIN_RIGHT = 21
116 | } OPEN_HAPHYSICALIO;
117 |
118 | typedef enum OPEN_HASYNTHPARAMSEXT
119 | {
120 | OPEN_HAVP_ATTENUATION,
121 | OPEN_HAVP_PITCH,
122 | OPEN_HAVP_FILTER_CUTOFF,
123 | OPEN_HAVP_FILTER_Q,
124 | OPEN_HAVP_DELAY_VOL_ENV,
125 | OPEN_HAVP_ATTACK_VOL_ENV,
126 | OPEN_HAVP_HOLD_VOL_ENV,
127 | OPEN_HAVP_DECAY_VOL_ENV,
128 | OPEN_HAVP_SUSTAIN_VOL_ENV,
129 | OPEN_HAVP_RELEASE_VOL_ENV,
130 | OPEN_HAVP_DELAY_MOD_ENV,
131 | OPEN_HAVP_ATTACK_MOD_ENV,
132 | OPEN_HAVP_HOLD_MOD_ENV,
133 | OPEN_HAVP_DECAY_MOD_ENV,
134 | OPEN_HAVP_SUSTAIN_MOD_ENV,
135 | OPEN_HAVP_RELEASE_MOD_ENV,
136 | OPEN_HAVP_DELAY_MOD_LFO,
137 | OPEN_HAVP_FREQ_MOD_LFO,
138 | OPEN_HAVP_DELAY_VIB_LFO,
139 | OPEN_HAVP_FREQ_VIB_LFO,
140 | OPEN_HAVP_MOD_LFO_TO_PITCH,
141 | OPEN_HAVP_VIB_LFO_TO_PITCH,
142 | OPEN_HAVP_MOD_LFO_TO_FILTER_CUTOFF,
143 | OPEN_HAVP_MOD_LFO_TO_ATTENUATION,
144 | OPEN_HAVP_MOD_ENV_TO_PITCH,
145 | OPEN_HAVP_MOD_ENV_TO_FILTER_CUTOFF
146 | } OPEN_HASYNTHPARAMSEXT;
147 |
148 | typedef enum
149 | {
150 | OPEN_VOICEIOCTL_SET_START_LOOP_OFFSET = 0x100,
151 | OPEN_VOICEIOCTL_SET_END_LOOP_OFFSET,
152 | OPEN_VOICEIOCTL_SET_END_OFFSET,
153 | OPEN_VOICEIOCTL_SET_PLAY_POSITION,
154 | OPEN_VOICEIOCTL_SET_LOOP_STATE,
155 | OPEN_VOICEIOCTL_SET_NOTIFICATION_POINT,
156 | OPEN_VOICEIOCTL_CLEAR_NOTIFICATION_POINT,
157 | OPEN_VOICEIOCTL_SET_NOTIFICATION_FREQUENCY
158 | } OPEN_VOICEIOCTL;
159 |
160 | typedef struct OPEN_SendRouteParamSetExt
161 | {
162 | unsigned int dwChannel;
163 | unsigned int dwSend;
164 | OPEN_HAROUTING dwDest;
165 | } OPEN_SendRouteParamSet;
166 |
167 | typedef struct OPEN_SendLevelParamSetExt
168 | {
169 | unsigned int dwChannel;
170 | unsigned int dwSend;
171 | unsigned int dwLevel;
172 | } OPEN_SendLevelParamSet;
173 |
174 | typedef struct OPEN_VoiceParamSetExt
175 | {
176 | OPEN_VOICEIOCTL VoiceIoctl;
177 | unsigned int dwParam1;
178 | unsigned int dwParam2;
179 | } OPEN_VoiceParamSet;
180 |
181 | typedef struct OPEN_SynthParamSetExt
182 | {
183 | OPEN_HASYNTHPARAMSEXT param;
184 | int lPARWValue;
185 | } OPEN_SynthParamSet;
186 |
187 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_Play(void* hHandle);
188 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_Pause(void* hHandle);
189 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_Stop(void* hHandle);
190 | __declspec(dllexport) OPEN_HAWOSTATUS SEGAAPI_GetPlaybackStatus(void* hHandle);
191 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetFormat(void* hHandle, OPEN_HAWOSEFORMAT* pFormat);
192 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_GetFormat(void* hHandle, OPEN_HAWOSEFORMAT* pFormat);
193 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetSampleRate(void* hHandle, unsigned int dwSampleRate);
194 | __declspec(dllexport) unsigned int SEGAAPI_GetSampleRate(void* hHandle);
195 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetPriority(void* hHandle, unsigned int dwPriority);
196 | __declspec(dllexport) unsigned int SEGAAPI_GetPriority(void* hHandle);
197 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetUserData(void* hHandle, void* hUserData);
198 | __declspec(dllexport) void* SEGAAPI_GetUserData(void* hHandle);
199 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetSendRouting(void* hHandle, unsigned int dwChannel, unsigned int dwSend,
200 | OPEN_HAROUTING dwDest);
201 | __declspec(dllexport) OPEN_HAROUTING SEGAAPI_GetSendRouting(void* hHandle, unsigned int dwChannel, unsigned int dwSend);
202 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetSendLevel(void* hHandle, unsigned int dwChannel, unsigned int dwSend,
203 | unsigned int dwLevel);
204 | __declspec(dllexport) unsigned int SEGAAPI_GetSendLevel(void* hHandle, unsigned int dwChannel, unsigned int dwSend);
205 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetChannelVolume(void* hHandle, unsigned int dwChannel,
206 | unsigned int dwVolume);
207 | __declspec(dllexport) unsigned int SEGAAPI_GetChannelVolume(void* hHandle, unsigned int dwChannel);
208 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetPlaybackPosition(void* hHandle, unsigned int dwPlaybackPos);
209 | __declspec(dllexport) unsigned int SEGAAPI_GetPlaybackPosition(void* hHandle);
210 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetNotificationFrequency(void* hHandle, unsigned int dwFrameCount);
211 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetNotificationPoint(void* hHandle, unsigned int dwBufferOffset);
212 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_ClearNotificationPoint(void* hHandle, unsigned int dwBufferOffset);
213 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetStartLoopOffset(void* hHandle, unsigned int dwOffset);
214 | __declspec(dllexport) unsigned int SEGAAPI_GetStartLoopOffset(void* hHandle);
215 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetEndLoopOffset(void* hHandle, unsigned int dwOffset);
216 | __declspec(dllexport) unsigned int SEGAAPI_GetEndLoopOffset(void* hHandle);
217 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetEndOffset(void* hHandle, unsigned int dwOffset);
218 | __declspec(dllexport) unsigned int SEGAAPI_GetEndOffset(void* hHandle);
219 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetLoopState(void* hHandle, int bDoContinuousLooping);
220 | __declspec(dllexport)int SEGAAPI_GetLoopState(void* hHandle);
221 | __declspec(dllexport)OPEN_SEGASTATUS SEGAAPI_UpdateBuffer(void* hHandle, unsigned int dwStartOffset,
222 | unsigned int dwLength);
223 | __declspec(dllexport)OPEN_SEGASTATUS SEGAAPI_SetSynthParam(void* hHandle, OPEN_HASYNTHPARAMSEXT param, int lPARWValue);
224 | __declspec(dllexport) int SEGAAPI_GetSynthParam(void* hHandle, OPEN_HASYNTHPARAMSEXT param);
225 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetSynthParamMultiple(void* hHandle, unsigned int dwNumParams,
226 | OPEN_SynthParamSet* pSynthParams);
227 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_GetSynthParamMultiple(void* hHandle, unsigned int dwNumParams,
228 | OPEN_SynthParamSet* pSynthParams);
229 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetReleaseState(void* hHandle, int bSet);
230 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_PlayWithSetup(
231 | void* hHandle,
232 | unsigned int dwNumSendRouteParams, OPEN_SendRouteParamSet* pSendRouteParams,
233 | unsigned int dwNumSendLevelParams, OPEN_SendLevelParamSet* pSendLevelParams,
234 | unsigned int dwNumVoiceParams, OPEN_VoiceParamSet* pVoiceParams,
235 | unsigned int dwNumSynthParams, OPEN_SynthParamSet* pSynthParams
236 | );
237 | typedef void(*OPEN_HAWOSEGABUFFERCALLBACK)(void* hHandle,
238 | OPEN_HAWOSMESSAGETYPE message);
239 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_CreateBuffer(OPEN_HAWOSEBUFFERCONFIG* pConfig,
240 | OPEN_HAWOSEGABUFFERCALLBACK pCallback,
241 | unsigned int dwFlags,
242 | void* * phHandle);
243 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_DestroyBuffer(void* hHandle);
244 | __declspec(dllexport) int SEGAAPI_SetGlobalEAXProperty(GUID* guid, unsigned long ulProperty, void* pData,
245 | unsigned long ulDataSize);
246 | __declspec(dllexport) int SEGAAPI_GetGlobalEAXProperty(GUID* guid, unsigned long ulProperty, void* pData,
247 | unsigned long ulDataSize);
248 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetSPDIFOutChannelStatus(
249 | unsigned int dwChannelStatus,
250 | unsigned int dwExtChannelStatus);
251 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_GetSPDIFOutChannelStatus(
252 | unsigned int* pdwChannelStatus,
253 | unsigned int* pdwExtChannelStatus);
254 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetSPDIFOutSampleRate(OPEN_HASPDIFOUTRATE dwSamplingRate);
255 | __declspec(dllexport) OPEN_HASPDIFOUTRATE SEGAAPI_GetSPDIFOutSampleRate(void);
256 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetSPDIFOutChannelRouting(
257 | unsigned int dwChannel,
258 | OPEN_HAROUTING dwSource);
259 | __declspec(dllexport) OPEN_HAROUTING SEGAAPI_GetSPDIFOutChannelRouting(unsigned int dwChannel);
260 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_SetIOVolume(OPEN_HAPHYSICALIO dwPhysIO, unsigned int dwVolume);
261 | __declspec(dllexport) unsigned int SEGAAPI_GetIOVolume(OPEN_HAPHYSICALIO dwPhysIO);
262 | __declspec(dllexport) void SEGAAPI_SetLastStatus(OPEN_SEGASTATUS LastStatus);
263 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_GetLastStatus(void);
264 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_Reset(void);
265 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_Init(void);
266 | __declspec(dllexport) OPEN_SEGASTATUS SEGAAPI_Exit(void);
267 |
--------------------------------------------------------------------------------
/Opensegaapi/src/resource.h:
--------------------------------------------------------------------------------
1 | //{{NO_DEPENDENCIES}}
2 | // Microsoft Visual C++ generated include file.
3 | // Used by Opensegaapi.rc
4 |
5 | // Next default values for new objects
6 | //
7 | #ifdef APSTUDIO_INVOKED
8 | #ifndef APSTUDIO_READONLY_SYMBOLS
9 | #define _APS_NEXT_RESOURCE_VALUE 101
10 | #define _APS_NEXT_COMMAND_VALUE 40001
11 | #define _APS_NEXT_CONTROL_VALUE 1001
12 | #define _APS_NEXT_SYMED_VALUE 101
13 | #endif
14 | #endif
15 |
--------------------------------------------------------------------------------
/Opensegaapi/src/tsf.h:
--------------------------------------------------------------------------------
1 | /* TinySoundFont - v0.8 - SoundFont2 synthesizer - https://github.com/schellingb/TinySoundFont
2 | no warranty implied; use at your own risk
3 | Do this:
4 | #define TSF_IMPLEMENTATION
5 | before you include this file in *one* C or C++ file to create the implementation.
6 | // i.e. it should look like this:
7 | #include ...
8 | #include ...
9 | #define TSF_IMPLEMENTATION
10 | #include "tsf.h"
11 |
12 | [OPTIONAL] #define TSF_NO_STDIO to remove stdio dependency
13 | [OPTIONAL] #define TSF_MALLOC, TSF_REALLOC, and TSF_FREE to avoid stdlib.h
14 | [OPTIONAL] #define TSF_MEMCPY, TSF_MEMSET to avoid string.h
15 | [OPTIONAL] #define TSF_POW, TSF_POWF, TSF_EXPF, TSF_LOG, TSF_TAN, TSF_LOG10, TSF_SQRT to avoid math.h
16 |
17 | NOT YET IMPLEMENTED
18 | - Support for ChorusEffectsSend and ReverbEffectsSend generators
19 | - Better low-pass filter without lowering performance too much
20 | - Support for modulators
21 |
22 | LICENSE (MIT)
23 |
24 | Copyright (C) 2017, 2018 Bernhard Schelling
25 | Based on SFZero, Copyright (C) 2012 Steve Folta (https://github.com/stevefolta/SFZero)
26 |
27 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
28 | software and associated documentation files (the "Software"), to deal in the Software
29 | without restriction, including without limitation the rights to use, copy, modify, merge,
30 | publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
31 | to whom the Software is furnished to do so, subject to the following conditions:
32 |
33 | The above copyright notice and this permission notice shall be included in all
34 | copies or substantial portions of the Software.
35 |
36 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
37 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
38 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
39 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
40 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
41 | USE OR OTHER DEALINGS IN THE SOFTWARE.
42 |
43 | */
44 |
45 | #ifndef TSF_INCLUDE_TSF_INL
46 | #define TSF_INCLUDE_TSF_INL
47 |
48 | #ifdef __cplusplus
49 | extern "C" {
50 | # define CPP_DEFAULT0 = 0
51 | #else
52 | # define CPP_DEFAULT0
53 | #endif
54 |
55 | //define this if you want the API functions to be static
56 | #ifdef TSF_STATIC
57 | #define TSFDEF static
58 | #else
59 | #define TSFDEF extern
60 | #endif
61 |
62 | // The load functions will return a pointer to a struct tsf which all functions
63 | // thereafter take as the first parameter.
64 | // On error the tsf_load* functions will return NULL most likely due to invalid
65 | // data (or if the file did not exist in tsf_load_filename).
66 | typedef struct tsf tsf;
67 |
68 | #ifndef TSF_NO_STDIO
69 | // Directly load a SoundFont from a .sf2 file path
70 | TSFDEF tsf* tsf_load_filename(const char* filename);
71 | #endif
72 |
73 | // Load a SoundFont from a block of memory
74 | TSFDEF tsf* tsf_load_memory(const void* buffer, int size);
75 |
76 | // Stream structure for the generic loading
77 | struct tsf_stream
78 | {
79 | // Custom data given to the functions as the first parameter
80 | void* data;
81 |
82 | // Function pointer will be called to read 'size' bytes into ptr (returns number of read bytes)
83 | int(*read)(void* data, void* ptr, unsigned int size);
84 |
85 | // Function pointer will be called to skip ahead over 'count' bytes (returns 1 on success, 0 on error)
86 | int(*skip)(void* data, unsigned int count);
87 | };
88 |
89 | // Generic SoundFont loading method using the stream structure above
90 | TSFDEF tsf* tsf_load(struct tsf_stream* stream);
91 |
92 | // Free the memory related to this tsf instance
93 | TSFDEF void tsf_close(tsf* f);
94 |
95 | // Stop all playing notes immediatly and reset all channel parameters
96 | TSFDEF void tsf_reset(tsf* f);
97 |
98 | // Returns the preset index from a bank and preset number, or -1 if it does not exist in the loaded SoundFont
99 | TSFDEF int tsf_get_presetindex(const tsf* f, int bank, int preset_number);
100 |
101 | // Returns the number of presets in the loaded SoundFont
102 | TSFDEF int tsf_get_presetcount(const tsf* f);
103 |
104 | // Returns the name of a preset index >= 0 and < tsf_get_presetcount()
105 | TSFDEF const char* tsf_get_presetname(const tsf* f, int preset_index);
106 |
107 | // Returns the name of a preset by bank and preset number
108 | TSFDEF const char* tsf_bank_get_presetname(const tsf* f, int bank, int preset_number);
109 |
110 | // Supported output modes by the render methods
111 | enum TSFOutputMode
112 | {
113 | // Two channels with single left/right samples one after another
114 | TSF_STEREO_INTERLEAVED,
115 | // Two channels with all samples for the left channel first then right
116 | TSF_STEREO_UNWEAVED,
117 | // A single channel (stereo instruments are mixed into center)
118 | TSF_MONO,
119 | };
120 |
121 | // Thread safety:
122 | // Your audio output which calls the tsf_render* functions will most likely
123 | // run on a different thread than where the playback tsf_note* functions
124 | // are called. In which case some sort of concurrency control like a
125 | // mutex needs to be used so they are not called at the same time.
126 |
127 | // Setup the parameters for the voice render methods
128 | // outputmode: if mono or stereo and how stereo channel data is ordered
129 | // samplerate: the number of samples per second (output frequency)
130 | // global_gain_db: volume gain in decibels (>0 means higher, <0 means lower)
131 | TSFDEF void tsf_set_output(tsf* f, enum TSFOutputMode outputmode, int samplerate, float global_gain_db CPP_DEFAULT0);
132 |
133 | // Start playing a note
134 | // preset_index: preset index >= 0 and < tsf_get_presetcount()
135 | // key: note value between 0 and 127 (60 being middle C)
136 | // vel: velocity as a float between 0.0 (equal to note off) and 1.0 (full)
137 | // bank: instrument bank number (alternative to preset_index)
138 | // preset_number: preset number (alternative to preset_index)
139 | // (bank_note_on returns 0 if preset does not exist, otherwise 1)
140 | TSFDEF void tsf_note_on(tsf* f, int preset_index, int key, float vel);
141 | TSFDEF int tsf_bank_note_on(tsf* f, int bank, int preset_number, int key, float vel);
142 |
143 | // Stop playing a note
144 | // (bank_note_off returns 0 if preset does not exist, otherwise 1)
145 | TSFDEF void tsf_note_off(tsf* f, int preset_index, int key);
146 | TSFDEF int tsf_bank_note_off(tsf* f, int bank, int preset_number, int key);
147 |
148 | // Stop playing all notes (end with sustain and release)
149 | TSFDEF void tsf_note_off_all(tsf* f);
150 |
151 | // Render output samples into a buffer
152 | // You can either render as signed 16-bit values (tsf_render_short) or
153 | // as 32-bit float values (tsf_render_float)
154 | // buffer: target buffer of size samples * output_channels * sizeof(type)
155 | // samples: number of samples to render
156 | // flag_mixing: if 0 clear the buffer first, otherwise mix into existing data
157 | TSFDEF void tsf_render_short(tsf* f, short* buffer, int samples, int flag_mixing CPP_DEFAULT0);
158 | TSFDEF void tsf_render_float(tsf* f, float* buffer, int samples, int flag_mixing CPP_DEFAULT0);
159 |
160 | // Higher level channel based functions, set up channel parameters
161 | // channel: channel number
162 | // preset_index: preset index >= 0 and < tsf_get_presetcount()
163 | // preset_number: preset number (alternative to preset_index)
164 | // flag_mididrums: 0 for normal channels, otherwise apply MIDI drum channel rules
165 | // bank: instrument bank number (alternative to preset_index)
166 | // pan: stereo panning value from 0.0 (left) to 1.0 (right) (default 0.5 center)
167 | // volume: linear volume scale factor (default 1.0 full)
168 | // pitch_wheel: pitch wheel position 0 to 16383 (default 8192 unpitched)
169 | // pitch_range: range of the pitch wheel in semitones (default 2.0)
170 | // tuning: tuning of all playing voices in semitones (default 0.0 standard (A440) tuning)
171 | // (set_preset_number and set_bank_preset return 0 if preset does not exist, otherwise 1)
172 | TSFDEF void tsf_channel_set_presetindex(tsf* f, int channel, int preset_index);
173 | TSFDEF int tsf_channel_set_presetnumber(tsf* f, int channel, int preset_number, int flag_mididrums CPP_DEFAULT0);
174 | TSFDEF void tsf_channel_set_bank(tsf* f, int channel, int bank);
175 | TSFDEF int tsf_channel_set_bank_preset(tsf* f, int channel, int bank, int preset_number);
176 | TSFDEF void tsf_channel_set_pan(tsf* f, int channel, float pan);
177 | TSFDEF void tsf_channel_set_volume(tsf* f, int channel, float volume);
178 | TSFDEF void tsf_channel_set_pitchwheel(tsf* f, int channel, int pitch_wheel);
179 | TSFDEF void tsf_channel_set_pitchrange(tsf* f, int channel, float pitch_range);
180 | TSFDEF void tsf_channel_set_tuning(tsf* f, int channel, float tuning);
181 |
182 | // Start or stop playing notes on a channel (needs channel preset to be set)
183 | // channel: channel number
184 | // key: note value between 0 and 127 (60 being middle C)
185 | // vel: velocity as a float between 0.0 (equal to note off) and 1.0 (full)
186 | TSFDEF void tsf_channel_note_on(tsf* f, int channel, int key, float vel);
187 | TSFDEF void tsf_channel_note_off(tsf* f, int channel, int key);
188 | TSFDEF void tsf_channel_note_off_all(tsf* f, int channel); //end with sustain and release
189 | TSFDEF void tsf_channel_sounds_off_all(tsf* f, int channel); //end immediatly
190 |
191 | // Apply a MIDI control change to the channel (not all controllers are supported!)
192 | TSFDEF void tsf_channel_midi_control(tsf* f, int channel, int controller, int control_value);
193 |
194 | // Get current values set on the channels
195 | TSFDEF int tsf_channel_get_preset_index(tsf* f, int channel);
196 | TSFDEF int tsf_channel_get_preset_bank(tsf* f, int channel);
197 | TSFDEF int tsf_channel_get_preset_number(tsf* f, int channel);
198 | TSFDEF int tsf_channel_get_pitchwheel(tsf* f, int channel);
199 | TSFDEF float tsf_channel_get_pan(tsf* f, int channel);
200 | TSFDEF float tsf_channel_get_volume(tsf* f, int channel);
201 |
202 | #ifdef __cplusplus
203 | # undef CPP_DEFAULT0
204 | }
205 | #endif
206 |
207 | // end header
208 | // ---------------------------------------------------------------------------------------------------------
209 | #endif //TSF_INCLUDE_TSF_INL
210 |
211 | #ifdef TSF_IMPLEMENTATION
212 |
213 | // The lower this block size is the more accurate the effects are.
214 | // Increasing the value significantly lowers the CPU usage of the voice rendering.
215 | // If LFO affects the low-pass filter it can be hearable even as low as 8.
216 | #ifndef TSF_RENDER_EFFECTSAMPLEBLOCK
217 | #define TSF_RENDER_EFFECTSAMPLEBLOCK 64
218 | #endif
219 |
220 | // Grace release time for quick voice off (avoid clicking noise)
221 | #define TSF_FASTRELEASETIME 0.01f
222 |
223 | #if !defined(TSF_MALLOC) || !defined(TSF_FREE) || !defined(TSF_REALLOC)
224 | # include
225 | # define TSF_MALLOC malloc
226 | # define TSF_FREE free
227 | # define TSF_REALLOC realloc
228 | #endif
229 |
230 | #if !defined(TSF_MEMCPY) || !defined(TSF_MEMSET)
231 | # include
232 | # define TSF_MEMCPY memcpy
233 | # define TSF_MEMSET memset
234 | #endif
235 |
236 | #if !defined(TSF_POW) || !defined(TSF_POWF) || !defined(TSF_EXPF) || !defined(TSF_LOG) || !defined(TSF_TAN) || !defined(TSF_LOG10) || !defined(TSF_SQRT)
237 | # include
238 | # if !defined(__cplusplus) && !defined(NAN) && !defined(powf) && !defined(expf) && !defined(sqrtf)
239 | # define powf (float)pow // deal with old math.h
240 | # define expf (float)exp // files that come without
241 | # define sqrtf (float)sqrt // powf, expf and sqrtf
242 | # endif
243 | # define TSF_POW pow
244 | # define TSF_POWF powf
245 | # define TSF_EXPF expf
246 | # define TSF_LOG log
247 | # define TSF_TAN tan
248 | # define TSF_LOG10 log10
249 | # define TSF_SQRTF sqrtf
250 | #endif
251 |
252 | #ifndef TSF_NO_STDIO
253 | # include
254 | #endif
255 |
256 | #define TSF_TRUE 1
257 | #define TSF_FALSE 0
258 | #define TSF_BOOL char
259 | #define TSF_PI 3.14159265358979323846264338327950288
260 | #define TSF_NULL 0
261 |
262 | #ifdef __cplusplus
263 | extern "C" {
264 | #endif
265 |
266 | typedef char tsf_fourcc[4];
267 | typedef signed char tsf_s8;
268 | typedef unsigned char tsf_u8;
269 | typedef unsigned short tsf_u16;
270 | typedef signed short tsf_s16;
271 | typedef unsigned int tsf_u32;
272 | typedef char tsf_char20[20];
273 |
274 | #define TSF_FourCCEquals(value1, value2) (value1[0] == value2[0] && value1[1] == value2[1] && value1[2] == value2[2] && value1[3] == value2[3])
275 |
276 | struct tsf
277 | {
278 | struct tsf_preset* presets;
279 | float* fontSamples;
280 | struct tsf_voice* voices;
281 | struct tsf_channels* channels;
282 | float* outputSamples;
283 |
284 | int presetNum;
285 | int voiceNum;
286 | int outputSampleSize;
287 | unsigned int voicePlayIndex;
288 |
289 | enum TSFOutputMode outputmode;
290 | float outSampleRate;
291 | float globalGainDB;
292 | };
293 |
294 | #ifndef TSF_NO_STDIO
295 | static int tsf_stream_stdio_read(FILE* f, void* ptr, unsigned int size) { return (int)fread(ptr, 1, size, f); }
296 | static int tsf_stream_stdio_skip(FILE* f, unsigned int count) { return !fseek(f, count, SEEK_CUR); }
297 | TSFDEF tsf* tsf_load_filename(const char* filename)
298 | {
299 | tsf* res;
300 | struct tsf_stream stream = { TSF_NULL, (int(*)(void*,void*,unsigned int))&tsf_stream_stdio_read, (int(*)(void*,unsigned int))&tsf_stream_stdio_skip };
301 | #if __STDC_WANT_SECURE_LIB__
302 | FILE* f = TSF_NULL; fopen_s(&f, filename, "rb");
303 | #else
304 | FILE* f = fopen(filename, "rb");
305 | #endif
306 | if (!f)
307 | {
308 | //if (e) *e = TSF_FILENOTFOUND;
309 | return TSF_NULL;
310 | }
311 | stream.data = f;
312 | res = tsf_load(&stream);
313 | fclose(f);
314 | return res;
315 | }
316 | #endif
317 |
318 | struct tsf_stream_memory { const char* buffer; unsigned int total, pos; };
319 | static int tsf_stream_memory_read(struct tsf_stream_memory* m, void* ptr, unsigned int size) { if (size > m->total - m->pos) size = m->total - m->pos; TSF_MEMCPY(ptr, m->buffer + m->pos, size); m->pos += size; return size; }
320 | static int tsf_stream_memory_skip(struct tsf_stream_memory* m, unsigned int count) { if (m->pos + count > m->total) return 0; m->pos += count; return 1; }
321 | TSFDEF tsf* tsf_load_memory(const void* buffer, int size)
322 | {
323 | struct tsf_stream stream = { TSF_NULL, (int(*)(void*,void*,unsigned int))&tsf_stream_memory_read, (int(*)(void*,unsigned int))&tsf_stream_memory_skip };
324 | struct tsf_stream_memory f = { 0, 0, 0 };
325 | f.buffer = (const char*)buffer;
326 | f.total = size;
327 | stream.data = &f;
328 | return tsf_load(&stream);
329 | }
330 |
331 | enum { TSF_LOOPMODE_NONE, TSF_LOOPMODE_CONTINUOUS, TSF_LOOPMODE_SUSTAIN };
332 |
333 | enum { TSF_SEGMENT_NONE, TSF_SEGMENT_DELAY, TSF_SEGMENT_ATTACK, TSF_SEGMENT_HOLD, TSF_SEGMENT_DECAY, TSF_SEGMENT_SUSTAIN, TSF_SEGMENT_RELEASE, TSF_SEGMENT_DONE };
334 |
335 | struct tsf_hydra
336 | {
337 | struct tsf_hydra_phdr *phdrs; struct tsf_hydra_pbag *pbags; struct tsf_hydra_pmod *pmods;
338 | struct tsf_hydra_pgen *pgens; struct tsf_hydra_inst *insts; struct tsf_hydra_ibag *ibags;
339 | struct tsf_hydra_imod *imods; struct tsf_hydra_igen *igens; struct tsf_hydra_shdr *shdrs;
340 | int phdrNum, pbagNum, pmodNum, pgenNum, instNum, ibagNum, imodNum, igenNum, shdrNum;
341 | };
342 |
343 | union tsf_hydra_genamount { struct { tsf_u8 lo, hi; } range; tsf_s16 shortAmount; tsf_u16 wordAmount; };
344 | struct tsf_hydra_phdr { tsf_char20 presetName; tsf_u16 preset, bank, presetBagNdx; tsf_u32 library, genre, morphology; };
345 | struct tsf_hydra_pbag { tsf_u16 genNdx, modNdx; };
346 | struct tsf_hydra_pmod { tsf_u16 modSrcOper, modDestOper; tsf_s16 modAmount; tsf_u16 modAmtSrcOper, modTransOper; };
347 | struct tsf_hydra_pgen { tsf_u16 genOper; union tsf_hydra_genamount genAmount; };
348 | struct tsf_hydra_inst { tsf_char20 instName; tsf_u16 instBagNdx; };
349 | struct tsf_hydra_ibag { tsf_u16 instGenNdx, instModNdx; };
350 | struct tsf_hydra_imod { tsf_u16 modSrcOper, modDestOper; tsf_s16 modAmount; tsf_u16 modAmtSrcOper, modTransOper; };
351 | struct tsf_hydra_igen { tsf_u16 genOper; union tsf_hydra_genamount genAmount; };
352 | struct tsf_hydra_shdr { tsf_char20 sampleName; tsf_u32 start, end, startLoop, endLoop, sampleRate; tsf_u8 originalPitch; tsf_s8 pitchCorrection; tsf_u16 sampleLink, sampleType; };
353 |
354 | #define TSFR(FIELD) stream->read(stream->data, &i->FIELD, sizeof(i->FIELD));
355 | static void tsf_hydra_read_phdr(struct tsf_hydra_phdr* i, struct tsf_stream* stream) { TSFR(presetName) TSFR(preset) TSFR(bank) TSFR(presetBagNdx) TSFR(library) TSFR(genre) TSFR(morphology) }
356 | static void tsf_hydra_read_pbag(struct tsf_hydra_pbag* i, struct tsf_stream* stream) { TSFR(genNdx) TSFR(modNdx) }
357 | static void tsf_hydra_read_pmod(struct tsf_hydra_pmod* i, struct tsf_stream* stream) { TSFR(modSrcOper) TSFR(modDestOper) TSFR(modAmount) TSFR(modAmtSrcOper) TSFR(modTransOper) }
358 | static void tsf_hydra_read_pgen(struct tsf_hydra_pgen* i, struct tsf_stream* stream) { TSFR(genOper) TSFR(genAmount) }
359 | static void tsf_hydra_read_inst(struct tsf_hydra_inst* i, struct tsf_stream* stream) { TSFR(instName) TSFR(instBagNdx) }
360 | static void tsf_hydra_read_ibag(struct tsf_hydra_ibag* i, struct tsf_stream* stream) { TSFR(instGenNdx) TSFR(instModNdx) }
361 | static void tsf_hydra_read_imod(struct tsf_hydra_imod* i, struct tsf_stream* stream) { TSFR(modSrcOper) TSFR(modDestOper) TSFR(modAmount) TSFR(modAmtSrcOper) TSFR(modTransOper) }
362 | static void tsf_hydra_read_igen(struct tsf_hydra_igen* i, struct tsf_stream* stream) { TSFR(genOper) TSFR(genAmount) }
363 | static void tsf_hydra_read_shdr(struct tsf_hydra_shdr* i, struct tsf_stream* stream) { TSFR(sampleName) TSFR(start) TSFR(end) TSFR(startLoop) TSFR(endLoop) TSFR(sampleRate) TSFR(originalPitch) TSFR(pitchCorrection) TSFR(sampleLink) TSFR(sampleType) }
364 | #undef TSFR
365 |
366 | struct tsf_riffchunk { tsf_fourcc id; tsf_u32 size; };
367 | struct tsf_envelope { float delay, attack, hold, decay, sustain, release, keynumToHold, keynumToDecay; };
368 | struct tsf_voice_envelope { float level, slope; int samplesUntilNextSegment; short segment, midiVelocity; struct tsf_envelope parameters; TSF_BOOL segmentIsExponential, isAmpEnv; };
369 | struct tsf_voice_lowpass { double QInv, a0, a1, b1, b2, z1, z2; TSF_BOOL active; };
370 | struct tsf_voice_lfo { int samplesUntil; float level, delta; };
371 |
372 | struct tsf_region
373 | {
374 | int loop_mode;
375 | unsigned int sample_rate;
376 | unsigned char lokey, hikey, lovel, hivel;
377 | unsigned int group, offset, end, loop_start, loop_end;
378 | int transpose, tune, pitch_keycenter, pitch_keytrack;
379 | float volume, pan;
380 | struct tsf_envelope ampenv, modenv;
381 | int initialFilterQ, initialFilterFc;
382 | int modEnvToPitch, modEnvToFilterFc, modLfoToFilterFc, modLfoToVolume;
383 | float delayModLFO;
384 | int freqModLFO, modLfoToPitch;
385 | float delayVibLFO;
386 | int freqVibLFO, vibLfoToPitch;
387 | };
388 |
389 | struct tsf_preset
390 | {
391 | tsf_char20 presetName;
392 | tsf_u16 preset, bank;
393 | struct tsf_region* regions;
394 | int regionNum;
395 | };
396 |
397 | struct tsf_voice
398 | {
399 | int playingPreset, playingKey, playingChannel;
400 | struct tsf_region* region;
401 | double pitchInputTimecents, pitchOutputFactor;
402 | double sourceSamplePosition;
403 | float noteGainDB, panFactorLeft, panFactorRight;
404 | unsigned int playIndex, loopStart, loopEnd;
405 | struct tsf_voice_envelope ampenv, modenv;
406 | struct tsf_voice_lowpass lowpass;
407 | struct tsf_voice_lfo modlfo, viblfo;
408 | };
409 |
410 | struct tsf_channel
411 | {
412 | unsigned short presetIndex, bank, pitchWheel, midiPan, midiVolume, midiExpression, midiRPN, midiData;
413 | float panOffset, gainDB, pitchRange, tuning;
414 | };
415 |
416 | struct tsf_channels
417 | {
418 | void(*setupVoice)(tsf* f, struct tsf_voice* voice);
419 | struct tsf_channel* channels;
420 | int channelNum, activeChannel;
421 | };
422 |
423 | static double tsf_timecents2Secsd(double timecents) { return TSF_POW(2.0, timecents / 1200.0); }
424 | static float tsf_timecents2Secsf(float timecents) { return TSF_POWF(2.0f, timecents / 1200.0f); }
425 | static float tsf_cents2Hertz(float cents) { return 8.176f * TSF_POWF(2.0f, cents / 1200.0f); }
426 | static float tsf_decibelsToGain(float db) { return (db > -100.f ? TSF_POWF(10.0f, db * 0.05f) : 0); }
427 | static float tsf_gainToDecibels(float gain) { return (gain <= .00001f ? -100.f : (float)(20.0 * TSF_LOG10(gain))); }
428 |
429 | static TSF_BOOL tsf_riffchunk_read(struct tsf_riffchunk* parent, struct tsf_riffchunk* chunk, struct tsf_stream* stream)
430 | {
431 | TSF_BOOL IsRiff, IsList;
432 | if (parent && sizeof(tsf_fourcc) + sizeof(tsf_u32) > parent->size) return TSF_FALSE;
433 | if (!stream->read(stream->data, &chunk->id, sizeof(tsf_fourcc)) || *chunk->id <= ' ' || *chunk->id >= 'z') return TSF_FALSE;
434 | if (!stream->read(stream->data, &chunk->size, sizeof(tsf_u32))) return TSF_FALSE;
435 | if (parent && sizeof(tsf_fourcc) + sizeof(tsf_u32) + chunk->size > parent->size) return TSF_FALSE;
436 | if (parent) parent->size -= sizeof(tsf_fourcc) + sizeof(tsf_u32) + chunk->size;
437 | IsRiff = TSF_FourCCEquals(chunk->id, "RIFF"), IsList = TSF_FourCCEquals(chunk->id, "LIST");
438 | if (IsRiff && parent) return TSF_FALSE; //not allowed
439 | if (!IsRiff && !IsList) return TSF_TRUE; //custom type without sub type
440 | if (!stream->read(stream->data, &chunk->id, sizeof(tsf_fourcc)) || *chunk->id <= ' ' || *chunk->id >= 'z') return TSF_FALSE;
441 | chunk->size -= sizeof(tsf_fourcc);
442 | return TSF_TRUE;
443 | }
444 |
445 | static void tsf_region_clear(struct tsf_region* i, TSF_BOOL for_relative)
446 | {
447 | TSF_MEMSET(i, 0, sizeof(struct tsf_region));
448 | i->hikey = i->hivel = 127;
449 | i->pitch_keycenter = 60; // C4
450 | if (for_relative) return;
451 |
452 | i->pitch_keytrack = 100;
453 |
454 | i->pitch_keycenter = -1;
455 |
456 | // SF2 defaults in timecents.
457 | i->ampenv.delay = i->ampenv.attack = i->ampenv.hold = i->ampenv.decay = i->ampenv.release = -12000.0f;
458 | i->modenv.delay = i->modenv.attack = i->modenv.hold = i->modenv.decay = i->modenv.release = -12000.0f;
459 |
460 | i->initialFilterFc = 13500;
461 |
462 | i->delayModLFO = -12000.0f;
463 | i->delayVibLFO = -12000.0f;
464 | }
465 |
466 | static void tsf_region_operator(struct tsf_region* region, tsf_u16 genOper, union tsf_hydra_genamount* amount)
467 | {
468 | enum
469 | {
470 | StartAddrsOffset, EndAddrsOffset, StartloopAddrsOffset, EndloopAddrsOffset, StartAddrsCoarseOffset, ModLfoToPitch, VibLfoToPitch, ModEnvToPitch,
471 | InitialFilterFc, InitialFilterQ, ModLfoToFilterFc, ModEnvToFilterFc, EndAddrsCoarseOffset, ModLfoToVolume, Unused1, ChorusEffectsSend,
472 | ReverbEffectsSend, Pan, Unused2, Unused3, Unused4, DelayModLFO, FreqModLFO, DelayVibLFO, FreqVibLFO, DelayModEnv, AttackModEnv, HoldModEnv,
473 | DecayModEnv, SustainModEnv, ReleaseModEnv, KeynumToModEnvHold, KeynumToModEnvDecay, DelayVolEnv, AttackVolEnv, HoldVolEnv, DecayVolEnv,
474 | SustainVolEnv, ReleaseVolEnv, KeynumToVolEnvHold, KeynumToVolEnvDecay, Instrument, Reserved1, KeyRange, VelRange, StartloopAddrsCoarseOffset,
475 | Keynum, Velocity, InitialAttenuation, Reserved2, EndloopAddrsCoarseOffset, CoarseTune, FineTune, SampleID, SampleModes, Reserved3, ScaleTuning,
476 | ExclusiveClass, OverridingRootKey, Unused5, EndOper
477 | };
478 | switch (genOper)
479 | {
480 | case StartAddrsOffset: region->offset += amount->shortAmount; break;
481 | case EndAddrsOffset: region->end += amount->shortAmount; break;
482 | case StartloopAddrsOffset: region->loop_start += amount->shortAmount; break;
483 | case EndloopAddrsOffset: region->loop_end += amount->shortAmount; break;
484 | case StartAddrsCoarseOffset: region->offset += amount->shortAmount * 32768; break;
485 | case ModLfoToPitch: region->modLfoToPitch = amount->shortAmount; break;
486 | case VibLfoToPitch: region->vibLfoToPitch = amount->shortAmount; break;
487 | case ModEnvToPitch: region->modEnvToPitch = amount->shortAmount; break;
488 | case InitialFilterFc: region->initialFilterFc = amount->shortAmount; break;
489 | case InitialFilterQ: region->initialFilterQ = amount->shortAmount; break;
490 | case ModLfoToFilterFc: region->modLfoToFilterFc = amount->shortAmount; break;
491 | case ModEnvToFilterFc: region->modEnvToFilterFc = amount->shortAmount; break;
492 | case EndAddrsCoarseOffset: region->end += amount->shortAmount * 32768; break;
493 | case ModLfoToVolume: region->modLfoToVolume = amount->shortAmount; break;
494 | case Pan: region->pan = amount->shortAmount / 1000.0f; break;
495 | case DelayModLFO: region->delayModLFO = amount->shortAmount; break;
496 | case FreqModLFO: region->freqModLFO = amount->shortAmount; break;
497 | case DelayVibLFO: region->delayVibLFO = amount->shortAmount; break;
498 | case FreqVibLFO: region->freqVibLFO = amount->shortAmount; break;
499 | case DelayModEnv: region->modenv.delay = amount->shortAmount; break;
500 | case AttackModEnv: region->modenv.attack = amount->shortAmount; break;
501 | case HoldModEnv: region->modenv.hold = amount->shortAmount; break;
502 | case DecayModEnv: region->modenv.decay = amount->shortAmount; break;
503 | case SustainModEnv: region->modenv.sustain = amount->shortAmount; break;
504 | case ReleaseModEnv: region->modenv.release = amount->shortAmount; break;
505 | case KeynumToModEnvHold: region->modenv.keynumToHold = amount->shortAmount; break;
506 | case KeynumToModEnvDecay: region->modenv.keynumToDecay = amount->shortAmount; break;
507 | case DelayVolEnv: region->ampenv.delay = amount->shortAmount; break;
508 | case AttackVolEnv: region->ampenv.attack = amount->shortAmount; break;
509 | case HoldVolEnv: region->ampenv.hold = amount->shortAmount; break;
510 | case DecayVolEnv: region->ampenv.decay = amount->shortAmount; break;
511 | case SustainVolEnv: region->ampenv.sustain = amount->shortAmount; break;
512 | case ReleaseVolEnv: region->ampenv.release = amount->shortAmount; break;
513 | case KeynumToVolEnvHold: region->ampenv.keynumToHold = amount->shortAmount; break;
514 | case KeynumToVolEnvDecay: region->ampenv.keynumToDecay = amount->shortAmount; break;
515 | case KeyRange: region->lokey = amount->range.lo; region->hikey = amount->range.hi; break;
516 | case VelRange: region->lovel = amount->range.lo; region->hivel = amount->range.hi; break;
517 | case StartloopAddrsCoarseOffset: region->loop_start += amount->shortAmount * 32768; break;
518 | case InitialAttenuation: region->volume += amount->shortAmount / 100.0f; break;
519 | case EndloopAddrsCoarseOffset: region->loop_end += amount->shortAmount * 32768; break;
520 | case CoarseTune: region->transpose += amount->shortAmount; break;
521 | case FineTune: region->tune += amount->shortAmount; break;
522 | case SampleModes: region->loop_mode = ((amount->wordAmount & 3) == 3 ? TSF_LOOPMODE_SUSTAIN : ((amount->wordAmount & 3) == 1 ? TSF_LOOPMODE_CONTINUOUS : TSF_LOOPMODE_NONE)); break;
523 | case ScaleTuning: region->pitch_keytrack = amount->shortAmount; break;
524 | case ExclusiveClass: region->group = amount->wordAmount; break;
525 | case OverridingRootKey: region->pitch_keycenter = amount->shortAmount; break;
526 | //case gen_endOper: break; // Ignore.
527 | //default: addUnsupportedOpcode(generator_name);
528 | }
529 | }
530 |
531 | static void tsf_region_envtosecs(struct tsf_envelope* p, TSF_BOOL sustainIsGain)
532 | {
533 | // EG times need to be converted from timecents to seconds.
534 | // Pin very short EG segments. Timecents don't get to zero, and our EG is
535 | // happier with zero values.
536 | p->delay = (p->delay < -11950.0f ? 0.0f : tsf_timecents2Secsf(p->delay));
537 | p->attack = (p->attack < -11950.0f ? 0.0f : tsf_timecents2Secsf(p->attack));
538 | p->release = (p->release < -11950.0f ? 0.0f : tsf_timecents2Secsf(p->release));
539 |
540 | // If we have dynamic hold or decay times depending on key number we need
541 | // to keep the values in timecents so we can calculate it during startNote
542 | if (!p->keynumToHold) p->hold = (p->hold < -11950.0f ? 0.0f : tsf_timecents2Secsf(p->hold));
543 | if (!p->keynumToDecay) p->decay = (p->decay < -11950.0f ? 0.0f : tsf_timecents2Secsf(p->decay));
544 |
545 | if (p->sustain < 0.0f) p->sustain = 0.0f;
546 | else if (sustainIsGain) p->sustain = tsf_decibelsToGain(-p->sustain / 10.0f);
547 | else p->sustain = 1.0f - (p->sustain / 1000.0f);
548 | }
549 |
550 | static void tsf_load_presets(tsf* res, struct tsf_hydra *hydra, unsigned int fontSampleCount)
551 | {
552 | enum { GenInstrument = 41, GenKeyRange = 43, GenVelRange = 44, GenSampleID = 53 };
553 | // Read each preset.
554 | struct tsf_hydra_phdr *pphdr, *pphdrMax;
555 | for (pphdr = hydra->phdrs, pphdrMax = pphdr + hydra->phdrNum - 1; pphdr != pphdrMax; pphdr++)
556 | {
557 | int sortedIndex = 0, region_index = 0;
558 | struct tsf_hydra_phdr *otherphdr;
559 | struct tsf_preset* preset;
560 | struct tsf_hydra_pbag *ppbag, *ppbagEnd;
561 | struct tsf_region globalRegion;
562 | for (otherphdr = hydra->phdrs; otherphdr != pphdrMax; otherphdr++)
563 | {
564 | if (otherphdr == pphdr || otherphdr->bank > pphdr->bank) continue;
565 | else if (otherphdr->bank < pphdr->bank) sortedIndex++;
566 | else if (otherphdr->preset > pphdr->preset) continue;
567 | else if (otherphdr->preset < pphdr->preset) sortedIndex++;
568 | else if (otherphdr < pphdr) sortedIndex++;
569 | }
570 |
571 | preset = &res->presets[sortedIndex];
572 | TSF_MEMCPY(preset->presetName, pphdr->presetName, sizeof(preset->presetName));
573 | preset->presetName[sizeof(preset->presetName) - 1] = '\0'; //should be zero terminated in source file but make sure
574 | preset->bank = pphdr->bank;
575 | preset->preset = pphdr->preset;
576 | preset->regionNum = 0;
577 |
578 | //count regions covered by this preset
579 | for (ppbag = hydra->pbags + pphdr->presetBagNdx, ppbagEnd = hydra->pbags + pphdr[1].presetBagNdx; ppbag != ppbagEnd; ppbag++)
580 | {
581 | unsigned char plokey = 0, phikey = 127, plovel = 0, phivel = 127;
582 | struct tsf_hydra_pgen *ppgen, *ppgenEnd; struct tsf_hydra_inst *pinst; struct tsf_hydra_ibag *pibag, *pibagEnd; struct tsf_hydra_igen *pigen, *pigenEnd;
583 | for (ppgen = hydra->pgens + ppbag->genNdx, ppgenEnd = hydra->pgens + ppbag[1].genNdx; ppgen != ppgenEnd; ppgen++)
584 | {
585 | if (ppgen->genOper == GenKeyRange) { plokey = ppgen->genAmount.range.lo; phikey = ppgen->genAmount.range.hi; continue; }
586 | if (ppgen->genOper == GenVelRange) { plovel = ppgen->genAmount.range.lo; phivel = ppgen->genAmount.range.hi; continue; }
587 | if (ppgen->genOper != GenInstrument) continue;
588 | if (ppgen->genAmount.wordAmount >= hydra->instNum) continue;
589 | pinst = hydra->insts + ppgen->genAmount.wordAmount;
590 | for (pibag = hydra->ibags + pinst->instBagNdx, pibagEnd = hydra->ibags + pinst[1].instBagNdx; pibag != pibagEnd; pibag++)
591 | {
592 | unsigned char ilokey = 0, ihikey = 127, ilovel = 0, ihivel = 127;
593 | for (pigen = hydra->igens + pibag->instGenNdx, pigenEnd = hydra->igens + pibag[1].instGenNdx; pigen != pigenEnd; pigen++)
594 | {
595 | if (pigen->genOper == GenKeyRange) { ilokey = pigen->genAmount.range.lo; ihikey = pigen->genAmount.range.hi; continue; }
596 | if (pigen->genOper == GenVelRange) { ilovel = pigen->genAmount.range.lo; ihivel = pigen->genAmount.range.hi; continue; }
597 | if (pigen->genOper == GenSampleID && ihikey >= plokey && ilokey <= phikey && ihivel >= plovel && ilovel <= phivel) preset->regionNum++;
598 | }
599 | }
600 | }
601 | }
602 |
603 | preset->regions = (struct tsf_region*)TSF_MALLOC(preset->regionNum * sizeof(struct tsf_region));
604 | tsf_region_clear(&globalRegion, TSF_TRUE);
605 |
606 | // Zones.
607 | for (ppbag = hydra->pbags + pphdr->presetBagNdx, ppbagEnd = hydra->pbags + pphdr[1].presetBagNdx; ppbag != ppbagEnd; ppbag++)
608 | {
609 | struct tsf_hydra_pgen *ppgen, *ppgenEnd; struct tsf_hydra_inst *pinst; struct tsf_hydra_ibag *pibag, *pibagEnd; struct tsf_hydra_igen *pigen, *pigenEnd;
610 | struct tsf_region presetRegion = globalRegion;
611 | int hadGenInstrument = 0;
612 |
613 | // Generators.
614 | for (ppgen = hydra->pgens + ppbag->genNdx, ppgenEnd = hydra->pgens + ppbag[1].genNdx; ppgen != ppgenEnd; ppgen++)
615 | {
616 | // Instrument.
617 | if (ppgen->genOper == GenInstrument)
618 | {
619 | struct tsf_region instRegion;
620 | tsf_u16 whichInst = ppgen->genAmount.wordAmount;
621 | if (whichInst >= hydra->instNum) continue;
622 |
623 | tsf_region_clear(&instRegion, TSF_FALSE);
624 | pinst = &hydra->insts[whichInst];
625 | for (pibag = hydra->ibags + pinst->instBagNdx, pibagEnd = hydra->ibags + pinst[1].instBagNdx; pibag != pibagEnd; pibag++)
626 | {
627 | // Generators.
628 | struct tsf_region zoneRegion = instRegion;
629 | int hadSampleID = 0;
630 | for (pigen = hydra->igens + pibag->instGenNdx, pigenEnd = hydra->igens + pibag[1].instGenNdx; pigen != pigenEnd; pigen++)
631 | {
632 | if (pigen->genOper == GenSampleID)
633 | {
634 | struct tsf_hydra_shdr* pshdr;
635 |
636 | //preset region key and vel ranges are a filter for the zone regions
637 | if (zoneRegion.hikey < presetRegion.lokey || zoneRegion.lokey > presetRegion.hikey) continue;
638 | if (zoneRegion.hivel < presetRegion.lovel || zoneRegion.lovel > presetRegion.hivel) continue;
639 | if (presetRegion.lokey > zoneRegion.lokey) zoneRegion.lokey = presetRegion.lokey;
640 | if (presetRegion.hikey < zoneRegion.hikey) zoneRegion.hikey = presetRegion.hikey;
641 | if (presetRegion.lovel > zoneRegion.lovel) zoneRegion.lovel = presetRegion.lovel;
642 | if (presetRegion.hivel < zoneRegion.hivel) zoneRegion.hivel = presetRegion.hivel;
643 |
644 | //sum regions
645 | zoneRegion.offset += presetRegion.offset;
646 | zoneRegion.end += presetRegion.end;
647 | zoneRegion.loop_start += presetRegion.loop_start;
648 | zoneRegion.loop_end += presetRegion.loop_end;
649 | zoneRegion.transpose += presetRegion.transpose;
650 | zoneRegion.tune += presetRegion.tune;
651 | zoneRegion.pitch_keytrack += presetRegion.pitch_keytrack;
652 | zoneRegion.volume += presetRegion.volume;
653 | zoneRegion.pan += presetRegion.pan;
654 | zoneRegion.ampenv.delay += presetRegion.ampenv.delay;
655 | zoneRegion.ampenv.attack += presetRegion.ampenv.attack;
656 | zoneRegion.ampenv.hold += presetRegion.ampenv.hold;
657 | zoneRegion.ampenv.decay += presetRegion.ampenv.decay;
658 | zoneRegion.ampenv.sustain += presetRegion.ampenv.sustain;
659 | zoneRegion.ampenv.release += presetRegion.ampenv.release;
660 | zoneRegion.modenv.delay += presetRegion.modenv.delay;
661 | zoneRegion.modenv.attack += presetRegion.modenv.attack;
662 | zoneRegion.modenv.hold += presetRegion.modenv.hold;
663 | zoneRegion.modenv.decay += presetRegion.modenv.decay;
664 | zoneRegion.modenv.sustain += presetRegion.modenv.sustain;
665 | zoneRegion.modenv.release += presetRegion.modenv.release;
666 | zoneRegion.initialFilterQ += presetRegion.initialFilterQ;
667 | zoneRegion.initialFilterFc += presetRegion.initialFilterFc;
668 | zoneRegion.modEnvToPitch += presetRegion.modEnvToPitch;
669 | zoneRegion.modEnvToFilterFc += presetRegion.modEnvToFilterFc;
670 | zoneRegion.delayModLFO += presetRegion.delayModLFO;
671 | zoneRegion.freqModLFO += presetRegion.freqModLFO;
672 | zoneRegion.modLfoToPitch += presetRegion.modLfoToPitch;
673 | zoneRegion.modLfoToFilterFc += presetRegion.modLfoToFilterFc;
674 | zoneRegion.modLfoToVolume += presetRegion.modLfoToVolume;
675 | zoneRegion.delayVibLFO += presetRegion.delayVibLFO;
676 | zoneRegion.freqVibLFO += presetRegion.freqVibLFO;
677 | zoneRegion.vibLfoToPitch += presetRegion.vibLfoToPitch;
678 |
679 | // EG times need to be converted from timecents to seconds.
680 | tsf_region_envtosecs(&zoneRegion.ampenv, TSF_TRUE);
681 | tsf_region_envtosecs(&zoneRegion.modenv, TSF_FALSE);
682 |
683 | // LFO times need to be converted from timecents to seconds.
684 | zoneRegion.delayModLFO = (zoneRegion.delayModLFO < -11950.0f ? 0.0f : tsf_timecents2Secsf(zoneRegion.delayModLFO));
685 | zoneRegion.delayVibLFO = (zoneRegion.delayVibLFO < -11950.0f ? 0.0f : tsf_timecents2Secsf(zoneRegion.delayVibLFO));
686 |
687 | // Pin values to their ranges.
688 | if (zoneRegion.pan < -0.5f) zoneRegion.pan = -0.5f;
689 | else if (zoneRegion.pan > 0.5f) zoneRegion.pan = 0.5f;
690 | if (zoneRegion.initialFilterQ < 1500 || zoneRegion.initialFilterQ > 13500) zoneRegion.initialFilterQ = 0;
691 |
692 | pshdr = &hydra->shdrs[pigen->genAmount.wordAmount];
693 | zoneRegion.offset += pshdr->start;
694 | zoneRegion.end += pshdr->end;
695 | zoneRegion.loop_start += pshdr->startLoop;
696 | zoneRegion.loop_end += pshdr->endLoop;
697 | if (pshdr->endLoop > 0) zoneRegion.loop_end -= 1;
698 | if (zoneRegion.pitch_keycenter == -1) zoneRegion.pitch_keycenter = pshdr->originalPitch;
699 | zoneRegion.tune += pshdr->pitchCorrection;
700 | zoneRegion.sample_rate = pshdr->sampleRate;
701 | if (zoneRegion.end && zoneRegion.end < fontSampleCount) zoneRegion.end++;
702 | else zoneRegion.end = fontSampleCount;
703 |
704 | preset->regions[region_index] = zoneRegion;
705 | region_index++;
706 | hadSampleID = 1;
707 | }
708 | else tsf_region_operator(&zoneRegion, pigen->genOper, &pigen->genAmount);
709 | }
710 |
711 | // Handle instrument's global zone.
712 | if (pibag == hydra->ibags + pinst->instBagNdx && !hadSampleID)
713 | instRegion = zoneRegion;
714 |
715 | // Modulators (TODO)
716 | //if (ibag->instModNdx < ibag[1].instModNdx) addUnsupportedOpcode("any modulator");
717 | }
718 | hadGenInstrument = 1;
719 | }
720 | else tsf_region_operator(&presetRegion, ppgen->genOper, &ppgen->genAmount);
721 | }
722 |
723 | // Modulators (TODO)
724 | //if (pbag->modNdx < pbag[1].modNdx) addUnsupportedOpcode("any modulator");
725 |
726 | // Handle preset's global zone.
727 | if (ppbag == hydra->pbags + pphdr->presetBagNdx && !hadGenInstrument)
728 | globalRegion = presetRegion;
729 | }
730 | }
731 | }
732 |
733 | static void tsf_load_samples(float** fontSamples, unsigned int* fontSampleCount, struct tsf_riffchunk *chunkSmpl, struct tsf_stream* stream)
734 | {
735 | // Read sample data into float format buffer.
736 | float* out; unsigned int samplesLeft, samplesToRead, samplesToConvert;
737 | samplesLeft = *fontSampleCount = chunkSmpl->size / sizeof(short);
738 | out = *fontSamples = (float*)TSF_MALLOC(samplesLeft * sizeof(float));
739 | for (; samplesLeft; samplesLeft -= samplesToRead)
740 | {
741 | short sampleBuffer[1024], *in = sampleBuffer;;
742 | samplesToRead = (samplesLeft > 1024 ? 1024 : samplesLeft);
743 | stream->read(stream->data, sampleBuffer, samplesToRead * sizeof(short));
744 |
745 | // Convert from signed 16-bit to float.
746 | for (samplesToConvert = samplesToRead; samplesToConvert > 0; --samplesToConvert)
747 | // If we ever need to compile for big-endian platforms, we'll need to byte-swap here.
748 | *out++ = (float)(*in++ / 32767.0);
749 | }
750 | }
751 |
752 | static void tsf_voice_envelope_nextsegment(struct tsf_voice_envelope* e, short active_segment, float outSampleRate)
753 | {
754 | switch (active_segment)
755 | {
756 | case TSF_SEGMENT_NONE:
757 | e->samplesUntilNextSegment = (int)(e->parameters.delay * outSampleRate);
758 | if (e->samplesUntilNextSegment > 0)
759 | {
760 | e->segment = TSF_SEGMENT_DELAY;
761 | e->segmentIsExponential = TSF_FALSE;
762 | e->level = 0.0;
763 | e->slope = 0.0;
764 | return;
765 | }
766 | case TSF_SEGMENT_DELAY:
767 | e->samplesUntilNextSegment = (int)(e->parameters.attack * outSampleRate);
768 | if (e->samplesUntilNextSegment > 0)
769 | {
770 | if (!e->isAmpEnv)
771 | {
772 | //mod env attack duration scales with velocity (velocity of 1 is full duration, max velocity is 0.125 times duration)
773 | e->samplesUntilNextSegment = (int)(e->parameters.attack * ((145 - e->midiVelocity) / 144.0f) * outSampleRate);
774 | }
775 | e->segment = TSF_SEGMENT_ATTACK;
776 | e->segmentIsExponential = TSF_FALSE;
777 | e->level = 0.0f;
778 | e->slope = 1.0f / e->samplesUntilNextSegment;
779 | return;
780 | }
781 | case TSF_SEGMENT_ATTACK:
782 | e->samplesUntilNextSegment = (int)(e->parameters.hold * outSampleRate);
783 | if (e->samplesUntilNextSegment > 0)
784 | {
785 | e->segment = TSF_SEGMENT_HOLD;
786 | e->segmentIsExponential = TSF_FALSE;
787 | e->level = 1.0f;
788 | e->slope = 0.0f;
789 | return;
790 | }
791 | case TSF_SEGMENT_HOLD:
792 | e->samplesUntilNextSegment = (int)(e->parameters.decay * outSampleRate);
793 | if (e->samplesUntilNextSegment > 0)
794 | {
795 | e->segment = TSF_SEGMENT_DECAY;
796 | e->level = 1.0f;
797 | if (e->isAmpEnv)
798 | {
799 | // I don't truly understand this; just following what LinuxSampler does.
800 | float mysterySlope = -9.226f / e->samplesUntilNextSegment;
801 | e->slope = TSF_EXPF(mysterySlope);
802 | e->segmentIsExponential = TSF_TRUE;
803 | if (e->parameters.sustain > 0.0f)
804 | {
805 | // Again, this is following LinuxSampler's example, which is similar to
806 | // SF2-style decay, where "decay" specifies the time it would take to
807 | // get to zero, not to the sustain level. The SFZ spec is not that
808 | // specific about what "decay" means, so perhaps it's really supposed
809 | // to specify the time to reach the sustain level.
810 | e->samplesUntilNextSegment = (int)(TSF_LOG(e->parameters.sustain) / mysterySlope);
811 | }
812 | }
813 | else
814 | {
815 | e->slope = -1.0f / e->samplesUntilNextSegment;
816 | e->samplesUntilNextSegment = (int)(e->parameters.decay * (1.0f - e->parameters.sustain) * outSampleRate);
817 | e->segmentIsExponential = TSF_FALSE;
818 | }
819 | return;
820 | }
821 | case TSF_SEGMENT_DECAY:
822 | e->segment = TSF_SEGMENT_SUSTAIN;
823 | e->level = e->parameters.sustain;
824 | e->slope = 0.0f;
825 | e->samplesUntilNextSegment = 0x7FFFFFFF;
826 | e->segmentIsExponential = TSF_FALSE;
827 | return;
828 | case TSF_SEGMENT_SUSTAIN:
829 | e->segment = TSF_SEGMENT_RELEASE;
830 | e->samplesUntilNextSegment = (int)((e->parameters.release <= 0 ? TSF_FASTRELEASETIME : e->parameters.release) * outSampleRate);
831 | if (e->isAmpEnv)
832 | {
833 | // I don't truly understand this; just following what LinuxSampler does.
834 | float mysterySlope = -9.226f / e->samplesUntilNextSegment;
835 | e->slope = TSF_EXPF(mysterySlope);
836 | e->segmentIsExponential = TSF_TRUE;
837 | }
838 | else
839 | {
840 | e->slope = -e->level / e->samplesUntilNextSegment;
841 | e->segmentIsExponential = TSF_FALSE;
842 | }
843 | return;
844 | case TSF_SEGMENT_RELEASE:
845 | default:
846 | e->segment = TSF_SEGMENT_DONE;
847 | e->segmentIsExponential = TSF_FALSE;
848 | e->level = e->slope = 0.0f;
849 | e->samplesUntilNextSegment = 0x7FFFFFF;
850 | }
851 | }
852 |
853 | static void tsf_voice_envelope_setup(struct tsf_voice_envelope* e, struct tsf_envelope* new_parameters, int midiNoteNumber, short midiVelocity, TSF_BOOL isAmpEnv, float outSampleRate)
854 | {
855 | e->parameters = *new_parameters;
856 | if (e->parameters.keynumToHold)
857 | {
858 | e->parameters.hold += e->parameters.keynumToHold * (60.0f - midiNoteNumber);
859 | e->parameters.hold = (e->parameters.hold < -10000.0f ? 0.0f : tsf_timecents2Secsf(e->parameters.hold));
860 | }
861 | if (e->parameters.keynumToDecay)
862 | {
863 | e->parameters.decay += e->parameters.keynumToDecay * (60.0f - midiNoteNumber);
864 | e->parameters.decay = (e->parameters.decay < -10000.0f ? 0.0f : tsf_timecents2Secsf(e->parameters.decay));
865 | }
866 | e->midiVelocity = midiVelocity;
867 | e->isAmpEnv = isAmpEnv;
868 | tsf_voice_envelope_nextsegment(e, TSF_SEGMENT_NONE, outSampleRate);
869 | }
870 |
871 | static void tsf_voice_envelope_process(struct tsf_voice_envelope* e, int numSamples, float outSampleRate)
872 | {
873 | if (e->slope)
874 | {
875 | if (e->segmentIsExponential) e->level *= TSF_POWF(e->slope, (float)numSamples);
876 | else e->level += (e->slope * numSamples);
877 | }
878 | if ((e->samplesUntilNextSegment -= numSamples) <= 0)
879 | tsf_voice_envelope_nextsegment(e, e->segment, outSampleRate);
880 | }
881 |
882 | static void tsf_voice_lowpass_setup(struct tsf_voice_lowpass* e, float Fc)
883 | {
884 | // Lowpass filter from http://www.earlevel.com/main/2012/11/26/biquad-c-source-code/
885 | double K = TSF_TAN(TSF_PI * Fc), KK = K * K;
886 | double norm = 1 / (1 + K * e->QInv + KK);
887 | e->a0 = KK * norm;
888 | e->a1 = 2 * e->a0;
889 | e->b1 = 2 * (KK - 1) * norm;
890 | e->b2 = (1 - K * e->QInv + KK) * norm;
891 | }
892 |
893 | static float tsf_voice_lowpass_process(struct tsf_voice_lowpass* e, double In)
894 | {
895 | double Out = In * e->a0 + e->z1; e->z1 = In * e->a1 + e->z2 - e->b1 * Out; e->z2 = In * e->a0 - e->b2 * Out; return (float)Out;
896 | }
897 |
898 | static void tsf_voice_lfo_setup(struct tsf_voice_lfo* e, float delay, int freqCents, float outSampleRate)
899 | {
900 | e->samplesUntil = (int)(delay * outSampleRate);
901 | e->delta = (4.0f * tsf_cents2Hertz((float)freqCents) / outSampleRate);
902 | e->level = 0;
903 | }
904 |
905 | static void tsf_voice_lfo_process(struct tsf_voice_lfo* e, int blockSamples)
906 | {
907 | if (e->samplesUntil > blockSamples) { e->samplesUntil -= blockSamples; return; }
908 | e->level += e->delta * blockSamples;
909 | if (e->level > 1.0f) { e->delta = -e->delta; e->level = 2.0f - e->level; }
910 | else if (e->level < -1.0f) { e->delta = -e->delta; e->level = -2.0f - e->level; }
911 | }
912 |
913 | static void tsf_voice_kill(struct tsf_voice* v)
914 | {
915 | v->region = TSF_NULL;
916 | v->playingPreset = -1;
917 | }
918 |
919 | static void tsf_voice_end(struct tsf_voice* v, float outSampleRate)
920 | {
921 | tsf_voice_envelope_nextsegment(&v->ampenv, TSF_SEGMENT_SUSTAIN, outSampleRate);
922 | tsf_voice_envelope_nextsegment(&v->modenv, TSF_SEGMENT_SUSTAIN, outSampleRate);
923 | if (v->region->loop_mode == TSF_LOOPMODE_SUSTAIN)
924 | {
925 | // Continue playing, but stop looping.
926 | v->loopEnd = v->loopStart;
927 | }
928 | }
929 |
930 | static void tsf_voice_endquick(struct tsf_voice* v, float outSampleRate)
931 | {
932 | v->ampenv.parameters.release = 0.0f; tsf_voice_envelope_nextsegment(&v->ampenv, TSF_SEGMENT_SUSTAIN, outSampleRate);
933 | v->modenv.parameters.release = 0.0f; tsf_voice_envelope_nextsegment(&v->modenv, TSF_SEGMENT_SUSTAIN, outSampleRate);
934 | }
935 |
936 | static void tsf_voice_calcpitchratio(struct tsf_voice* v, float pitchShift, float outSampleRate)
937 | {
938 | double note = v->playingKey + v->region->transpose + v->region->tune / 100.0;
939 | double adjustedPitch = v->region->pitch_keycenter + (note - v->region->pitch_keycenter) * (v->region->pitch_keytrack / 100.0);
940 | if (pitchShift) adjustedPitch += pitchShift;
941 | v->pitchInputTimecents = adjustedPitch * 100.0;
942 | v->pitchOutputFactor = v->region->sample_rate / (tsf_timecents2Secsd(v->region->pitch_keycenter * 100.0) * outSampleRate);
943 | }
944 |
945 | static void tsf_voice_render(tsf* f, struct tsf_voice* v, float* outputBuffer, int numSamples)
946 | {
947 | struct tsf_region* region = v->region;
948 | float* input = f->fontSamples;
949 | float* outL = outputBuffer;
950 | float* outR = (f->outputmode == TSF_STEREO_UNWEAVED ? outL + numSamples : TSF_NULL);
951 |
952 | // Cache some values, to give them at least some chance of ending up in registers.
953 | TSF_BOOL updateModEnv = (region->modEnvToPitch || region->modEnvToFilterFc);
954 | TSF_BOOL updateModLFO = (v->modlfo.delta && (region->modLfoToPitch || region->modLfoToFilterFc || region->modLfoToVolume));
955 | TSF_BOOL updateVibLFO = (v->viblfo.delta && (region->vibLfoToPitch));
956 | TSF_BOOL isLooping = (v->loopStart < v->loopEnd);
957 | unsigned int tmpLoopStart = v->loopStart, tmpLoopEnd = v->loopEnd;
958 | double tmpSampleEndDbl = (double)region->end, tmpLoopEndDbl = (double)tmpLoopEnd + 1.0;
959 | double tmpSourceSamplePosition = v->sourceSamplePosition;
960 | struct tsf_voice_lowpass tmpLowpass = v->lowpass;
961 |
962 | TSF_BOOL dynamicLowpass = (region->modLfoToFilterFc || region->modEnvToFilterFc);
963 | float tmpSampleRate, tmpInitialFilterFc, tmpModLfoToFilterFc, tmpModEnvToFilterFc;
964 |
965 | TSF_BOOL dynamicPitchRatio = (region->modLfoToPitch || region->modEnvToPitch || region->vibLfoToPitch);
966 | double pitchRatio;
967 | float tmpModLfoToPitch, tmpVibLfoToPitch, tmpModEnvToPitch;
968 |
969 | TSF_BOOL dynamicGain = (region->modLfoToVolume != 0);
970 | float noteGain = 0, tmpModLfoToVolume;
971 |
972 | if (dynamicLowpass) tmpSampleRate = f->outSampleRate, tmpInitialFilterFc = (float)region->initialFilterFc, tmpModLfoToFilterFc = (float)region->modLfoToFilterFc, tmpModEnvToFilterFc = (float)region->modEnvToFilterFc;
973 | else tmpSampleRate = 0, tmpInitialFilterFc = 0, tmpModLfoToFilterFc = 0, tmpModEnvToFilterFc = 0;
974 |
975 | if (dynamicPitchRatio) pitchRatio = 0, tmpModLfoToPitch = (float)region->modLfoToPitch, tmpVibLfoToPitch = (float)region->vibLfoToPitch, tmpModEnvToPitch = (float)region->modEnvToPitch;
976 | else pitchRatio = tsf_timecents2Secsd(v->pitchInputTimecents) * v->pitchOutputFactor, tmpModLfoToPitch = 0, tmpVibLfoToPitch = 0, tmpModEnvToPitch = 0;
977 |
978 | if (dynamicGain) tmpModLfoToVolume = (float)region->modLfoToVolume * 0.1f;
979 | else noteGain = tsf_decibelsToGain(v->noteGainDB), tmpModLfoToVolume = 0;
980 |
981 | while (numSamples)
982 | {
983 | float gainMono, gainLeft, gainRight;
984 | int blockSamples = (numSamples > TSF_RENDER_EFFECTSAMPLEBLOCK ? TSF_RENDER_EFFECTSAMPLEBLOCK : numSamples);
985 | numSamples -= blockSamples;
986 |
987 | if (dynamicLowpass)
988 | {
989 | float fres = tmpInitialFilterFc + v->modlfo.level * tmpModLfoToFilterFc + v->modenv.level * tmpModEnvToFilterFc;
990 | tmpLowpass.active = (fres <= 13500.0f);
991 | if (tmpLowpass.active) tsf_voice_lowpass_setup(&tmpLowpass, tsf_cents2Hertz(fres) / tmpSampleRate);
992 | }
993 |
994 | if (dynamicPitchRatio)
995 | pitchRatio = tsf_timecents2Secsd(v->pitchInputTimecents + (v->modlfo.level * tmpModLfoToPitch + v->viblfo.level * tmpVibLfoToPitch + v->modenv.level * tmpModEnvToPitch)) * v->pitchOutputFactor;
996 |
997 | if (dynamicGain)
998 | noteGain = tsf_decibelsToGain(v->noteGainDB + (v->modlfo.level * tmpModLfoToVolume));
999 |
1000 | gainMono = noteGain * v->ampenv.level;
1001 |
1002 | // Update EG.
1003 | tsf_voice_envelope_process(&v->ampenv, blockSamples, f->outSampleRate);
1004 | if (updateModEnv) tsf_voice_envelope_process(&v->modenv, blockSamples, f->outSampleRate);
1005 |
1006 | // Update LFOs.
1007 | if (updateModLFO) tsf_voice_lfo_process(&v->modlfo, blockSamples);
1008 | if (updateVibLFO) tsf_voice_lfo_process(&v->viblfo, blockSamples);
1009 |
1010 | switch (f->outputmode)
1011 | {
1012 | case TSF_STEREO_INTERLEAVED:
1013 | gainLeft = gainMono * v->panFactorLeft, gainRight = gainMono * v->panFactorRight;
1014 | while (blockSamples-- && tmpSourceSamplePosition < tmpSampleEndDbl)
1015 | {
1016 | unsigned int pos = (unsigned int)tmpSourceSamplePosition, nextPos = (pos >= tmpLoopEnd && isLooping ? tmpLoopStart : pos + 1);
1017 |
1018 | // Simple linear interpolation.
1019 | float alpha = (float)(tmpSourceSamplePosition - pos), val = (input[pos] * (1.0f - alpha) + input[nextPos] * alpha);
1020 |
1021 | // Low-pass filter.
1022 | if (tmpLowpass.active) val = tsf_voice_lowpass_process(&tmpLowpass, val);
1023 |
1024 | *outL++ += val * gainLeft;
1025 | *outL++ += val * gainRight;
1026 |
1027 | // Next sample.
1028 | tmpSourceSamplePosition += pitchRatio;
1029 | if (tmpSourceSamplePosition >= tmpLoopEndDbl && isLooping) tmpSourceSamplePosition -= (tmpLoopEnd - tmpLoopStart + 1.0);
1030 | }
1031 | break;
1032 |
1033 | case TSF_STEREO_UNWEAVED:
1034 | gainLeft = gainMono * v->panFactorLeft, gainRight = gainMono * v->panFactorRight;
1035 | while (blockSamples-- && tmpSourceSamplePosition < tmpSampleEndDbl)
1036 | {
1037 | unsigned int pos = (unsigned int)tmpSourceSamplePosition, nextPos = (pos >= tmpLoopEnd && isLooping ? tmpLoopStart : pos + 1);
1038 |
1039 | // Simple linear interpolation.
1040 | float alpha = (float)(tmpSourceSamplePosition - pos), val = (input[pos] * (1.0f - alpha) + input[nextPos] * alpha);
1041 |
1042 | // Low-pass filter.
1043 | if (tmpLowpass.active) val = tsf_voice_lowpass_process(&tmpLowpass, val);
1044 |
1045 | *outL++ += val * gainLeft;
1046 | *outR++ += val * gainRight;
1047 |
1048 | // Next sample.
1049 | tmpSourceSamplePosition += pitchRatio;
1050 | if (tmpSourceSamplePosition >= tmpLoopEndDbl && isLooping) tmpSourceSamplePosition -= (tmpLoopEnd - tmpLoopStart + 1.0);
1051 | }
1052 | break;
1053 |
1054 | case TSF_MONO:
1055 | while (blockSamples-- && tmpSourceSamplePosition < tmpSampleEndDbl)
1056 | {
1057 | unsigned int pos = (unsigned int)tmpSourceSamplePosition, nextPos = (pos >= tmpLoopEnd && isLooping ? tmpLoopStart : pos + 1);
1058 |
1059 | // Simple linear interpolation.
1060 | float alpha = (float)(tmpSourceSamplePosition - pos), val = (input[pos] * (1.0f - alpha) + input[nextPos] * alpha);
1061 |
1062 | // Low-pass filter.
1063 | if (tmpLowpass.active) val = tsf_voice_lowpass_process(&tmpLowpass, val);
1064 |
1065 | *outL++ += val * gainMono;
1066 |
1067 | // Next sample.
1068 | tmpSourceSamplePosition += pitchRatio;
1069 | if (tmpSourceSamplePosition >= tmpLoopEndDbl && isLooping) tmpSourceSamplePosition -= (tmpLoopEnd - tmpLoopStart + 1.0);
1070 | }
1071 | break;
1072 | }
1073 |
1074 | if (tmpSourceSamplePosition >= tmpSampleEndDbl || v->ampenv.segment == TSF_SEGMENT_DONE)
1075 | {
1076 | tsf_voice_kill(v);
1077 | return;
1078 | }
1079 | }
1080 |
1081 | v->sourceSamplePosition = tmpSourceSamplePosition;
1082 | if (tmpLowpass.active || dynamicLowpass) v->lowpass = tmpLowpass;
1083 | }
1084 |
1085 | TSFDEF tsf* tsf_load(struct tsf_stream* stream)
1086 | {
1087 | tsf* res = TSF_NULL;
1088 | struct tsf_riffchunk chunkHead;
1089 | struct tsf_riffchunk chunkList;
1090 | struct tsf_hydra hydra;
1091 | float* fontSamples = TSF_NULL;
1092 | unsigned int fontSampleCount;
1093 |
1094 | if (!tsf_riffchunk_read(TSF_NULL, &chunkHead, stream) || !TSF_FourCCEquals(chunkHead.id, "sfbk"))
1095 | {
1096 | //if (e) *e = TSF_INVALID_NOSF2HEADER;
1097 | return res;
1098 | }
1099 |
1100 | // Read hydra and locate sample data.
1101 | TSF_MEMSET(&hydra, 0, sizeof(hydra));
1102 | while (tsf_riffchunk_read(&chunkHead, &chunkList, stream))
1103 | {
1104 | struct tsf_riffchunk chunk;
1105 | if (TSF_FourCCEquals(chunkList.id, "pdta"))
1106 | {
1107 | while (tsf_riffchunk_read(&chunkList, &chunk, stream))
1108 | {
1109 | #define HandleChunk(chunkName) (TSF_FourCCEquals(chunk.id, #chunkName) && !(chunk.size % chunkName##SizeInFile)) \
1110 | { \
1111 | int num = chunk.size / chunkName##SizeInFile, i; \
1112 | hydra.chunkName##Num = num; \
1113 | hydra.chunkName##s = (struct tsf_hydra_##chunkName*)TSF_MALLOC(num * sizeof(struct tsf_hydra_##chunkName)); \
1114 | for (i = 0; i < num; ++i) tsf_hydra_read_##chunkName(&hydra.chunkName##s[i], stream); \
1115 | }
1116 | enum
1117 | {
1118 | phdrSizeInFile = 38, pbagSizeInFile = 4, pmodSizeInFile = 10,
1119 | pgenSizeInFile = 4, instSizeInFile = 22, ibagSizeInFile = 4,
1120 | imodSizeInFile = 10, igenSizeInFile = 4, shdrSizeInFile = 46
1121 | };
1122 | if HandleChunk(phdr) else if HandleChunk(pbag) else if HandleChunk(pmod)
1123 | else if HandleChunk(pgen) else if HandleChunk(inst) else if HandleChunk(ibag)
1124 | else if HandleChunk(imod) else if HandleChunk(igen) else if HandleChunk(shdr)
1125 | else stream->skip(stream->data, chunk.size);
1126 | #undef HandleChunk
1127 | }
1128 | }
1129 | else if (TSF_FourCCEquals(chunkList.id, "sdta"))
1130 | {
1131 | while (tsf_riffchunk_read(&chunkList, &chunk, stream))
1132 | {
1133 | if (TSF_FourCCEquals(chunk.id, "smpl"))
1134 | {
1135 | tsf_load_samples(&fontSamples, &fontSampleCount, &chunk, stream);
1136 | }
1137 | else stream->skip(stream->data, chunk.size);
1138 | }
1139 | }
1140 | else stream->skip(stream->data, chunkList.size);
1141 | }
1142 | if (!hydra.phdrs || !hydra.pbags || !hydra.pmods || !hydra.pgens || !hydra.insts || !hydra.ibags || !hydra.imods || !hydra.igens || !hydra.shdrs)
1143 | {
1144 | //if (e) *e = TSF_INVALID_INCOMPLETE;
1145 | }
1146 | else if (fontSamples == TSF_NULL)
1147 | {
1148 | //if (e) *e = TSF_INVALID_NOSAMPLEDATA;
1149 | }
1150 | else
1151 | {
1152 | res = (tsf*)TSF_MALLOC(sizeof(tsf));
1153 | TSF_MEMSET(res, 0, sizeof(tsf));
1154 | res->presetNum = hydra.phdrNum - 1;
1155 | res->presets = (struct tsf_preset*)TSF_MALLOC(res->presetNum * sizeof(struct tsf_preset));
1156 | res->fontSamples = fontSamples;
1157 | res->outSampleRate = 44100.0f;
1158 | fontSamples = TSF_NULL; //don't free below
1159 | tsf_load_presets(res, &hydra, fontSampleCount);
1160 | }
1161 | TSF_FREE(hydra.phdrs); TSF_FREE(hydra.pbags); TSF_FREE(hydra.pmods);
1162 | TSF_FREE(hydra.pgens); TSF_FREE(hydra.insts); TSF_FREE(hydra.ibags);
1163 | TSF_FREE(hydra.imods); TSF_FREE(hydra.igens); TSF_FREE(hydra.shdrs);
1164 | TSF_FREE(fontSamples);
1165 | return res;
1166 | }
1167 |
1168 | TSFDEF void tsf_close(tsf* f)
1169 | {
1170 | struct tsf_preset *preset, *presetEnd;
1171 | if (!f) return;
1172 | for (preset = f->presets, presetEnd = preset + f->presetNum; preset != presetEnd; preset++)
1173 | TSF_FREE(preset->regions);
1174 | TSF_FREE(f->presets);
1175 | TSF_FREE(f->fontSamples);
1176 | TSF_FREE(f->voices);
1177 | if (f->channels) { TSF_FREE(f->channels->channels); TSF_FREE(f->channels); }
1178 | TSF_FREE(f->outputSamples);
1179 | TSF_FREE(f);
1180 | }
1181 |
1182 | TSFDEF void tsf_reset(tsf* f)
1183 | {
1184 | struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum;
1185 | for (; v != vEnd; v++)
1186 | if (v->playingPreset != -1 && (v->ampenv.segment < TSF_SEGMENT_RELEASE || v->ampenv.parameters.release))
1187 | tsf_voice_endquick(v, f->outSampleRate);
1188 | if (f->channels) { TSF_FREE(f->channels->channels); TSF_FREE(f->channels); f->channels = TSF_NULL; }
1189 | }
1190 |
1191 | TSFDEF int tsf_get_presetindex(const tsf* f, int bank, int preset_number)
1192 | {
1193 | const struct tsf_preset *presets;
1194 | int i, iMax;
1195 | for (presets = f->presets, i = 0, iMax = f->presetNum; i < iMax; i++)
1196 | if (presets[i].preset == preset_number && presets[i].bank == bank)
1197 | return i;
1198 | return -1;
1199 | }
1200 |
1201 | TSFDEF int tsf_get_presetcount(const tsf* f)
1202 | {
1203 | return f->presetNum;
1204 | }
1205 |
1206 | TSFDEF const char* tsf_get_presetname(const tsf* f, int preset)
1207 | {
1208 | return (preset < 0 || preset >= f->presetNum ? TSF_NULL : f->presets[preset].presetName);
1209 | }
1210 |
1211 | TSFDEF const char* tsf_bank_get_presetname(const tsf* f, int bank, int preset_number)
1212 | {
1213 | return tsf_get_presetname(f, tsf_get_presetindex(f, bank, preset_number));
1214 | }
1215 |
1216 | TSFDEF void tsf_set_output(tsf* f, enum TSFOutputMode outputmode, int samplerate, float global_gain_db)
1217 | {
1218 | f->outputmode = outputmode;
1219 | f->outSampleRate = (float)(samplerate >= 1 ? samplerate : 44100.0f);
1220 | f->globalGainDB = global_gain_db;
1221 | }
1222 |
1223 | TSFDEF void tsf_note_on(tsf* f, int preset_index, int key, float vel)
1224 | {
1225 | int midiVelocity = (int)(vel * 127), voicePlayIndex;
1226 | struct tsf_region *region, *regionEnd;
1227 |
1228 | if (preset_index < 0 || preset_index >= f->presetNum) return;
1229 | if (vel <= 0.0f) { tsf_note_off(f, preset_index, key); return; }
1230 |
1231 | // Play all matching regions.
1232 | voicePlayIndex = f->voicePlayIndex++;
1233 | for (region = f->presets[preset_index].regions, regionEnd = region + f->presets[preset_index].regionNum; region != regionEnd; region++)
1234 | {
1235 | struct tsf_voice *voice, *v, *vEnd; TSF_BOOL doLoop; float filterQDB;
1236 | if (key < region->lokey || key > region->hikey || midiVelocity < region->lovel || midiVelocity > region->hivel) continue;
1237 |
1238 | voice = TSF_NULL, v = f->voices, vEnd = v + f->voiceNum;
1239 | if (region->group)
1240 | {
1241 | for (; v != vEnd; v++)
1242 | if (v->playingPreset == preset_index && v->region->group == region->group) tsf_voice_endquick(v, f->outSampleRate);
1243 | else if (v->playingPreset == -1 && !voice) voice = v;
1244 | }
1245 | else for (; v != vEnd; v++) if (v->playingPreset == -1) { voice = v; break; }
1246 |
1247 | if (!voice)
1248 | {
1249 | f->voiceNum += 4;
1250 | f->voices = (struct tsf_voice*)TSF_REALLOC(f->voices, f->voiceNum * sizeof(struct tsf_voice));
1251 | voice = &f->voices[f->voiceNum - 4];
1252 | voice[1].playingPreset = voice[2].playingPreset = voice[3].playingPreset = -1;
1253 | }
1254 |
1255 | voice->region = region;
1256 | voice->playingPreset = preset_index;
1257 | voice->playingKey = key;
1258 | voice->playIndex = voicePlayIndex;
1259 | voice->noteGainDB = f->globalGainDB - region->volume - tsf_gainToDecibels(1.0f / vel);
1260 |
1261 | if (f->channels)
1262 | {
1263 | f->channels->setupVoice(f, voice);
1264 | }
1265 | else
1266 | {
1267 | tsf_voice_calcpitchratio(voice, 0, f->outSampleRate);
1268 | // The SFZ spec is silent about the pan curve, but a 3dB pan law seems common. This sqrt() curve matches what Dimension LE does; Alchemy Free seems closer to sin(adjustedPan * pi/2).
1269 | voice->panFactorLeft = TSF_SQRTF(0.5f - region->pan);
1270 | voice->panFactorRight = TSF_SQRTF(0.5f + region->pan);
1271 | }
1272 |
1273 | // Offset/end.
1274 | voice->sourceSamplePosition = region->offset;
1275 |
1276 | // Loop.
1277 | doLoop = (region->loop_mode != TSF_LOOPMODE_NONE && region->loop_start < region->loop_end);
1278 | voice->loopStart = (doLoop ? region->loop_start : 0);
1279 | voice->loopEnd = (doLoop ? region->loop_end : 0);
1280 |
1281 | // Setup envelopes.
1282 | tsf_voice_envelope_setup(&voice->ampenv, ®ion->ampenv, key, midiVelocity, TSF_TRUE, f->outSampleRate);
1283 | tsf_voice_envelope_setup(&voice->modenv, ®ion->modenv, key, midiVelocity, TSF_FALSE, f->outSampleRate);
1284 |
1285 | // Setup lowpass filter.
1286 | filterQDB = region->initialFilterQ / 10.0f;
1287 | voice->lowpass.QInv = 1.0 / TSF_POW(10.0, (filterQDB / 20.0));
1288 | voice->lowpass.z1 = voice->lowpass.z2 = 0;
1289 | voice->lowpass.active = (region->initialFilterFc <= 13500);
1290 | if (voice->lowpass.active) tsf_voice_lowpass_setup(&voice->lowpass, tsf_cents2Hertz((float)region->initialFilterFc) / f->outSampleRate);
1291 |
1292 | // Setup LFO filters.
1293 | tsf_voice_lfo_setup(&voice->modlfo, region->delayModLFO, region->freqModLFO, f->outSampleRate);
1294 | tsf_voice_lfo_setup(&voice->viblfo, region->delayVibLFO, region->freqVibLFO, f->outSampleRate);
1295 | }
1296 | }
1297 |
1298 | TSFDEF int tsf_bank_note_on(tsf* f, int bank, int preset_number, int key, float vel)
1299 | {
1300 | int preset_index = tsf_get_presetindex(f, bank, preset_number);
1301 | if (preset_index == -1) return 0;
1302 | tsf_note_on(f, preset_index, key, vel);
1303 | return 1;
1304 | }
1305 |
1306 | TSFDEF void tsf_note_off(tsf* f, int preset_index, int key)
1307 | {
1308 | struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum, *vMatchFirst = TSF_NULL, *vMatchLast;
1309 | for (; v != vEnd; v++)
1310 | {
1311 | //Find the first and last entry in the voices list with matching preset, key and look up the smallest play index
1312 | if (v->playingPreset != preset_index || v->playingKey != key || v->ampenv.segment >= TSF_SEGMENT_RELEASE) continue;
1313 | else if (!vMatchFirst || v->playIndex < vMatchFirst->playIndex) vMatchFirst = vMatchLast = v;
1314 | else if (v->playIndex == vMatchFirst->playIndex) vMatchLast = v;
1315 | }
1316 | if (!vMatchFirst) return;
1317 | for (v = vMatchFirst; v <= vMatchLast; v++)
1318 | {
1319 | //Stop all voices with matching preset, key and the smallest play index which was enumerated above
1320 | if (v != vMatchFirst && v != vMatchLast &&
1321 | (v->playIndex != vMatchFirst->playIndex || v->playingPreset != preset_index || v->playingKey != key || v->ampenv.segment >= TSF_SEGMENT_RELEASE)) continue;
1322 | tsf_voice_end(v, f->outSampleRate);
1323 | }
1324 | }
1325 |
1326 | TSFDEF int tsf_bank_note_off(tsf* f, int bank, int preset_number, int key)
1327 | {
1328 | int preset_index = tsf_get_presetindex(f, bank, preset_number);
1329 | if (preset_index == -1) return 0;
1330 | tsf_note_off(f, preset_index, key);
1331 | return 1;
1332 | }
1333 |
1334 | TSFDEF void tsf_note_off_all(tsf* f)
1335 | {
1336 | struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum;
1337 | for (; v != vEnd; v++) if (v->playingPreset != -1 && v->ampenv.segment < TSF_SEGMENT_RELEASE)
1338 | tsf_voice_end(v, f->outSampleRate);
1339 | }
1340 |
1341 | TSFDEF void tsf_render_short(tsf* f, short* buffer, int samples, int flag_mixing)
1342 | {
1343 | float *floatSamples;
1344 | int channelSamples = (f->outputmode == TSF_MONO ? 1 : 2) * samples, floatBufferSize = channelSamples * sizeof(float);
1345 | short* bufferEnd = buffer + channelSamples;
1346 | if (floatBufferSize > f->outputSampleSize)
1347 | {
1348 | TSF_FREE(f->outputSamples);
1349 | f->outputSamples = (float*)TSF_MALLOC(floatBufferSize);
1350 | f->outputSampleSize = floatBufferSize;
1351 | }
1352 |
1353 | tsf_render_float(f, f->outputSamples, samples, TSF_FALSE);
1354 |
1355 | floatSamples = f->outputSamples;
1356 | if (flag_mixing)
1357 | while (buffer != bufferEnd)
1358 | {
1359 | float v = *floatSamples++;
1360 | int vi = *buffer + (v < -1.00004566f ? (int)-32768 : (v > 1.00001514f ? (int)32767 : (int)(v * 32767.5f)));
1361 | *buffer++ = (vi < -32768 ? (short)-32768 : (vi > 32767 ? (short)32767 : (short)vi));
1362 | }
1363 | else
1364 | while (buffer != bufferEnd)
1365 | {
1366 | float v = *floatSamples++;
1367 | *buffer++ = (v < -1.00004566f ? (short)-32768 : (v > 1.00001514f ? (short)32767 : (short)(v * 32767.5f)));
1368 | }
1369 | }
1370 |
1371 | TSFDEF void tsf_render_float(tsf* f, float* buffer, int samples, int flag_mixing)
1372 | {
1373 | struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum;
1374 | if (!flag_mixing) TSF_MEMSET(buffer, 0, (f->outputmode == TSF_MONO ? 1 : 2) * sizeof(float) * samples);
1375 | for (; v != vEnd; v++)
1376 | if (v->playingPreset != -1)
1377 | tsf_voice_render(f, v, buffer, samples);
1378 | }
1379 |
1380 | static void tsf_channel_setup_voice(tsf* f, struct tsf_voice* v)
1381 | {
1382 | struct tsf_channel* c = &f->channels->channels[f->channels->activeChannel];
1383 | float newpan = v->region->pan + c->panOffset;
1384 | v->playingChannel = f->channels->activeChannel;
1385 | v->noteGainDB += c->gainDB;
1386 | tsf_voice_calcpitchratio(v, (c->pitchWheel == 8192 ? c->tuning : ((c->pitchWheel / 16383.0f * c->pitchRange * 2.0f) - c->pitchRange + c->tuning)), f->outSampleRate);
1387 | if (newpan <= -0.5f) { v->panFactorLeft = 1.0f; v->panFactorRight = 0.0f; }
1388 | else if (newpan >= 0.5f) { v->panFactorLeft = 0.0f; v->panFactorRight = 1.0f; }
1389 | else { v->panFactorLeft = TSF_SQRTF(0.5f - newpan); v->panFactorRight = TSF_SQRTF(0.5f + newpan); }
1390 | }
1391 |
1392 | static struct tsf_channel* tsf_channel_init(tsf* f, int channel)
1393 | {
1394 | int i;
1395 | if (f->channels && channel < f->channels->channelNum) return &f->channels->channels[channel];
1396 | if (!f->channels)
1397 | {
1398 | f->channels = (struct tsf_channels*)TSF_MALLOC(sizeof(struct tsf_channels));
1399 | f->channels->setupVoice = &tsf_channel_setup_voice;
1400 | f->channels->channels = NULL;
1401 | f->channels->channelNum = 0;
1402 | f->channels->activeChannel = 0;
1403 | }
1404 | i = f->channels->channelNum;
1405 | f->channels->channelNum = channel + 1;
1406 | f->channels->channels = (struct tsf_channel*)TSF_REALLOC(f->channels->channels, f->channels->channelNum * sizeof(struct tsf_channel));
1407 | for (; i <= channel; i++)
1408 | {
1409 | struct tsf_channel* c = &f->channels->channels[i];
1410 | c->presetIndex = c->bank = 0;
1411 | c->pitchWheel = c->midiPan = 8192;
1412 | c->midiVolume = c->midiExpression = 16383;
1413 | c->midiRPN = 0xFFFF;
1414 | c->midiData = 0;
1415 | c->panOffset = 0.0f;
1416 | c->gainDB = 0.0f;
1417 | c->pitchRange = 2.0f;
1418 | c->tuning = 0.0f;
1419 | }
1420 | return &f->channels->channels[channel];
1421 | }
1422 |
1423 | static void tsf_channel_applypitch(tsf* f, int channel, struct tsf_channel* c)
1424 | {
1425 | struct tsf_voice *v, *vEnd;
1426 | float pitchShift = (c->pitchWheel == 8192 ? c->tuning : ((c->pitchWheel / 16383.0f * c->pitchRange * 2.0f) - c->pitchRange + c->tuning));
1427 | for (v = f->voices, vEnd = v + f->voiceNum; v != vEnd; v++)
1428 | if (v->playingChannel == channel && v->playingPreset != -1)
1429 | tsf_voice_calcpitchratio(v, pitchShift, f->outSampleRate);
1430 | }
1431 |
1432 | TSFDEF void tsf_channel_set_presetindex(tsf* f, int channel, int preset_index)
1433 | {
1434 | tsf_channel_init(f, channel)->presetIndex = (unsigned short)preset_index;
1435 | }
1436 |
1437 | TSFDEF int tsf_channel_set_presetnumber(tsf* f, int channel, int preset_number, int flag_mididrums)
1438 | {
1439 | struct tsf_channel *c = tsf_channel_init(f, channel);
1440 | int preset_index;
1441 | if (flag_mididrums)
1442 | {
1443 | preset_index = tsf_get_presetindex(f, 128 | (c->bank & 0x7FFF), preset_number);
1444 | if (preset_index == -1) preset_index = tsf_get_presetindex(f, 128, preset_number);
1445 | if (preset_index == -1) preset_index = tsf_get_presetindex(f, 128, 0);
1446 | if (preset_index == -1) preset_index = tsf_get_presetindex(f, (c->bank & 0x7FFF), preset_number);
1447 | }
1448 | else preset_index = tsf_get_presetindex(f, (c->bank & 0x7FFF), preset_number);
1449 | if (preset_index == -1) preset_index = tsf_get_presetindex(f, 0, preset_number);
1450 | if (preset_index != -1) { c->presetIndex = preset_index; return 1; }
1451 | return 0;
1452 | }
1453 |
1454 | TSFDEF void tsf_channel_set_bank(tsf* f, int channel, int bank)
1455 | {
1456 | tsf_channel_init(f, channel)->bank = bank;
1457 | }
1458 |
1459 | TSFDEF int tsf_channel_set_bank_preset(tsf* f, int channel, int bank, int preset_number)
1460 | {
1461 | struct tsf_channel *c = tsf_channel_init(f, channel);
1462 | int preset_index = tsf_get_presetindex(f, bank, preset_number);
1463 | if (preset_index == -1) return 0;
1464 | c->presetIndex = preset_index;
1465 | c->bank = bank;
1466 | return 1;
1467 | }
1468 | TSFDEF void tsf_channel_set_pitchwheel(tsf* f, int channel, int pitch_wheel)
1469 | {
1470 | struct tsf_channel *c = tsf_channel_init(f, channel);
1471 | if (c->pitchWheel == pitch_wheel) return;
1472 | c->pitchWheel = pitch_wheel;
1473 | tsf_channel_applypitch(f, channel, c);
1474 | }
1475 |
1476 | TSFDEF void tsf_channel_set_pitchrange(tsf* f, int channel, float pitch_range)
1477 | {
1478 | struct tsf_channel *c = tsf_channel_init(f, channel);
1479 | if (c->pitchRange == pitch_range) return;
1480 | c->pitchRange = pitch_range;
1481 | if (c->pitchWheel != 8192) tsf_channel_applypitch(f, channel, c);
1482 | }
1483 |
1484 | TSFDEF void tsf_channel_set_tuning(tsf* f, int channel, float tuning)
1485 | {
1486 | struct tsf_channel *c = tsf_channel_init(f, channel);
1487 | if (c->tuning == tuning) return;
1488 | c->tuning = tuning;
1489 | tsf_channel_applypitch(f, channel, c);
1490 | }
1491 |
1492 | TSFDEF void tsf_channel_set_pan(tsf* f, int channel, float pan)
1493 | {
1494 | struct tsf_voice *v, *vEnd;
1495 | for (v = f->voices, vEnd = v + f->voiceNum; v != vEnd; v++)
1496 | if (v->playingChannel == channel && v->playingPreset != -1)
1497 | {
1498 | float newpan = v->region->pan + pan - 0.5f;
1499 | if (newpan <= -0.5f) { v->panFactorLeft = 1.0f; v->panFactorRight = 0.0f; }
1500 | else if (newpan >= 0.5f) { v->panFactorLeft = 0.0f; v->panFactorRight = 1.0f; }
1501 | else { v->panFactorLeft = TSF_SQRTF(0.5f - newpan); v->panFactorRight = TSF_SQRTF(0.5f + newpan); }
1502 | }
1503 | tsf_channel_init(f, channel)->panOffset = pan - 0.5f;
1504 | }
1505 |
1506 | TSFDEF void tsf_channel_set_volume(tsf* f, int channel, float volume)
1507 | {
1508 | struct tsf_channel *c = tsf_channel_init(f, channel);
1509 | float gainDB = tsf_gainToDecibels(volume), gainDBChange = gainDB - c->gainDB;
1510 | struct tsf_voice *v, *vEnd;
1511 | if (gainDBChange == 0) return;
1512 | for (v = f->voices, vEnd = v + f->voiceNum; v != vEnd; v++)
1513 | if (v->playingChannel == channel && v->playingPreset != -1)
1514 | v->noteGainDB += gainDBChange;
1515 | c->gainDB = gainDB;
1516 | }
1517 |
1518 | TSFDEF void tsf_channel_note_on(tsf* f, int channel, int key, float vel)
1519 | {
1520 | if (!f->channels || channel >= f->channels->channelNum) return;
1521 | f->channels->activeChannel = channel;
1522 | tsf_note_on(f, f->channels->channels[channel].presetIndex, key, vel);
1523 | }
1524 |
1525 | TSFDEF void tsf_channel_note_off(tsf* f, int channel, int key)
1526 | {
1527 | struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum, *vMatchFirst = TSF_NULL, *vMatchLast;
1528 | for (; v != vEnd; v++)
1529 | {
1530 | //Find the first and last entry in the voices list with matching channel, key and look up the smallest play index
1531 | if (v->playingPreset == -1 || v->playingChannel != channel || v->playingKey != key || v->ampenv.segment >= TSF_SEGMENT_RELEASE) continue;
1532 | else if (!vMatchFirst || v->playIndex < vMatchFirst->playIndex) vMatchFirst = vMatchLast = v;
1533 | else if (v->playIndex == vMatchFirst->playIndex) vMatchLast = v;
1534 | }
1535 | if (!vMatchFirst) return;
1536 | for (v = vMatchFirst; v <= vMatchLast; v++)
1537 | {
1538 | //Stop all voices with matching channel, key and the smallest play index which was enumerated above
1539 | if (v != vMatchFirst && v != vMatchLast &&
1540 | (v->playIndex != vMatchFirst->playIndex || v->playingPreset == -1 || v->playingChannel != channel || v->playingKey != key || v->ampenv.segment >= TSF_SEGMENT_RELEASE)) continue;
1541 | tsf_voice_end(v, f->outSampleRate);
1542 | }
1543 | }
1544 |
1545 | TSFDEF void tsf_channel_note_off_all(tsf* f, int channel)
1546 | {
1547 | struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum;
1548 | for (; v != vEnd; v++)
1549 | if (v->playingPreset != -1 && v->playingChannel == channel && v->ampenv.segment < TSF_SEGMENT_RELEASE)
1550 | tsf_voice_end(v, f->outSampleRate);
1551 | }
1552 |
1553 | TSFDEF void tsf_channel_sounds_off_all(tsf* f, int channel)
1554 | {
1555 | struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum;
1556 | for (; v != vEnd; v++)
1557 | if (v->playingPreset != -1 && v->playingChannel == channel && (v->ampenv.segment < TSF_SEGMENT_RELEASE || v->ampenv.parameters.release))
1558 | tsf_voice_endquick(v, f->outSampleRate);
1559 | }
1560 |
1561 | TSFDEF void tsf_channel_midi_control(tsf* f, int channel, int controller, int control_value)
1562 | {
1563 | struct tsf_channel* c = tsf_channel_init(f, channel);
1564 | switch (controller)
1565 | {
1566 | case 7 /*VOLUME_MSB*/: c->midiVolume = (c->midiVolume & 0x7F) | (control_value << 7); goto TCMC_SET_VOLUME;
1567 | case 39 /*VOLUME_LSB*/: c->midiVolume = (c->midiVolume & 0x3F80) | control_value; goto TCMC_SET_VOLUME;
1568 | case 11 /*EXPRESSION_MSB*/: c->midiExpression = (c->midiExpression & 0x7F) | (control_value << 7); goto TCMC_SET_VOLUME;
1569 | case 43 /*EXPRESSION_LSB*/: c->midiExpression = (c->midiExpression & 0x3F80) | control_value; goto TCMC_SET_VOLUME;
1570 | case 10 /*PAN_MSB*/: c->midiPan = (c->midiPan & 0x7F) | (control_value << 7); goto TCMC_SET_PAN;
1571 | case 42 /*PAN_LSB*/: c->midiPan = (c->midiPan & 0x3F80) | control_value; goto TCMC_SET_PAN;
1572 | case 6 /*DATA_ENTRY_MSB*/: c->midiData = (c->midiData & 0x7F) | (control_value << 7); goto TCMC_SET_DATA;
1573 | case 38 /*DATA_ENTRY_LSB*/: c->midiData = (c->midiData & 0x3F80) | control_value; goto TCMC_SET_DATA;
1574 | case 0 /*BANK_SELECT_MSB*/: c->bank = 0x8000 | control_value; return; //bank select MSB alone acts like LSB
1575 | case 32 /*BANK_SELECT_LSB*/: c->bank = (c->bank & 0x8000 ? ((c->bank & 0x7F) << 7) : 0) | control_value; return;
1576 | case 101 /*RPN_MSB*/: c->midiRPN = ((c->midiRPN == 0xFFFF ? 0 : c->midiRPN) & 0x7F) | (control_value << 7); return;
1577 | case 100 /*RPN_LSB*/: c->midiRPN = ((c->midiRPN == 0xFFFF ? 0 : c->midiRPN) & 0x3F80) | control_value; return;
1578 | case 98 /*NRPN_LSB*/: c->midiRPN = 0xFFFF; return;
1579 | case 99 /*NRPN_MSB*/: c->midiRPN = 0xFFFF; return;
1580 | case 120 /*ALL_SOUND_OFF*/: tsf_channel_sounds_off_all(f, channel); return;
1581 | case 123 /*ALL_NOTES_OFF*/: tsf_channel_note_off_all(f, channel); return;
1582 | case 121 /*ALL_CTRL_OFF*/:
1583 | c->midiVolume = c->midiExpression = 16383;
1584 | c->midiPan = 8192;
1585 | c->bank = 0;
1586 | tsf_channel_set_volume(f, channel, 1.0f);
1587 | tsf_channel_set_pan(f, channel, 0.5f);
1588 | tsf_channel_set_pitchrange(f, channel, 2.0f);
1589 | return;
1590 | }
1591 | return;
1592 | TCMC_SET_VOLUME:
1593 | //Raising to the power of 3 seems to result in a decent sounding volume curve for MIDI
1594 | tsf_channel_set_volume(f, channel, TSF_POWF((c->midiVolume / 16383.0f) * (c->midiExpression / 16383.0f), 3.0f));
1595 | return;
1596 | TCMC_SET_PAN:
1597 | tsf_channel_set_pan(f, channel, c->midiPan / 16383.0f);
1598 | return;
1599 | TCMC_SET_DATA:
1600 | if (c->midiRPN == 0) tsf_channel_set_pitchrange(f, channel, (c->midiData >> 8) + 0.01f * (c->midiData & 0x7F));
1601 | else if (c->midiRPN == 1) tsf_channel_set_tuning(f, channel, (int)c->tuning + ((float)c->midiData - 8192.0f) / 8192.0f); //fine tune
1602 | else if (c->midiRPN == 2 && controller == 6) tsf_channel_set_tuning(f, channel, ((float)control_value - 64.0f) + (c->tuning - (int)c->tuning)); //coarse tune
1603 | return;
1604 | }
1605 |
1606 | TSFDEF int tsf_channel_get_preset_index(tsf* f, int channel)
1607 | {
1608 | return (f->channels && channel < f->channels->channelNum ? f->channels->channels[channel].presetIndex : 0);
1609 | }
1610 |
1611 | TSFDEF int tsf_channel_get_preset_bank(tsf* f, int channel)
1612 | {
1613 | return (f->channels && channel < f->channels->channelNum ? (f->channels->channels[channel].bank & 0x7FFF) : 0);
1614 | }
1615 |
1616 | TSFDEF int tsf_channel_get_preset_number(tsf* f, int channel)
1617 | {
1618 | return (f->channels && channel < f->channels->channelNum ? f->presets[f->channels->channels[channel].presetIndex].preset : 0);
1619 | }
1620 |
1621 | TSFDEF int tsf_channel_get_pitchwheel(tsf* f, int channel)
1622 | {
1623 | return (f->channels && channel < f->channels->channelNum ? f->channels->channels[channel].pitchWheel : 8192);
1624 | }
1625 |
1626 | TSFDEF float tsf_channel_get_pan(tsf* f, int channel)
1627 | {
1628 | return (f->channels && channel < f->channels->channelNum ? f->channels->channels[channel].panOffset - 0.5f : 0.5f);
1629 | }
1630 |
1631 | TSFDEF float tsf_channel_get_volume(tsf* f, int channel)
1632 | {
1633 | return (f->channels && channel < f->channels->channelNum ? tsf_decibelsToGain(f->channels->channels[channel].gainDB) : 1.0f);
1634 | }
1635 |
1636 | #ifdef __cplusplus
1637 | }
1638 | #endif
1639 |
1640 | #endif //TSF_IMPLEMENTATION
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Opensegaapi
2 | Open source Lindbergh audio emulator
3 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | version: 1.0.0.{build}
2 | image: Visual Studio 2017
3 | configuration: Release
4 | # Do not build on tags (GitHub only)
5 | skip_tags: true
6 | platform:
7 | - Win32
8 |
9 | before_build:
10 | - cmd: premake5.exe vs2017
11 | - ps: >-
12 | Get-Content .\Opensegaapi\src\Opensegaapi.rc | ForEach-Object { $_ -replace "1.0.0.0", $env:appveyor_build_version } | Set-Content .\Opensegaapi\src\Opensegaapi2.rc
13 |
14 | del .\Opensegaapi\src\Opensegaapi.rc
15 |
16 | mv .\Opensegaapi\src\Opensegaapi2.rc .\Opensegaapi\src\Opensegaapi.rc
17 |
18 | build:
19 | project: Opensegaapi.sln
20 | verbosity: minimal
21 |
22 | artifacts:
23 | - path: build\bin\release\output\
24 | name: Opensegaapi
25 | deploy:
26 | - provider: GitHub
27 | tag: OpenSegaAPI
28 | release: $(APPVEYOR_BUILD_VERSION)
29 | description: $(APPVEYOR_REPO_COMMIT_MESSAGE)
30 | auth_token:
31 | secure: a2B+6mDTHuBa0fw8nm739eGJIZBcZp0IenhKvvXvreLR6ZUoHg9pflMP8ahNUK6o
32 | repository: teknogods/Opensegaapi
33 | artifact: build\bin\release\Opensegaapi.zip
34 | force_update: true
35 |
--------------------------------------------------------------------------------
/build.ps1:
--------------------------------------------------------------------------------
1 | Get-Content .\Opensegaapi\src\Opensegaapi.rc | ForEach-Object { $_ -replace "1.0.0.0", $APPVEYOR_BUILD_VERSION } | Set-Content .\Opensegaapi\src\Opensegaapi2.rc
2 | del .\Opensegaapi\src\Opensegaapi.rc
3 | mv .\Opensegaapi\src\Opensegaapi2.rc .\Opensegaapi\src\Opensegaapi.rc
--------------------------------------------------------------------------------
/premake5.bat:
--------------------------------------------------------------------------------
1 | premake5 vs2017
2 | pause
--------------------------------------------------------------------------------
/premake5.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/teknogods/OpenSegaAPI/037bc53769dac693b7fd0cda6cd80864753dfcef/premake5.exe
--------------------------------------------------------------------------------
/premake5.lua:
--------------------------------------------------------------------------------
1 | workspace "Opensegaapi"
2 | configurations { "Debug", "Release"}
3 | platforms { "x86" }
4 |
5 | flags { "StaticRuntime", "No64BitChecks" }
6 |
7 | systemversion "10.0.16299.0"
8 |
9 | symbols "On"
10 |
11 | characterset "Unicode"
12 |
13 | flags { "NoIncrementalLink", "NoEditAndContinue", "NoMinimalRebuild" }
14 |
15 | buildoptions { "/MP", "/std:c++17" }
16 |
17 | configuration "Debug*"
18 | targetdir "build/bin/debug"
19 | defines "NDEBUG"
20 | objdir "build/obj/debug"
21 |
22 | configuration "Release*"
23 | targetdir "build/bin/release"
24 | defines "NDEBUG"
25 | optimize "speed"
26 | objdir "build/obj/release"
27 |
28 | filter "platforms:x86"
29 | architecture "x32"
30 |
31 | include "Opensegaapi"
--------------------------------------------------------------------------------