├── .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 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" --------------------------------------------------------------------------------